proxygen
folly::test::DeterministicSchedule Class Reference

#include <DeterministicSchedule.h>

Inheritance diagram for folly::test::DeterministicSchedule:

Public Member Functions

 DeterministicSchedule (const std::function< size_t(size_t)> &scheduler)
 
 ~DeterministicSchedule ()
 

Static Public Member Functions

static std::function< size_t(size_t)> uniform (uint64_t seed)
 
static std::function< size_t(size_t)> uniformSubset (uint64_t seed, size_t n=2, size_t m=64)
 
static void beforeSharedAccess ()
 
static void afterSharedAccess ()
 
static void afterSharedAccess (bool success)
 
template<typename Func , typename... Args>
static std::thread thread (Func &&func, Args &&...args)
 
static void join (std::thread &child)
 
static void post (sem_t *sem)
 
static bool tryWait (sem_t *sem)
 
static void wait (sem_t *sem)
 
static size_t getRandNumber (size_t n)
 
static int getcpu (unsigned *cpu, unsigned *node, void *unused)
 
static void setAuxAct (AuxAct &aux)
 
static void setAuxChk (AuxChk &aux)
 
static void clearAuxChk ()
 
static sem_t * descheduleCurrentThread ()
 
static void reschedule (sem_t *sem)
 

Private Member Functions

sem_t * beforeThreadCreate ()
 
void afterThreadCreate (sem_t *)
 
void beforeThreadExit ()
 
void callAux (bool)
 

Private Attributes

std::function< size_t(size_t)> scheduler_
 
std::vector< sem_t * > sems_
 
std::unordered_set< std::thread::id > active_
 
std::unordered_map< std::thread::id, sem_t * > joins_
 
unsigned nextThreadId_
 
uint64_t step_
 

Static Private Attributes

static FOLLY_TLS sem_t * tls_sem
 
static FOLLY_TLS DeterministicScheduletls_sched
 
static FOLLY_TLS unsigned tls_threadId
 
static thread_local AuxAct tls_aux_act
 
static AuxChk aux_chk
 

Detailed Description

DeterministicSchedule coordinates the inter-thread communication of a set of threads under test, so that despite concurrency the execution is the same every time. It works by stashing a reference to the schedule in a thread-local variable, then blocking all but one thread at a time.

In order for DeterministicSchedule to work, it needs to intercept all inter-thread communication. To do this you should use DeterministicAtomic<T> instead of std::atomic<T>, create threads using DeterministicSchedule::thread() instead of the std::thread constructor, DeterministicSchedule::join(thr) instead of thr.join(), and access semaphores via the helper functions in DeterministicSchedule. Locks are not yet supported, although they would be easy to add with the same strategy as the mapping of sem_wait.

The actual schedule is defined by a function from n -> [0,n). At each step, the function will be given the number of active threads (n), and it returns the index of the thread that should be run next. Invocations of the scheduler function will be serialized, but will occur from multiple threads. A good starting schedule is uniform(0).

Definition at line 75 of file DeterministicSchedule.h.

Constructor & Destructor Documentation

folly::test::DeterministicSchedule::DeterministicSchedule ( const std::function< size_t(size_t)> &  scheduler)
explicit

Arranges for the current thread (and all threads created by DeterministicSchedule::thread on a thread participating in this schedule) to participate in a deterministic schedule.

Definition at line 47 of file DeterministicSchedule.cpp.

References sems_, tls_aux_act, tls_sched, and tls_sem.

50  assert(tls_sem == nullptr);
51  assert(tls_sched == nullptr);
52  assert(tls_aux_act == nullptr);
53 
54  tls_sem = new sem_t;
55  sem_init(tls_sem, 0, 1);
56  sems_.push_back(tls_sem);
57 
58  tls_sched = this;
59 }
std::function< size_t(size_t)> scheduler_
static FOLLY_TLS DeterministicSchedule * tls_sched
std::shared_ptr< folly::FunctionScheduler > scheduler
Definition: FilePoller.cpp:50
static thread_local AuxAct tls_aux_act
folly::test::DeterministicSchedule::~DeterministicSchedule ( )

Completes the schedule.

Definition at line 61 of file DeterministicSchedule.cpp.

References beforeThreadExit(), sems_, tls_sched, and tls_sem.

61  {
62  assert(tls_sched == this);
63  assert(sems_.size() == 1);
64  assert(sems_[0] == tls_sem);
66 }
static FOLLY_TLS DeterministicSchedule * tls_sched

Member Function Documentation

void folly::test::DeterministicSchedule::afterSharedAccess ( )
static

