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