Include the sys/types.h portability header
[folly.git] / folly / test / DeterministicSchedule.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 // This needs to be above semaphore.h due to the windows
20 // libevent implementation needing mode_t to be defined,
21 // but defining it differently than our portability
22 // headers do.
23 #include <folly/portability/SysTypes.h>
24
25 #include <assert.h>
26 #include <boost/noncopyable.hpp>
27 #include <errno.h>
28 #include <glog/logging.h>
29 #include <semaphore.h>
30 #include <atomic>
31 #include <functional>
32 #include <mutex>
33 #include <thread>
34 #include <unordered_set>
35 #include <vector>
36
37 #include <folly/ScopeGuard.h>
38 #include <folly/detail/CacheLocality.h>
39 #include <folly/detail/Futex.h>
40
41 namespace folly {
42 namespace test {
43
44 // This is ugly, but better perf for DeterministicAtomic translates
45 // directly to more states explored and tested
46 #define FOLLY_TEST_DSCHED_VLOG(...)                             \
47   do {                                                          \
48     if (false) {                                                \
49       VLOG(2) << std::hex << std::this_thread::get_id() << ": " \
50               << __VA_ARGS__;                                   \
51     }                                                           \
52   } while (false)
53
54 /**
55  * DeterministicSchedule coordinates the inter-thread communication of a
56  * set of threads under test, so that despite concurrency the execution is
57  * the same every time.  It works by stashing a reference to the schedule
58  * in a thread-local variable, then blocking all but one thread at a time.
59  *
60  * In order for DeterministicSchedule to work, it needs to intercept
61  * all inter-thread communication.  To do this you should use
62  * DeterministicAtomic<T> instead of std::atomic<T>, create threads
63  * using DeterministicSchedule::thread() instead of the std::thread
64  * constructor, DeterministicSchedule::join(thr) instead of thr.join(),
65  * and access semaphores via the helper functions in DeterministicSchedule.
66  * Locks are not yet supported, although they would be easy to add with
67  * the same strategy as the mapping of sem_wait.
68  *
69  * The actual schedule is defined by a function from n -> [0,n). At
70  * each step, the function will be given the number of active threads
71  * (n), and it returns the index of the thread that should be run next.
72  * Invocations of the scheduler function will be serialized, but will
73  * occur from multiple threads.  A good starting schedule is uniform(0).
74  */
75 class DeterministicSchedule : boost::noncopyable {
76  public:
77   /**
78    * Arranges for the current thread (and all threads created by
79    * DeterministicSchedule::thread on a thread participating in this
80    * schedule) to participate in a deterministic schedule.
81    */
82   explicit DeterministicSchedule(const std::function<int(int)>& scheduler);
83
84   /** Completes the schedule. */
85   ~DeterministicSchedule();
86
87   /**
88    * Returns a scheduling function that randomly chooses one of the
89    * runnable threads at each step, with no history.  This implements
90    * a schedule that is equivalent to one in which the steps between
91    * inter-thread communication are random variables following a poisson
92    * distribution.
93    */
94   static std::function<int(int)> uniform(long seed);
95
96   /**
97    * Returns a scheduling function that chooses a subset of the active
98    * threads and randomly chooses a member of the subset as the next
99    * runnable thread.  The subset is chosen with size n, and the choice
100    * is made every m steps.
101    */
102   static std::function<int(int)> uniformSubset(long seed,
103                                                int n = 2,
104                                                int m = 64);
105
106   /** Obtains permission for the current thread to perform inter-thread
107    *  communication. */
108   static void beforeSharedAccess();
109
110   /** Releases permission for the current thread to perform inter-thread
111    *  communication. */
112   static void afterSharedAccess();
113
114   /** Launches a thread that will participate in the same deterministic
115    *  schedule as the current thread. */
116   template <typename Func, typename... Args>
117   static inline std::thread thread(Func&& func, Args&&... args) {
118     // TODO: maybe future versions of gcc will allow forwarding to thread
119     auto sched = tls_sched;
120     auto sem = sched ? sched->beforeThreadCreate() : nullptr;
121     auto child = std::thread([=](Args... a) {
122       if (sched) {
123         sched->afterThreadCreate(sem);
124         beforeSharedAccess();
125         FOLLY_TEST_DSCHED_VLOG("running");
126         afterSharedAccess();
127       }
128       SCOPE_EXIT {
129         if (sched) {
130           sched->beforeThreadExit();
131         }
132       };
133       func(a...);
134     }, args...);
135     if (sched) {
136       beforeSharedAccess();
137       sched->active_.insert(child.get_id());
138       FOLLY_TEST_DSCHED_VLOG("forked " << std::hex << child.get_id());
139       afterSharedAccess();
140     }
141     return child;
142   }
143
144   /** Calls child.join() as part of a deterministic schedule. */
145   static void join(std::thread& child);
146
147   /** Calls sem_post(sem) as part of a deterministic schedule. */
148   static void post(sem_t* sem);
149
150   /** Calls sem_trywait(sem) as part of a deterministic schedule, returning
151    *  true on success and false on transient failure. */
152   static bool tryWait(sem_t* sem);
153
154   /** Calls sem_wait(sem) as part of a deterministic schedule. */
155   static void wait(sem_t* sem);
156
157   /** Used scheduler_ to get a random number b/w [0, n). If tls_sched is
158    *  not set-up it falls back to std::rand() */
159   static int getRandNumber(int n);
160
161   /** Deterministic implemencation of getcpu */
162   static int getcpu(unsigned* cpu, unsigned* node, void* unused);
163
164  private:
165   static FOLLY_TLS sem_t* tls_sem;
166   static FOLLY_TLS DeterministicSchedule* tls_sched;
167   static FOLLY_TLS unsigned tls_threadId;
168
169   std::function<int(int)> scheduler_;
170   std::vector<sem_t*> sems_;
171   std::unordered_set<std::thread::id> active_;
172   unsigned nextThreadId_;
173
174   sem_t* beforeThreadCreate();
175   void afterThreadCreate(sem_t*);
176   void beforeThreadExit();
177 };
178
179 /**
180  * DeterministicAtomic<T> is a drop-in replacement std::atomic<T> that
181  * cooperates with DeterministicSchedule.
182  */
183 template <typename T>
184 struct DeterministicAtomic {
185   std::atomic<T> data;
186
187   DeterministicAtomic() = default;
188   ~DeterministicAtomic() = default;
189   DeterministicAtomic(DeterministicAtomic<T> const&) = delete;
190   DeterministicAtomic<T>& operator=(DeterministicAtomic<T> const&) = delete;
191
192   constexpr /* implicit */ DeterministicAtomic(T v) noexcept : data(v) {}
193
194   bool is_lock_free() const noexcept { return data.is_lock_free(); }
195
196   bool compare_exchange_strong(
197       T& v0, T v1, std::memory_order mo = std::memory_order_seq_cst) noexcept {
198     DeterministicSchedule::beforeSharedAccess();
199     auto orig = v0;
200     bool rv = data.compare_exchange_strong(v0, v1, mo);
201     FOLLY_TEST_DSCHED_VLOG(this << ".compare_exchange_strong(" << std::hex
202                                 << orig << ", " << std::hex << v1 << ") -> "
203                                 << rv << "," << std::hex << v0);
204     DeterministicSchedule::afterSharedAccess();
205     return rv;
206   }
207
208   bool compare_exchange_weak(
209       T& v0, T v1, std::memory_order mo = std::memory_order_seq_cst) noexcept {
210     DeterministicSchedule::beforeSharedAccess();
211     auto orig = v0;
212     bool rv = data.compare_exchange_weak(v0, v1, mo);
213     FOLLY_TEST_DSCHED_VLOG(this << ".compare_exchange_weak(" << std::hex << orig
214                                 << ", " << std::hex << v1 << ") -> " << rv
215                                 << "," << std::hex << v0);
216     DeterministicSchedule::afterSharedAccess();
217     return rv;
218   }
219
220   T exchange(T v, std::memory_order mo = std::memory_order_seq_cst) noexcept {
221     DeterministicSchedule::beforeSharedAccess();
222     T rv = data.exchange(v, mo);
223     FOLLY_TEST_DSCHED_VLOG(this << ".exchange(" << std::hex << v << ") -> "
224                                 << std::hex << rv);
225     DeterministicSchedule::afterSharedAccess();
226     return rv;
227   }
228
229   /* implicit */ operator T() const noexcept {
230     DeterministicSchedule::beforeSharedAccess();
231     T rv = data;
232     FOLLY_TEST_DSCHED_VLOG(this << "() -> " << std::hex << rv);
233     DeterministicSchedule::afterSharedAccess();
234     return rv;
235   }
236
237   T load(std::memory_order mo = std::memory_order_seq_cst) const noexcept {
238     DeterministicSchedule::beforeSharedAccess();
239     T rv = data.load(mo);
240     FOLLY_TEST_DSCHED_VLOG(this << ".load() -> " << std::hex << rv);
241     DeterministicSchedule::afterSharedAccess();
242     return rv;
243   }
244
245   T operator=(T v) noexcept {
246     DeterministicSchedule::beforeSharedAccess();
247     T rv = (data = v);
248     FOLLY_TEST_DSCHED_VLOG(this << " = " << std::hex << v);
249     DeterministicSchedule::afterSharedAccess();
250     return rv;
251   }
252
253   void store(T v, std::memory_order mo = std::memory_order_seq_cst) noexcept {
254     DeterministicSchedule::beforeSharedAccess();
255     data.store(v, mo);
256     FOLLY_TEST_DSCHED_VLOG(this << ".store(" << std::hex << v << ")");
257     DeterministicSchedule::afterSharedAccess();
258   }
259
260   T operator++() noexcept {
261     DeterministicSchedule::beforeSharedAccess();
262     T rv = ++data;
263     FOLLY_TEST_DSCHED_VLOG(this << " pre++ -> " << std::hex << rv);
264     DeterministicSchedule::afterSharedAccess();
265     return rv;
266   }
267
268   T operator++(int /* postDummy */) noexcept {
269     DeterministicSchedule::beforeSharedAccess();
270     T rv = data++;
271     FOLLY_TEST_DSCHED_VLOG(this << " post++ -> " << std::hex << rv);
272     DeterministicSchedule::afterSharedAccess();
273     return rv;
274   }
275
276   T operator--() noexcept {
277     DeterministicSchedule::beforeSharedAccess();
278     T rv = --data;
279     FOLLY_TEST_DSCHED_VLOG(this << " pre-- -> " << std::hex << rv);
280     DeterministicSchedule::afterSharedAccess();
281     return rv;
282   }
283
284   T operator--(int /* postDummy */) noexcept {
285     DeterministicSchedule::beforeSharedAccess();
286     T rv = data--;
287     FOLLY_TEST_DSCHED_VLOG(this << " post-- -> " << std::hex << rv);
288     DeterministicSchedule::afterSharedAccess();
289     return rv;
290   }
291
292   T operator+=(T v) noexcept {
293     DeterministicSchedule::beforeSharedAccess();
294     T rv = (data += v);
295     FOLLY_TEST_DSCHED_VLOG(this << " += " << std::hex << v << " -> " << std::hex
296                                 << rv);
297     DeterministicSchedule::afterSharedAccess();
298     return rv;
299   }
300
301   T fetch_add(T v,
302               std::memory_order /* mo */ = std::memory_order_seq_cst) noexcept {
303     DeterministicSchedule::beforeSharedAccess();
304     T rv = data;
305     data += v;
306     FOLLY_TEST_DSCHED_VLOG(this << ".fetch_add(" << std::hex << v << ") -> "
307                                 << std::hex << rv);
308     DeterministicSchedule::afterSharedAccess();
309     return rv;
310   }
311
312   T operator-=(T v) noexcept {
313     DeterministicSchedule::beforeSharedAccess();
314     T rv = (data -= v);
315     FOLLY_TEST_DSCHED_VLOG(this << " -= " << std::hex << v << " -> " << std::hex
316                                 << rv);
317     DeterministicSchedule::afterSharedAccess();
318     return rv;
319   }
320
321   T fetch_sub(T v,
322               std::memory_order /* mo */ = std::memory_order_seq_cst) noexcept {
323     DeterministicSchedule::beforeSharedAccess();
324     T rv = data;
325     data -= v;
326     FOLLY_TEST_DSCHED_VLOG(this << ".fetch_sub(" << std::hex << v << ") -> "
327                                 << std::hex << rv);
328     DeterministicSchedule::afterSharedAccess();
329     return rv;
330   }
331
332   T operator&=(T v) noexcept {
333     DeterministicSchedule::beforeSharedAccess();
334     T rv = (data &= v);
335     FOLLY_TEST_DSCHED_VLOG(this << " &= " << std::hex << v << " -> " << std::hex
336                                 << rv);
337     DeterministicSchedule::afterSharedAccess();
338     return rv;
339   }
340
341   T fetch_and(T v,
342               std::memory_order /* mo */ = std::memory_order_seq_cst) noexcept {
343     DeterministicSchedule::beforeSharedAccess();
344     T rv = data;
345     data &= v;
346     FOLLY_TEST_DSCHED_VLOG(this << ".fetch_and(" << std::hex << v << ") -> "
347                                 << std::hex << rv);
348     DeterministicSchedule::afterSharedAccess();
349     return rv;
350   }
351
352   T operator|=(T v) noexcept {
353     DeterministicSchedule::beforeSharedAccess();
354     T rv = (data |= v);
355     FOLLY_TEST_DSCHED_VLOG(this << " |= " << std::hex << v << " -> " << std::hex
356                                 << rv);
357     DeterministicSchedule::afterSharedAccess();
358     return rv;
359   }
360
361   T fetch_or(T v,
362              std::memory_order /* mo */ = std::memory_order_seq_cst) noexcept {
363     DeterministicSchedule::beforeSharedAccess();
364     T rv = data;
365     data |= v;
366     FOLLY_TEST_DSCHED_VLOG(this << ".fetch_or(" << std::hex << v << ") -> "
367                                 << std::hex << rv);
368     DeterministicSchedule::afterSharedAccess();
369     return rv;
370   }
371
372   T operator^=(T v) noexcept {
373     DeterministicSchedule::beforeSharedAccess();
374     T rv = (data ^= v);
375     FOLLY_TEST_DSCHED_VLOG(this << " ^= " << std::hex << v << " -> " << std::hex
376                                 << rv);
377     DeterministicSchedule::afterSharedAccess();
378     return rv;
379   }
380
381   T fetch_xor(T v,
382               std::memory_order /* mo */ = std::memory_order_seq_cst) noexcept {
383     DeterministicSchedule::beforeSharedAccess();
384     T rv = data;
385     data ^= v;
386     FOLLY_TEST_DSCHED_VLOG(this << ".fetch_xor(" << std::hex << v << ") -> "
387                                 << std::hex << rv);
388     DeterministicSchedule::afterSharedAccess();
389     return rv;
390   }
391 };
392
393 /**
394  * DeterministicMutex is a drop-in replacement of std::mutex that
395  * cooperates with DeterministicSchedule.
396  */
397 struct DeterministicMutex {
398   std::mutex m;
399
400   DeterministicMutex() = default;
401   ~DeterministicMutex() = default;
402   DeterministicMutex(DeterministicMutex const&) = delete;
403   DeterministicMutex& operator=(DeterministicMutex const&) = delete;
404
405   void lock() {
406     FOLLY_TEST_DSCHED_VLOG(this << ".lock()");
407     while (!try_lock()) {
408       // Not calling m.lock() in order to avoid deadlock when the
409       // mutex m is held by another thread. The deadlock would be
410       // between the call to m.lock() and the lock holder's wait on
411       // its own tls_sem scheduling semaphore.
412     }
413   }
414
415   bool try_lock() {
416     DeterministicSchedule::beforeSharedAccess();
417     bool rv = m.try_lock();
418     FOLLY_TEST_DSCHED_VLOG(this << ".try_lock() -> " << rv);
419     DeterministicSchedule::afterSharedAccess();
420     return rv;
421   }
422
423   void unlock() {
424     FOLLY_TEST_DSCHED_VLOG(this << ".unlock()");
425     m.unlock();
426   }
427 };
428 }
429 } // namespace folly::test
430
431 /* Specialization declarations */
432
433 namespace folly {
434 namespace detail {
435
436 template <>
437 int Futex<test::DeterministicAtomic>::futexWake(int count, uint32_t wakeMask);
438
439 template <>
440 FutexResult Futex<test::DeterministicAtomic>::futexWaitImpl(
441     uint32_t expected,
442     std::chrono::time_point<std::chrono::system_clock>* absSystemTime,
443     std::chrono::time_point<std::chrono::steady_clock>* absSteadyTime,
444     uint32_t waitMask);
445
446 template <>
447 Getcpu::Func AccessSpreader<test::DeterministicAtomic>::pickGetcpuFunc();
448 }
449 } // namespace folly::detail