proxygen
folly::detail::distributed_mutex Namespace Reference

Classes

class  DistributedMutex
 
class  Waiter
 
class  WakerMetadata
 

Functions

std::chrono::nanoseconds time ()
 
template<typename Type >
TypeextractAddress (std::uintptr_t from)
 
std::uint64_t strip (std::chrono::nanoseconds t)
 
std::uint64_t recover (std::uint64_t from)
 
template<typename Waiter >
bool spin (Waiter &waiter)
 
template<typename Waiter >
void doFutexWake (Waiter *waiter)
 
template<typename Waiter >
bool doFutexWait (Waiter *waiter, Waiter *&next)
 
template<typename Waiter >
bool wait (Waiter *waiter, bool shouldSleep, Waiter *&next)
 
void recordTimedWaiterAndClearTimedBit (bool &timedWaiter, std::uintptr_t &previous)
 
bool preempted (std::uint64_t value)
 
bool isSleeper (std::uintptr_t value)
 
template<typename Waiter >
std::uintptr_t tryWake (bool publishing, Waiter *waiter, std::uintptr_t value, WakerMetadata metadata, Waiter *&sleepers)
 
template<typename Waiter >
bool wake (bool publishing, Waiter &waiter, WakerMetadata metadata, Waiter *&sleepers)
 
template<typename Atomic >
void wakeTimedWaiters (Atomic *state, bool timedWaiters)
 
template<typename Atomic , typename Proxy , typename Sleepers >
bool tryUnlockClean (Atomic &state, Proxy &proxy, Sleepers sleepers)
 
template<typename Atomic , typename Deadline , typename MakeProxy >
auto timedLock (Atomic &state, Deadline deadline, MakeProxy proxy)
 

Variables

constexpr auto kUnlocked = std::uintptr_t{0b0}
 
constexpr auto kLocked = std::uintptr_t{0b1}
 
constexpr auto kTimedWaiter = std::uintptr_t{0b10}
 
constexpr auto kUninitialized = std::uint32_t{0b0}
 
constexpr auto kWaiting = std::uint32_t{0b1}
 
constexpr auto kWake = std::uint32_t{0b10}
 
constexpr auto kSkipped = std::uint32_t{0b11}
 
constexpr auto kAboutToWait = std::uint32_t{0b100}
 
constexpr auto kSleeping = std::uint32_t{0b101}
 
constexpr auto kScheduledAwaySpinThreshold = std::chrono::nanoseconds{200}
 
constexpr auto kMaxSpins = 4000
 

Function Documentation

template<typename Waiter >
bool folly::detail::distributed_mutex::doFutexWait ( Waiter waiter,
Waiter *&  next 
)

Definition at line 378 of file DistributedMutex-inl.h.

References doFutexWake(), folly::exchange(), folly::detail::futexWait(), and kAboutToWait.

Referenced by wait().

378  {
379  // first we get ready to sleep by calling exchange() on the futex with a
380  // kSleeping value
381  DCHECK((*waiter)->futex_.load(std::memory_order_relaxed) == kAboutToWait);
382 
383  // note the semantics of using a futex here, when we exchange the sleeper_
384  // with kSleeping, we are getting ready to sleep, but before sleeping we get
385  // ready to sleep, and we return from futexWait() when the value of
386  // sleeper_ might have changed. We can also wake up because of a spurious
387  // wakeup, so we always check against the value in sleeper_ after returning
388  // from futexWait(), if the value is not kWake, then we continue
389  auto pre = (*waiter)->sleeper_.exchange(kSleeping, std::memory_order_acq_rel);
390 
391  // Seeing a kSleeping on a futex word before we set it ourselves means only
392  // one thing - an unlocking thread caught us before we went to futex(), and
393  // we now have the lock, so we abort
394  //
395  // if we were given an early delivery, we can return from this function with
396  // a true, meaning that we now have the lock
397  if (pre == kSleeping) {
398  return true;
399  }
400 
401  // if we reach here then were were not given an early delivery, and any
402  // thread that goes to wake us up will see a consistent view of the rest of
403  // the contention chain (since the next_ variable is set before the
404  // kSleeping exchange above)
405  while (pre != kWake) {
406  // before enqueueing on the futex, we wake any waiters that we were
407  // possibly responsible for
408  doFutexWake(exchange(next, nullptr));
409 
410  // then we wait on the futex
411  //
412  // note that we have to protect ourselves against spurious wakeups here.
413  // Because the corresponding futexWake() above does not synchronize
414  // wakeups around the futex word. Because doing so would become
415  // inefficient
416  futexWait(&(*waiter)->sleeper_, kSleeping);
417  pre = (*waiter)->sleeper_.load(std::memory_order_acquire);
418  DCHECK((pre == kSleeping) || (pre == kWake));
419  }
420 
421  // when coming out of a futex, we might have some other sleeping threads
422  // that we were supposed to wake up, assign that to the next pointer
423  DCHECK(next == nullptr);
424  next = extractAddress<Waiter>((*waiter)->next_);
425  return false;
426 }
T exchange(T &obj, U &&new_value)
Definition: Utility.h:120
FutexResult futexWait(const Futex< MockAtom > *futex, uint32_t expected, uint32_t waitMask)
def next(obj)
Definition: ast.py:58
template<typename Waiter >
void folly::detail::distributed_mutex::doFutexWake ( Waiter waiter)

