Timed wait operations for spin-only Baton
[folly.git] / folly / synchronization / test / ParkingLotTest.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
17 #include <thread>
18
19 #include <folly/synchronization/ParkingLot.h>
20
21 #include <folly/portability/GTest.h>
22 #include <folly/synchronization/Baton.h>
23
24 using namespace folly;
25
26 TEST(ParkingLot, multilot) {
27   using SmallLot = ParkingLot<bool>;
28   using LargeLot = ParkingLot<uint64_t>;
29   SmallLot smalllot;
30   LargeLot largelot;
31   folly::Baton<> sb;
32   folly::Baton<> lb;
33
34   std::thread small([&]() {
35     smalllot.park(0, false, [] { return true; }, [&]() { sb.post(); });
36   });
37   std::thread large([&]() {
38     largelot.park(0, true, [] { return true; }, [&]() { lb.post(); });
39   });
40   sb.wait();
41   lb.wait();
42
43   int count = 0;
44   smalllot.unpark(0, [&](bool data) {
45     count++;
46     EXPECT_EQ(data, false);
47     return UnparkControl::RemoveContinue;
48   });
49   EXPECT_EQ(count, 1);
50   count = 0;
51   largelot.unpark(0, [&](bool data) {
52     count++;
53     EXPECT_EQ(data, true);
54     return UnparkControl::RemoveContinue;
55   });
56   EXPECT_EQ(count, 1);
57
58   small.join();
59   large.join();
60 }
61
62 // This is not possible to implement with Futex, because futex
63 // and the native linux syscall are 32-bit only.
64 TEST(ParkingLot, LargeWord) {
65   ParkingLot<uint64_t> lot;
66   std::atomic<uint64_t> w{0};
67
68   lot.park(0, false, [&]() { return w == 1; }, []() {});
69
70   // Validate should return false, will hang otherwise.
71 }
72
73 class WaitableMutex : public std::mutex {
74   using Lot = ParkingLot<std::function<bool(void)>>;
75   static Lot lot;
76
77  public:
78   void unlock() {
79     bool unparked = false;
80     lot.unpark(uint64_t(this), [&](std::function<bool(void)> wfunc) {
81       if (wfunc()) {
82         unparked = true;
83         return UnparkControl::RemoveBreak;
84       } else {
85         return UnparkControl::RemoveContinue;
86       }
87     });
88     if (!unparked) {
89       std::mutex::unlock();
90     }
91     // Otherwise, we pass mutex directly to waiter without needing to unlock.
92   }
93
94   template <typename Wait>
95   void wait(Wait wfunc) {
96     lot.park(
97         uint64_t(this),
98         wfunc,
99         [&]() { return !wfunc(); },
100         [&]() { std::mutex::unlock(); });
101   }
102 };
103
104 WaitableMutex::Lot WaitableMutex::lot;
105
106 TEST(ParkingLot, WaitableMutexTest) {
107   std::atomic<bool> go{false};
108   WaitableMutex mu;
109   std::thread t([&]() {
110     std::lock_guard<WaitableMutex> g(mu);
111     mu.wait([&]() { return go == true; });
112   });
113   sleep(1);
114
115   {
116     std::lock_guard<WaitableMutex> g(mu);
117     go = true;
118   }
119   t.join();
120 }