Releases permission for the current thread to perform inter-thread communication.

Definition at line 137 of file DeterministicSchedule.cpp.

References tls_sched.

Referenced by afterThreadCreate(), beforeThreadCreate(), beforeThreadExit(), folly::test::futexWaitImpl(), folly::test::futexWakeImpl(), getcpu(), join(), folly::test::DeterministicMutex::lock(), post(), folly::test::DeterministicMutex::try_lock(), tryWait(), and folly::test::DeterministicMutex::unlock().

137  {
138  auto sched = tls_sched;
139  if (!sched) {
140  return;
141  }
142  sem_post(sched->sems_[sched->scheduler_(sched->sems_.size())]);
143 }
static FOLLY_TLS DeterministicSchedule * tls_sched
void folly::test::DeterministicSchedule::afterSharedAccess ( bool  success)
static

Calls a user-defined auxiliary function if any, and releases permission for the current thread to perform inter-thread communication. The bool parameter indicates the success of the shared access (if conditional, true otherwise).

Definition at line 145 of file DeterministicSchedule.cpp.

References callAux(), and tls_sched.

145  {
146  auto sched = tls_sched;
147  if (!sched) {
148  return;
149  }
150  sched->callAux(success);
151  sem_post(sched->sems_[sched->scheduler_(sched->sems_.size())]);
152 }
static FOLLY_TLS DeterministicSchedule * tls_sched
void folly::test::DeterministicSchedule::afterThreadCreate ( sem_t *  sem)
private

Definition at line 216 of file DeterministicSchedule.cpp.

References active_, afterSharedAccess(), beforeSharedAccess(), tls_sched, and tls_sem.

216  {
217  assert(tls_sem == nullptr);
218  assert(tls_sched == nullptr);
219  tls_sem = sem;
220  tls_sched = this;
221  bool started = false;
222  while (!started) {
224  if (active_.count(std::this_thread::get_id()) == 1) {
225  started = true;
226  }
228  }
229 }
static FOLLY_TLS DeterministicSchedule * tls_sched
std::unordered_set< std::thread::id > active_
void folly::test::DeterministicSchedule::beforeSharedAccess ( )
static

Obtains permission for the current thread to perform inter-thread communication.

Definition at line 131 of file DeterministicSchedule.cpp.

References tls_sem.

Referenced by afterThreadCreate(), beforeThreadCreate(), beforeThreadExit(), folly::test::futexWaitImpl(), folly::test::futexWakeImpl(), getcpu(), join(), folly::test::DeterministicMutex::lock(), post(), folly::test::DeterministicMutex::try_lock(), tryWait(), and folly::test::DeterministicMutex::unlock().

131  {
132  if (tls_sem) {
133  sem_wait(tls_sem);
134  }
135 }
sem_t * folly::test::DeterministicSchedule::beforeThreadCreate ( )
private

Definition at line 207 of file DeterministicSchedule.cpp.

References afterSharedAccess(), beforeSharedAccess(), s, and sems_.

Referenced by thread().

207  {
208  sem_t* s = new sem_t;
209  sem_init(s, 0, 0);
211  sems_.push_back(s);
213  return s;
214 }
static set< string > s
void folly::test::DeterministicSchedule::beforeThreadExit ( )
private

Definition at line 231 of file DeterministicSchedule.cpp.

References active_, afterSharedAccess(), beforeSharedAccess(), FOLLY_TEST_DSCHED_VLOG, joins_, parent, reschedule(), sems_, tls_aux_act, tls_sched, and tls_sem.

Referenced by ~DeterministicSchedule().

231  {
232  assert(tls_sched == this);
234  auto parent = joins_.find(std::this_thread::get_id());
235  if (parent != joins_.end()) {
236  reschedule(parent->second);
237  joins_.erase(parent);
238  }
239  sems_.erase(std::find(sems_.begin(), sems_.end(), tls_sem));
240  active_.erase(std::this_thread::get_id());
241  if (sems_.size() > 0) {
242  FOLLY_TEST_DSCHED_VLOG("exiting");
244  }
245  sem_destroy(tls_sem);
246  delete tls_sem;
247  tls_sem = nullptr;
248  tls_sched = nullptr;
249  tls_aux_act = nullptr;
250 }
#define FOLLY_TEST_DSCHED_VLOG(...)
static FOLLY_TLS DeterministicSchedule * tls_sched
std::unordered_set< std::thread::id > active_
std::unordered_map< std::thread::id, sem_t * > joins_
static thread_local AuxAct tls_aux_act
folly::Function< void()> parent
Definition: AtFork.cpp:34
void folly::test::DeterministicSchedule::callAux ( bool  success)
private

Calls user-defined auxiliary function (if any)

Definition at line 271 of file DeterministicSchedule.cpp.

References aux_chk, step_, and tls_aux_act.

Referenced by afterSharedAccess().

271  {
272  ++step_;
273  if (tls_aux_act) {
274  tls_aux_act(success);
275  tls_aux_act = nullptr;
276  }
277  if (aux_chk) {
278  aux_chk(step_);
279  }
280 }
static thread_local AuxAct tls_aux_act
void folly::test::DeterministicSchedule::clearAuxChk ( )
static

Clears the function set by setAuxChk

Definition at line 187 of file DeterministicSchedule.cpp.

References aux_chk.

Referenced by AnnotatedAtomicCounter< T >::clearAuxChk().

187  {
188  aux_chk = nullptr;
189 }
sem_t * folly::test::DeterministicSchedule::descheduleCurrentThread ( )
static

Remove the current thread's semaphore from sems_

Definition at line 198 of file DeterministicSchedule.cpp.

References sems_, tls_sched, and tls_sem.

Referenced by join(), and folly::test::DeterministicMutex::lock().

198  {
199  auto sched = tls_sched;
200  if (sched) {
201  sched->sems_.erase(
202  std::find(sched->sems_.begin(), sched->sems_.end(), tls_sem));
203  }
204  return tls_sem;
205 }
static FOLLY_TLS DeterministicSchedule * tls_sched
int folly::test::DeterministicSchedule::getcpu ( unsigned *  cpu,
unsigned *  node,
void *  unused 
)
static

Deterministic implemencation of getcpu

Definition at line 161 of file DeterministicSchedule.cpp.

References afterSharedAccess(), beforeSharedAccess(), nextThreadId_, tls_sched, and tls_threadId.

Referenced by folly::AccessSpreader< Atom >::pickGetcpuFunc().

164  {
165  if (!tls_threadId && tls_sched) {
169  }
170  if (cpu) {
171  *cpu = tls_threadId;
172  }
173  if (node) {
174  *node = tls_threadId;
175  }
176  return 0;
177 }
static FOLLY_TLS unsigned tls_threadId
static FOLLY_TLS DeterministicSchedule * tls_sched
size_t folly::test::DeterministicSchedule::getRandNumber ( size_t  n)
static

Used scheduler_ to get a random number b/w [0, n). If tls_sched is not set-up it falls back to std::rand()

Definition at line 154 of file DeterministicSchedule.cpp.

References folly::Random::rand32(), scheduler_, and tls_sched.

Referenced by folly::test::futexWaitImpl().

154  {
155  if (tls_sched) {
156  return tls_sched->scheduler_(n);
157  }
158  return Random::rand32() % n;
159 }
std::function< size_t(size_t)> scheduler_
static FOLLY_TLS DeterministicSchedule * tls_sched
static uint32_t rand32()
Definition: Random.h:213
void folly::test::DeterministicSchedule::join ( std::thread &  child)
static

Calls child.join() as part of a deterministic schedule.

Definition at line 252 of file DeterministicSchedule.cpp.

References afterSharedAccess(), beforeSharedAccess(), descheduleCurrentThread(), FOLLY_TEST_DSCHED_VLOG, and tls_sched.

Referenced by folly::test::atomic_notify_one(), cleanup_test(), DSchedMixedTest(), lifo_test(), liveClockWaitUntilTests(), mt_linked_test(), priv_dtor_test(), producerConsumerBench(), run_basic_tests(), run_mt_sequencer_test(), run_multi_poster_multi_waiter_test(), folly::test::run_pingpong_test(), run_pingpong_test(), folly::test::run_timed_wait_regular_test(), folly::test::run_timed_wait_tmo_tests(), run_wake_blocked_test(), runAllAndValidate(), runContendedReaders(), runMixed(), runNeverFailTest(), runPingPong(), runRemoteUnlock(), runTryEnqDeqTest(), swmr_test(), TEST(), folly::TEST(), and TEST_F().