Definition at line 342 of file DistributedMutex-inl.h.

References folly::detail::futexWake().

Referenced by doFutexWait(), tryUnlockClean(), and folly::detail::distributed_mutex::DistributedMutex< Atomic, TimePublishing >::unlock().

342  {
343  if (waiter) {
344  // We can use a simple store operation here and not worry about checking
345  // to see if the thread had actually started waiting on the futex, that is
346  // already done in tryWake() when a sleeping thread is collected
347  //
348  // We now do not know whether the waiter had already enqueued on the futex
349  // or whether it had just stored kSleeping in its futex and was about to
350  // call futexWait(). We treat both these scenarios the same
351  //
352  // the below can theoretically cause a problem if we set the
353  // wake signal and the waiter was in between setting kSleeping in its
354  // futex and enqueueing on the futex. In this case the waiter will just
355  // return from futexWait() immediately. This leaves the address that the
356  // waiter was using for futexWait() possibly dangling, and the thread that
357  // we woke in the exchange above might have used that address for some
358  // other object
359  //
360  // however, even if the thread had indeed woken up simply becasue of the
361  // above exchange(), the futexWake() below is not incorrect. It is not
362  // incorrect because futexWake() does not actually change the memory of
363  // the futex word. It just uses the address to do a lookup in the kernel
364  // futex table. And even if we call futexWake() on some other address,
365  // and that address was being used to wait on futex() that thread will
366  // protect itself from spurious wakeups, check the value in the futex word
367  // and enqueue itself back on the futex
368  //
369  // this dangilng pointer possibility is why we use a pointer to the futex
370  // word, and avoid dereferencing after the store() operation
371  auto sleeper = &(*waiter)->sleeper_;
372  sleeper->store(kWake, std::memory_order_release);
373  futexWake(sleeper, 1);
374  }
375 }
int futexWake(const Futex *futex, int count, uint32_t wakeMask)
Definition: Futex-inl.h:107
template<typename Type >
Type* folly::detail::distributed_mutex::extractAddress ( std::uintptr_t  from)

Zero out the other bits used by the implementation and return just an address from a uintptr_t

Definition at line 199 of file DistributedMutex-inl.h.

References max.

Referenced by folly::detail::distributed_mutex::DistributedMutex< Atomic, TimePublishing >::lock().

199  {
200  // shift one bit off the end, to get all 1s followed by a single 0
202  mask >>= 1;
203  mask <<= 1;
204  CHECK(!(mask & 0b1));
205 
206  return reinterpret_cast<Type*>(from & mask);
207 }
LogLevel max
Definition: LogLevel.cpp:31
PUSHMI_INLINE_VAR constexpr struct folly::pushmi::operators::from_fn from
bool folly::detail::distributed_mutex::isSleeper ( std::uintptr_t  value)
inline

Definition at line 552 of file DistributedMutex-inl.h.

Referenced by tryWake().

552  {
553  return (value == kAboutToWait);
554 }
static const char *const value
Definition: Conv.cpp:50
bool folly::detail::distributed_mutex::preempted ( std::uint64_t  value)
inline

Definition at line 541 of file DistributedMutex-inl.h.

References kUninitialized, recover(), strip(), and time().

Referenced by tryWake().

