return numAwoken;
}
+template <typename F>
FutexResult emulatedFutexWaitImpl(
- void* addr,
- uint32_t expected,
- time_point<system_clock>* absSystemTime,
- time_point<steady_clock>* absSteadyTime,
- uint32_t waitMask) {
+ F* futex,
+ uint32_t expected,
+ time_point<system_clock>* absSystemTime,
+ time_point<steady_clock>* absSteadyTime,
+ uint32_t waitMask) {
+ static_assert(
+ std::is_same<F, Futex<std::atomic>>::value ||
+ std::is_same<F, Futex<EmulatedFutexAtomic>>::value,
+ "Type F must be either Futex<std::atomic> or Futex<EmulatedFutexAtomic>");
+ void* addr = static_cast<void*>(futex);
auto& bucket = EmulatedFutexBucket::bucketFor(addr);
EmulatedFutexWaitNode node(addr, waitMask);
{
std::unique_lock<std::mutex> bucketLock(bucket.mutex_);
- uint32_t actual;
- memcpy(&actual, addr, sizeof(uint32_t));
- if (actual != expected) {
+ if (futex->load(std::memory_order_relaxed) != expected) {
return FutexResult::VALUE_CHANGED;
}
EXPECT_TRUE(A <= B && B <= C);
}
+template <template <typename> class Atom>
+void run_wake_blocked_test() {
+ Futex<Atom> f(0);
+
+ auto thr = DSched::thread([&] { EXPECT_TRUE(f.futexWait(0)); });
+
+ // A sleep here guarantees to a large extent that 'thr' will execute
+ // futexWait before we wake it up, thus testing late futexWake.
+ std::this_thread::sleep_for(std::chrono::milliseconds(2));
+
+ f.store(1);
+ f.futexWake(1);
+ DSched::join(thr);
+}
+
TEST(Futex, clock_source) {
run_system_clock_test();
run_basic_tests<DeterministicAtomic>();
run_wait_until_tests<DeterministicAtomic>();
}
+
+TEST(Futex, wake_blocked_live) {
+ run_wake_blocked_test<std::atomic>();
+}
+
+TEST(Futex, wake_blocked_emulated) {
+ run_wake_blocked_test<EmulatedFutexAtomic>();
+}