252  {
253  auto sched = tls_sched;
254  if (sched) {
256  assert(sched->joins_.count(child.get_id()) == 0);
257  if (sched->active_.count(child.get_id())) {
258  sem_t* sem = descheduleCurrentThread();
259  sched->joins_.insert({child.get_id(), sem});
261  // Wait to be scheduled by exiting child thread
263  assert(!sched->active_.count(child.get_id()));
264  }
266  }
267  FOLLY_TEST_DSCHED_VLOG("joined " << std::hex << child.get_id());
268  child.join();
269 }
#define FOLLY_TEST_DSCHED_VLOG(...)
static FOLLY_TLS DeterministicSchedule * tls_sched
folly::Function< void()> child
Definition: AtFork.cpp:35
void folly::test::DeterministicSchedule::post ( sem_t *  sem)
static

Calls sem_post(sem) as part of a deterministic schedule.

Definition at line 282 of file DeterministicSchedule.cpp.

References afterSharedAccess(), beforeSharedAccess(), and FOLLY_TEST_DSCHED_VLOG.

282  {
284  sem_post(sem);
285  FOLLY_TEST_DSCHED_VLOG("sem_post(" << sem << ")");
287 }
#define FOLLY_TEST_DSCHED_VLOG(...)
void folly::test::DeterministicSchedule::reschedule ( sem_t *  sem)
static

Add sem back into sems_

Definition at line 191 of file DeterministicSchedule.cpp.

References sems_, and tls_sched.

Referenced by beforeThreadExit(), and folly::test::DeterministicMutex::unlock().

191  {
192  auto sched = tls_sched;
193  if (sched) {
194  sched->sems_.push_back(sem);
195  }
196 }
static FOLLY_TLS DeterministicSchedule * tls_sched
void folly::test::DeterministicSchedule::setAuxAct ( AuxAct aux)
static

Sets up a thread-specific function for call immediately after the next shared access by the thread for managing auxiliary data. The function takes a bool parameter that indicates the success of the shared access (if it is conditional, true otherwise). The function is cleared after one use.

Definition at line 179 of file DeterministicSchedule.cpp.

References tls_aux_act.

179  {
180  tls_aux_act = aux;
181 }
static thread_local AuxAct tls_aux_act
void folly::test::DeterministicSchedule::setAuxChk ( AuxChk aux)
static

Sets up a function to be called after every subsequent shared access (until clearAuxChk() is called) for checking global invariants and logging. The function takes a uint64_t parameter that indicates the number of shared accesses so far.

Definition at line 183 of file DeterministicSchedule.cpp.

References aux_chk.

Referenced by AnnotatedAtomicCounter< T >::setAuxChk().

183  {
184  aux_chk = aux;
185 }
template<typename Func , typename... Args>
static std::thread folly::test::DeterministicSchedule::thread ( Func &&  func,
Args &&...  args 
)
inlinestatic

Launches a thread that will participate in the same deterministic schedule as the current thread.

Definition at line 123 of file DeterministicSchedule.h.

References a, testing::Args(), beforeThreadCreate(), child, SCOPE_EXIT, and tls_sched.

Referenced by folly::test::atomic_notify_one(), cleanup_test(), DSchedMixedTest(), lifo_test(), liveClockWaitUntilTests(), mt_linked_test(), priv_dtor_test(), producerConsumerBench(), run_basic_tests(), run_mt_sequencer_test(), run_multi_poster_multi_waiter_test(), folly::test::run_pingpong_test(), run_pingpong_test(), folly::test::run_timed_wait_regular_test(), folly::test::run_timed_wait_tmo_tests(), run_wake_blocked_test(), runAllAndValidate(), runContendedReaders(), runMixed(), runNeverFailTest(), runPingPong(), runRemoteUnlock(), runTryEnqDeqTest(), swmr_test(), TEST(), folly::TEST(), and TEST_F().

123  {
124  // TODO: maybe future versions of gcc will allow forwarding to thread
125  auto sched = tls_sched;
126  auto sem = sched ? sched->beforeThreadCreate() : nullptr;
127  auto child = std::thread(
128  [=](Args... a) {
129  if (sched) {
130  sched->afterThreadCreate(sem);
131  beforeSharedAccess();
132  FOLLY_TEST_DSCHED_VLOG("running");
133  afterSharedAccess();
134  }
135  SCOPE_EXIT {
136  if (sched) {
137  sched->beforeThreadExit();
138  }
139  };
140  func(a...);
141  },
142  args...);
143  if (sched) {
145  sched->active_.insert(child.get_id());
146  FOLLY_TEST_DSCHED_VLOG("forked " << std::hex << child.get_id());
148  }
149  return child;
150  }
#define FOLLY_TEST_DSCHED_VLOG(...)
static FOLLY_TLS DeterministicSchedule * tls_sched
#define SCOPE_EXIT
Definition: ScopeGuard.h:274
internal::ArgsMatcher< InnerMatcher > Args(const InnerMatcher &matcher)
char a
folly::Function< void()> child
Definition: AtFork.cpp:35
bool folly::test::DeterministicSchedule::tryWait ( sem_t *  sem)
static