541  {
542  auto currentTime = recover(strip(time()));
543  auto nodeTime = recover(value);
544  auto preempted = currentTime > nodeTime + kScheduledAwaySpinThreshold.count();
545 
546  // we say that the thread has been preempted if its timestamp says so, and
547  // also if it is neither uninitialized nor skipped
548  DCHECK(value != kSkipped);
549  return (preempted) && (value != kUninitialized);
550 }
bool preempted(std::uint64_t value)
std::uint64_t strip(std::chrono::nanoseconds t)
std::uint64_t recover(std::uint64_t from)
static const char *const value
Definition: Conv.cpp:50
std::chrono::nanoseconds time()
void folly::detail::distributed_mutex::recordTimedWaiterAndClearTimedBit ( bool &  timedWaiter,
std::uintptr_t &  previous 
)
inline

Definition at line 437 of file DistributedMutex-inl.h.

References UNLIKELY.

Referenced by folly::detail::distributed_mutex::DistributedMutex< Atomic, TimePublishing >::lock(), and folly::detail::distributed_mutex::DistributedMutex< Atomic, TimePublishing >::unlock().

439  {
440  // the previous value in the mutex can never be kTimedWaiter, timed waiters
441  // always set (kTimedWaiter | kLocked) in the mutex word when they try and
442  // acquire the mutex
443  DCHECK(previous != kTimedWaiter);
444 
445  if (UNLIKELY(previous & kTimedWaiter)) {
446  // record whether there was a timed waiter in the previous mutex state, and
447  // clear the timed bit from the previous state
448  timedWaiter = true;
449  previous = previous & (~kTimedWaiter);
450  }
451 }
#define UNLIKELY(x)
Definition: Likely.h:48
std::uint64_t folly::detail::distributed_mutex::recover ( std::uint64_t  from)
inline

Recover the timestamp value from an integer that has the timestamp encoded in it

Definition at line 224 of file DistributedMutex-inl.h.

Referenced by preempted().

224  {
225  return from >> 8;
226 }
PUSHMI_INLINE_VAR constexpr struct folly::pushmi::operators::from_fn from
template<typename Waiter >
bool folly::detail::distributed_mutex::spin ( Waiter waiter)

Definition at line 309 of file DistributedMutex-inl.h.

References folly::asm_volatile_pause(), folly::data(), folly::detail::distributed_mutex::Waiter< Atomic >::futex_, kMaxSpins, kSkipped, kWaiting, max, now(), folly::detail::Sleeper::sleep(), strip(), and time().

Referenced by main(), and wait().

309  {
310  auto spins = 0;
311  while (true) {
312  // publish our current time in the futex as a part of the spin waiting
313  // process
314  //
315  // if we are under the maximum number of spins allowed before sleeping, we
316  // publish the exact timestamp, otherwise we publish the minimum possible
317  // timestamp to force the waking thread to skip us
318  ++spins;
319  auto now = (spins < kMaxSpins) ? time() : decltype(time())::zero();
320  auto data = strip(now) | kWaiting;
321  auto signal = waiter.futex_.exchange(data, std::memory_order_acq_rel);
323 
324  // if we got skipped, make a note of it and return if we got a skipped
325  // signal or a signal to wake up
326  auto skipped = signal == kSkipped;
327  if (skipped || (signal == kWake)) {
328  return !skipped;
329  }
330 
331  // if we are under the spin threshold, pause to allow the other
332  // hyperthread to run. If not, then sleep
333  if (spins < kMaxSpins) {
335  } else {
336  Sleeper::sleep();
337  }
338  }
339 }
LogLevel max
Definition: LogLevel.cpp:31
Future< Unit > sleep(Duration dur, Timekeeper *tk)
Definition: Future.cpp:42
std::chrono::steady_clock::time_point now()
std::uint64_t strip(std::chrono::nanoseconds t)
static constexpr uint64_t data[1]
Definition: Fingerprint.cpp:43
std::chrono::nanoseconds time()
void asm_volatile_pause()
Definition: Asm.h:37
std::uint64_t folly::detail::distributed_mutex::strip ( std::chrono::nanoseconds  t)
inline

Strips the given nanoseconds into only the least significant 56 bits by moving the least significant 56 bits over by 8 zeroing out the bottom 8 bits to be used as a medium of information transfer for the thread wait nodes

Definition at line 215 of file DistributedMutex-inl.h.

References time().

Referenced by upload.MercurialVCS::__init__(), cpp.gmock_class::_GenerateMethods(), cpp.ast.AstBuilder::_GenerateOne(), pump.Output::Append(), upload.VersionControlSystem::CheckForUnknownFiles(), upload.MercurialVCS::GetBaseFile(), upload::GetEmail(), gmock_doctor::main(), preempted(), upload::RealMain(), spin(), fuse_gtest_files::VerifyOutputFile(), pump::WrapCode(), and pump::WrapComment().

215  {
216  auto time = t.count();
217  return time << 8;
218 }
std::chrono::nanoseconds time()
template<typename Atomic , typename Deadline , typename MakeProxy >
auto folly::detail::distributed_mutex::timedLock ( Atomic &  state,
Deadline  deadline,
MakeProxy  proxy 
)

Definition at line 784 of file DistributedMutex-inl.h.

References folly::atomic_wait_until(), folly::data(), and kLocked.

Referenced by folly::detail::distributed_mutex::DistributedMutex< Atomic, TimePublishing >::try_lock_for(), and folly::detail::distributed_mutex::DistributedMutex< Atomic, TimePublishing >::try_lock_until().

784  {
785  while (true) {
786  // we put a bit on the central state to show that there is a timed waiter
787  // and go to sleep on the central state
788  //
789  // when this thread goes to unlock the mutex, it will expect a 0b1 in the
790  // mutex state (0b1, not 0b11), but then it will see that the value in the
791  // mutex state is 0b11 and not 0b1, meaning that there might have been
792  // another timed waiter. Even though there might not have been another
793  // timed waiter in the time being. This sort of missed wakeup is
794  // desirable for timed waiters; it helps avoid thundering herds of timed
795  // waiters. Because the mutex is packed in 8 bytes, and we need an
796  // address to be stored in those 8 bytes, we don't have much room to play
797  // with. The only other solution is to issue a futexWake(INT_MAX) to wake
798  // up all waiters when a clean unlock is committed, when a thread saw a
799  // timed waiter in the mutex previously.
800  //
801  // putting a 0b11 here works for a set of reasons that is a superset of
802  // the set of reasons that make it okay to put a kLocked (0b1) in the
803  // mutex state. Now that the thread has put (kTimedWaiter | kLocked)
804  // (0b11) in the mutex state and it expects a kLocked (0b1), there are two
805  // scenarios possible. The first being when there is no contention chain
806  // formation in the mutex from the time a timed waiter got a lock to
807  // unlock. In this case, the unlocker sees a 0b11 in the mutex state,
808  // adjusts to the presence of a timed waiter and cleanly unlocks with a
809  // kUnlocked (0b0). The second is when there is a contention chain.
810  // When a thread puts its address in the mutex and sees the timed bit, it
811  // records the presence of a timed waiter, and then pretends as if it
812  // hadn't seen the timed bit. So future contention chain releases, will
813  // terminate with a kLocked (0b1) and not a (kLocked | kTimedWaiter)
814  // (0b11). This just works naturally with the rest of the algorithm
815  // without incurring a perf hit for the regular non-timed case
816  //
817  // this strategy does however mean, that when threads try to acquire the
818  // mutex and all time out, there will be a wasteful syscall to issue wakeups
819  // to waiting threads. We don't do anything to try and minimize this
820  //
821  // we need to use a fetch_or() here because we need to convey two bits of
822  // information - 1, whether the mutex is locked or not, and 2, whether
823  // there is a timed waiter. The alternative here is to use the second bit
824  // to convey information only, we can use a fetch_set() on the second bit
825  // to make this faster, but that comes at the expense of requiring regular
826  // fast path lock attempts. Which use a single bit read-modify-write for
827  // better performance
828  auto data = kTimedWaiter | kLocked;
829  auto previous = state.fetch_or(data, std::memory_order_acquire);
830  if (!(previous & 0b1)) {
831  DCHECK(!previous);
832  return proxy(nullptr, kLocked, true);
833  }
834 
835  // wait on the futex until signalled, if we get a timeout, the try_lock
836  // fails
837  auto result = atomic_wait_until(&state, previous | data, deadline);
838  if (result == std::cv_status::timeout) {
839  return proxy(nullptr, std::uintptr_t{0}, false);
840  }
841  }
842 }
static constexpr uint64_t data[1]
Definition: Fingerprint.cpp:43
state
Definition: http_parser.c:272
std::cv_status atomic_wait_until(const std::atomic< Integer > *atomic, Integer expected, const std::chrono::time_point< Clock, Duration > &deadline)
template<typename Atomic , typename Proxy , typename Sleepers >
bool folly::detail::distributed_mutex::tryUnlockClean ( Atomic &  state,
Proxy &  proxy,
Sleepers  sleepers 
)

Definition at line 677 of file DistributedMutex-inl.h.

References doFutexWake(), and UNLIKELY.

