Use the GTest portability headers
[folly.git] / folly / test / LockTraitsTest.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 #include <folly/LockTraits.h>
17 #include <folly/LockTraitsBoost.h>
18
19 #include <mutex>
20
21 #include <folly/RWSpinLock.h>
22 #include <folly/SharedMutex.h>
23 #include <folly/SpinLock.h>
24 #include <folly/portability/GTest.h>
25
26 using namespace folly;
27
28 static constexpr auto one_ms = std::chrono::milliseconds(1);
29
30 /**
31  * Test mutex to help to automate assertions
32  */
33 class FakeAllPowerfulAssertingMutex {
34  public:
35   enum class CurrentLockState { UNLOCKED, SHARED, UPGRADE, UNIQUE };
36
37   void lock() {
38     EXPECT_EQ(this->lock_state, CurrentLockState::UNLOCKED);
39     this->lock_state = CurrentLockState::UNIQUE;
40   }
41   void unlock() {
42     EXPECT_EQ(this->lock_state, CurrentLockState::UNIQUE);
43     this->lock_state = CurrentLockState::UNLOCKED;
44   }
45   void lock_shared() {
46     EXPECT_EQ(this->lock_state, CurrentLockState::UNLOCKED);
47     this->lock_state = CurrentLockState::SHARED;
48   }
49   void unlock_shared() {
50     EXPECT_EQ(this->lock_state, CurrentLockState::SHARED);
51     this->lock_state = CurrentLockState::UNLOCKED;
52   }
53   void lock_upgrade() {
54     EXPECT_EQ(this->lock_state, CurrentLockState::UNLOCKED);
55     this->lock_state = CurrentLockState::UPGRADE;
56   }
57   void unlock_upgrade() {
58     EXPECT_EQ(this->lock_state, CurrentLockState::UPGRADE);
59     this->lock_state = CurrentLockState::UNLOCKED;
60   }
61
62   void unlock_upgrade_and_lock() {
63     EXPECT_EQ(this->lock_state, CurrentLockState::UPGRADE);
64     this->lock_state = CurrentLockState::UNIQUE;
65   }
66   void unlock_and_lock_upgrade() {
67     EXPECT_EQ(this->lock_state, CurrentLockState::UNIQUE);
68     this->lock_state = CurrentLockState::UPGRADE;
69   }
70   void unlock_and_lock_shared() {
71     EXPECT_EQ(this->lock_state, CurrentLockState::UNIQUE);
72     this->lock_state = CurrentLockState::SHARED;
73   }
74   void unlock_upgrade_and_lock_shared() {
75     EXPECT_EQ(this->lock_state, CurrentLockState::UPGRADE);
76     this->lock_state = CurrentLockState::SHARED;
77   }
78
79   template <class Rep, class Period>
80   bool try_lock_for(const std::chrono::duration<Rep, Period>&) {
81     EXPECT_EQ(this->lock_state, CurrentLockState::UNLOCKED);
82     this->lock_state = CurrentLockState::UNIQUE;
83     return true;
84   }
85
86   template <class Rep, class Period>
87   bool try_lock_upgrade_for(const std::chrono::duration<Rep, Period>&) {
88     EXPECT_EQ(this->lock_state, CurrentLockState::UNLOCKED);
89     this->lock_state = CurrentLockState::UPGRADE;
90     return true;
91   }
92
93   template <class Rep, class Period>
94   bool try_unlock_upgrade_and_lock_for(
95       const std::chrono::duration<Rep, Period>&) {
96     EXPECT_EQ(this->lock_state, CurrentLockState::UPGRADE);
97     this->lock_state = CurrentLockState::UNIQUE;
98     return true;
99   }
100
101   /*
102    * Initialize the FakeMutex with an unlocked state
103    */
104   CurrentLockState lock_state{CurrentLockState::UNLOCKED};
105 };
106
107 TEST(LockTraits, std_mutex) {
108   using traits = LockTraits<std::mutex>;
109   static_assert(!traits::is_timed, "std:mutex is not a timed lock");
110   static_assert(!traits::is_shared, "std:mutex is not a shared lock");
111   static_assert(!traits::is_upgrade, "std::mutex is not an upgradable lock");
112
113   std::mutex mutex;
114   traits::lock(mutex);
115   traits::unlock(mutex);
116
117   lock_shared_or_unique(mutex);
118   unlock_shared_or_unique(mutex);
119 }
120
121 TEST(LockTraits, SharedMutex) {
122   using traits = LockTraits<SharedMutex>;
123   static_assert(traits::is_timed, "folly::SharedMutex is a timed lock");
124   static_assert(traits::is_shared, "folly::SharedMutex is a shared lock");
125   static_assert(traits::is_upgrade, "folly::SharedMutex is an upgradable lock");
126
127   SharedMutex mutex;
128   traits::lock(mutex);
129   traits::unlock(mutex);
130
131   traits::lock_shared(mutex);
132   traits::lock_shared(mutex);
133   traits::unlock_shared(mutex);
134   traits::unlock_shared(mutex);
135
136   lock_shared_or_unique(mutex);
137   lock_shared_or_unique(mutex);
138   unlock_shared_or_unique(mutex);
139   unlock_shared_or_unique(mutex);
140
141   traits::lock_upgrade(mutex);
142   traits::unlock_upgrade(mutex);
143
144   // test upgrade and downgrades
145   traits::lock_upgrade(mutex);
146   traits::unlock_upgrade_and_lock(mutex);
147   bool gotLock = traits::try_lock_for(mutex, one_ms);
148   EXPECT_FALSE(gotLock) << "Should not have been able to acquire an exclusive "
149                            "lock after upgrading to an exclusive lock";
150   gotLock = traits::try_lock_upgrade_for(mutex, one_ms);
151   EXPECT_FALSE(gotLock) << "Should not have been able to acquire an upgrade "
152                            "lock after upgrading to an exclusive lock";
153   gotLock = traits::try_lock_shared_for(mutex, one_ms);
154   EXPECT_FALSE(gotLock) << "Should not have been able to acquire a shared "
155                            "lock after upgrading to an exclusive lock";
156   traits::unlock(mutex);
157
158   traits::lock_upgrade(mutex);
159   traits::unlock_upgrade_and_lock_shared(mutex);
160   gotLock = traits::try_lock_for(mutex, one_ms);
161   EXPECT_FALSE(gotLock) << "Should not have been able to acquire an exclusive "
162                            "mutex after downgrading from an upgrade to a "
163                            "shared lock";
164   traits::unlock_shared(mutex);
165
166   traits::lock(mutex);
167   gotLock = traits::try_lock_for(mutex, one_ms);
168   EXPECT_FALSE(gotLock) << "Should not have been able to acquire an exclusive "
169                            "lock after acquiring an exclusive lock";
170   gotLock = traits::try_lock_upgrade_for(mutex, one_ms);
171   EXPECT_FALSE(gotLock) << "Should not have been able to acquire an upgrade "
172                            "lock after acquiring an exclusive lock";
173   gotLock = traits::try_lock_shared_for(mutex, one_ms);
174   EXPECT_FALSE(gotLock) << "Should not have been able to acquire a shared "
175                            "lock after acquiring an exclusive lock";
176   traits::unlock_and_lock_upgrade(mutex);
177   gotLock = traits::try_lock_for(mutex, one_ms);
178   EXPECT_FALSE(gotLock) << "Should not have been able to acquire an exclusive "
179                            "lock after downgrading to an upgrade lock";
180   traits::unlock_upgrade(mutex);
181
182   traits::lock(mutex);
183   traits::unlock_and_lock_shared(mutex);
184   gotLock = traits::try_lock_for(mutex, one_ms);
185   EXPECT_FALSE(gotLock) << "Should not have been able to acquire an exclusive "
186                            "lock after downgrading to a shared lock";
187   traits::unlock_shared(mutex);
188 }
189
190 TEST(LockTraits, SpinLock) {
191   using traits = LockTraits<SpinLock>;
192   static_assert(!traits::is_timed, "folly::SpinLock is not a timed lock");
193   static_assert(!traits::is_shared, "folly::SpinLock is not a shared lock");
194   static_assert(
195       !traits::is_upgrade, "folly::SpinLock is not an upgradable lock");
196
197   SpinLock mutex;
198   traits::lock(mutex);
199   traits::unlock(mutex);
200
201   lock_shared_or_unique(mutex);
202   unlock_shared_or_unique(mutex);
203 }
204
205 TEST(LockTraits, RWSpinLock) {
206   using traits = LockTraits<RWSpinLock>;
207   static_assert(!traits::is_timed, "folly::RWSpinLock is not a timed lock");
208   static_assert(traits::is_shared, "folly::RWSpinLock is a shared lock");
209   static_assert(traits::is_upgrade, "folly::RWSpinLock is an upgradable lock");
210
211   RWSpinLock mutex;
212   traits::lock(mutex);
213   traits::unlock(mutex);
214
215   traits::lock_shared(mutex);
216   traits::lock_shared(mutex);
217   traits::unlock_shared(mutex);
218   traits::unlock_shared(mutex);
219
220   lock_shared_or_unique(mutex);
221   lock_shared_or_unique(mutex);
222   unlock_shared_or_unique(mutex);
223   unlock_shared_or_unique(mutex);
224 }
225
226 TEST(LockTraits, boost_mutex) {
227   using traits = LockTraits<boost::mutex>;
228   static_assert(!traits::is_timed, "boost::mutex is not a timed lock");
229   static_assert(!traits::is_shared, "boost::mutex is not a shared lock");
230   static_assert(!traits::is_upgrade, "boost::mutex is not an upgradable lock");
231
232   boost::mutex mutex;
233   traits::lock(mutex);
234   traits::unlock(mutex);
235
236   lock_shared_or_unique(mutex);
237   unlock_shared_or_unique(mutex);
238 }
239
240 TEST(LockTraits, boost_recursive_mutex) {
241   using traits = LockTraits<boost::recursive_mutex>;
242   static_assert(
243       !traits::is_timed, "boost::recursive_mutex is not a timed lock");
244   static_assert(
245       !traits::is_shared, "boost::recursive_mutex is not a shared lock");
246   static_assert(
247       !traits::is_upgrade, "boost::recursive_mutex is not an upgradable lock");
248
249   boost::recursive_mutex mutex;
250   traits::lock(mutex);
251   traits::lock(mutex);
252   traits::unlock(mutex);
253   traits::unlock(mutex);
254
255   lock_shared_or_unique(mutex);
256   lock_shared_or_unique(mutex);
257   unlock_shared_or_unique(mutex);
258   unlock_shared_or_unique(mutex);
259 }
260
261 #if FOLLY_LOCK_TRAITS_HAVE_TIMED_MUTEXES
262 TEST(LockTraits, timed_mutex) {
263   using traits = LockTraits<std::timed_mutex>;
264   static_assert(traits::is_timed, "std::timed_mutex is a timed lock");
265   static_assert(!traits::is_shared, "std::timed_mutex is not a shared lock");
266   static_assert(
267       !traits::is_upgrade, "std::timed_mutex is not an upgradable lock");
268
269   std::timed_mutex mutex;
270   traits::lock(mutex);
271   bool gotLock = traits::try_lock_for(mutex, std::chrono::milliseconds(1));
272   EXPECT_FALSE(gotLock) << "should not have been able to acquire the "
273                         << "timed_mutex a second time";
274   traits::unlock(mutex);
275
276   lock_shared_or_unique(mutex);
277   gotLock = try_lock_shared_or_unique_for(mutex, std::chrono::milliseconds(1));
278   EXPECT_FALSE(gotLock) << "should not have been able to acquire the "
279                         << "timed_mutex a second time";
280   unlock_shared_or_unique(mutex);
281 }
282
283 TEST(LockTraits, recursive_timed_mutex) {
284   using traits = LockTraits<std::recursive_timed_mutex>;
285   static_assert(traits::is_timed, "std::recursive_timed_mutex is a timed lock");
286   static_assert(
287       !traits::is_shared, "std::recursive_timed_mutex is not a shared lock");
288   static_assert(
289       !traits::is_upgrade,
290       "std::recursive_timed_mutex is not an upgradable lock");
291
292   std::recursive_timed_mutex mutex;
293   traits::lock(mutex);
294   auto gotLock = traits::try_lock_for(mutex, std::chrono::milliseconds(10));
295   EXPECT_TRUE(gotLock) << "should have been able to acquire the "
296                        << "recursive_timed_mutex a second time";
297   traits::unlock(mutex);
298   traits::unlock(mutex);
299
300   lock_shared_or_unique(mutex);
301   gotLock = try_lock_shared_or_unique_for(mutex, std::chrono::milliseconds(10));
302   EXPECT_TRUE(gotLock) << "should have been able to acquire the "
303                        << "recursive_timed_mutex a second time";
304   unlock_shared_or_unique(mutex);
305   unlock_shared_or_unique(mutex);
306 }
307
308 TEST(LockTraits, boost_shared_mutex) {
309   using traits = LockTraits<boost::shared_mutex>;
310   static_assert(traits::is_timed, "boost::shared_mutex is a timed lock");
311   static_assert(traits::is_shared, "boost::shared_mutex is a shared lock");
312   static_assert(
313       traits::is_upgrade, "boost::shared_mutex is an upgradable lock");
314
315   boost::shared_mutex mutex;
316   traits::lock(mutex);
317   auto gotLock = traits::try_lock_for(mutex, std::chrono::milliseconds(1));
318   EXPECT_FALSE(gotLock) << "should not have been able to acquire the "
319                         << "shared_mutex a second time";
320   gotLock = traits::try_lock_shared_for(mutex, std::chrono::milliseconds(1));
321   EXPECT_FALSE(gotLock) << "should not have been able to acquire the "
322                         << "shared_mutex a second time";
323   traits::unlock(mutex);
324
325   traits::lock_shared(mutex);
326   gotLock = traits::try_lock_for(mutex, std::chrono::milliseconds(1));
327   EXPECT_FALSE(gotLock) << "should not have been able to acquire the "
328                         << "shared_mutex a second time";
329   gotLock = traits::try_lock_shared_for(mutex, std::chrono::milliseconds(10));
330   EXPECT_TRUE(gotLock) << "should have been able to acquire the "
331                        << "shared_mutex a second time in shared mode";
332   traits::unlock_shared(mutex);
333   traits::unlock_shared(mutex);
334
335   lock_shared_or_unique(mutex);
336   gotLock = traits::try_lock_for(mutex, std::chrono::milliseconds(1));
337   EXPECT_FALSE(gotLock) << "should not have been able to acquire the "
338                         << "shared_mutex a second time";
339   gotLock = try_lock_shared_or_unique_for(mutex, std::chrono::milliseconds(10));
340   EXPECT_TRUE(gotLock) << "should have been able to acquire the "
341                        << "shared_mutex a second time in shared mode";
342   unlock_shared_or_unique(mutex);
343   unlock_shared_or_unique(mutex);
344 }
345 #endif // FOLLY_LOCK_TRAITS_HAVE_TIMED_MUTEXES
346
347 /**
348  * Chain the asserts from the previous test to the next lock, unlock or
349  * upgrade method calls.  Each making sure that the previous was correct.
350  */
351 TEST(LockTraits, LockPolicy) {
352   using Mutex = FakeAllPowerfulAssertingMutex;
353   Mutex mutex;
354
355   // test the lock and unlock functions
356   LockPolicyUpgrade::lock(mutex);
357   mutex.unlock_upgrade();
358   mutex.lock_upgrade();
359   LockPolicyUpgrade::unlock(mutex);
360
361   mutex.lock_upgrade();
362   LockPolicyFromUpgradeToExclusive::lock(mutex);
363   mutex.unlock();
364   mutex.lock();
365   LockPolicyFromUpgradeToExclusive::unlock(mutex);
366
367   mutex.lock();
368   LockPolicyFromExclusiveToUpgrade::lock(mutex);
369   mutex.unlock_upgrade();
370   mutex.lock_upgrade();
371   LockPolicyFromExclusiveToUpgrade::unlock(mutex);
372
373   mutex.lock_upgrade();
374   LockPolicyFromUpgradeToShared::lock(mutex);
375   mutex.unlock_shared();
376   mutex.lock_shared();
377   LockPolicyFromUpgradeToShared::unlock(mutex);
378
379   mutex.lock();
380   LockPolicyFromExclusiveToShared::lock(mutex);
381   mutex.unlock_shared();
382   mutex.lock_shared();
383   LockPolicyFromExclusiveToShared::unlock(mutex);
384
385   EXPECT_EQ(mutex.lock_state, Mutex::CurrentLockState::UNLOCKED);
386 }
387
388 /**
389  * Similar to the test above but tests the timed version of the updates
390  */
391 TEST(LockTraits, LockPolicyTimed) {
392   using Mutex = FakeAllPowerfulAssertingMutex;
393   Mutex mutex;
394
395   bool gotLock = LockPolicyUpgrade::try_lock_for(mutex, one_ms);
396   EXPECT_TRUE(gotLock) << "Should have been able to acquire the fake mutex";
397   LockPolicyUpgrade::unlock(mutex);
398
399   mutex.lock_upgrade();
400   gotLock = LockPolicyFromUpgradeToExclusive::try_lock_for(mutex, one_ms);
401   EXPECT_TRUE(gotLock)
402       << "Should have been able to upgrade from upgrade to unique";
403   mutex.unlock();
404
405   mutex.lock();
406   gotLock = LockPolicyFromExclusiveToUpgrade::try_lock_for(mutex, one_ms);
407   EXPECT_TRUE(gotLock) << "Should have been able to downgrade from exclusive "
408                           "to upgrade";
409   mutex.unlock_upgrade();
410
411   mutex.lock_upgrade();
412   gotLock = LockPolicyFromUpgradeToShared::try_lock_for(mutex, one_ms);
413   EXPECT_TRUE(gotLock) << "Should have been able to downgrade from upgrade to "
414                           "shared";
415   mutex.unlock_shared();
416
417   mutex.lock();
418   gotLock = LockPolicyFromExclusiveToShared::try_lock_for(mutex, one_ms);
419   EXPECT_TRUE(gotLock) << "Should have been able to downgrade from exclusive "
420                           "to shared";
421   mutex.unlock_shared();
422 }