Calls sem_trywait(sem) as part of a deterministic schedule, returning true on success and false on transient failure.

Definition at line 289 of file DeterministicSchedule.cpp.

References afterSharedAccess(), beforeSharedAccess(), and FOLLY_TEST_DSCHED_VLOG.

Referenced by wait().

289  {
291  int rv = sem_trywait(sem);
292  int e = rv == 0 ? 0 : errno;
294  "sem_trywait(" << sem << ") = " << rv << " errno=" << e);
296  if (rv == 0) {
297  return true;
298  } else {
299  assert(e == EAGAIN);
300  return false;
301  }
302 }
#define FOLLY_TEST_DSCHED_VLOG(...)
std::function< size_t(size_t)> folly::test::DeterministicSchedule::uniform ( uint64_t  seed)
static

Returns a scheduling function that randomly chooses one of the runnable threads at each step, with no history. This implements a schedule that is equivalent to one in which the steps between inter-thread communication are random variables following a poisson distribution.

Definition at line 68 of file DeterministicSchedule.cpp.

References dist, and seed.

Referenced by runMtNeverFailDeterministic(), runMtProdConsDeterministic(), runMtProdConsDeterministicDynamic(), TEST(), folly::TEST(), and TEST_F().

68  {
69  auto rand = std::make_shared<std::ranlux48>(seed);
70  return [rand](size_t numActive) {
71  auto dist = std::uniform_int_distribution<size_t>(0, numActive - 1);
72  return dist(*rand);
73  };
74 }
static const int seed
std::uniform_int_distribution< milliseconds::rep > dist
std::function< size_t(size_t)> folly::test::DeterministicSchedule::uniformSubset ( uint64_t  seed,
size_t  n = 2,
size_t  m = 64 
)
static

Returns a scheduling function that chooses a subset of the active threads and randomly chooses a member of the subset as the next runnable thread. The subset is chosen with size n, and the choice is made every m steps.

Definition at line 126 of file DeterministicSchedule.cpp.

References m, and seed.

Referenced by runMtNeverFailDeterministic(), TEST(), and TEST_F().

126  {
127  auto gen = std::make_shared<UniformSubset>(seed, n, m);
128  return [=](size_t numActive) { return (*gen)(numActive); };
129 }
static const int seed
static map< string, int > m
void folly::test::DeterministicSchedule::wait ( sem_t *  sem)
static

Calls sem_wait(sem) as part of a deterministic schedule.

Definition at line 304 of file DeterministicSchedule.cpp.

References tryWait().

304  {
305  while (!tryWait(sem)) {
306  // we're not busy waiting because this is a deterministic schedule
307  }
308 }

Member Data Documentation

std::unordered_set<std::thread::id> folly::test::DeterministicSchedule::active_
private

Definition at line 203 of file DeterministicSchedule.h.

Referenced by afterThreadCreate(), and beforeThreadExit().

AuxChk folly::test::DeterministicSchedule::aux_chk
staticprivate

Definition at line 199 of file DeterministicSchedule.h.

Referenced by callAux(), clearAuxChk(), and setAuxChk().

std::unordered_map<std::thread::id, sem_t*> folly::test::DeterministicSchedule::joins_
private

Definition at line 204 of file DeterministicSchedule.h.

Referenced by beforeThreadExit().

unsigned folly::test::DeterministicSchedule::nextThreadId_
private

Definition at line 205 of file DeterministicSchedule.h.

Referenced by getcpu().

std::function<size_t(size_t)> folly::test::DeterministicSchedule::scheduler_
private

Definition at line 201 of file DeterministicSchedule.h.

Referenced by getRandNumber().

std::vector<sem_t*> folly::test::DeterministicSchedule::sems_
private
uint64_t folly::test::DeterministicSchedule::step_
private

Definition at line 212 of file DeterministicSchedule.h.

Referenced by callAux().

thread_local AuxAct folly::test::DeterministicSchedule::tls_aux_act
staticprivate
FOLLY_TLS sem_t * folly::test::DeterministicSchedule::tls_sem
staticprivate
FOLLY_TLS unsigned folly::test::DeterministicSchedule::tls_threadId
staticprivate

Definition at line 197 of file DeterministicSchedule.h.

Referenced by getcpu().


The documentation for this class was generated from the following files: