Change trylock() to try_lock() in folly::SpinLock to conform to standard Lockable.
[folly.git] / folly / test / SpinLockTest.cpp
1 /*
2  * Copyright 2017 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 #include <folly/SpinLock.h>
17
18 #include <folly/Random.h>
19
20 #include <thread>
21
22 #include <folly/portability/Asm.h>
23 #include <folly/portability/GTest.h>
24
25 using folly::SpinLockGuardImpl;
26
27 namespace {
28
29 template <typename LOCK>
30 struct LockedVal {
31   int ar[1024];
32   LOCK lock;
33
34   LockedVal() {
35     memset(ar, 0, sizeof ar);
36   }
37 };
38
39 template <typename LOCK>
40 void spinlockTestThread(LockedVal<LOCK>* v) {
41   const int max = 1000;
42   auto rng = folly::ThreadLocalPRNG();
43   for (int i = 0; i < max; i++) {
44     folly::asm_pause();
45     SpinLockGuardImpl<LOCK> g(v->lock);
46
47     int first = v->ar[0];
48     for (size_t j = 1; j < sizeof v->ar / sizeof j; ++j) {
49       EXPECT_EQ(first, v->ar[j]);
50     }
51
52     int byte = folly::Random::rand32(rng);
53     memset(v->ar, char(byte), sizeof v->ar);
54   }
55 }
56
57 template <typename LOCK>
58 struct TryLockState {
59   LOCK lock1;
60   LOCK lock2;
61   bool locked{false};
62   uint64_t obtained{0};
63   uint64_t failed{0};
64 };
65
66 template <typename LOCK>
67 void trylockTestThread(TryLockState<LOCK>* state, size_t count) {
68   while (true) {
69     folly::asm_pause();
70     SpinLockGuardImpl<LOCK> g(state->lock1);
71     if (state->obtained >= count) {
72       break;
73     }
74
75     bool ret = state->lock2.try_lock();
76     EXPECT_NE(state->locked, ret);
77
78     if (ret) {
79       // We got lock2.
80       ++state->obtained;
81       state->locked = true;
82
83       // Release lock1 and wait until at least one other thread fails to
84       // obtain the lock2 before continuing.
85       auto oldFailed = state->failed;
86       while (state->failed == oldFailed && state->obtained < count) {
87         state->lock1.unlock();
88         folly::asm_pause();
89         state->lock1.lock();
90       }
91
92       state->locked = false;
93       state->lock2.unlock();
94     } else {
95       ++state->failed;
96     }
97   }
98 }
99
100 template <typename LOCK>
101 void correctnessTest() {
102   int nthrs = sysconf(_SC_NPROCESSORS_ONLN) * 2;
103   std::vector<std::thread> threads;
104   LockedVal<LOCK> v;
105   for (int i = 0; i < nthrs; ++i) {
106     threads.push_back(std::thread(spinlockTestThread<LOCK>, &v));
107   }
108   for (auto& t : threads) {
109     t.join();
110   }
111 }
112
113 template <typename LOCK>
114 void trylockTest() {
115   int nthrs = sysconf(_SC_NPROCESSORS_ONLN) + 4;
116   std::vector<std::thread> threads;
117   TryLockState<LOCK> state;
118   size_t count = 100;
119   for (int i = 0; i < nthrs; ++i) {
120     threads.push_back(std::thread(trylockTestThread<LOCK>, &state, count));
121   }
122   for (auto& t : threads) {
123     t.join();
124   }
125
126   EXPECT_EQ(count, state.obtained);
127   // Each time the code obtains lock2 it waits for another thread to fail
128   // to acquire it.  The only time this might not happen is on the very last
129   // loop when no other threads are left.
130   EXPECT_GE(state.failed + 1, state.obtained);
131 }
132
133 } // unnamed namespace
134
135 #if __x86_64__
136 TEST(SpinLock, MslCorrectness) {
137   correctnessTest<folly::SpinLockMslImpl>();
138 }
139 TEST(SpinLock, MslTryLock) {
140   trylockTest<folly::SpinLockMslImpl>();
141 }
142 #endif
143
144 #if __APPLE__
145 TEST(SpinLock, AppleCorrectness) {
146   correctnessTest<folly::SpinLockAppleImpl>();
147 }
148 TEST(SpinLock, AppleTryLock) {
149   trylockTest<folly::SpinLockAppleImpl>();
150 }
151 #endif
152
153 #if FOLLY_HAVE_PTHREAD_SPINLOCK_T
154 TEST(SpinLock, PthreadCorrectness) {
155   correctnessTest<folly::SpinLockPthreadImpl>();
156 }
157 TEST(SpinLock, PthreadTryLock) {
158   trylockTest<folly::SpinLockPthreadImpl>();
159 }
160 #endif
161
162 TEST(SpinLock, MutexCorrectness) {
163   correctnessTest<folly::SpinLockPthreadMutexImpl>();
164 }
165 TEST(SpinLock, MutexTryLock) {
166   trylockTest<folly::SpinLockPthreadMutexImpl>();
167 }