c289ab4df58f4a783eac9a6e56808660c6659c95
[folly.git] / folly / test / SynchronizedTestLib-inl.h
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 #pragma once
18
19 #include <gtest/gtest.h>
20
21 #include <folly/Foreach.h>
22 #include <folly/Random.h>
23 #include <folly/Synchronized.h>
24 #include <glog/logging.h>
25 #include <algorithm>
26 #include <functional>
27 #include <map>
28 #include <random>
29 #include <thread>
30 #include <vector>
31
32 inline std::mt19937& getRNG() {
33   static const auto seed = folly::randomNumberSeed();
34   static std::mt19937 rng(seed);
35   return rng;
36 }
37
38 template <class Integral1, class Integral2>
39 Integral2 random(Integral1 low, Integral2 up) {
40   std::uniform_int_distribution<> range(low, up);
41   return range(getRNG());
42 }
43
44 template <class Mutex>
45 void testBasic() {
46   folly::Synchronized<std::vector<int>, Mutex> obj;
47
48   obj->resize(1000);
49
50   auto obj2 = obj;
51   EXPECT_EQ(obj2->size(), 1000);
52
53   SYNCHRONIZED (obj) {
54     obj.push_back(10);
55     EXPECT_EQ(obj.size(), 1001);
56     EXPECT_EQ(obj.back(), 10);
57     EXPECT_EQ(obj2->size(), 1000);
58
59     UNSYNCHRONIZED(obj) {
60       EXPECT_EQ(obj->size(), 1001);
61     }
62   }
63
64   SYNCHRONIZED_CONST (obj) {
65     EXPECT_EQ(obj.size(), 1001);
66     UNSYNCHRONIZED(obj) {
67       EXPECT_EQ(obj->size(), 1001);
68     }
69   }
70
71   SYNCHRONIZED (lockedObj, *&obj) {
72     lockedObj.front() = 2;
73   }
74
75   EXPECT_EQ(obj->size(), 1001);
76   EXPECT_EQ(obj->back(), 10);
77   EXPECT_EQ(obj2->size(), 1000);
78
79   EXPECT_EQ(FB_ARG_2_OR_1(1, 2), 2);
80   EXPECT_EQ(FB_ARG_2_OR_1(1), 1);
81 }
82
83 template <class Mutex> void testConcurrency() {
84   folly::Synchronized<std::vector<int>, Mutex> v;
85
86   struct Local {
87     static bool threadMain(int i,
88                            folly::Synchronized<std::vector<int>, Mutex>& pv) {
89       usleep(::random(100 * 1000, 1000 * 1000));
90
91       // Test operator->
92       pv->push_back(2 * i);
93
94       // Aaand test the SYNCHRONIZED macro
95       SYNCHRONIZED (v, pv) {
96         v.push_back(2 * i + 1);
97       }
98
99       return true;
100     }
101   };
102
103   std::vector<std::thread> results;
104
105   static const size_t threads = 100;
106   FOR_EACH_RANGE (i, 0, threads) {
107     results.push_back(std::thread([&, i]() { Local::threadMain(i, v); }));
108   }
109
110   FOR_EACH (i, results) {
111     i->join();
112   }
113
114   std::vector<int> result;
115   v.swap(result);
116
117   EXPECT_EQ(result.size(), 2 * threads);
118   sort(result.begin(), result.end());
119
120   FOR_EACH_RANGE (i, 0, 2 * threads) {
121     EXPECT_EQ(result[i], i);
122   }
123 }
124
125 template <class Mutex> void testDualLocking() {
126   folly::Synchronized<std::vector<int>, Mutex> v;
127   folly::Synchronized<std::map<int, int>, Mutex> m;
128
129   struct Local {
130     static bool threadMain(
131       int i,
132       folly::Synchronized<std::vector<int>, Mutex>& pv,
133       folly::Synchronized<std::map<int, int>, Mutex>& pm) {
134
135       usleep(::random(100 * 1000, 1000 * 1000));
136
137       if (i & 1) {
138         SYNCHRONIZED_DUAL (v, pv, m, pm) {
139           v.push_back(i);
140           m[i] = i + 1;
141         }
142       } else {
143         SYNCHRONIZED_DUAL (m, pm, v, pv) {
144           v.push_back(i);
145           m[i] = i + 1;
146         }
147       }
148
149       return true;
150     }
151   };
152
153   std::vector<std::thread> results;
154
155   static const size_t threads = 100;
156   FOR_EACH_RANGE (i, 0, threads) {
157     results.push_back(
158       std::thread([&, i]() { Local::threadMain(i, v, m); }));
159   }
160
161   FOR_EACH (i, results) {
162     i->join();
163   }
164
165   std::vector<int> result;
166   v.swap(result);
167
168   EXPECT_EQ(result.size(), threads);
169   sort(result.begin(), result.end());
170
171   FOR_EACH_RANGE (i, 0, threads) {
172     EXPECT_EQ(result[i], i);
173   }
174 }
175
176 template <class Mutex> void testDualLockingWithConst() {
177   folly::Synchronized<std::vector<int>, Mutex> v;
178   folly::Synchronized<std::map<int, int>, Mutex> m;
179
180   struct Local {
181     static bool threadMain(
182       int i,
183       folly::Synchronized<std::vector<int>, Mutex>& pv,
184       const folly::Synchronized<std::map<int, int>, Mutex>& pm) {
185
186       usleep(::random(100 * 1000, 1000 * 1000));
187
188       if (i & 1) {
189         SYNCHRONIZED_DUAL (v, pv, m, pm) {
190           (void)m.size();
191           v.push_back(i);
192         }
193       } else {
194         SYNCHRONIZED_DUAL (m, pm, v, pv) {
195           (void)m.size();
196           v.push_back(i);
197         }
198       }
199
200       return true;
201     }
202   };
203
204   std::vector<std::thread> results;
205
206   static const size_t threads = 100;
207   FOR_EACH_RANGE (i, 0, threads) {
208     results.push_back(
209       std::thread([&, i]() { Local::threadMain(i, v, m); }));
210   }
211
212   FOR_EACH (i, results) {
213     i->join();
214   }
215
216   std::vector<int> result;
217   v.swap(result);
218
219   EXPECT_EQ(result.size(), threads);
220   sort(result.begin(), result.end());
221
222   FOR_EACH_RANGE (i, 0, threads) {
223     EXPECT_EQ(result[i], i);
224   }
225 }
226
227 template <class Mutex> void testTimedSynchronized() {
228   folly::Synchronized<std::vector<int>, Mutex> v;
229
230   struct Local {
231     static bool threadMain(int i,
232                            folly::Synchronized<std::vector<int>, Mutex>& pv) {
233       usleep(::random(100 * 1000, 1000 * 1000));
234
235       // Test operator->
236       pv->push_back(2 * i);
237
238       // Aaand test the TIMED_SYNCHRONIZED macro
239       for (;;)
240         TIMED_SYNCHRONIZED (10, v, pv) {
241           if (v) {
242             usleep(::random(15 * 1000, 150 * 1000));
243             v->push_back(2 * i + 1);
244             return true;
245           }
246           else {
247             // do nothing
248             usleep(::random(10 * 1000, 100 * 1000));
249           }
250         }
251
252       return true;
253     }
254   };
255
256   std::vector<std::thread> results;
257
258   static const size_t threads = 100;
259   FOR_EACH_RANGE (i, 0, threads) {
260     results.push_back(std::thread([&, i]() { Local::threadMain(i, v); }));
261   }
262
263   FOR_EACH (i, results) {
264     i->join();
265   }
266
267   std::vector<int> result;
268   v.swap(result);
269
270   EXPECT_EQ(result.size(), 2 * threads);
271   sort(result.begin(), result.end());
272
273   FOR_EACH_RANGE (i, 0, 2 * threads) {
274     EXPECT_EQ(result[i], i);
275   }
276 }
277
278 template <class Mutex> void testTimedSynchronizedWithConst() {
279   folly::Synchronized<std::vector<int>, Mutex> v;
280
281   struct Local {
282     static bool threadMain(int i,
283                            folly::Synchronized<std::vector<int>, Mutex>& pv) {
284       usleep(::random(100 * 1000, 1000 * 1000));
285
286       // Test operator->
287       pv->push_back(i);
288
289       usleep(::random(5 * 1000, 1000 * 1000));
290       // Test TIMED_SYNCHRONIZED_CONST
291       for (;;) {
292         TIMED_SYNCHRONIZED_CONST (10, v, pv) {
293           if (v) {
294             auto found = std::find(v->begin(), v->end(),  i);
295             CHECK(found != v->end());
296             return true;
297           } else {
298             // do nothing
299             usleep(::random(10 * 1000, 100 * 1000));
300           }
301         }
302       }
303     }
304   };
305
306   std::vector<std::thread> results;
307
308   static const size_t threads = 100;
309   FOR_EACH_RANGE (i, 0, threads) {
310     results.push_back(std::thread([&, i]() { Local::threadMain(i, v); }));
311   }
312
313   FOR_EACH (i, results) {
314     i->join();
315   }
316
317   std::vector<int> result;
318   v.swap(result);
319
320   EXPECT_EQ(result.size(), threads);
321   sort(result.begin(), result.end());
322
323   FOR_EACH_RANGE (i, 0, threads) {
324     EXPECT_EQ(result[i], i);
325   }
326 }
327
328 template <class Mutex> void testConstCopy() {
329   std::vector<int> input = {1, 2, 3};
330   const folly::Synchronized<std::vector<int>, Mutex> v(input);
331
332   std::vector<int> result;
333
334   v.copy(&result);
335   EXPECT_EQ(result, input);
336
337   result = v.copy();
338   EXPECT_EQ(result, input);
339 }
340
341 struct NotCopiableNotMovable {
342   NotCopiableNotMovable(int, const char*) {}
343   NotCopiableNotMovable(const NotCopiableNotMovable&) = delete;
344   NotCopiableNotMovable& operator=(const NotCopiableNotMovable&) = delete;
345   NotCopiableNotMovable(NotCopiableNotMovable&&) = delete;
346   NotCopiableNotMovable& operator=(NotCopiableNotMovable&&) = delete;
347 };
348
349 template <class Mutex> void testInPlaceConstruction() {
350   // This won't compile without construct_in_place
351   folly::Synchronized<NotCopiableNotMovable> a(
352     folly::construct_in_place, 5, "a"
353   );
354 }