FOLLY_TLS sem_t* DeterministicSchedule::tls_sem;
FOLLY_TLS DeterministicSchedule* DeterministicSchedule::tls_sched;
FOLLY_TLS unsigned DeterministicSchedule::tls_threadId;
+FOLLY_TLS std::function<void(uint64_t, bool)>* DeterministicSchedule::tls_aux;
// access is protected by futexLock
static std::unordered_map<detail::Futex<DeterministicAtomic>*,
DeterministicSchedule::DeterministicSchedule(
const std::function<int(int)>& scheduler)
- : scheduler_(scheduler), nextThreadId_(1) {
+ : scheduler_(scheduler), nextThreadId_(1), step_(0) {
assert(tls_sem == nullptr);
assert(tls_sched == nullptr);
+ assert(tls_aux == nullptr);
tls_sem = new sem_t;
sem_init(tls_sem, 0, 1);
if (!sched) {
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())]);
}
return 0;
}
+void DeterministicSchedule::setAux(std::function<void(uint64_t, bool)>& aux) {
+ tls_aux = &aux;
+}
+
sem_t* DeterministicSchedule::beforeThreadCreate() {
sem_t* s = new sem_t;
sem_init(s, 0, 0);
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) {
beforeSharedAccess();
sem_post(sem);
* communication. */
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
* schedule as the current thread. */
template <typename Func, typename... Args>
/** Deterministic implemencation of getcpu */
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:
static FOLLY_TLS sem_t* tls_sem;
static FOLLY_TLS DeterministicSchedule* tls_sched;
static FOLLY_TLS unsigned tls_threadId;
+ static FOLLY_TLS std::function<void(uint64_t, bool)>* tls_aux;
std::function<int(int)> scheduler_;
std::vector<sem_t*> sems_;
std::unordered_set<std::thread::id> active_;
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();
void afterThreadCreate(sem_t*);
void beforeThreadExit();
+ /** Calls user-defined auxiliary function (if any) */
+ void callAux(bool);
};
/**
FOLLY_TEST_DSCHED_VLOG(this << ".compare_exchange_strong(" << std::hex
<< orig << ", " << std::hex << v1 << ") -> "
<< rv << "," << std::hex << v0);
- DeterministicSchedule::afterSharedAccess();
+ DeterministicSchedule::afterSharedAccess(rv);
return rv;
}
FOLLY_TEST_DSCHED_VLOG(this << ".compare_exchange_weak(" << std::hex << orig
<< ", " << std::hex << v1 << ") -> " << rv
<< "," << std::hex << v0);
- DeterministicSchedule::afterSharedAccess();
+ DeterministicSchedule::afterSharedAccess(rv);
return rv;
}
T rv = data.exchange(v, mo);
FOLLY_TEST_DSCHED_VLOG(this << ".exchange(" << std::hex << v << ") -> "
<< std::hex << rv);
- DeterministicSchedule::afterSharedAccess();
+ DeterministicSchedule::afterSharedAccess(true);
return rv;
}
DeterministicSchedule::beforeSharedAccess();
T rv = data;
FOLLY_TEST_DSCHED_VLOG(this << "() -> " << std::hex << rv);
- DeterministicSchedule::afterSharedAccess();
+ DeterministicSchedule::afterSharedAccess(true);
return rv;
}
DeterministicSchedule::beforeSharedAccess();
T rv = data.load(mo);
FOLLY_TEST_DSCHED_VLOG(this << ".load() -> " << std::hex << rv);
- DeterministicSchedule::afterSharedAccess();
+ DeterministicSchedule::afterSharedAccess(true);
return rv;
}
DeterministicSchedule::beforeSharedAccess();
T rv = (data = v);
FOLLY_TEST_DSCHED_VLOG(this << " = " << std::hex << v);
- DeterministicSchedule::afterSharedAccess();
+ DeterministicSchedule::afterSharedAccess(true);
return rv;
}
DeterministicSchedule::beforeSharedAccess();
data.store(v, mo);
FOLLY_TEST_DSCHED_VLOG(this << ".store(" << std::hex << v << ")");
- DeterministicSchedule::afterSharedAccess();
+ DeterministicSchedule::afterSharedAccess(true);
}
T operator++() noexcept {
DeterministicSchedule::beforeSharedAccess();
T rv = ++data;
FOLLY_TEST_DSCHED_VLOG(this << " pre++ -> " << std::hex << rv);
- DeterministicSchedule::afterSharedAccess();
+ DeterministicSchedule::afterSharedAccess(true);
return rv;
}
DeterministicSchedule::beforeSharedAccess();
T rv = data++;
FOLLY_TEST_DSCHED_VLOG(this << " post++ -> " << std::hex << rv);
- DeterministicSchedule::afterSharedAccess();
+ DeterministicSchedule::afterSharedAccess(true);
return rv;
}
DeterministicSchedule::beforeSharedAccess();
T rv = --data;
FOLLY_TEST_DSCHED_VLOG(this << " pre-- -> " << std::hex << rv);
- DeterministicSchedule::afterSharedAccess();
+ DeterministicSchedule::afterSharedAccess(true);
return rv;
}
DeterministicSchedule::beforeSharedAccess();
T rv = data--;
FOLLY_TEST_DSCHED_VLOG(this << " post-- -> " << std::hex << rv);
- DeterministicSchedule::afterSharedAccess();
+ DeterministicSchedule::afterSharedAccess(true);
return rv;
}
T rv = (data += v);
FOLLY_TEST_DSCHED_VLOG(this << " += " << std::hex << v << " -> " << std::hex
<< rv);
- DeterministicSchedule::afterSharedAccess();
+ DeterministicSchedule::afterSharedAccess(true);
return rv;
}
data += v;
FOLLY_TEST_DSCHED_VLOG(this << ".fetch_add(" << std::hex << v << ") -> "
<< std::hex << rv);
- DeterministicSchedule::afterSharedAccess();
+ DeterministicSchedule::afterSharedAccess(true);
return rv;
}
T rv = (data -= v);
FOLLY_TEST_DSCHED_VLOG(this << " -= " << std::hex << v << " -> " << std::hex
<< rv);
- DeterministicSchedule::afterSharedAccess();
+ DeterministicSchedule::afterSharedAccess(true);
return rv;
}
data -= v;
FOLLY_TEST_DSCHED_VLOG(this << ".fetch_sub(" << std::hex << v << ") -> "
<< std::hex << rv);
- DeterministicSchedule::afterSharedAccess();
+ DeterministicSchedule::afterSharedAccess(true);
return rv;
}
T rv = (data &= v);
FOLLY_TEST_DSCHED_VLOG(this << " &= " << std::hex << v << " -> " << std::hex
<< rv);
- DeterministicSchedule::afterSharedAccess();
+ DeterministicSchedule::afterSharedAccess(true);
return rv;
}
data &= v;
FOLLY_TEST_DSCHED_VLOG(this << ".fetch_and(" << std::hex << v << ") -> "
<< std::hex << rv);
- DeterministicSchedule::afterSharedAccess();
+ DeterministicSchedule::afterSharedAccess(true);
return rv;
}
T rv = (data |= v);
FOLLY_TEST_DSCHED_VLOG(this << " |= " << std::hex << v << " -> " << std::hex
<< rv);
- DeterministicSchedule::afterSharedAccess();
+ DeterministicSchedule::afterSharedAccess(true);
return rv;
}
data |= v;
FOLLY_TEST_DSCHED_VLOG(this << ".fetch_or(" << std::hex << v << ") -> "
<< std::hex << rv);
- DeterministicSchedule::afterSharedAccess();
+ DeterministicSchedule::afterSharedAccess(true);
return rv;
}
T rv = (data ^= v);
FOLLY_TEST_DSCHED_VLOG(this << " ^= " << std::hex << v << " -> " << std::hex
<< rv);
- DeterministicSchedule::afterSharedAccess();
+ DeterministicSchedule::afterSharedAccess(true);
return rv;
}
data ^= v;
FOLLY_TEST_DSCHED_VLOG(this << ".fetch_xor(" << std::hex << v << ") -> "
<< std::hex << rv);
- DeterministicSchedule::afterSharedAccess();
+ DeterministicSchedule::afterSharedAccess(true);
return rv;
}
+
+ /** Read the value of the atomic variable without context switching */
+ T load_direct() const noexcept {
+ return data.load(std::memory_order_relaxed);
+ }
};
/**