Commit 566f0478 authored by Doron Roberts-Kedes's avatar Doron Roberts-Kedes Committed by Facebook Github Bot

DeterministicSchedule: Deschedule threads waiting to acquire DeterministicMutex

Summary: Eliminate spinlock behavior from DeterministicMutex::lock by descheduling threads waiting to acquire the mutex, and placing the thread local semaphore in a waitqueue for the mutex. The unlocking thread reschedules a single waiting thread if the workqueue is non empty.

Reviewed By: djwatson

Differential Revision: D8789304

fbshipit-source-id: 8ffe3e289c9abfe7515b678ff98f0cefef2461c0
parent a72a920c
......@@ -183,6 +183,22 @@ void DeterministicSchedule::clearAuxChk() {
aux_chk = nullptr;
}
void DeterministicSchedule::reschedule(sem_t* sem) {
auto sched = tls_sched;
if (sched) {
sched->sems_.push_back(sem);
}
}
sem_t* DeterministicSchedule::descheduleCurrentThread() {
auto sched = tls_sched;
if (sched) {
sched->sems_.erase(
std::find(sched->sems_.begin(), sched->sems_.end(), tls_sem));
}
return tls_sem;
}
sem_t* DeterministicSchedule::beforeThreadCreate() {
sem_t* s = new sem_t;
sem_init(s, 0, 0);
......
......@@ -23,6 +23,7 @@
#include <atomic>
#include <functional>
#include <mutex>
#include <queue>
#include <thread>
#include <unordered_set>
#include <vector>
......@@ -182,6 +183,12 @@ class DeterministicSchedule : boost::noncopyable {
/** Clears the function set by setAuxChk */
static void clearAuxChk();
/** Remove the current thread's semaphore from sems_ */
static sem_t* descheduleCurrentThread();
/** Add sem back into sems_ */
static void reschedule(sem_t* sem);
private:
static FOLLY_TLS sem_t* tls_sem;
static FOLLY_TLS DeterministicSchedule* tls_sched;
......@@ -453,6 +460,7 @@ struct DeterministicAtomic {
*/
struct DeterministicMutex {
std::mutex m;
std::queue<sem_t*> waiters_;
DeterministicMutex() = default;
~DeterministicMutex() = default;
......@@ -461,12 +469,17 @@ struct DeterministicMutex {
void lock() {
FOLLY_TEST_DSCHED_VLOG(this << ".lock()");
while (!try_lock()) {
// Not calling m.lock() in order to avoid deadlock when the
// mutex m is held by another thread. The deadlock would be
// between the call to m.lock() and the lock holder's wait on
// its own tls_sem scheduling semaphore.
DeterministicSchedule::beforeSharedAccess();
while (!m.try_lock()) {
sem_t* sem = DeterministicSchedule::descheduleCurrentThread();
if (sem) {
waiters_.push(sem);
}
DeterministicSchedule::afterSharedAccess();
// Wait to be scheduled by unlock
DeterministicSchedule::beforeSharedAccess();
}
DeterministicSchedule::afterSharedAccess();
}
bool try_lock() {
......@@ -480,6 +493,13 @@ struct DeterministicMutex {
void unlock() {
FOLLY_TEST_DSCHED_VLOG(this << ".unlock()");
m.unlock();
DeterministicSchedule::beforeSharedAccess();
if (!waiters_.empty()) {
sem_t* sem = waiters_.front();
DeterministicSchedule::reschedule(sem);
waiters_.pop();
}
DeterministicSchedule::afterSharedAccess();
}
};
} // namespace test
......
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