Commit 61d53283 authored by Maged Michael's avatar Maged Michael Committed by Facebook Github Bot 6

DeterministicSchedule support for global invariants and auxiliary variables

Summary:
Support for user-defined auxiliary variables and global invariants.
- Add two fields to DSched:
  -- tls_aux: static FOLLY_TLS std::function<void(uint64_t, bool)>*. User-defined auxiliary function with parameters: count of synchronization steps, and boolean indicator of the success of the current step.
  -- step_: uint64_t. Count of shared accesses that correspond to user synchronization steps (atomic accesses for now).
- Add two static functions to DSched:
  -- void setAux(std::function<void(uint64_t, bool)>*).
  -- void callAux(bool success). Calls the aux function with the step count and the bool success argument.
- Add a version of afterSharedAccess(bool) that takes a bool success parameter and calls callAux(success). This version is used in every atomic operation of DeterministicAtomic.
- Add direct load interface to  DeterministicAtomic for use by auxiliary functions.

Note: This the base of a stacked diff with:
- Test the new capabilities in DeterministicScheduleTest.h
Next steps:
- Use the new capabilities to test dynamic MPMCQueue
Other possible additions:
- Change the implementation of DeterministicMutex to allow inspecting its internal state.
- Test the new capabilities for mutexes and semaphores in DeterministicScheduleTest.h
- Performance optimization: e.g., user-space context switching, using regular variables to implements atomics.

Reviewed By: djwatson

Differential Revision: D3648146

fbshipit-source-id: 4f838ff7cfd41ab71cfdf22bb67def3221948311
parent 1ddb975d
...@@ -33,6 +33,7 @@ namespace test { ...@@ -33,6 +33,7 @@ namespace test {
FOLLY_TLS sem_t* DeterministicSchedule::tls_sem; FOLLY_TLS sem_t* DeterministicSchedule::tls_sem;
FOLLY_TLS DeterministicSchedule* DeterministicSchedule::tls_sched; FOLLY_TLS DeterministicSchedule* DeterministicSchedule::tls_sched;
FOLLY_TLS unsigned DeterministicSchedule::tls_threadId; FOLLY_TLS unsigned DeterministicSchedule::tls_threadId;
FOLLY_TLS std::function<void(uint64_t, bool)>* DeterministicSchedule::tls_aux;
// access is protected by futexLock // access is protected by futexLock
static std::unordered_map<detail::Futex<DeterministicAtomic>*, static std::unordered_map<detail::Futex<DeterministicAtomic>*,
...@@ -42,9 +43,10 @@ static std::mutex futexLock; ...@@ -42,9 +43,10 @@ static std::mutex futexLock;
DeterministicSchedule::DeterministicSchedule( DeterministicSchedule::DeterministicSchedule(
const std::function<int(int)>& scheduler) const std::function<int(int)>& scheduler)
: scheduler_(scheduler), nextThreadId_(1) { : scheduler_(scheduler), nextThreadId_(1), step_(0) {
assert(tls_sem == nullptr); assert(tls_sem == nullptr);
assert(tls_sched == nullptr); assert(tls_sched == nullptr);
assert(tls_aux == nullptr);
tls_sem = new sem_t; tls_sem = new sem_t;
sem_init(tls_sem, 0, 1); sem_init(tls_sem, 0, 1);
...@@ -133,7 +135,15 @@ void DeterministicSchedule::afterSharedAccess() { ...@@ -133,7 +135,15 @@ void DeterministicSchedule::afterSharedAccess() {
if (!sched) { if (!sched) {
return; return;
} }
sem_post(sched->sems_[sched->scheduler_(sched->sems_.size())]);
}
void DeterministicSchedule::afterSharedAccess(bool success) {
auto sched = tls_sched;
if (!sched) {
return;
}
sched->callAux(success);
sem_post(sched->sems_[sched->scheduler_(sched->sems_.size())]); sem_post(sched->sems_[sched->scheduler_(sched->sems_.size())]);
} }
...@@ -161,6 +171,10 @@ int DeterministicSchedule::getcpu(unsigned* cpu, ...@@ -161,6 +171,10 @@ int DeterministicSchedule::getcpu(unsigned* cpu,
return 0; return 0;
} }
void DeterministicSchedule::setAux(std::function<void(uint64_t, bool)>& aux) {
tls_aux = &aux;
}
sem_t* DeterministicSchedule::beforeThreadCreate() { sem_t* DeterministicSchedule::beforeThreadCreate() {
sem_t* s = new sem_t; sem_t* s = new sem_t;
sem_init(s, 0, 0); sem_init(s, 0, 0);
...@@ -216,6 +230,16 @@ void DeterministicSchedule::join(std::thread& child) { ...@@ -216,6 +230,16 @@ void DeterministicSchedule::join(std::thread& child) {
child.join(); child.join();
} }
void DeterministicSchedule::callAux(bool success) {
++step_;
auto aux = tls_aux;
if (!aux) {
return;
}
(*aux)(step_, success);
tls_aux = nullptr;
}
void DeterministicSchedule::post(sem_t* sem) { void DeterministicSchedule::post(sem_t* sem) {
beforeSharedAccess(); beforeSharedAccess();
sem_post(sem); sem_post(sem);
......
...@@ -111,6 +111,12 @@ class DeterministicSchedule : boost::noncopyable { ...@@ -111,6 +111,12 @@ class DeterministicSchedule : boost::noncopyable {
* communication. */ * communication. */
static void afterSharedAccess(); static void afterSharedAccess();
/** 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). */
static void afterSharedAccess(bool success);
/** Launches a thread that will participate in the same deterministic /** Launches a thread that will participate in the same deterministic
* schedule as the current thread. */ * schedule as the current thread. */
template <typename Func, typename... Args> template <typename Func, typename... Args>
...@@ -161,19 +167,37 @@ class DeterministicSchedule : boost::noncopyable { ...@@ -161,19 +167,37 @@ class DeterministicSchedule : boost::noncopyable {
/** Deterministic implemencation of getcpu */ /** Deterministic implemencation of getcpu */
static int getcpu(unsigned* cpu, unsigned* node, void* unused); static int getcpu(unsigned* cpu, unsigned* node, void* unused);
/** Sets up a thread-specific function for call immediately after
* the next shared access for managing auxiliary data and checking
* global invariants. The parameters of the function are: a
* uint64_t that indicates the step number (i.e., the number of
* shared accesses so far), and a bool that indicates the success
* of the shared access (if it is conditional, true otherwise). */
static void setAux(std::function<void(uint64_t, bool)>& aux);
private: private:
static FOLLY_TLS sem_t* tls_sem; static FOLLY_TLS sem_t* tls_sem;
static FOLLY_TLS DeterministicSchedule* tls_sched; static FOLLY_TLS DeterministicSchedule* tls_sched;
static FOLLY_TLS unsigned tls_threadId; static FOLLY_TLS unsigned tls_threadId;
static FOLLY_TLS std::function<void(uint64_t, bool)>* tls_aux;
std::function<int(int)> scheduler_; std::function<int(int)> scheduler_;
std::vector<sem_t*> sems_; std::vector<sem_t*> sems_;
std::unordered_set<std::thread::id> active_; std::unordered_set<std::thread::id> active_;
unsigned nextThreadId_; unsigned nextThreadId_;
/* step_ keeps count of shared accesses that correspond to user
* synchronization steps (atomic accesses for now).
* The reason for keeping track of this here and not just with
* auxiliary data is to provide users with warning signs (e.g.,
* skipped steps) if they inadvertently forget to set up aux
* functions for some shared accesses. */
uint64_t step_;
sem_t* beforeThreadCreate(); sem_t* beforeThreadCreate();
void afterThreadCreate(sem_t*); void afterThreadCreate(sem_t*);
void beforeThreadExit(); void beforeThreadExit();
/** Calls user-defined auxiliary function (if any) */
void callAux(bool);
}; };
/** /**
...@@ -201,7 +225,7 @@ struct DeterministicAtomic { ...@@ -201,7 +225,7 @@ struct DeterministicAtomic {
FOLLY_TEST_DSCHED_VLOG(this << ".compare_exchange_strong(" << std::hex FOLLY_TEST_DSCHED_VLOG(this << ".compare_exchange_strong(" << std::hex
<< orig << ", " << std::hex << v1 << ") -> " << orig << ", " << std::hex << v1 << ") -> "
<< rv << "," << std::hex << v0); << rv << "," << std::hex << v0);
DeterministicSchedule::afterSharedAccess(); DeterministicSchedule::afterSharedAccess(rv);
return rv; return rv;
} }
...@@ -213,7 +237,7 @@ struct DeterministicAtomic { ...@@ -213,7 +237,7 @@ struct DeterministicAtomic {
FOLLY_TEST_DSCHED_VLOG(this << ".compare_exchange_weak(" << std::hex << orig FOLLY_TEST_DSCHED_VLOG(this << ".compare_exchange_weak(" << std::hex << orig
<< ", " << std::hex << v1 << ") -> " << rv << ", " << std::hex << v1 << ") -> " << rv
<< "," << std::hex << v0); << "," << std::hex << v0);
DeterministicSchedule::afterSharedAccess(); DeterministicSchedule::afterSharedAccess(rv);
return rv; return rv;
} }
...@@ -222,7 +246,7 @@ struct DeterministicAtomic { ...@@ -222,7 +246,7 @@ struct DeterministicAtomic {
T rv = data.exchange(v, mo); T rv = data.exchange(v, mo);
FOLLY_TEST_DSCHED_VLOG(this << ".exchange(" << std::hex << v << ") -> " FOLLY_TEST_DSCHED_VLOG(this << ".exchange(" << std::hex << v << ") -> "
<< std::hex << rv); << std::hex << rv);
DeterministicSchedule::afterSharedAccess(); DeterministicSchedule::afterSharedAccess(true);
return rv; return rv;
} }
...@@ -230,7 +254,7 @@ struct DeterministicAtomic { ...@@ -230,7 +254,7 @@ struct DeterministicAtomic {
DeterministicSchedule::beforeSharedAccess(); DeterministicSchedule::beforeSharedAccess();
T rv = data; T rv = data;
FOLLY_TEST_DSCHED_VLOG(this << "() -> " << std::hex << rv); FOLLY_TEST_DSCHED_VLOG(this << "() -> " << std::hex << rv);
DeterministicSchedule::afterSharedAccess(); DeterministicSchedule::afterSharedAccess(true);
return rv; return rv;
} }
...@@ -238,7 +262,7 @@ struct DeterministicAtomic { ...@@ -238,7 +262,7 @@ struct DeterministicAtomic {
DeterministicSchedule::beforeSharedAccess(); DeterministicSchedule::beforeSharedAccess();
T rv = data.load(mo); T rv = data.load(mo);
FOLLY_TEST_DSCHED_VLOG(this << ".load() -> " << std::hex << rv); FOLLY_TEST_DSCHED_VLOG(this << ".load() -> " << std::hex << rv);
DeterministicSchedule::afterSharedAccess(); DeterministicSchedule::afterSharedAccess(true);
return rv; return rv;
} }
...@@ -246,7 +270,7 @@ struct DeterministicAtomic { ...@@ -246,7 +270,7 @@ struct DeterministicAtomic {
DeterministicSchedule::beforeSharedAccess(); DeterministicSchedule::beforeSharedAccess();
T rv = (data = v); T rv = (data = v);
FOLLY_TEST_DSCHED_VLOG(this << " = " << std::hex << v); FOLLY_TEST_DSCHED_VLOG(this << " = " << std::hex << v);
DeterministicSchedule::afterSharedAccess(); DeterministicSchedule::afterSharedAccess(true);
return rv; return rv;
} }
...@@ -254,14 +278,14 @@ struct DeterministicAtomic { ...@@ -254,14 +278,14 @@ struct DeterministicAtomic {
DeterministicSchedule::beforeSharedAccess(); DeterministicSchedule::beforeSharedAccess();
data.store(v, mo); data.store(v, mo);
FOLLY_TEST_DSCHED_VLOG(this << ".store(" << std::hex << v << ")"); FOLLY_TEST_DSCHED_VLOG(this << ".store(" << std::hex << v << ")");
DeterministicSchedule::afterSharedAccess(); DeterministicSchedule::afterSharedAccess(true);
} }
T operator++() noexcept { T operator++() noexcept {
DeterministicSchedule::beforeSharedAccess(); DeterministicSchedule::beforeSharedAccess();
T rv = ++data; T rv = ++data;
FOLLY_TEST_DSCHED_VLOG(this << " pre++ -> " << std::hex << rv); FOLLY_TEST_DSCHED_VLOG(this << " pre++ -> " << std::hex << rv);
DeterministicSchedule::afterSharedAccess(); DeterministicSchedule::afterSharedAccess(true);
return rv; return rv;
} }
...@@ -269,7 +293,7 @@ struct DeterministicAtomic { ...@@ -269,7 +293,7 @@ struct DeterministicAtomic {
DeterministicSchedule::beforeSharedAccess(); DeterministicSchedule::beforeSharedAccess();
T rv = data++; T rv = data++;
FOLLY_TEST_DSCHED_VLOG(this << " post++ -> " << std::hex << rv); FOLLY_TEST_DSCHED_VLOG(this << " post++ -> " << std::hex << rv);
DeterministicSchedule::afterSharedAccess(); DeterministicSchedule::afterSharedAccess(true);
return rv; return rv;
} }
...@@ -277,7 +301,7 @@ struct DeterministicAtomic { ...@@ -277,7 +301,7 @@ struct DeterministicAtomic {
DeterministicSchedule::beforeSharedAccess(); DeterministicSchedule::beforeSharedAccess();
T rv = --data; T rv = --data;
FOLLY_TEST_DSCHED_VLOG(this << " pre-- -> " << std::hex << rv); FOLLY_TEST_DSCHED_VLOG(this << " pre-- -> " << std::hex << rv);
DeterministicSchedule::afterSharedAccess(); DeterministicSchedule::afterSharedAccess(true);
return rv; return rv;
} }
...@@ -285,7 +309,7 @@ struct DeterministicAtomic { ...@@ -285,7 +309,7 @@ struct DeterministicAtomic {
DeterministicSchedule::beforeSharedAccess(); DeterministicSchedule::beforeSharedAccess();
T rv = data--; T rv = data--;
FOLLY_TEST_DSCHED_VLOG(this << " post-- -> " << std::hex << rv); FOLLY_TEST_DSCHED_VLOG(this << " post-- -> " << std::hex << rv);
DeterministicSchedule::afterSharedAccess(); DeterministicSchedule::afterSharedAccess(true);
return rv; return rv;
} }
...@@ -294,7 +318,7 @@ struct DeterministicAtomic { ...@@ -294,7 +318,7 @@ struct DeterministicAtomic {
T rv = (data += v); T rv = (data += v);
FOLLY_TEST_DSCHED_VLOG(this << " += " << std::hex << v << " -> " << std::hex FOLLY_TEST_DSCHED_VLOG(this << " += " << std::hex << v << " -> " << std::hex
<< rv); << rv);
DeterministicSchedule::afterSharedAccess(); DeterministicSchedule::afterSharedAccess(true);
return rv; return rv;
} }
...@@ -305,7 +329,7 @@ struct DeterministicAtomic { ...@@ -305,7 +329,7 @@ struct DeterministicAtomic {
data += v; data += v;
FOLLY_TEST_DSCHED_VLOG(this << ".fetch_add(" << std::hex << v << ") -> " FOLLY_TEST_DSCHED_VLOG(this << ".fetch_add(" << std::hex << v << ") -> "
<< std::hex << rv); << std::hex << rv);
DeterministicSchedule::afterSharedAccess(); DeterministicSchedule::afterSharedAccess(true);
return rv; return rv;
} }
...@@ -314,7 +338,7 @@ struct DeterministicAtomic { ...@@ -314,7 +338,7 @@ struct DeterministicAtomic {
T rv = (data -= v); T rv = (data -= v);
FOLLY_TEST_DSCHED_VLOG(this << " -= " << std::hex << v << " -> " << std::hex FOLLY_TEST_DSCHED_VLOG(this << " -= " << std::hex << v << " -> " << std::hex
<< rv); << rv);
DeterministicSchedule::afterSharedAccess(); DeterministicSchedule::afterSharedAccess(true);
return rv; return rv;
} }
...@@ -325,7 +349,7 @@ struct DeterministicAtomic { ...@@ -325,7 +349,7 @@ struct DeterministicAtomic {
data -= v; data -= v;
FOLLY_TEST_DSCHED_VLOG(this << ".fetch_sub(" << std::hex << v << ") -> " FOLLY_TEST_DSCHED_VLOG(this << ".fetch_sub(" << std::hex << v << ") -> "
<< std::hex << rv); << std::hex << rv);
DeterministicSchedule::afterSharedAccess(); DeterministicSchedule::afterSharedAccess(true);
return rv; return rv;
} }
...@@ -334,7 +358,7 @@ struct DeterministicAtomic { ...@@ -334,7 +358,7 @@ struct DeterministicAtomic {
T rv = (data &= v); T rv = (data &= v);
FOLLY_TEST_DSCHED_VLOG(this << " &= " << std::hex << v << " -> " << std::hex FOLLY_TEST_DSCHED_VLOG(this << " &= " << std::hex << v << " -> " << std::hex
<< rv); << rv);
DeterministicSchedule::afterSharedAccess(); DeterministicSchedule::afterSharedAccess(true);
return rv; return rv;
} }
...@@ -345,7 +369,7 @@ struct DeterministicAtomic { ...@@ -345,7 +369,7 @@ struct DeterministicAtomic {
data &= v; data &= v;
FOLLY_TEST_DSCHED_VLOG(this << ".fetch_and(" << std::hex << v << ") -> " FOLLY_TEST_DSCHED_VLOG(this << ".fetch_and(" << std::hex << v << ") -> "
<< std::hex << rv); << std::hex << rv);
DeterministicSchedule::afterSharedAccess(); DeterministicSchedule::afterSharedAccess(true);
return rv; return rv;
} }
...@@ -354,7 +378,7 @@ struct DeterministicAtomic { ...@@ -354,7 +378,7 @@ struct DeterministicAtomic {
T rv = (data |= v); T rv = (data |= v);
FOLLY_TEST_DSCHED_VLOG(this << " |= " << std::hex << v << " -> " << std::hex FOLLY_TEST_DSCHED_VLOG(this << " |= " << std::hex << v << " -> " << std::hex
<< rv); << rv);
DeterministicSchedule::afterSharedAccess(); DeterministicSchedule::afterSharedAccess(true);
return rv; return rv;
} }
...@@ -365,7 +389,7 @@ struct DeterministicAtomic { ...@@ -365,7 +389,7 @@ struct DeterministicAtomic {
data |= v; data |= v;
FOLLY_TEST_DSCHED_VLOG(this << ".fetch_or(" << std::hex << v << ") -> " FOLLY_TEST_DSCHED_VLOG(this << ".fetch_or(" << std::hex << v << ") -> "
<< std::hex << rv); << std::hex << rv);
DeterministicSchedule::afterSharedAccess(); DeterministicSchedule::afterSharedAccess(true);
return rv; return rv;
} }
...@@ -374,7 +398,7 @@ struct DeterministicAtomic { ...@@ -374,7 +398,7 @@ struct DeterministicAtomic {
T rv = (data ^= v); T rv = (data ^= v);
FOLLY_TEST_DSCHED_VLOG(this << " ^= " << std::hex << v << " -> " << std::hex FOLLY_TEST_DSCHED_VLOG(this << " ^= " << std::hex << v << " -> " << std::hex
<< rv); << rv);
DeterministicSchedule::afterSharedAccess(); DeterministicSchedule::afterSharedAccess(true);
return rv; return rv;
} }
...@@ -385,9 +409,14 @@ struct DeterministicAtomic { ...@@ -385,9 +409,14 @@ struct DeterministicAtomic {
data ^= v; data ^= v;
FOLLY_TEST_DSCHED_VLOG(this << ".fetch_xor(" << std::hex << v << ") -> " FOLLY_TEST_DSCHED_VLOG(this << ".fetch_xor(" << std::hex << v << ") -> "
<< std::hex << rv); << std::hex << rv);
DeterministicSchedule::afterSharedAccess(); DeterministicSchedule::afterSharedAccess(true);
return rv; return rv;
} }
/** Read the value of the atomic variable without context switching */
T load_direct() const noexcept {
return data.load(std::memory_order_relaxed);
}
}; };
/** /**
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment