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