Copyright 2013 -> 2014
[folly.git] / folly / test / SmallLocksTest.cpp
1 /*
2  * Copyright 2014 Facebook, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include "folly/SmallLocks.h"
18 #include <cassert>
19 #include <cstdio>
20 #include <mutex>
21 #include <string>
22 #include <vector>
23 #include <pthread.h>
24 #include <unistd.h>
25
26 #include <thread>
27
28 #include <gtest/gtest.h>
29
30 using std::string;
31 using folly::MicroSpinLock;
32 using folly::PicoSpinLock;
33 using folly::MSLGuard;
34
35 namespace {
36
37 struct LockedVal {
38   int ar[1024];
39   MicroSpinLock lock;
40
41   LockedVal() {
42     lock.init();
43     memset(ar, 0, sizeof ar);
44   }
45 };
46
47 // Compile time test for packed struct support (requires that both of
48 // these classes are POD).
49 struct ignore1 { MicroSpinLock msl; int16_t foo; } __attribute__((packed));
50 struct ignore2 { PicoSpinLock<uint32_t> psl; int16_t foo; }
51   __attribute__((packed));
52 static_assert(sizeof(ignore1) == 3, "Size check failed");
53 static_assert(sizeof(ignore2) == 6, "Size check failed");
54
55 LockedVal v;
56 void splock_test() {
57
58   const int max = 1000;
59   unsigned int seed = (uintptr_t)pthread_self();
60   for (int i = 0; i < max; i++) {
61     asm("pause");
62     MSLGuard g(v.lock);
63
64     int first = v.ar[0];
65     for (int i = 1; i < sizeof v.ar / sizeof i; ++i) {
66       EXPECT_EQ(first, v.ar[i]);
67     }
68
69     int byte = rand_r(&seed);
70     memset(v.ar, char(byte), sizeof v.ar);
71   }
72 }
73
74 template<class T> struct PslTest {
75   PicoSpinLock<T> lock;
76
77   PslTest() { lock.init(); }
78
79   void doTest() {
80     T ourVal = rand() % (T(1) << (sizeof(T) * 8 - 1));
81     for (int i = 0; i < 10000; ++i) {
82       std::lock_guard<PicoSpinLock<T>> guard(lock);
83       lock.setData(ourVal);
84       for (int n = 0; n < 10; ++n) {
85         asm volatile("pause");
86         EXPECT_EQ(lock.getData(), ourVal);
87       }
88     }
89   }
90 };
91
92 template<class T>
93 void doPslTest() {
94   PslTest<T> testObj;
95
96   const int nthrs = 17;
97   std::vector<std::thread> threads;
98   for (int i = 0; i < nthrs; ++i) {
99     threads.push_back(std::thread(&PslTest<T>::doTest, &testObj));
100   }
101   for (auto& t : threads) {
102     t.join();
103   }
104 }
105
106 struct TestClobber {
107   TestClobber() {
108     lock_.init();
109   }
110
111   void go() {
112     std::lock_guard<MicroSpinLock> g(lock_);
113     // This bug depends on gcc register allocation and is very sensitive. We
114     // have to use DCHECK instead of EXPECT_*.
115     DCHECK(!lock_.try_lock());
116   }
117
118  private:
119   MicroSpinLock lock_;
120 };
121
122 }
123
124 TEST(SmallLocks, SpinLockCorrectness) {
125   EXPECT_EQ(sizeof(MicroSpinLock), 1);
126
127   int nthrs = sysconf(_SC_NPROCESSORS_ONLN) * 2;
128   std::vector<std::thread> threads;
129   for (int i = 0; i < nthrs; ++i) {
130     threads.push_back(std::thread(splock_test));
131   }
132   for (auto& t : threads) {
133     t.join();
134   }
135 }
136
137 TEST(SmallLocks, PicoSpinCorrectness) {
138   doPslTest<int16_t>();
139   doPslTest<uint16_t>();
140   doPslTest<int32_t>();
141   doPslTest<uint32_t>();
142   doPslTest<int64_t>();
143   doPslTest<uint64_t>();
144 }
145
146 TEST(SmallLocks, PicoSpinSigned) {
147   typedef PicoSpinLock<int16_t,0> Lock;
148   Lock val;
149   val.init(-4);
150   EXPECT_EQ(val.getData(), -4);
151
152   {
153     std::lock_guard<Lock> guard(val);
154     EXPECT_EQ(val.getData(), -4);
155     val.setData(-8);
156     EXPECT_EQ(val.getData(), -8);
157   }
158   EXPECT_EQ(val.getData(), -8);
159 }
160
161 TEST(SmallLocks, RegClobber) {
162   TestClobber().go();
163 }