ARM64 assembler fixes for Folly.
[folly.git] / folly / test / SmallLocksTest.cpp
1 /*
2  * Copyright 2015 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 FOLLY_PACK_PUSH
50 struct ignore1 { MicroSpinLock msl; int16_t foo; } FOLLY_PACK_ATTR;
51 struct ignore2 { PicoSpinLock<uint32_t> psl; int16_t foo; } FOLLY_PACK_ATTR;
52 static_assert(sizeof(ignore1) == 3, "Size check failed");
53 static_assert(sizeof(ignore2) == 6, "Size check failed");
54 static_assert(sizeof(MicroSpinLock) == 1, "Size check failed");
55 FOLLY_PACK_POP
56
57 LockedVal v;
58 void splock_test() {
59
60   const int max = 1000;
61   unsigned int seed = (uintptr_t)pthread_self();
62   for (int i = 0; i < max; i++) {
63     folly::asm_pause();
64     MSLGuard g(v.lock);
65
66     int first = v.ar[0];
67     for (size_t i = 1; i < sizeof v.ar / sizeof i; ++i) {
68       EXPECT_EQ(first, v.ar[i]);
69     }
70
71     int byte = rand_r(&seed);
72     memset(v.ar, char(byte), sizeof v.ar);
73   }
74 }
75
76 template<class T> struct PslTest {
77   PicoSpinLock<T> lock;
78
79   PslTest() { lock.init(); }
80
81   void doTest() {
82     T ourVal = rand() % (T(1) << (sizeof(T) * 8 - 1));
83     for (int i = 0; i < 10000; ++i) {
84       std::lock_guard<PicoSpinLock<T>> guard(lock);
85       lock.setData(ourVal);
86       for (int n = 0; n < 10; ++n) {
87         folly::asm_volatile_pause();
88         EXPECT_EQ(lock.getData(), ourVal);
89       }
90     }
91   }
92 };
93
94 template<class T>
95 void doPslTest() {
96   PslTest<T> testObj;
97
98   const int nthrs = 17;
99   std::vector<std::thread> threads;
100   for (int i = 0; i < nthrs; ++i) {
101     threads.push_back(std::thread(&PslTest<T>::doTest, &testObj));
102   }
103   for (auto& t : threads) {
104     t.join();
105   }
106 }
107
108 struct TestClobber {
109   TestClobber() {
110     lock_.init();
111   }
112
113   void go() {
114     std::lock_guard<MicroSpinLock> g(lock_);
115     // This bug depends on gcc register allocation and is very sensitive. We
116     // have to use DCHECK instead of EXPECT_*.
117     DCHECK(!lock_.try_lock());
118   }
119
120  private:
121   MicroSpinLock lock_;
122 };
123
124 }
125
126 TEST(SmallLocks, SpinLockCorrectness) {
127   EXPECT_EQ(sizeof(MicroSpinLock), 1);
128
129   int nthrs = sysconf(_SC_NPROCESSORS_ONLN) * 2;
130   std::vector<std::thread> threads;
131   for (int i = 0; i < nthrs; ++i) {
132     threads.push_back(std::thread(splock_test));
133   }
134   for (auto& t : threads) {
135     t.join();
136   }
137 }
138
139 TEST(SmallLocks, PicoSpinCorrectness) {
140   doPslTest<int16_t>();
141   doPslTest<uint16_t>();
142   doPslTest<int32_t>();
143   doPslTest<uint32_t>();
144   doPslTest<int64_t>();
145   doPslTest<uint64_t>();
146 }
147
148 TEST(SmallLocks, PicoSpinSigned) {
149   typedef PicoSpinLock<int16_t,0> Lock;
150   Lock val;
151   val.init(-4);
152   EXPECT_EQ(val.getData(), -4);
153
154   {
155     std::lock_guard<Lock> guard(val);
156     EXPECT_EQ(val.getData(), -4);
157     val.setData(-8);
158     EXPECT_EQ(val.getData(), -8);
159   }
160   EXPECT_EQ(val.getData(), -8);
161 }
162
163 TEST(SmallLocks, RegClobber) {
164   TestClobber().go();
165 }