Referenced by folly::detail::distributed_mutex::DistributedMutex< Atomic, TimePublishing >::unlock().

677  {
678  auto expected = proxy.expected_;
679  while (true) {
680  if (state.compare_exchange_strong(
681  expected,
682  kUnlocked,
683  std::memory_order_release,
684  std::memory_order_relaxed)) {
685  // if we were able to commit an unlocked, we need to wake up the futex
686  // waiters, if any
687  doFutexWake(sleepers);
688  return true;
689  }
690 
691  // if we failed the compare_exchange_strong() above, we check to see if
692  // the failure was because of the presence of a timed waiter. If that
693  // was the case then we try one more time with the kTimedWaiter bit set
694  if (UNLIKELY(expected == (proxy.expected_ | kTimedWaiter))) {
695  proxy.timedWaiters_ = true;
696  continue;
697  }
698 
699  // otherwise break, we have a contention chain
700  return false;
701  }
702 }
#define UNLIKELY(x)
Definition: Likely.h:48
state
Definition: http_parser.c:272
template<typename Waiter >
std::uintptr_t folly::detail::distributed_mutex::tryWake ( bool  publishing,
Waiter waiter,
std::uintptr_t  value,
WakerMetadata  metadata,
Waiter *&  sleepers 
)

Definition at line 557 of file DistributedMutex-inl.h.

References isSleeper(), cpp.ast::next(), and preempted().

Referenced by wake().

562  {
563  // first we see if we can wake the current thread that is spinning
564  if ((!publishing || !preempted(value)) && !isSleeper(value)) {
565  // we need release here because of the write to wakerMetadata_
566  (*waiter)->wakerMetadata_ = metadata;
567  (*waiter)->waiters_ = reinterpret_cast<std::uintptr_t>(sleepers);
568  (*waiter)->futex_.store(kWake, std::memory_order_release);
569  return 0;
570  }
571 
572  // if the thread is not a sleeper, and we were not able to catch it before
573  // preemption, we can just return a false, it is safe to read next_ because
574  // the thread was preempted. Preemption signals can only come after the
575  // thread has set the next_ pointer, since the timestamp writes only start
576  // occurring after that point
577  //
578  // if a thread was preempted it must have stored next_ in the waiter struct,
579  // as the store to futex_ that resets the value from kUninitialized happens
580  // after the write to next
581  CHECK(publishing);
582  if (!isSleeper(value)) {
583  // go on to the next one
584  //
585  // Also, we need a memory_order_release here to prevent missed wakeups. A
586  // missed wakeup here can happen when we see that a thread had been
587  // preempted and skip it. Then go on to release the lock, and then when
588  // the thread which got skipped does an exchange on the central storage,
589  // still sees the locked bit, and never gets woken up
590  //
591  // Can we relax this?
592  DCHECK(preempted(value));
593  auto next = (*waiter)->next_;
594  (*waiter)->futex_.store(kSkipped, std::memory_order_release);
595  return next;
596  }
597 
598  // if we are here the thread is a sleeper
599  //
600  // we attempt to catch the thread before it goes to futex(). If we are able
601  // to catch the thread before it sleeps on a futex, we are done, and don't
602  // need to go any further
603  //
604  // if we are not able to catch the thread before it goes to futex, we
605  // collect the current thread in the list of sleeping threads represented by
606  // sleepers, and return the next thread in the list and return false along
607  // with the previous next value
608  //
609  // it is safe to read the next_ pointer in the waiter struct if we were
610  // unable to catch the thread before it went to futex() because we use
611  // acquire-release ordering for the exchange operation below. And if we see
612  // that the thread was already sleeping, we have synchronized with the write
613  // to next_ in the context of the sleeping thread
614  //
615  // Also we need to set the value of waiters_ and wakerMetadata_ in the
616  // thread before doing the exchange because we need to pass on the list of
617  // sleepers in the event that we were able to catch the thread before it
618  // went to futex(). If we were unable to catch the thread before it slept,
619  // these fields will be ignored when the thread wakes up anyway
620  DCHECK(isSleeper(value));
621  (*waiter)->wakerMetadata_ = metadata;
622  (*waiter)->waiters_ = reinterpret_cast<std::uintptr_t>(sleepers);
623  auto pre = (*waiter)->sleeper_.exchange(kSleeping, std::memory_order_acq_rel);
624 
625  // we were able to catch the thread before it went to sleep, return true
626  if (pre != kSleeping) {
627  return 0;
628  }
629 
630  // otherwise return false, with the value of next_, it is safe to read next
631  // because of the same logic as when a thread was preempted
632  //
633  // we also need to collect this sleeper in the list of sleepers being built
634  // up
635  auto next = (*waiter)->next_;
636  (*waiter)->next_ = reinterpret_cast<std::uintptr_t>(sleepers);
637  sleepers = waiter;
638  return next;
639 }
bool preempted(std::uint64_t value)
static const char *const value
Definition: Conv.cpp:50
bool isSleeper(std::uintptr_t value)
def next(obj)
Definition: ast.py:58
template<typename Waiter >
bool folly::detail::distributed_mutex::wait ( Waiter waiter,
bool  shouldSleep,
Waiter *&  next 
)
template<typename Waiter >
bool folly::detail::distributed_mutex::wake ( bool  publishing,
Waiter waiter,
WakerMetadata  metadata,
Waiter *&  sleepers 
)

