Have internal tests use folly::Random instead of rand_r
[folly.git] / folly / test / SmallLocksTest.cpp
1 /*
2  * Copyright 2016 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
19 #include <folly/Random.h>
20
21 #include <cassert>
22 #include <cstdio>
23 #include <mutex>
24 #include <string>
25 #include <vector>
26 #include <pthread.h>
27 #include <unistd.h>
28
29 #include <thread>
30
31 #include <gtest/gtest.h>
32
33 using std::string;
34 using folly::MicroSpinLock;
35 using folly::PicoSpinLock;
36 using folly::MSLGuard;
37
38 namespace {
39
40 struct LockedVal {
41   int ar[1024];
42   MicroSpinLock lock;
43
44   LockedVal() {
45     lock.init();
46     memset(ar, 0, sizeof ar);
47   }
48 };
49
50 // Compile time test for packed struct support (requires that both of
51 // these classes are POD).
52 FOLLY_PACK_PUSH
53 struct ignore1 { MicroSpinLock msl; int16_t foo; } FOLLY_PACK_ATTR;
54 struct ignore2 { PicoSpinLock<uint32_t> psl; int16_t foo; } FOLLY_PACK_ATTR;
55 static_assert(sizeof(ignore1) == 3, "Size check failed");
56 static_assert(sizeof(ignore2) == 6, "Size check failed");
57 static_assert(sizeof(MicroSpinLock) == 1, "Size check failed");
58 FOLLY_PACK_POP
59
60 LockedVal v;
61 void splock_test() {
62
63   const int max = 1000;
64   auto rng = folly::ThreadLocalPRNG();
65   for (int i = 0; i < max; i++) {
66     folly::asm_pause();
67     MSLGuard g(v.lock);
68
69     int first = v.ar[0];
70     for (size_t i = 1; i < sizeof v.ar / sizeof i; ++i) {
71       EXPECT_EQ(first, v.ar[i]);
72     }
73
74     int byte = folly::Random::rand32(rng);
75     memset(v.ar, char(byte), sizeof v.ar);
76   }
77 }
78
79 template<class T> struct PslTest {
80   PicoSpinLock<T> lock;
81
82   PslTest() { lock.init(); }
83
84   void doTest() {
85     T ourVal = rand() % (T(1) << (sizeof(T) * 8 - 1));
86     for (int i = 0; i < 10000; ++i) {
87       std::lock_guard<PicoSpinLock<T>> guard(lock);
88       lock.setData(ourVal);
89       for (int n = 0; n < 10; ++n) {
90         folly::asm_volatile_pause();
91         EXPECT_EQ(lock.getData(), ourVal);
92       }
93     }
94   }
95 };
96
97 template<class T>
98 void doPslTest() {
99   PslTest<T> testObj;
100
101   const int nthrs = 17;
102   std::vector<std::thread> threads;
103   for (int i = 0; i < nthrs; ++i) {
104     threads.push_back(std::thread(&PslTest<T>::doTest, &testObj));
105   }
106   for (auto& t : threads) {
107     t.join();
108   }
109 }
110
111 struct TestClobber {
112   TestClobber() {
113     lock_.init();
114   }
115
116   void go() {
117     std::lock_guard<MicroSpinLock> g(lock_);
118     // This bug depends on gcc register allocation and is very sensitive. We
119     // have to use DCHECK instead of EXPECT_*.
120     DCHECK(!lock_.try_lock());
121   }
122
123  private:
124   MicroSpinLock lock_;
125 };
126
127 }
128
129 TEST(SmallLocks, SpinLockCorrectness) {
130   EXPECT_EQ(sizeof(MicroSpinLock), 1);
131
132   int nthrs = sysconf(_SC_NPROCESSORS_ONLN) * 2;
133   std::vector<std::thread> threads;
134   for (int i = 0; i < nthrs; ++i) {
135     threads.push_back(std::thread(splock_test));
136   }
137   for (auto& t : threads) {
138     t.join();
139   }
140 }
141
142 TEST(SmallLocks, PicoSpinCorrectness) {
143   doPslTest<int16_t>();
144   doPslTest<uint16_t>();
145   doPslTest<int32_t>();
146   doPslTest<uint32_t>();
147   doPslTest<int64_t>();
148   doPslTest<uint64_t>();
149 }
150
151 TEST(SmallLocks, PicoSpinSigned) {
152   typedef PicoSpinLock<int16_t,0> Lock;
153   Lock val;
154   val.init(-4);
155   EXPECT_EQ(val.getData(), -4);
156
157   {
158     std::lock_guard<Lock> guard(val);
159     EXPECT_EQ(val.getData(), -4);
160     val.setData(-8);
161     EXPECT_EQ(val.getData(), -8);
162   }
163   EXPECT_EQ(val.getData(), -8);
164 }
165
166 TEST(SmallLocks, RegClobber) {
167   TestClobber().go();
168 }