Commit a4fc31fc authored by Nathan Bronson's avatar Nathan Bronson Committed by facebook-github-bot-1

improvements to DeterministicSchedule

Summary: This diff isolates the CacheLocality back end for DeterministicSchedule
from one invocation to the next (the last one was deterministic
across the entire program but not per-DeterministicSchedule instance),
and makes it easy to do extensive tracing of memory accesses during
a deterministic test.  These changes were made while tracking down a
bug, but don't fix any bugs on their own (and in fact don't affect any
production code at all).

Reviewed By: @yfeldblum

Differential Revision: D1842390
parent 2f0b7834
...@@ -23,21 +23,22 @@ ...@@ -23,21 +23,22 @@
#include <unordered_map> #include <unordered_map>
#include <assert.h> #include <assert.h>
namespace folly { namespace test { namespace folly {
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;
// access is protected by futexLock // access is protected by futexLock
static std::unordered_map<detail::Futex<DeterministicAtomic>*, static std::unordered_map<detail::Futex<DeterministicAtomic>*,
std::list<std::pair<uint32_t,bool*>>> futexQueues; std::list<std::pair<uint32_t, bool*>>> futexQueues;
static std::mutex futexLock; static std::mutex futexLock;
DeterministicSchedule::DeterministicSchedule( DeterministicSchedule::DeterministicSchedule(
const std::function<int(int)>& scheduler) const std::function<int(int)>& scheduler)
: scheduler_(scheduler) : scheduler_(scheduler), nextThreadId_(1) {
{
assert(tls_sem == nullptr); assert(tls_sem == nullptr);
assert(tls_sched == nullptr); assert(tls_sched == nullptr);
...@@ -55,8 +56,7 @@ DeterministicSchedule::~DeterministicSchedule() { ...@@ -55,8 +56,7 @@ DeterministicSchedule::~DeterministicSchedule() {
beforeThreadExit(); beforeThreadExit();
} }
std::function<int(int)> std::function<int(int)> DeterministicSchedule::uniform(long seed) {
DeterministicSchedule::uniform(long seed) {
auto rand = std::make_shared<std::ranlux48>(seed); auto rand = std::make_shared<std::ranlux48>(seed);
return [rand](size_t numActive) { return [rand](size_t numActive) {
auto dist = std::uniform_int_distribution<int>(0, numActive - 1); auto dist = std::uniform_int_distribution<int>(0, numActive - 1);
...@@ -66,12 +66,10 @@ DeterministicSchedule::uniform(long seed) { ...@@ -66,12 +66,10 @@ DeterministicSchedule::uniform(long seed) {
struct UniformSubset { struct UniformSubset {
UniformSubset(long seed, int subsetSize, int stepsBetweenSelect) UniformSubset(long seed, int subsetSize, int stepsBetweenSelect)
: uniform_(DeterministicSchedule::uniform(seed)) : uniform_(DeterministicSchedule::uniform(seed)),
, subsetSize_(subsetSize) subsetSize_(subsetSize),
, stepsBetweenSelect_(stepsBetweenSelect) stepsBetweenSelect_(stepsBetweenSelect),
, stepsLeft_(0) stepsLeft_(0) {}
{
}
size_t operator()(size_t numActive) { size_t operator()(size_t numActive) {
adjustPermSize(numActive); adjustPermSize(numActive);
...@@ -93,8 +91,9 @@ struct UniformSubset { ...@@ -93,8 +91,9 @@ struct UniformSubset {
void adjustPermSize(size_t numActive) { void adjustPermSize(size_t numActive) {
if (perm_.size() > numActive) { if (perm_.size() > numActive) {
perm_.erase(std::remove_if(perm_.begin(), perm_.end(), perm_.erase(std::remove_if(perm_.begin(), perm_.end(), [=](size_t x) {
[=](size_t x){ return x >= numActive; }), perm_.end()); return x >= numActive;
}), perm_.end());
} else { } else {
while (perm_.size() < numActive) { while (perm_.size() < numActive) {
perm_.push_back(perm_.size()); perm_.push_back(perm_.size());
...@@ -111,21 +110,20 @@ struct UniformSubset { ...@@ -111,21 +110,20 @@ struct UniformSubset {
} }
}; };
std::function<int(int)> std::function<int(int)> DeterministicSchedule::uniformSubset(long seed,
DeterministicSchedule::uniformSubset(long seed, int n, int m) { int n,
int m) {
auto gen = std::make_shared<UniformSubset>(seed, n, m); auto gen = std::make_shared<UniformSubset>(seed, n, m);
return [=](size_t numActive) { return (*gen)(numActive); }; return [=](size_t numActive) { return (*gen)(numActive); };
} }
void void DeterministicSchedule::beforeSharedAccess() {
DeterministicSchedule::beforeSharedAccess() {
if (tls_sem) { if (tls_sem) {
sem_wait(tls_sem); sem_wait(tls_sem);
} }
} }
void void DeterministicSchedule::afterSharedAccess() {
DeterministicSchedule::afterSharedAccess() {
auto sched = tls_sched; auto sched = tls_sched;
if (!sched) { if (!sched) {
return; return;
...@@ -134,16 +132,29 @@ DeterministicSchedule::afterSharedAccess() { ...@@ -134,16 +132,29 @@ DeterministicSchedule::afterSharedAccess() {
sem_post(sched->sems_[sched->scheduler_(sched->sems_.size())]); sem_post(sched->sems_[sched->scheduler_(sched->sems_.size())]);
} }
int int DeterministicSchedule::getRandNumber(int n) {
DeterministicSchedule::getRandNumber(int n) {
if (tls_sched) { if (tls_sched) {
return tls_sched->scheduler_(n); return tls_sched->scheduler_(n);
} }
return std::rand() % n; return std::rand() % n;
} }
sem_t* int DeterministicSchedule::getcpu(unsigned* cpu, unsigned* node, void* unused) {
DeterministicSchedule::beforeThreadCreate() { if (!tls_threadId && tls_sched) {
beforeSharedAccess();
tls_threadId = tls_sched->nextThreadId_++;
afterSharedAccess();
}
if (cpu) {
*cpu = tls_threadId;
}
if (node) {
*node = tls_threadId;
}
return 0;
}
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);
beforeSharedAccess(); beforeSharedAccess();
...@@ -152,8 +163,7 @@ DeterministicSchedule::beforeThreadCreate() { ...@@ -152,8 +163,7 @@ DeterministicSchedule::beforeThreadCreate() {
return s; return s;
} }
void void DeterministicSchedule::afterThreadCreate(sem_t* sem) {
DeterministicSchedule::afterThreadCreate(sem_t* sem) {
assert(tls_sem == nullptr); assert(tls_sem == nullptr);
assert(tls_sched == nullptr); assert(tls_sched == nullptr);
tls_sem = sem; tls_sem = sem;
...@@ -168,13 +178,13 @@ DeterministicSchedule::afterThreadCreate(sem_t* sem) { ...@@ -168,13 +178,13 @@ DeterministicSchedule::afterThreadCreate(sem_t* sem) {
} }
} }
void void DeterministicSchedule::beforeThreadExit() {
DeterministicSchedule::beforeThreadExit() {
assert(tls_sched == this); assert(tls_sched == this);
beforeSharedAccess(); beforeSharedAccess();
sems_.erase(std::find(sems_.begin(), sems_.end(), tls_sem)); sems_.erase(std::find(sems_.begin(), sems_.end(), tls_sem));
active_.erase(std::this_thread::get_id()); active_.erase(std::this_thread::get_id());
if (sems_.size() > 0) { if (sems_.size() > 0) {
FOLLY_TEST_DSCHED_VLOG("exiting");
afterSharedAccess(); afterSharedAccess();
} }
sem_destroy(tls_sem); sem_destroy(tls_sem);
...@@ -183,67 +193,73 @@ DeterministicSchedule::beforeThreadExit() { ...@@ -183,67 +193,73 @@ DeterministicSchedule::beforeThreadExit() {
tls_sched = nullptr; tls_sched = nullptr;
} }
void void DeterministicSchedule::join(std::thread& child) {
DeterministicSchedule::join(std::thread& child) {
auto sched = tls_sched; auto sched = tls_sched;
if (sched) { if (sched) {
bool done = false; bool done = false;
while (!done) { while (!done) {
beforeSharedAccess(); beforeSharedAccess();
done = !sched->active_.count(child.get_id()); done = !sched->active_.count(child.get_id());
if (done) {
FOLLY_TEST_DSCHED_VLOG("joined " << std::hex << child.get_id());
}
afterSharedAccess(); afterSharedAccess();
} }
} }
child.join(); child.join();
} }
void void DeterministicSchedule::post(sem_t* sem) {
DeterministicSchedule::post(sem_t* sem) {
beforeSharedAccess(); beforeSharedAccess();
sem_post(sem); sem_post(sem);
FOLLY_TEST_DSCHED_VLOG("sem_post(" << sem << ")");
afterSharedAccess(); afterSharedAccess();
} }
bool bool DeterministicSchedule::tryWait(sem_t* sem) {
DeterministicSchedule::tryWait(sem_t* sem) {
beforeSharedAccess(); beforeSharedAccess();
int rv = sem_trywait(sem); int rv = sem_trywait(sem);
int e = rv == 0 ? 0 : errno;
FOLLY_TEST_DSCHED_VLOG("sem_trywait(" << sem << ") = " << rv
<< " errno=" << e);
afterSharedAccess(); afterSharedAccess();
if (rv == 0) { if (rv == 0) {
return true; return true;
} else { } else {
assert(errno == EAGAIN); assert(e == EAGAIN);
return false; return false;
} }
} }
void void DeterministicSchedule::wait(sem_t* sem) {
DeterministicSchedule::wait(sem_t* sem) {
while (!tryWait(sem)) { while (!tryWait(sem)) {
// we're not busy waiting because this is a deterministic schedule // we're not busy waiting because this is a deterministic schedule
} }
} }
}
}
}} namespace folly {
namespace detail {
namespace folly { namespace detail {
using namespace test; using namespace test;
using namespace std::chrono; using namespace std::chrono;
template <> template <>
FutexResult FutexResult Futex<DeterministicAtomic>::futexWaitImpl(
Futex<DeterministicAtomic>::futexWaitImpl( uint32_t expected,
uint32_t expected, time_point<system_clock>* absSystemTimeout,
time_point<system_clock>* absSystemTimeout, time_point<steady_clock>* absSteadyTimeout,
time_point<steady_clock>* absSteadyTimeout, uint32_t waitMask) {
uint32_t waitMask) {
bool hasTimeout = absSystemTimeout != nullptr || absSteadyTimeout != nullptr; bool hasTimeout = absSystemTimeout != nullptr || absSteadyTimeout != nullptr;
bool awoken = false; bool awoken = false;
FutexResult result = FutexResult::AWOKEN; FutexResult result = FutexResult::AWOKEN;
int futexErrno = 0; int futexErrno = 0;
DeterministicSchedule::beforeSharedAccess(); DeterministicSchedule::beforeSharedAccess();
FOLLY_TEST_DSCHED_VLOG(this << ".futexWait(" << std::hex << expected
<< ", .., " << std::hex << waitMask
<< ") beginning..");
futexLock.lock(); futexLock.lock();
if (data == expected) { if (data == expected) {
auto& queue = futexQueues[this]; auto& queue = futexQueues[this];
...@@ -260,17 +276,16 @@ Futex<DeterministicAtomic>::futexWaitImpl( ...@@ -260,17 +276,16 @@ Futex<DeterministicAtomic>::futexWaitImpl(
// a 10% probability if we haven't been woken up already // a 10% probability if we haven't been woken up already
if (!awoken && hasTimeout && if (!awoken && hasTimeout &&
DeterministicSchedule::getRandNumber(100) < 10) { DeterministicSchedule::getRandNumber(100) < 10) {
assert(futexQueues.count(this) != 0 && assert(futexQueues.count(this) != 0 && &futexQueues[this] == &queue);
&futexQueues[this] == &queue);
queue.erase(ours); queue.erase(ours);
if (queue.empty()) { if (queue.empty()) {
futexQueues.erase(this); futexQueues.erase(this);
} }
// Simulate ETIMEDOUT 90% of the time and other failures // Simulate ETIMEDOUT 90% of the time and other failures
// remaining time // remaining time
result = result = DeterministicSchedule::getRandNumber(100) >= 10
DeterministicSchedule::getRandNumber(100) >= 10 ? FutexResult::TIMEDOUT
? FutexResult::TIMEDOUT : FutexResult::INTERRUPTED; : FutexResult::INTERRUPTED;
break; break;
} }
} }
...@@ -278,13 +293,31 @@ Futex<DeterministicAtomic>::futexWaitImpl( ...@@ -278,13 +293,31 @@ Futex<DeterministicAtomic>::futexWaitImpl(
result = FutexResult::VALUE_CHANGED; result = FutexResult::VALUE_CHANGED;
} }
futexLock.unlock(); futexLock.unlock();
char const* resultStr = "?";
switch (result) {
case FutexResult::AWOKEN:
resultStr = "AWOKEN";
break;
case FutexResult::TIMEDOUT:
resultStr = "TIMEDOUT";
break;
case FutexResult::INTERRUPTED:
resultStr = "INTERRUPTED";
break;
case FutexResult::VALUE_CHANGED:
resultStr = "VALUE_CHANGED";
break;
}
FOLLY_TEST_DSCHED_VLOG(this << ".futexWait(" << std::hex << expected
<< ", .., " << std::hex << waitMask << ") -> "
<< resultStr);
DeterministicSchedule::afterSharedAccess(); DeterministicSchedule::afterSharedAccess();
return result; return result;
} }
template<> template <>
int int Futex<DeterministicAtomic>::futexWake(int count, uint32_t wakeMask) {
Futex<DeterministicAtomic>::futexWake(int count, uint32_t wakeMask) {
int rv = 0; int rv = 0;
DeterministicSchedule::beforeSharedAccess(); DeterministicSchedule::beforeSharedAccess();
futexLock.lock(); futexLock.lock();
...@@ -304,44 +337,36 @@ Futex<DeterministicAtomic>::futexWake(int count, uint32_t wakeMask) { ...@@ -304,44 +337,36 @@ Futex<DeterministicAtomic>::futexWake(int count, uint32_t wakeMask) {
} }
} }
futexLock.unlock(); futexLock.unlock();
FOLLY_TEST_DSCHED_VLOG(this << ".futexWake(" << count << ", " << std::hex
<< wakeMask << ") -> " << rv);
DeterministicSchedule::afterSharedAccess(); DeterministicSchedule::afterSharedAccess();
return rv; return rv;
} }
template <>
template<>
CacheLocality const& CacheLocality::system<test::DeterministicAtomic>() { CacheLocality const& CacheLocality::system<test::DeterministicAtomic>() {
static CacheLocality cache(CacheLocality::uniform(16)); static CacheLocality cache(CacheLocality::uniform(16));
return cache; return cache;
} }
template<> template <>
test::DeterministicAtomic<size_t>
SequentialThreadId<test::DeterministicAtomic>::prevId(0);
template<>
FOLLY_TLS size_t
SequentialThreadId<test::DeterministicAtomic>::currentId(0);
template<>
const AccessSpreader<test::DeterministicAtomic> const AccessSpreader<test::DeterministicAtomic>
AccessSpreader<test::DeterministicAtomic>::stripeByCore( AccessSpreader<test::DeterministicAtomic>::stripeByCore(
CacheLocality::system<>().numCachesByLevel.front()); CacheLocality::system<>().numCachesByLevel.front());
template<> template <>
const AccessSpreader<test::DeterministicAtomic> const AccessSpreader<test::DeterministicAtomic>
AccessSpreader<test::DeterministicAtomic>::stripeByChip( AccessSpreader<test::DeterministicAtomic>::stripeByChip(
CacheLocality::system<>().numCachesByLevel.back()); CacheLocality::system<>().numCachesByLevel.back());
template<>
AccessSpreaderArray<test::DeterministicAtomic,128>
AccessSpreaderArray<test::DeterministicAtomic,128>::sharedInstance = {};
template <>
AccessSpreaderArray<test::DeterministicAtomic, 128>
AccessSpreaderArray<test::DeterministicAtomic, 128>::sharedInstance = {};
template<> template <>
Getcpu::Func Getcpu::Func AccessSpreader<test::DeterministicAtomic>::pickGetcpuFunc(
AccessSpreader<test::DeterministicAtomic>::pickGetcpuFunc(size_t numStripes) { size_t numStripes) {
return &SequentialThreadId<test::DeterministicAtomic>::getcpu; return &DeterministicSchedule::getcpu;
}
}
} }
}}
...@@ -25,12 +25,23 @@ ...@@ -25,12 +25,23 @@
#include <semaphore.h> #include <semaphore.h>
#include <errno.h> #include <errno.h>
#include <assert.h> #include <assert.h>
#include <glog/logging.h>
#include <folly/ScopeGuard.h> #include <folly/ScopeGuard.h>
#include <folly/detail/CacheLocality.h> #include <folly/detail/CacheLocality.h>
#include <folly/detail/Futex.h> #include <folly/detail/Futex.h>
namespace folly { namespace test { namespace folly {
namespace test {
// This is ugly, but better perf for DeterministicAtomic translates
// directly to more states explored and tested
#define FOLLY_TEST_DSCHED_VLOG(msg...) \
do { \
if (false) { \
VLOG(2) << std::hex << std::this_thread::get_id() << ": " << msg; \
} \
} while (false)
/** /**
* DeterministicSchedule coordinates the inter-thread communication of a * DeterministicSchedule coordinates the inter-thread communication of a
...@@ -80,7 +91,8 @@ class DeterministicSchedule : boost::noncopyable { ...@@ -80,7 +91,8 @@ class DeterministicSchedule : boost::noncopyable {
* runnable thread. The subset is chosen with size n, and the choice * runnable thread. The subset is chosen with size n, and the choice
* is made every m steps. * is made every m steps.
*/ */
static std::function<int(int)> uniformSubset(long seed, int n = 2, static std::function<int(int)> uniformSubset(long seed,
int n = 2,
int m = 64); int m = 64);
/** Obtains permission for the current thread to perform inter-thread /** Obtains permission for the current thread to perform inter-thread
...@@ -99,13 +111,23 @@ class DeterministicSchedule : boost::noncopyable { ...@@ -99,13 +111,23 @@ class DeterministicSchedule : boost::noncopyable {
auto sched = tls_sched; auto sched = tls_sched;
auto sem = sched ? sched->beforeThreadCreate() : nullptr; auto sem = sched ? sched->beforeThreadCreate() : nullptr;
auto child = std::thread([=](Args... a) { auto child = std::thread([=](Args... a) {
if (sched) sched->afterThreadCreate(sem); if (sched) {
SCOPE_EXIT { if (sched) sched->beforeThreadExit(); }; sched->afterThreadCreate(sem);
beforeSharedAccess();
FOLLY_TEST_DSCHED_VLOG("running");
afterSharedAccess();
}
SCOPE_EXIT {
if (sched) {
sched->beforeThreadExit();
}
};
func(a...); func(a...);
}, args...); }, args...);
if (sched) { if (sched) {
beforeSharedAccess(); beforeSharedAccess();
sched->active_.insert(child.get_id()); sched->active_.insert(child.get_id());
FOLLY_TEST_DSCHED_VLOG("forked " << std::hex << child.get_id());
afterSharedAccess(); afterSharedAccess();
} }
return child; return child;
...@@ -128,20 +150,24 @@ class DeterministicSchedule : boost::noncopyable { ...@@ -128,20 +150,24 @@ class DeterministicSchedule : boost::noncopyable {
* not set-up it falls back to std::rand() */ * not set-up it falls back to std::rand() */
static int getRandNumber(int n); static int getRandNumber(int n);
/** Deterministic implemencation of getcpu */
static int getcpu(unsigned* cpu, unsigned* node, void* unused);
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;
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_;
sem_t* beforeThreadCreate(); sem_t* beforeThreadCreate();
void afterThreadCreate(sem_t*); void afterThreadCreate(sem_t*);
void beforeThreadExit(); void beforeThreadExit();
}; };
/** /**
* DeterministicAtomic<T> is a drop-in replacement std::atomic<T> that * DeterministicAtomic<T> is a drop-in replacement std::atomic<T> that
* cooperates with DeterministicSchedule. * cooperates with DeterministicSchedule.
...@@ -152,29 +178,33 @@ struct DeterministicAtomic { ...@@ -152,29 +178,33 @@ struct DeterministicAtomic {
DeterministicAtomic() = default; DeterministicAtomic() = default;
~DeterministicAtomic() = default; ~DeterministicAtomic() = default;
DeterministicAtomic(DeterministicAtomic<T> const &) = delete; DeterministicAtomic(DeterministicAtomic<T> const&) = delete;
DeterministicAtomic<T>& operator= (DeterministicAtomic<T> const &) = delete; DeterministicAtomic<T>& operator=(DeterministicAtomic<T> const&) = delete;
constexpr /* implicit */ DeterministicAtomic(T v) noexcept : data(v) {} constexpr /* implicit */ DeterministicAtomic(T v) noexcept : data(v) {}
bool is_lock_free() const noexcept { bool is_lock_free() const noexcept { return data.is_lock_free(); }
return data.is_lock_free();
}
bool compare_exchange_strong( bool compare_exchange_strong(
T& v0, T v1, T& v0, T v1, std::memory_order mo = std::memory_order_seq_cst) noexcept {
std::memory_order mo = std::memory_order_seq_cst) noexcept {
DeterministicSchedule::beforeSharedAccess(); DeterministicSchedule::beforeSharedAccess();
auto orig = v0;
bool rv = data.compare_exchange_strong(v0, v1, mo); bool rv = data.compare_exchange_strong(v0, v1, mo);
FOLLY_TEST_DSCHED_VLOG(this << ".compare_exchange_strong(" << std::hex
<< orig << ", " << std::hex << v1 << ") -> "
<< rv << "," << std::hex << v0);
DeterministicSchedule::afterSharedAccess(); DeterministicSchedule::afterSharedAccess();
return rv; return rv;
} }
bool compare_exchange_weak( bool compare_exchange_weak(
T& v0, T v1, T& v0, T v1, std::memory_order mo = std::memory_order_seq_cst) noexcept {
std::memory_order mo = std::memory_order_seq_cst) noexcept {
DeterministicSchedule::beforeSharedAccess(); DeterministicSchedule::beforeSharedAccess();
auto orig = v0;
bool rv = data.compare_exchange_weak(v0, v1, mo); bool rv = data.compare_exchange_weak(v0, v1, mo);
FOLLY_TEST_DSCHED_VLOG(this << ".compare_exchange_weak(" << std::hex << orig
<< ", " << std::hex << v1 << ") -> " << rv
<< "," << std::hex << v0);
DeterministicSchedule::afterSharedAccess(); DeterministicSchedule::afterSharedAccess();
return rv; return rv;
} }
...@@ -182,13 +212,16 @@ struct DeterministicAtomic { ...@@ -182,13 +212,16 @@ struct DeterministicAtomic {
T exchange(T v, std::memory_order mo = std::memory_order_seq_cst) noexcept { T exchange(T v, std::memory_order mo = std::memory_order_seq_cst) noexcept {
DeterministicSchedule::beforeSharedAccess(); DeterministicSchedule::beforeSharedAccess();
T rv = data.exchange(v, mo); T rv = data.exchange(v, mo);
FOLLY_TEST_DSCHED_VLOG(this << ".exchange(" << std::hex << v << ") -> "
<< std::hex << rv);
DeterministicSchedule::afterSharedAccess(); DeterministicSchedule::afterSharedAccess();
return rv; return rv;
} }
/* implicit */ operator T () const noexcept { /* implicit */ operator T() const noexcept {
DeterministicSchedule::beforeSharedAccess(); DeterministicSchedule::beforeSharedAccess();
T rv = data; T rv = data;
FOLLY_TEST_DSCHED_VLOG(this << "() -> " << std::hex << rv);
DeterministicSchedule::afterSharedAccess(); DeterministicSchedule::afterSharedAccess();
return rv; return rv;
} }
...@@ -196,13 +229,15 @@ struct DeterministicAtomic { ...@@ -196,13 +229,15 @@ struct DeterministicAtomic {
T load(std::memory_order mo = std::memory_order_seq_cst) const noexcept { T load(std::memory_order mo = std::memory_order_seq_cst) const noexcept {
DeterministicSchedule::beforeSharedAccess(); DeterministicSchedule::beforeSharedAccess();
T rv = data.load(mo); T rv = data.load(mo);
FOLLY_TEST_DSCHED_VLOG(this << ".load() -> " << std::hex << rv);
DeterministicSchedule::afterSharedAccess(); DeterministicSchedule::afterSharedAccess();
return rv; return rv;
} }
T operator= (T v) noexcept { T operator=(T v) noexcept {
DeterministicSchedule::beforeSharedAccess(); DeterministicSchedule::beforeSharedAccess();
T rv = (data = v); T rv = (data = v);
FOLLY_TEST_DSCHED_VLOG(this << " = " << std::hex << v);
DeterministicSchedule::afterSharedAccess(); DeterministicSchedule::afterSharedAccess();
return rv; return rv;
} }
...@@ -210,40 +245,47 @@ struct DeterministicAtomic { ...@@ -210,40 +245,47 @@ struct DeterministicAtomic {
void store(T v, std::memory_order mo = std::memory_order_seq_cst) noexcept { void store(T v, std::memory_order mo = std::memory_order_seq_cst) noexcept {
DeterministicSchedule::beforeSharedAccess(); DeterministicSchedule::beforeSharedAccess();
data.store(v, mo); data.store(v, mo);
FOLLY_TEST_DSCHED_VLOG(this << ".store(" << std::hex << v << ")");
DeterministicSchedule::afterSharedAccess(); DeterministicSchedule::afterSharedAccess();
} }
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);
DeterministicSchedule::afterSharedAccess(); DeterministicSchedule::afterSharedAccess();
return rv; return rv;
} }
T operator++ (int postDummy) noexcept { T operator++(int postDummy) noexcept {
DeterministicSchedule::beforeSharedAccess(); DeterministicSchedule::beforeSharedAccess();
T rv = data++; T rv = data++;
FOLLY_TEST_DSCHED_VLOG(this << " post++ -> " << std::hex << rv);
DeterministicSchedule::afterSharedAccess(); DeterministicSchedule::afterSharedAccess();
return rv; return rv;
} }
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);
DeterministicSchedule::afterSharedAccess(); DeterministicSchedule::afterSharedAccess();
return rv; return rv;
} }
T operator-- (int postDummy) noexcept { T operator--(int postDummy) noexcept {
DeterministicSchedule::beforeSharedAccess(); DeterministicSchedule::beforeSharedAccess();
T rv = data--; T rv = data--;
FOLLY_TEST_DSCHED_VLOG(this << " post-- -> " << std::hex << rv);
DeterministicSchedule::afterSharedAccess(); DeterministicSchedule::afterSharedAccess();
return rv; return rv;
} }
T operator+= (T v) noexcept { T operator+=(T v) noexcept {
DeterministicSchedule::beforeSharedAccess(); DeterministicSchedule::beforeSharedAccess();
T rv = (data += v); T rv = (data += v);
FOLLY_TEST_DSCHED_VLOG(this << " += " << std::hex << v << " -> " << std::hex
<< rv);
DeterministicSchedule::afterSharedAccess(); DeterministicSchedule::afterSharedAccess();
return rv; return rv;
} }
...@@ -252,13 +294,17 @@ struct DeterministicAtomic { ...@@ -252,13 +294,17 @@ struct DeterministicAtomic {
DeterministicSchedule::beforeSharedAccess(); DeterministicSchedule::beforeSharedAccess();
T rv = data; T rv = data;
data += v; data += v;
FOLLY_TEST_DSCHED_VLOG(this << ".fetch_add(" << std::hex << v << ") -> "
<< std::hex << rv);
DeterministicSchedule::afterSharedAccess(); DeterministicSchedule::afterSharedAccess();
return rv; return rv;
} }
T operator-= (T v) noexcept { T operator-=(T v) noexcept {
DeterministicSchedule::beforeSharedAccess(); DeterministicSchedule::beforeSharedAccess();
T rv = (data -= v); T rv = (data -= v);
FOLLY_TEST_DSCHED_VLOG(this << " -= " << std::hex << v << " -> " << std::hex
<< rv);
DeterministicSchedule::afterSharedAccess(); DeterministicSchedule::afterSharedAccess();
return rv; return rv;
} }
...@@ -267,13 +313,17 @@ struct DeterministicAtomic { ...@@ -267,13 +313,17 @@ struct DeterministicAtomic {
DeterministicSchedule::beforeSharedAccess(); DeterministicSchedule::beforeSharedAccess();
T rv = data; T rv = data;
data -= v; data -= v;
FOLLY_TEST_DSCHED_VLOG(this << ".fetch_sub(" << std::hex << v << ") -> "
<< std::hex << rv);
DeterministicSchedule::afterSharedAccess(); DeterministicSchedule::afterSharedAccess();
return rv; return rv;
} }
T operator&= (T v) noexcept { T operator&=(T v) noexcept {
DeterministicSchedule::beforeSharedAccess(); DeterministicSchedule::beforeSharedAccess();
T rv = (data &= v); T rv = (data &= v);
FOLLY_TEST_DSCHED_VLOG(this << " &= " << std::hex << v << " -> " << std::hex
<< rv);
DeterministicSchedule::afterSharedAccess(); DeterministicSchedule::afterSharedAccess();
return rv; return rv;
} }
...@@ -282,13 +332,17 @@ struct DeterministicAtomic { ...@@ -282,13 +332,17 @@ struct DeterministicAtomic {
DeterministicSchedule::beforeSharedAccess(); DeterministicSchedule::beforeSharedAccess();
T rv = data; T rv = data;
data &= v; data &= v;
FOLLY_TEST_DSCHED_VLOG(this << ".fetch_and(" << std::hex << v << ") -> "
<< std::hex << rv);
DeterministicSchedule::afterSharedAccess(); DeterministicSchedule::afterSharedAccess();
return rv; return rv;
} }
T operator|= (T v) noexcept { T operator|=(T v) noexcept {
DeterministicSchedule::beforeSharedAccess(); DeterministicSchedule::beforeSharedAccess();
T rv = (data |= v); T rv = (data |= v);
FOLLY_TEST_DSCHED_VLOG(this << " |= " << std::hex << v << " -> " << std::hex
<< rv);
DeterministicSchedule::afterSharedAccess(); DeterministicSchedule::afterSharedAccess();
return rv; return rv;
} }
...@@ -297,13 +351,17 @@ struct DeterministicAtomic { ...@@ -297,13 +351,17 @@ struct DeterministicAtomic {
DeterministicSchedule::beforeSharedAccess(); DeterministicSchedule::beforeSharedAccess();
T rv = data; T rv = data;
data |= v; data |= v;
FOLLY_TEST_DSCHED_VLOG(this << ".fetch_or(" << std::hex << v << ") -> "
<< std::hex << rv);
DeterministicSchedule::afterSharedAccess(); DeterministicSchedule::afterSharedAccess();
return rv; return rv;
} }
T operator^= (T v) noexcept { T operator^=(T v) noexcept {
DeterministicSchedule::beforeSharedAccess(); DeterministicSchedule::beforeSharedAccess();
T rv = (data ^= v); T rv = (data ^= v);
FOLLY_TEST_DSCHED_VLOG(this << " ^= " << std::hex << v << " -> " << std::hex
<< rv);
DeterministicSchedule::afterSharedAccess(); DeterministicSchedule::afterSharedAccess();
return rv; return rv;
} }
...@@ -312,29 +370,32 @@ struct DeterministicAtomic { ...@@ -312,29 +370,32 @@ struct DeterministicAtomic {
DeterministicSchedule::beforeSharedAccess(); DeterministicSchedule::beforeSharedAccess();
T rv = data; T rv = data;
data ^= v; data ^= v;
FOLLY_TEST_DSCHED_VLOG(this << ".fetch_xor(" << std::hex << v << ") -> "
<< std::hex << rv);
DeterministicSchedule::afterSharedAccess(); DeterministicSchedule::afterSharedAccess();
return rv; return rv;
} }
}; };
}
}} // namespace folly::test } // namespace folly::test
/* Specialization declarations */ /* Specialization declarations */
namespace folly { namespace detail { namespace folly {
namespace detail {
template<> template <>
int Futex<test::DeterministicAtomic>::futexWake(int count, uint32_t wakeMask); int Futex<test::DeterministicAtomic>::futexWake(int count, uint32_t wakeMask);
template<> template <>
FutexResult Futex<test::DeterministicAtomic>::futexWaitImpl( FutexResult Futex<test::DeterministicAtomic>::futexWaitImpl(
uint32_t expected, uint32_t expected,
std::chrono::time_point<std::chrono::system_clock>* absSystemTime, std::chrono::time_point<std::chrono::system_clock>* absSystemTime,
std::chrono::time_point<std::chrono::steady_clock>* absSteadyTime, std::chrono::time_point<std::chrono::steady_clock>* absSteadyTime,
uint32_t waitMask); uint32_t waitMask);
template<> template <>
Getcpu::Func Getcpu::Func AccessSpreader<test::DeterministicAtomic>::pickGetcpuFunc(
AccessSpreader<test::DeterministicAtomic>::pickGetcpuFunc(size_t numStripes); size_t numStripes);
}
}} // namespace folly::detail } // namespace folly::detail
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