Definition at line 642 of file DistributedMutex-inl.h.

References current, cpp.ast::next(), tryWake(), folly::value(), and folly::detail::distributed_mutex::WakerMetadata::waker_.

Referenced by folly::detail::distributed_mutex::DistributedMutex< Atomic, TimePublishing >::unlock().

646  {
647  // loop till we find a node that is either at the end of the list (as
648  // specified by metadata) or we find a node that is active (as specified by
649  // the last published timestamp of the node)
650  auto current = &waiter;
651  while (current) {
652  auto value = (*current)->futex_.load(std::memory_order_acquire);
653  auto next = tryWake(publishing, current, value, metadata, sleepers);
654  if (!next) {
655  return true;
656  }
657 
658  // we need to read the value of the next node in the list before skipping
659  // it, this is because after we skip it the node might wake up and enqueue
660  // itself, and thereby gain a new next node
661  CHECK(publishing);
662  current =
663  (next == metadata.waker_) ? nullptr : extractAddress<Waiter>(next);
664  }
665 
666  return false;
667 }
std::uintptr_t tryWake(bool publishing, Waiter *waiter, std::uintptr_t value, WakerMetadata metadata, Waiter *&sleepers)
int current
static const char *const value
Definition: Conv.cpp:50
def next(obj)
Definition: ast.py:58
template<typename Atomic >
void folly::detail::distributed_mutex::wakeTimedWaiters ( Atomic *  state,
bool  timedWaiters 
)

Definition at line 670 of file DistributedMutex-inl.h.

References folly::atomic_notify_one(), and UNLIKELY.

Referenced by folly::detail::distributed_mutex::DistributedMutex< Atomic, TimePublishing >::unlock().

670  {
671  if (UNLIKELY(timedWaiters)) {
673  }
674 }
void atomic_notify_one(const std::atomic< Integer > *atomic)
#define UNLIKELY(x)
Definition: Likely.h:48
state
Definition: http_parser.c:272

Variable Documentation

constexpr auto folly::detail::distributed_mutex::kAboutToWait = std::uint32_t{0b100}
constexpr auto folly::detail::distributed_mutex::kMaxSpins = 4000

Definition at line 116 of file DistributedMutex-inl.h.

Referenced by spin().

constexpr auto folly::detail::distributed_mutex::kScheduledAwaySpinThreshold = std::chrono::nanoseconds{200}

Definition at line 113 of file DistributedMutex-inl.h.

constexpr auto folly::detail::distributed_mutex::kSkipped = std::uint32_t{0b11}

Definition at line 85 of file DistributedMutex-inl.h.

Referenced by spin().

constexpr auto folly::detail::distributed_mutex::kSleeping = std::uint32_t{0b101}

Definition at line 107 of file DistributedMutex-inl.h.

constexpr auto folly::detail::distributed_mutex::kTimedWaiter = std::uintptr_t{0b10}

Definition at line 68 of file DistributedMutex-inl.h.

constexpr auto folly::detail::distributed_mutex::kUninitialized = std::uint32_t{0b0}
constexpr auto folly::detail::distributed_mutex::kUnlocked = std::uintptr_t{0b0}

Definition at line 47 of file DistributedMutex-inl.h.

constexpr auto folly::detail::distributed_mutex::kWaiting = std::uint32_t{0b1}

Definition at line 79 of file DistributedMutex-inl.h.

Referenced by spin().

constexpr auto folly::detail::distributed_mutex::kWake = std::uint32_t{0b10}

Definition at line 81 of file DistributedMutex-inl.h.