/// This is basically a stripped-down semaphore that supports only a
/// single call to sem_post and a single call to sem_wait.
///
-/// The non-blocking version (Blocking == false) provides more speed
+/// The non-blocking version (MayBlock == false) provides more speed
/// by using only load acquire and store release operations in the
/// critical path, at the cost of disallowing blocking and timing out.
///
/// DeterministicSchedule. By having a much more restrictive
/// lifecycle we can also add a bunch of assertions that can help to
/// catch race conditions ahead of time.
-template <
- template <typename> class Atom = std::atomic,
- bool Blocking = true> // blocking vs spinning
+template <bool MayBlock = true, template <typename> class Atom = std::atomic>
struct Baton {
constexpr Baton() noexcept : state_(INIT) {}
/// destruction or reset()) there can be at most one call to post(),
/// in the single poster version. Any thread may call post().
void post() noexcept {
- if (!Blocking) {
- /// Non-blocking version
+ if (!MayBlock) {
+ /// Spin-only version
///
assert([&] {
auto state = state_.load(std::memory_order_relaxed);
return;
}
- /// Blocking versions
+ /// May-block versions
///
uint32_t before = state_.load(std::memory_order_acquire);
FOLLY_ALWAYS_INLINE bool try_wait_for(
const std::chrono::duration<Rep, Period>& timeout) noexcept {
static_assert(
- Blocking, "Non-blocking Baton does not support try_wait_for.");
+ MayBlock, "Non-blocking Baton does not support try_wait_for.");
if (try_wait()) {
return true;
FOLLY_ALWAYS_INLINE bool try_wait_until(
const std::chrono::time_point<Clock, Duration>& deadline) noexcept {
static_assert(
- Blocking, "Non-blocking Baton does not support try_wait_until.");
+ MayBlock, "Non-blocking Baton does not support try_wait_until.");
if (try_wait()) {
return true;
return;
}
- if (!Blocking) {
+ if (!MayBlock) {
while (!try_wait()) {
std::this_thread::yield();
}
template <
template <typename> class Atom = std::atomic,
- class BatonType = Baton<Atom>>
+ class BatonType = Baton<true, Atom>>
struct LifoSemImpl;
/// LifoSem is a semaphore that wakes its waiters in a manner intended to
typedef DeterministicSchedule DSched;
BENCHMARK(baton_pingpong_blocking, iters) {
- run_pingpong_test<std::atomic, true>(iters);
+ run_pingpong_test<true, std::atomic>(iters);
}
BENCHMARK(baton_pingpong_nonblocking, iters) {
- run_pingpong_test<std::atomic, false>(iters);
+ run_pingpong_test<false, std::atomic>(iters);
}
BENCHMARK_DRAW_LINE()
BENCHMARK(baton_pingpong_emulated_futex_blocking, iters) {
- run_pingpong_test<EmulatedFutexAtomic, true>(iters);
+ run_pingpong_test<true, EmulatedFutexAtomic>(iters);
}
BENCHMARK(baton_pingpong_emulated_futex_nonblocking, iters) {
- run_pingpong_test<EmulatedFutexAtomic, false>(iters);
+ run_pingpong_test<false, EmulatedFutexAtomic>(iters);
}
BENCHMARK_DRAW_LINE()
/// Basic test
TEST(Baton, basic_blocking) {
- run_basic_test<std::atomic, true>();
- run_basic_test<EmulatedFutexAtomic, true>();
- run_basic_test<DeterministicAtomic, true>();
+ run_basic_test<true, std::atomic>();
+ run_basic_test<true, EmulatedFutexAtomic>();
+ run_basic_test<true, DeterministicAtomic>();
}
TEST(Baton, basic_nonblocking) {
- run_basic_test<std::atomic, false>();
- run_basic_test<EmulatedFutexAtomic, false>();
- run_basic_test<DeterministicAtomic, false>();
+ run_basic_test<false, std::atomic>();
+ run_basic_test<false, EmulatedFutexAtomic>();
+ run_basic_test<false, DeterministicAtomic>();
}
/// Ping pong tests
TEST(Baton, pingpong_blocking) {
DSched sched(DSched::uniform(0));
- run_pingpong_test<DeterministicAtomic, true>(1000);
+ run_pingpong_test<true, DeterministicAtomic>(1000);
}
TEST(Baton, pingpong_nonblocking) {
DSched sched(DSched::uniform(0));
- run_pingpong_test<DeterministicAtomic, false>(1000);
+ run_pingpong_test<false, DeterministicAtomic>(1000);
}
/// Timed wait tests - Nonblocking Baton does not support try_wait_until()
/// Try wait tests
TEST(Baton, try_wait_blocking) {
- run_try_wait_tests<std::atomic, true>();
- run_try_wait_tests<EmulatedFutexAtomic, true>();
- run_try_wait_tests<DeterministicAtomic, true>();
+ run_try_wait_tests<true, std::atomic>();
+ run_try_wait_tests<true, EmulatedFutexAtomic>();
+ run_try_wait_tests<true, DeterministicAtomic>();
}
TEST(Baton, try_wait_nonblocking) {
- run_try_wait_tests<std::atomic, false>();
- run_try_wait_tests<EmulatedFutexAtomic, false>();
- run_try_wait_tests<DeterministicAtomic, false>();
+ run_try_wait_tests<false, std::atomic>();
+ run_try_wait_tests<false, EmulatedFutexAtomic>();
+ run_try_wait_tests<false, DeterministicAtomic>();
}
typedef DeterministicSchedule DSched;
-template <template <typename> class Atom, bool Blocking>
+template <bool MayBlock, template <typename> class Atom>
void run_basic_test() {
- Baton<Atom, Blocking> b;
+ Baton<MayBlock, Atom> b;
b.post();
b.wait();
}
-template <template <typename> class Atom, bool Blocking>
+template <bool MayBlock, template <typename> class Atom>
void run_pingpong_test(int numRounds) {
- using B = Baton<Atom, Blocking>;
+ using B = Baton<MayBlock, Atom>;
B batons[17];
B& a = batons[0];
B& b = batons[16]; // to get it on a different cache line
template <template <typename> class Atom, typename Clock>
void run_basic_timed_wait_tests() {
- Baton<Atom> b;
+ Baton<true, Atom> b;
b.post();
// tests if early delivery works fine
EXPECT_TRUE(b.try_wait_until(Clock::now()));
template <template <typename> class Atom, typename Clock>
void run_timed_wait_tmo_tests() {
- Baton<Atom> b;
+ Baton<true, Atom> b;
auto thr = DSched::thread([&] {
bool rv = b.try_wait_until(Clock::now() + std::chrono::milliseconds(1));
template <template <typename> class Atom, typename Clock>
void run_timed_wait_regular_test() {
- Baton<Atom> b;
+ Baton<true, Atom> b;
auto thr = DSched::thread([&] {
// To wait forever we'd like to use time_point<Clock>::max, but
DSched::join(thr);
}
-template <template <typename> class Atom, bool Blocking>
+template <bool MayBlock, template <typename> class Atom>
void run_try_wait_tests() {
- Baton<Atom, Blocking> b;
+ Baton<MayBlock, Atom> b;
EXPECT_FALSE(b.ready());
EXPECT_FALSE(b.try_wait());
b.post();