/*
- * Copyright 2013 Facebook, Inc.
+ * Copyright 2014 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* limitations under the License.
*/
-#include "DeterministicSchedule.h"
+#include <folly/test/DeterministicSchedule.h>
#include <algorithm>
#include <list>
#include <mutex>
namespace folly { namespace test {
-__thread sem_t* DeterministicSchedule::tls_sem;
-__thread DeterministicSchedule* DeterministicSchedule::tls_sched;
+FOLLY_TLS sem_t* DeterministicSchedule::tls_sem;
+FOLLY_TLS DeterministicSchedule* DeterministicSchedule::tls_sched;
// access is protected by futexLock
static std::unordered_map<detail::Futex<DeterministicAtomic>*,
std::function<int(int)>
DeterministicSchedule::uniform(long seed) {
auto rand = std::make_shared<std::ranlux48>(seed);
- return [rand](int numActive) {
+ return [rand](size_t numActive) {
auto dist = std::uniform_int_distribution<int>(0, numActive - 1);
return dist(*rand);
};
{
}
- int operator()(int numActive) {
+ size_t operator()(size_t numActive) {
adjustPermSize(numActive);
if (stepsLeft_-- == 0) {
stepsLeft_ = stepsBetweenSelect_ - 1;
private:
std::function<int(int)> uniform_;
- const int subsetSize_;
+ const size_t subsetSize_;
const int stepsBetweenSelect_;
int stepsLeft_;
// only the first subsetSize_ is properly randomized
std::vector<int> perm_;
- void adjustPermSize(int numActive) {
+ void adjustPermSize(size_t numActive) {
if (perm_.size() > numActive) {
perm_.erase(std::remove_if(perm_.begin(), perm_.end(),
- [=](int x){ return x >= numActive; }), perm_.end());
+ [=](size_t x){ return x >= numActive; }), perm_.end());
} else {
while (perm_.size() < numActive) {
perm_.push_back(perm_.size());
}
void shufflePrefix() {
- for (int i = 0; i < std::min(int(perm_.size() - 1), subsetSize_); ++i) {
+ for (size_t i = 0; i < std::min(perm_.size() - 1, subsetSize_); ++i) {
int j = uniform_(perm_.size() - i) + i;
std::swap(perm_[i], perm_[j]);
}
std::function<int(int)>
DeterministicSchedule::uniformSubset(long seed, int n, int m) {
auto gen = std::make_shared<UniformSubset>(seed, n, m);
- return [=](int numActive) { return (*gen)(numActive); };
+ return [=](size_t numActive) { return (*gen)(numActive); };
}
void
sem_post(sched->sems_[sched->scheduler_(sched->sems_.size())]);
}
+int
+DeterministicSchedule::getRandNumber(int n) {
+ if (tls_sched) {
+ return tls_sched->scheduler_(n);
+ }
+ return std::rand() % n;
+}
+
sem_t*
DeterministicSchedule::beforeThreadCreate() {
sem_t* s = new sem_t;
namespace folly { namespace detail {
using namespace test;
+using namespace std::chrono;
+
+template <>
+FutexResult
+Futex<DeterministicAtomic>::futexWaitImpl(
+ uint32_t expected,
+ time_point<system_clock>* absSystemTimeout,
+ time_point<steady_clock>* absSteadyTimeout,
+ uint32_t waitMask) {
+ bool hasTimeout = absSystemTimeout != nullptr || absSteadyTimeout != nullptr;
+ bool awoken = false;
+ FutexResult result = FutexResult::AWOKEN;
+ int futexErrno = 0;
-template<>
-bool Futex<DeterministicAtomic>::futexWait(uint32_t expected,
- uint32_t waitMask) {
- bool rv;
DeterministicSchedule::beforeSharedAccess();
futexLock.lock();
- if (data != expected) {
- rv = false;
- } else {
+ if (data == expected) {
auto& queue = futexQueues[this];
- bool done = false;
- queue.push_back(std::make_pair(waitMask, &done));
- while (!done) {
+ queue.push_back(std::make_pair(waitMask, &awoken));
+ auto ours = queue.end();
+ ours--;
+ while (!awoken) {
futexLock.unlock();
DeterministicSchedule::afterSharedAccess();
DeterministicSchedule::beforeSharedAccess();
futexLock.lock();
+
+ // Simulate spurious wake-ups, timeouts each time with
+ // a 10% probability if we haven't been woken up already
+ if (!awoken && hasTimeout &&
+ DeterministicSchedule::getRandNumber(100) < 10) {
+ assert(futexQueues.count(this) != 0 &&
+ &futexQueues[this] == &queue);
+ queue.erase(ours);
+ if (queue.empty()) {
+ futexQueues.erase(this);
+ }
+ // Simulate ETIMEDOUT 90% of the time and other failures
+ // remaining time
+ result =
+ DeterministicSchedule::getRandNumber(100) >= 10
+ ? FutexResult::TIMEDOUT : FutexResult::INTERRUPTED;
+ break;
+ }
}
- rv = true;
+ } else {
+ result = FutexResult::VALUE_CHANGED;
}
futexLock.unlock();
DeterministicSchedule::afterSharedAccess();
- return rv;
+ return result;
}
template<>
-int Futex<DeterministicAtomic>::futexWake(int count, uint32_t wakeMask) {
+int
+Futex<DeterministicAtomic>::futexWake(int count, uint32_t wakeMask) {
int rv = 0;
DeterministicSchedule::beforeSharedAccess();
futexLock.lock();
SequentialThreadId<test::DeterministicAtomic>::prevId(0);
template<>
-__thread size_t SequentialThreadId<test::DeterministicAtomic>::currentId(0);
+FOLLY_TLS size_t
+ SequentialThreadId<test::DeterministicAtomic>::currentId(0);
template<>
const AccessSpreader<test::DeterministicAtomic>