-// Our emulated futex uses 4096 lists of wait nodes. There are two levels
-// of locking: a per-list mutex that controls access to the list and a
-// per-node mutex, condvar, and bool that are used for the actual wakeups.
-// The per-node mutex allows us to do precise wakeups without thundering
-// herds.
-
-struct EmulatedFutexWaitNode : public boost::intrusive::list_base_hook<> {
- void* const addr_;
- const uint32_t waitMask_;
-
- // tricky: hold both bucket and node mutex to write, either to read
- bool signaled_;
- std::mutex mutex_;
- std::condition_variable cond_;
-
- EmulatedFutexWaitNode(void* addr, uint32_t waitMask)
- : addr_(addr)
- , waitMask_(waitMask)
- , signaled_(false)
- {
- }
-};
-
-struct EmulatedFutexBucket {
- std::mutex mutex_;
- boost::intrusive::list<EmulatedFutexWaitNode> waiters_;
-
- static constexpr size_t const kNumBuckets = kIsMobile ? 256 : 4096;
-
- static EmulatedFutexBucket& bucketFor(void* addr) {
- // Statically allocating this lets us use this in allocation-sensitive
- // contexts. This relies on the assumption that std::mutex won't dynamically
- // allocate memory, which we assume to be the case on Linux and iOS.
- static Indestructible<std::array<EmulatedFutexBucket, kNumBuckets>>
- gBuckets;
- uint64_t mixedBits =
- folly::hash::twang_mix64(reinterpret_cast<uintptr_t>(addr));
- return (*gBuckets)[mixedBits % kNumBuckets];
- }
-};