1 //  Copyright (c) 2011-present, Facebook, Inc.  All rights reserved.
2 //  This source code is licensed under both the GPLv2 (found in the
3 //  COPYING file in the root directory) and Apache 2.0 License
4 //  (found in the LICENSE.Apache file in the root directory).
5 
6 #include <folly/synchronization/DistributedMutex.h>
7 
8 #include <folly/ConstexprMath.h>
9 #include <folly/Portability.h>
10 #include <folly/ScopeGuard.h>
11 #include <folly/Utility.h>
12 #include <folly/chrono/Hardware.h>
13 #include <folly/detail/Futex.h>
14 #include <folly/lang/Align.h>
15 #include <folly/lang/Bits.h>
16 #include <folly/portability/Asm.h>
17 #include <folly/synchronization/AtomicNotification.h>
18 #include <folly/synchronization/AtomicUtil.h>
19 #include <folly/synchronization/detail/InlineFunctionRef.h>
20 #include <folly/synchronization/detail/Sleeper.h>
21 
22 #include <array>
23 #include <atomic>
24 #include <cstddef>
25 #include <cstdint>
26 #include <limits>
27 #include <stdexcept>
28 #include <thread>
29 #include <utility>
30 
31 namespace folly {
32 namespace detail {
33 namespace distributed_mutex {
34 // kUnlocked is used to show unlocked state
35 //
36 // When locking threads encounter kUnlocked in the underlying storage, they
37 // can just acquire the lock without any further effort
38 constexpr auto kUnlocked = std::uintptr_t{0b0};
39 // kLocked is used to show that the mutex is currently locked, and future
40 // attempts to lock the mutex should enqueue on the central storage
41 //
42 // Locking threads find this on central storage only when there is a
43 // contention chain that is undergoing wakeups, in every other case, a locker
44 // will either find kUnlocked or an arbitrary address with the kLocked bit set
45 constexpr auto kLocked = std::uintptr_t{0b1};
46 // kTimedWaiter is set when there is at least one timed waiter on the mutex
47 //
48 // Timed waiters do not follow the sleeping strategy employed by regular,
49 // non-timed threads.  They sleep on the central mutex atomic through an
50 // extended futex() interface that allows sleeping with the same semantics for
51 // non-standard integer widths
52 //
53 // When a regular non-timed thread unlocks or enqueues on the mutex, and sees
54 // a timed waiter, it takes ownership of all the timed waiters.  The thread
55 // that has taken ownership of the timed waiter releases the timed waiters
56 // when it gets a chance at the critical section.  At which point it issues a
57 // wakeup to single timed waiter, timed waiters always issue wake() calls to
58 // other timed waiters
59 constexpr auto kTimedWaiter = std::uintptr_t{0b10};
60 
61 // kUninitialized means that the thread has just enqueued, and has not yet
62 // gotten to initializing itself with the address of its successor
63 //
64 // this becomes significant for threads that are trying to wake up the
65 // uninitialized thread, if they see that the thread is not yet initialized,
66 // they can do nothing but spin, and wait for the thread to get initialized
67 //
68 // This also plays a role in the functioning of flat combining as implemented
69 // in DistributedMutex.  When a thread owning the lock goes through the
70 // contention chain to either unlock the mutex or combine critical sections
71 // from the other end.  The presence of kUninitialized means that the
72 // combining thread is not able to make progress after this point.  So we
73 // transfer the lock.
74 constexpr auto kUninitialized = std::uint32_t{0b0};
75 // kWaiting will be set in the waiter's futex structs while they are spinning
76 // while waiting for the mutex
77 constexpr auto kWaiting = std::uint32_t{0b1};
78 // kWake will be set by threads that are waking up waiters that have enqueued
79 constexpr auto kWake = std::uint32_t{0b10};
80 // kSkipped will be set by a waker when they see that a waiter has been
81 // preempted away by the kernel, in this case the thread that got skipped will
82 // have to wake up and put itself back on the queue
83 constexpr auto kSkipped = std::uint32_t{0b11};
84 // kAboutToWait will be set by a waiter that enqueues itself with the purpose
85 // of waiting on a futex
86 constexpr auto kAboutToWait = std::uint32_t{0b100};
87 // kSleeping will be set by a waiter right before enqueueing on a futex.  When
88 // a thread wants to wake up a waiter that has enqueued on a futex, it should
89 // set the futex to contain kWake
90 //
91 // a thread that is unlocking and wants to skip over a sleeping thread also
92 // calls futex_.exchange(kSleeping) on the sleeping thread's futex word.  It
93 // does this to 1. detect whether the sleeping thread had actually gone to
94 // sleeping on the futex word so it can skip it, and 2. to synchronize with
95 // other non atomic writes in the sleeping thread's context (such as the write
96 // to track the next waiting thread).
97 //
98 // We reuse kSleeping instead of say using another constant kEarlyDelivery to
99 // avoid situations where a thread has to enter kernel mode due to calling
100 // futexWait() twice because of the presence of a waking thread.  This
101 // situation can arise when an unlocking thread goes to skip over a sleeping
102 // thread, sees that the thread has slept and move on, but the sleeping thread
103 // had not yet entered futex().  This interleaving causes the thread calling
104 // futex() to return spuriously, as the futex word is not what it should be
105 constexpr auto kSleeping = std::uint32_t{0b101};
106 // kCombined is set by the lock holder to let the waiter thread know that its
107 // combine request was successfully completed by the lock holder.  A
108 // successful combine means that the thread requesting the combine operation
109 // does not need to unlock the mutex; in fact, doing so would be an error.
110 constexpr auto kCombined = std::uint32_t{0b111};
111 // kCombineUninitialized is like kUninitialized but is set by a thread when it
112 // enqueues in hopes of getting its critical section combined with the lock
113 // holder
114 constexpr auto kCombineUninitialized = std::uint32_t{0b1000};
115 // kCombineWaiting is set by a thread when it is ready to have its combine
116 // record fulfilled by the lock holder.  In particular, this signals to the
117 // lock holder that the thread has set its next_ pointer in the contention
118 // chain
119 constexpr auto kCombineWaiting = std::uint32_t{0b1001};
120 // kExceptionOccurred is set on the waiter futex when the remote task throws
121 // an exception.  It is the caller's responsibility to retrieve the exception
122 // and rethrow it in their own context.  Note that when the caller uses a
123 // noexcept function as their critical section, they can avoid checking for
124 // this value
125 //
126 // This allows us to avoid all cost of exceptions in the memory layout of the
127 // fast path (no errors) as exceptions are stored as an std::exception_ptr in
128 // the same union that stores the return value of the critical section.  We
129 // also avoid all CPU overhead because the combiner uses a try-catch block
130 // without any additional branching to handle exceptions
131 constexpr auto kExceptionOccurred = std::uint32_t{0b1010};
132 
133 // The number of spins that we are allowed to do before we resort to marking a
134 // thread as having slept
135 //
136 // This is just a magic number from benchmarks
137 constexpr auto kScheduledAwaySpinThreshold = std::chrono::nanoseconds{200};
138 // The maximum number of spins before a thread starts yielding its processor
139 // in hopes of getting skipped
140 constexpr auto kMaxSpins = 4000;
141 // The maximum number of contention chains we can resolve with flat combining.
142 // After this number of contention chains, the mutex falls back to regular
143 // two-phased mutual exclusion to ensure that we don't starve the combiner
144 // thread
145 constexpr auto kMaxCombineIterations = 2;
146 
147 /**
148  * Write only data that is available to the thread that is waking up another.
149  * Only the waking thread is allowed to write to this, the thread to be woken
150  * is allowed to read from this after a wakeup has been issued
151  */
152 template <template <typename> class Atomic>
153 class WakerMetadata {
154  public:
155   explicit WakerMetadata(
156       std::uintptr_t waker = 0,
157       std::uintptr_t waiters = 0,
158       std::uint32_t sleeper = kUninitialized)
159     : waker_{waker}, waiters_{waiters}, sleeper_{sleeper} {}
160 
161   // This is the thread that initiated wakeups for the contention chain.
162   // There can only ever be one thread that initiates the wakeup for a
163   // chain in the spin only version of this mutex.  When a thread that just
164   // woke up sees this as the next thread to wake up, it knows that it is the
165   // terminal node in the contention chain.  This means that it was the one
166   // that took off the thread that had acquired the mutex off the centralized
167   // state.  Therefore, the current thread is the last in its contention
168   // chain.  It will fall back to centralized storage to pick up the next
169   // waiter or release the mutex
170   //
171   // When we move to a full sleeping implementation, this might need to change
172   // to a small_vector<> to account for failed wakeups, or we can put threads
173   // to sleep on the central futex, which is an easier implementation
174   // strategy.  Although, since this is allocated on the stack, we can set a
175   // prohitively large threshold to avoid heap allocations, this strategy
176   // however, might cause increased cache misses on wakeup signalling
177   std::uintptr_t waker_{0};
178   // the list of threads that the waker had previously seen to be sleeping on
179   // a futex(),
180   //
181   // this is given to the current thread as a means to pass on
182   // information.  When the current thread goes to unlock the mutex and does
183   // not see contention, it should go and wake up the head of this list.  If
184   // the current thread sees a contention chain on the mutex, it should pass
185   // on this list to the next thread that gets woken up
186   std::uintptr_t waiters_{0};
187   // The futex that this waiter will sleep on
188   //
189   // how can we reuse futex_ from above for futex management?
190   Futex<Atomic> sleeper_{kUninitialized};
191 };
192 
193 /**
194  * Type of the type-erased callable that is used for combining from the lock
195  * holder's end.  This has 48 bytes of inline storage that can be used to
196  * minimize cache misses when combining
197  */
198 using CombineFunction = detail::InlineFunctionRef<void(), 48>;
199 
200 /**
201  * Waiter encapsulates the state required for waiting on the mutex, this
202  * contains potentially heavy state and is intended to be allocated on the
203  * stack as part of a lock() function call
204  *
205  * To ensure that synchronization does not cause unintended side effects on
206  * the rest of the thread stack (eg. metadata in lockImplementation(), or any
207  * other data in the user's thread), we aggresively pad this struct and use
208  * custom alignment internally to ensure that the relevant data fits within a
209  * single cacheline.  The added alignment here also gives us some room to
210  * wiggle in the bottom few bits of the mutex, where we store extra metadata
211  */
212 template <template <typename> class Atomic>
213 class Waiter {
214  public:
Waiter()215   Waiter() {}
216   Waiter(Waiter&&) = delete;
217   Waiter(const Waiter&) = delete;
218   Waiter& operator=(Waiter&&) = delete;
219   Waiter& operator=(const Waiter&) = delete;
220 
initialize(std::uint64_t futex,CombineFunction task)221   void initialize(std::uint64_t futex, CombineFunction task) {
222     // we only initialize the function if we were actually given a non-null
223     // task, otherwise
224     if (task) {
225       assert(futex == kCombineUninitialized);
226       new (&function_) CombineFunction(task);
227     } else {
228       assert((futex == kUninitialized) || (futex == kAboutToWait));
229       new (&metadata_) WakerMetadata<Atomic>{};
230     }
231 
232     // this pedantic store is needed to ensure that the waking thread
233     // synchronizes with the state in the waiter struct when it loads the
234     // value of the futex word
235     //
236     // on x86, this gets optimized away to just a regular store, it might be
237     // needed on platforms where explicit acquire-release barriers are
238     // required for synchronization
239     //
240     // note that we release here at the end of the constructor because
241     // construction is complete here, any thread that acquires this release
242     // will see a well constructed wait node
243     futex_.store(futex, std::memory_order_release);
244   }
245 
246   std::array<std::uint8_t, hardware_destructive_interference_size> padding1;
247   // the atomic that this thread will spin on while waiting for the mutex to
248   // be unlocked
alignas(hardware_destructive_interference_size)249   alignas(hardware_destructive_interference_size) Atomic<std::uint64_t> futex_{
250       kUninitialized};
251   // The successor of this node.  This will be the thread that had its address
252   // on the mutex previously
253   //
254   // We can do without making this atomic since the remote thread synchronizes
255   // on the futex variable above.  If this were not atomic, the remote thread
256   // would only be allowed to read from it after the waiter has moved into the
257   // waiting state to avoid risk of a load racing with a write.  However, it
258   // helps to make this atomic because we can use an unconditional load and make
259   // full use of the load buffer to coalesce both reads into a single clock
260   // cycle after the line arrives in the combiner core.  This is a heavily
261   // contended line, so an RFO from the enqueueing thread is highly likely and
262   // has the potential to cause an immediate invalidation; blocking the combiner
263   // thread from making progress until the line is pulled back to read this
264   // value
265   //
266   // Further, making this atomic prevents the compiler from making an incorrect
267   // optimization where it does not load the value as written in the code, but
268   // rather dereferences it through a pointer whenever needed (since the value
269   // of the pointer to this is readily available on the stack).  Doing this
270   // causes multiple invalidation requests from the enqueueing thread, blocking
271   // remote progress
272   //
273   // Note that we use relaxed loads and stores, so this should not have any
274   // additional overhead compared to a regular load on most architectures
275   std::atomic<std::uintptr_t> next_{0};
276   // We use an anonymous union for the combined critical section request and
277   // the metadata that will be filled in from the leader's end.  Only one is
278   // active at a time - if a leader decides to combine the requested critical
279   // section into its execution, it will not touch the metadata field.  If a
280   // leader decides to migrate the lock to the waiter, it will not touch the
281   // function
282   //
283   // this allows us to transfer more state when combining a critical section
284   // and reduce the cache misses originating from executing an arbitrary
285   // lambda
286   //
287   // note that this is an anonymous union, not an unnamed union, the members
288   // leak into the surrounding scope
289   union {
290     // metadata for the waker
291     WakerMetadata<Atomic> metadata_;
292     // The critical section that can potentially be combined into the critical
293     // section of the locking thread
294     //
295     // This is kept as a FunctionRef because the original function is preserved
296     // until the lock_combine() function returns.  A consequence of using
297     // FunctionRef here is that we don't need to do any allocations and can
298     // allow users to capture unbounded state into the critical section.  Flat
299     // combining means that the user does not have access to the thread
300     // executing the critical section, so assumptions about thread local
301     // references can be invalidated.  Being able to capture arbitrary state
302     // allows the user to do thread local accesses right before the critical
303     // section and pass them as state to the callable being referenced here
304     CombineFunction function_;
305     // The user is allowed to use a combined critical section that returns a
306     // value.  This buffer is used to implement the value transfer to the
307     // waiting thread.  We reuse the same union because this helps us combine
308     // one synchronization operation with a material value transfer.
309     //
310     // The waker thread needs to synchronize on this cacheline to issue a
311     // wakeup to the waiter, meaning that the entire line needs to be pulled
312     // into the remote core in exclusive mode.  So we reuse the coherence
313     // operation to transfer the return value in addition to the
314     // synchronization signal.  In the case that the user's data item is
315     // small, the data is transferred all inline as part of the same line,
316     // which pretty much arrives into the CPU cache in the same clock cycle or
317     // two after a read-for-ownership request.  This gives us a high chance of
318     // coalescing the entire transitive store buffer together into one cache
319     // coherence operation from the waker's end.  This allows us to make use
320     // of the CPU bus bandwidth which would have otherwise gone to waste.
321     // Benchmarks prove this theory under a wide range of contention, value
322     // sizes, NUMA interactions and processor models
323     //
324     // The current version of the Intel optimization manual confirms this
325     // theory somewhat as well in section 2.3.5.1 (Load and Store Operation
326     // Overview)
327     //
328     //    When an instruction writes data to a memory location [...], the
329     //    processor ensures that it has the line containing this memory location
330     //    is in its L1d cache [...]. If the cache line is not there, it fetches
331     //    from the next levels using a RFO request [...] RFO and storing the
332     //    data happens after instruction retirement.  Therefore, the store
333     //    latency usually does not affect the store instruction itself
334     //
335     // This gives the user the ability to input up to 48 bytes into the
336     // combined critical section through an InlineFunctionRef and output 48
337     // bytes from it basically without any cost.  The type of the entity
338     // stored in the buffer has to be matched by the type erased callable that
339     // the caller has used.  At this point, the caller is still in the
340     // template instantiation leading to the combine request, so it has
341     // knowledge of the return type and can apply the appropriate
342     // reinterpret_cast and launder operation to safely retrieve the data from
343     // this buffer
344     _t<std::aligned_storage<48, 8>> storage_;
345   };
346   std::array<std::uint8_t, hardware_destructive_interference_size> padding2;
347 };
348 
349 /**
350  * A template that helps us differentiate between the different ways to return
351  * a value from a combined critical section.  A return value of type void
352  * cannot be stored anywhere, so we use specializations and pick the right one
353  * switched through std::conditional_t
354  *
355  * This is then used by CoalescedTask and its family of functions to implement
356  * efficient return value transfers to the waiting threads
357  */
358 template <typename Func>
359 class RequestWithReturn {
360  public:
361   using F = Func;
362   using ReturnType = decltype(std::declval<const Func&>()());
RequestWithReturn(Func func)363   explicit RequestWithReturn(Func func) : func_{std::move(func)} {}
364 
365   /**
366    * We need to define the destructor here because C++ requires (with good
367    * reason) that a union with non-default destructor be explicitly destroyed
368    * from the surrounding class, as neither the runtime nor compiler have the
369    * knowledge of what to do with a union at the time of destruction
370    *
371    * Each request that has a valid return value set will have the value
372    * retrieved from the get() method, where the value is destroyed.  So we
373    * don't need to destroy it here
374    */
~RequestWithReturn()375   ~RequestWithReturn() {}
376 
377   /**
378    * This method can be used to return a value from the request.  This returns
379    * the underlying value because return type of the function we were
380    * instantiated with is not void
381    */
get()382   ReturnType get() && {
383     // when the return value has been processed, we destroy the value
384     // contained in this request.  Using a scope_exit means that we don't have
385     // to worry about storing the value somewhere and causing potentially an
386     // extra move
387     //
388     // note that the invariant here is that this function is only called if the
389     // requesting thread had it's critical section combined, and the value_
390     // member constructed through detach()
391     SCOPE_EXIT {
392       value_.~ReturnType();
393     };
394     return std::move(value_);
395   }
396 
397   // this contains a copy of the function the waiter had requested to be
398   // executed as a combined critical section
399   Func func_;
400   // this stores the return value used in the request, we use a union here to
401   // avoid laundering and allow return types that are not default
402   // constructible to be propagated through the execution of the critical
403   // section
404   //
405   // note that this is an anonymous union, the member leaks into the
406   // surrounding scope as a member variable
407   union {
408     ReturnType value_;
409   };
410 };
411 
412 template <typename Func>
413 class RequestWithoutReturn {
414  public:
415   using F = Func;
416   using ReturnType = void;
RequestWithoutReturn(Func func)417   explicit RequestWithoutReturn(Func func) : func_{std::move(func)} {}
418 
419   /**
420    * In this version of the request class, get() returns nothing as there is
421    * no stored value
422    */
get()423   void get() && {}
424 
425   // this contains a copy of the function the waiter had requested to be
426   // executed as a combined critical section
427   Func func_;
428 };
429 
430 // we need to use std::integral_constant::value here as opposed to
431 // std::integral_constant::operator T() because MSVC errors out with the
432 // implicit conversion
433 template <typename Func>
434 using Request = _t<std::conditional<
435     std::is_void<decltype(std::declval<const Func&>()())>::value,
436     RequestWithoutReturn<Func>,
437     RequestWithReturn<Func>>>;
438 
439 /**
440  * A template that helps us to transform a callable returning a value to one
441  * that returns void so it can be type erased and passed on to the waker.  If
442  * the return value is small enough, it gets coalesced into the wait struct
443  * for optimal data transfer.  When it's not small enough to fit in the waiter
444  * storage buffer, we place it on it's own cacheline with isolation to prevent
445  * false-sharing with the on-stack metadata of the waiter thread
446  *
447  * This helps a combined critical section feel more normal in the case where
448  * the user wants to return a value, for example
449  *
450  *    auto value = mutex_.lock_combine([&]() {
451  *      return data_.value();
452  *    });
453  *
454  * Without this, the user would typically create a dummy object that they
455  * would then assign to from within the lambda.  With return value chaining,
456  * this pattern feels more natural
457  *
458  * Note that it is important to copy the entire callble into this class.
459  * Storing something like a reference instead is not desirable because it does
460  * not allow InlineFunctionRef to use inline storage to represent the user's
461  * callable without extra indirections
462  *
463  * We use std::conditional_t and switch to the right type of task with the
464  * CoalescedTask type alias
465  */
466 template <typename Func, typename Waiter>
467 class TaskWithCoalesce {
468  public:
469   using ReturnType = decltype(std::declval<const Func&>()());
470   using StorageType = folly::Unit;
TaskWithCoalesce(Func func,Waiter & waiter)471   explicit TaskWithCoalesce(Func func, Waiter& waiter)
472       : func_{std::move(func)}, waiter_(waiter) {}
473 
operator()474   void operator()() const {
475     auto value = func_();
476     new (&waiter_.storage_) ReturnType(std::move(value));
477   }
478 
479  private:
480   Func func_;
481   Waiter& waiter_;
482 
483   static_assert(!std::is_void<ReturnType>{}, "");
484   static_assert(alignof(decltype(waiter_.storage_)) >= alignof(ReturnType), "");
485   static_assert(sizeof(decltype(waiter_.storage_)) >= sizeof(ReturnType), "");
486 };
487 
488 template <typename Func, typename Waiter>
489 class TaskWithoutCoalesce {
490  public:
491   using ReturnType = void;
492   using StorageType = folly::Unit;
TaskWithoutCoalesce(Func func,Waiter &)493   explicit TaskWithoutCoalesce(Func func, Waiter&) : func_{std::move(func)} {}
494 
operator()495   void operator()() const {
496     func_();
497   }
498 
499  private:
500   Func func_;
501 };
502 
503 template <typename Func, typename Waiter>
504 class TaskWithBigReturnValue {
505  public:
506   // Using storage that is aligned on the cacheline boundary helps us avoid a
507   // situation where the data ends up being allocated on two separate
508   // cachelines.  This would require the remote thread to pull in both lines
509   // to issue a write.
510   //
511   // We also isolate the storage by appending some padding to the end to
512   // ensure we avoid false-sharing with the metadata used while the waiter
513   // waits
514   using ReturnType = decltype(std::declval<const Func&>()());
515   static const auto kReturnValueAlignment = folly::kIsMsvc
516       ? 8
517       : folly::constexpr_max(
518             alignof(ReturnType),
519             folly::hardware_destructive_interference_size);
520   using StorageType = _t<std::aligned_storage<
521       sizeof(
522           _t<std::aligned_storage<sizeof(ReturnType), kReturnValueAlignment>>),
523       kReturnValueAlignment>>;
524 
TaskWithBigReturnValue(Func func,Waiter &)525   explicit TaskWithBigReturnValue(Func func, Waiter&)
526       : func_{std::move(func)} {}
527 
operator()528   void operator()() const {
529     assert(storage_);
530     auto value = func_();
531     new (storage_) ReturnType(std::move(value));
532   }
533 
attach(StorageType * storage)534   void attach(StorageType* storage) {
535     assert(!storage_);
536     storage_ = storage;
537   }
538 
539  private:
540   Func func_;
541   StorageType* storage_{nullptr};
542 
543   static_assert(!std::is_void<ReturnType>{}, "");
544   static_assert(sizeof(Waiter::storage_) < sizeof(ReturnType), "");
545 };
546 
547 template <typename T, bool>
548 struct Sizeof_;
549 template <typename T>
550 struct Sizeof_<T, false> : std::integral_constant<std::size_t, sizeof(T)> {};
551 template <typename T>
552 struct Sizeof_<T, true> : std::integral_constant<std::size_t, 0> {};
553 template <typename T>
554 struct Sizeof : Sizeof_<T, std::is_void<T>::value> {};
555 
556 // we need to use std::integral_constant::value here as opposed to
557 // std::integral_constant::operator T() because MSVC errors out with the
558 // implicit conversion
559 template <typename Func, typename Waiter>
560 using CoalescedTask = _t<std::conditional<
561     std::is_void<decltype(std::declval<const Func&>()())>::value,
562     TaskWithoutCoalesce<Func, Waiter>,
563     _t<std::conditional<
564         Sizeof<decltype(std::declval<const Func&>()())>::value <=
565             sizeof(Waiter::storage_),
566         TaskWithCoalesce<Func, Waiter>,
567         TaskWithBigReturnValue<Func, Waiter>>>>>;
568 
569 /**
570  * Given a request and a wait node, coalesce them into a CoalescedTask that
571  * coalesces the return value into the wait node when invoked from a remote
572  * thread
573  *
574  * When given a null request through nullptr_t, coalesce() returns null as well
575  */
576 template <typename Waiter>
577 std::nullptr_t coalesce(std::nullptr_t&, Waiter&) {
578   return nullptr;
579 }
580 
581 template <
582     typename Request,
583     typename Waiter,
584     typename Func = typename Request::F>
585 CoalescedTask<Func, Waiter> coalesce(Request& request, Waiter& waiter) {
586   static_assert(!std::is_same<Request, std::nullptr_t>{}, "");
587   return CoalescedTask<Func, Waiter>{request.func_, waiter};
588 }
589 
590 /**
591  * Given a task, create storage for the return value.  When we get a type
592  * of CoalescedTask, this returns an instance of CoalescedTask::StorageType.
593  * std::nullptr_t otherwise
594  */
595 inline std::nullptr_t makeReturnValueStorageFor(std::nullptr_t&) {
596   return {};
597 }
598 
599 template <
600     typename CoalescedTask,
601     typename StorageType = typename CoalescedTask::StorageType>
602 StorageType makeReturnValueStorageFor(CoalescedTask&) {
603   return {};
604 }
605 
606 /**
607  * Given a task and storage, attach them together if needed.  This only helps
608  * when we have a task that returns a value bigger than can be coalesced.  In
609  * that case, we need to attach the storage with the task so the return value
610  * can be transferred to this thread from the remote thread
611  */
612 template <typename Task, typename Storage>
613 void attach(Task&, Storage&) {
614   static_assert(
615       std::is_same<Storage, std::nullptr_t>{} ||
616           std::is_same<Storage, folly::Unit>{},
617       "");
618 }
619 
620 template <
621     typename R,
622     typename W,
623     typename StorageType = typename TaskWithBigReturnValue<R, W>::StorageType>
624 void attach(TaskWithBigReturnValue<R, W>& task, StorageType& storage) {
625   task.attach(&storage);
626 }
627 
628 template <typename Request, typename Waiter>
629 void throwIfExceptionOccurred(Request&, Waiter& waiter, bool exception) {
630   using Storage = decltype(waiter.storage_);
631   using F = typename Request::F;
632   static_assert(sizeof(Storage) >= sizeof(std::exception_ptr), "");
633   static_assert(alignof(Storage) >= alignof(std::exception_ptr), "");
634 
635   // we only need to check for an exception in the waiter struct if the passed
636   // callable is not noexcept
637   //
638   // we need to make another instance of the exception with automatic storage
639   // duration and destroy the exception held in the storage *before throwing* to
640   // avoid leaks.  If we don't destroy the exception_ptr in storage, the
641   // refcount for the internal exception will never hit zero, thereby leaking
642   // memory
643   if ((!noexcept(std::declval<const F&>()()) && exception)) {
644     auto storage = &waiter.storage_;
645     auto exc = folly::launder(reinterpret_cast<std::exception_ptr*>(storage));
646     auto copy = std::move(*exc);
647     exc->std::exception_ptr::~exception_ptr();
648     std::rethrow_exception(std::move(copy));
649   }
650 }
651 
652 /**
653  * Given a CoalescedTask, a wait node and a request.  Detach the return value
654  * into the request from the wait node and task.
655  */
656 template <typename Waiter>
657 void detach(std::nullptr_t&, Waiter&, bool exception, std::nullptr_t&) {
658   assert(!exception);
659 }
660 
661 template <typename Waiter, typename F>
662 void detach(
663     RequestWithoutReturn<F>& request,
664     Waiter& waiter,
665     bool exception,
666     folly::Unit&) {
667   throwIfExceptionOccurred(request, waiter, exception);
668 }
669 
670 template <typename Waiter, typename F>
671 void detach(
672     RequestWithReturn<F>& request,
673     Waiter& waiter,
674     bool exception,
675     folly::Unit&) {
676   throwIfExceptionOccurred(request, waiter, exception);
677 
678   using ReturnType = typename RequestWithReturn<F>::ReturnType;
679   static_assert(!std::is_same<ReturnType, void>{}, "");
680   static_assert(sizeof(waiter.storage_) >= sizeof(ReturnType), "");
681 
682   auto& val = *folly::launder(reinterpret_cast<ReturnType*>(&waiter.storage_));
683   new (&request.value_) ReturnType(std::move(val));
684   val.~ReturnType();
685 }
686 
687 template <typename Waiter, typename F, typename Storage>
688 void detach(
689     RequestWithReturn<F>& request,
690     Waiter& waiter,
691     bool exception,
692     Storage& storage) {
693   throwIfExceptionOccurred(request, waiter, exception);
694 
695   using ReturnType = typename RequestWithReturn<F>::ReturnType;
696   static_assert(!std::is_same<ReturnType, void>{}, "");
697   static_assert(sizeof(storage) >= sizeof(ReturnType), "");
698 
699   auto& val = *folly::launder(reinterpret_cast<ReturnType*>(&storage));
700   new (&request.value_) ReturnType(std::move(val));
701   val.~ReturnType();
702 }
703 
704 /**
705  * Get the time since epoch in nanoseconds
706  *
707  * This is faster than std::chrono::steady_clock because it avoids a VDSO
708  * access to get the timestamp counter
709  *
710  * Note that the hardware timestamp counter on x86, like std::steady_clock is
711  * guaranteed to be monotonically increasing -
712  * https://c9x.me/x86/html/file_module_x86_id_278.html
713  */
714 inline std::chrono::nanoseconds time() {
715   return std::chrono::nanoseconds{hardware_timestamp()};
716 }
717 
718 /**
719  * Zero out the other bits used by the implementation and return just an
720  * address from a uintptr_t
721  */
722 template <typename Type>
723 Type* extractPtr(std::uintptr_t from) {
724   // shift one bit off the end, to get all 1s followed by a single 0
725   auto mask = std::numeric_limits<std::uintptr_t>::max();
726   mask >>= 1;
727   mask <<= 1;
728   assert(!(mask & 0b1));
729 
730   return folly::bit_cast<Type*>(from & mask);
731 }
732 
733 /**
734  * Strips the given nanoseconds into only the least significant 56 bits by
735  * moving the least significant 56 bits over by 8 zeroing out the bottom 8
736  * bits to be used as a medium of information transfer for the thread wait
737  * nodes
738  */
739 inline std::uint64_t strip(std::chrono::nanoseconds t) {
740   auto time = t.count();
741   return static_cast<std::uint64_t>(time) << 8;
742 }
743 
744 /**
745  * Recover the timestamp value from an integer that has the timestamp encoded
746  * in it
747  */
748 inline std::uint64_t recover(std::uint64_t from) {
749   return from >> 8;
750 }
751 
752 template <template <typename> class Atomic, bool TimePublishing>
753 class DistributedMutex<Atomic, TimePublishing>::DistributedMutexStateProxy {
754  public:
755   // DistributedMutexStateProxy is move constructible and assignable for
756   // convenience
757   DistributedMutexStateProxy(DistributedMutexStateProxy&& other) {
758     *this = std::move(other);
759   }
760 
761   DistributedMutexStateProxy& operator=(DistributedMutexStateProxy&& other) {
762     assert(!(*this));
763 
764     next_ = folly::exchange(other.next_, nullptr);
765     expected_ = folly::exchange(other.expected_, 0);
766     timedWaiters_ = folly::exchange(other.timedWaiters_, false);
767     combined_ = folly::exchange(other.combined_, false);
768     waker_ = folly::exchange(other.waker_, 0);
769     waiters_ = folly::exchange(other.waiters_, nullptr);
770     ready_ = folly::exchange(other.ready_, nullptr);
771 
772     return *this;
773   }
774 
775   // The proxy is valid when a mutex acquisition attempt was successful,
776   // lock() is guaranteed to return a valid proxy, try_lock() is not
777   explicit operator bool() const {
778     return expected_;
779   }
780 
781   // private:
782   // friend the mutex class, since that will be accessing state private to
783   // this class
784   friend class DistributedMutex<Atomic, TimePublishing>;
785 
786   DistributedMutexStateProxy(
787       Waiter<Atomic>* next,
788       std::uintptr_t expected,
789       bool timedWaiter = false,
790       bool combined = false,
791       std::uintptr_t waker = 0,
792       Waiter<Atomic>* waiters = nullptr,
793       Waiter<Atomic>* ready = nullptr)
794       : next_{next},
795         expected_{expected},
796         timedWaiters_{timedWaiter},
797         combined_{combined},
798         waker_{waker},
799         waiters_{waiters},
800         ready_{ready} {}
801 
802   // the next thread that is to be woken up, this being null at the time of
803   // unlock() shows that the current thread acquired the mutex without
804   // contention or it was the terminal thread in the queue of threads waking up
805   Waiter<Atomic>* next_{nullptr};
806   // this is the value that the current thread should expect to find on
807   // unlock, and if this value is not there on unlock, the current thread
808   // should assume that other threads are enqueued waiting for the mutex
809   //
810   // note that if the mutex has the same state set at unlock time, and this is
811   // set to an address (and not say kLocked in the case of a terminal waker)
812   // then it must have been the case that no other thread had enqueued itself,
813   // since threads in the domain of this mutex do not share stack space
814   //
815   // if we want to support stack sharing, we can solve the problem by looping
816   // at lock time, and setting a variable that says whether we have acquired
817   // the lock or not perhaps
818   std::uintptr_t expected_{0};
819   // a boolean that will be set when the mutex has timed waiters that the
820   // current thread is responsible for waking, in such a case, the current
821   // thread will issue an atomic_notify_one() call after unlocking the mutex
822   //
823   // note that a timed waiter will itself always have this flag set.  This is
824   // done so we can avoid having to issue a atomic_notify_all() call (and
825   // subsequently a thundering herd) when waking up timed-wait threads
826   bool timedWaiters_{false};
827   // a boolean that contains true if the state proxy is not meant to be passed
828   // to the unlock() function.  This is set only when there is contention and
829   // a thread had asked for its critical section to be combined
830   bool combined_{false};
831   // metadata passed along from the thread that woke this thread up
832   std::uintptr_t waker_{0};
833   // the list of threads that are waiting on a futex
834   //
835   // the current threads is meant to wake up this list of waiters if it is
836   // able to commit an unlock() on the mutex without seeing a contention chain
837   Waiter<Atomic>* waiters_{nullptr};
838   // after a thread has woken up from a futex() call, it will have the rest of
839   // the threads that it were waiting behind it in this list, a thread that
840   // unlocks has to wake up threads from this list if it has any, before it
841   // goes to sleep to prevent pathological unfairness
842   Waiter<Atomic>* ready_{nullptr};
843 };
844 
845 template <template <typename> class Atomic, bool TimePublishing>
846 DistributedMutex<Atomic, TimePublishing>::DistributedMutex()
847     : state_{kUnlocked} {}
848 
849 template <typename Waiter>
850 std::uint64_t publish(
851     std::uint64_t spins,
852     bool& shouldPublish,
853     std::chrono::nanoseconds& previous,
854     Waiter& waiter,
855     std::uint32_t waitMode) {
856   // time publishing has some overhead because it executes an atomic exchange on
857   // the futex word.  If this line is in a remote thread (eg.  the combiner),
858   // then each time we publish a timestamp, this thread has to submit an RFO to
859   // the remote core for the cacheline, blocking progress for both threads.
860   //
861   // the remote core uses a store in the fast path - why then does an RFO make a
862   // difference?  The only educated guess we have here is that the added
863   // roundtrip delays draining of the store buffer, which essentially exerts
864   // backpressure on future stores, preventing parallelization
865   //
866   // if we have requested a combine, time publishing is less important as it
867   // only comes into play when the combiner has exhausted their max combine
868   // passes.  So we defer time publishing to the point when the current thread
869   // gets preempted
870   auto current = time();
871   if ((current - previous) >= kScheduledAwaySpinThreshold) {
872     shouldPublish = true;
873   }
874   previous = current;
875 
876   // if we have requested a combine, and this is the first iteration of the
877   // wait-loop, we publish a max timestamp to optimistically convey that we have
878   // not yet been preempted (the remote knows the meaning of max timestamps)
879   //
880   // then if we are under the maximum number of spins allowed before sleeping,
881   // we publish the exact timestamp, otherwise we publish the minimum possible
882   // timestamp to force the waking thread to skip us
883   auto now = ((waitMode == kCombineWaiting) && !spins)
884       ? decltype(time())::max()
885       : (spins < kMaxSpins) ? previous : decltype(time())::zero();
886 
887   // the wait mode information is published in the bottom 8 bits of the futex
888   // word, the rest contains time information as computed above.  Overflows are
889   // not really a correctness concern because time publishing is only a
890   // heuristic.  This leaves us 56 bits of nanoseconds (2 years) before we hit
891   // two consecutive wraparounds, so the lack of bits to respresent time is
892   // neither a performance nor correctness concern
893   auto data = strip(now) | waitMode;
894   auto signal = (shouldPublish || !spins || (waitMode != kCombineWaiting))
895       ? waiter.futex_.exchange(data, std::memory_order_acq_rel)
896       : waiter.futex_.load(std::memory_order_acquire);
897   return signal & std::numeric_limits<std::uint8_t>::max();
898 }
899 
900 template <typename Waiter>
901 bool spin(Waiter& waiter, std::uint32_t& sig, std::uint32_t mode) {
902   auto spins = std::uint64_t{0};
903   auto waitMode = (mode == kCombineUninitialized) ? kCombineWaiting : kWaiting;
904   auto previous = time();
905   auto shouldPublish = false;
906   while (true) {
907     auto signal = publish(spins++, shouldPublish, previous, waiter, waitMode);
908 
909     // if we got skipped, make a note of it and return if we got a skipped
910     // signal or a signal to wake up
911     auto skipped = (signal == kSkipped);
912     auto combined = (signal == kCombined);
913     auto exceptionOccurred = (signal == kExceptionOccurred);
914     auto woken = (signal == kWake);
915     if (skipped || woken || combined || exceptionOccurred) {
916       sig = static_cast<std::uint32_t>(signal);
917       return !skipped;
918     }
919 
920     // if we are under the spin threshold, pause to allow the other
921     // hyperthread to run.  If not, then sleep
922     if (spins < kMaxSpins) {
923       asm_volatile_pause();
924     } else {
925       Sleeper::sleep();
926     }
927   }
928 }
929 
930 template <typename Waiter>
931 void doFutexWake(Waiter* waiter) {
932   if (waiter) {
933     // We can use a simple store operation here and not worry about checking
934     // to see if the thread had actually started waiting on the futex, that is
935     // already done in tryWake() when a sleeping thread is collected
936     //
937     // We now do not know whether the waiter had already enqueued on the futex
938     // or whether it had just stored kSleeping in its futex and was about to
939     // call futexWait().  We treat both these scenarios the same
940     //
941     // the below can theoretically cause a problem if we set the
942     // wake signal and the waiter was in between setting kSleeping in its
943     // futex and enqueueing on the futex.  In this case the waiter will just
944     // return from futexWait() immediately.  This leaves the address that the
945     // waiter was using for futexWait() possibly dangling, and the thread that
946     // we woke in the exchange above might have used that address for some
947     // other object
948     //
949     // however, even if the thread had indeed woken up simply becasue of the
950     // above exchange(), the futexWake() below is not incorrect.  It is not
951     // incorrect because futexWake() does not actually change the memory of
952     // the futex word.  It just uses the address to do a lookup in the kernel
953     // futex table.  And even if we call futexWake() on some other address,
954     // and that address was being used to wait on futex() that thread will
955     // protect itself from spurious wakeups, check the value in the futex word
956     // and enqueue itself back on the futex
957     //
958     // this dangilng pointer possibility is why we use a pointer to the futex
959     // word, and avoid dereferencing after the store() operation
960     auto sleeper = &waiter->metadata_.sleeper_;
961     sleeper->store(kWake, std::memory_order_release);
962     futexWake(sleeper, 1);
963   }
964 }
965 
966 template <typename Waiter>
967 bool doFutexWait(Waiter* waiter, Waiter*& next) {
968   // first we get ready to sleep by calling exchange() on the futex with a
969   // kSleeping value
970   assert(waiter->futex_.load(std::memory_order_relaxed) == kAboutToWait);
971 
972   // note the semantics of using a futex here, when we exchange the sleeper_
973   // with kSleeping, we are getting ready to sleep, but before sleeping we get
974   // ready to sleep, and we return from futexWait() when the value of
975   // sleeper_ might have changed.  We can also wake up because of a spurious
976   // wakeup, so we always check against the value in sleeper_ after returning
977   // from futexWait(), if the value is not kWake, then we continue
978   auto pre =
979       waiter->metadata_.sleeper_.exchange(kSleeping, std::memory_order_acq_rel);
980 
981   // Seeing a kSleeping on a futex word before we set it ourselves means only
982   // one thing - an unlocking thread caught us before we went to futex(), and
983   // we now have the lock, so we abort
984   //
985   // if we were given an early delivery, we can return from this function with
986   // a true, meaning that we now have the lock
987   if (pre == kSleeping) {
988     return true;
989   }
990 
991   // if we reach here then were were not given an early delivery, and any
992   // thread that goes to wake us up will see a consistent view of the rest of
993   // the contention chain (since the next_ variable is set before the
994   // kSleeping exchange above)
995   while (pre != kWake) {
996     // before enqueueing on the futex, we wake any waiters that we were
997     // possibly responsible for
998     doFutexWake(folly::exchange(next, nullptr));
999 
1000     // then we wait on the futex
1001     //
1002     // note that we have to protect ourselves against spurious wakeups here.
1003     // Because the corresponding futexWake() above does not synchronize
1004     // wakeups around the futex word.  Because doing so would become
1005     // inefficient
1006     futexWait(&waiter->metadata_.sleeper_, kSleeping);
1007     pre = waiter->metadata_.sleeper_.load(std::memory_order_acquire);
1008     assert((pre == kSleeping) || (pre == kWake));
1009   }
1010 
1011   // when coming out of a futex, we might have some other sleeping threads
1012   // that we were supposed to wake up, assign that to the next pointer
1013   assert(next == nullptr);
1014   next = extractPtr<Waiter>(waiter->next_.load(std::memory_order_relaxed));
1015   return false;
1016 }
1017 
1018 template <typename Waiter>
1019 bool wait(Waiter* waiter, std::uint32_t mode, Waiter*& next, uint32_t& signal) {
1020   if (mode == kAboutToWait) {
1021     return doFutexWait(waiter, next);
1022   }
1023 
1024   return spin(*waiter, signal, mode);
1025 }
1026 
1027 inline void recordTimedWaiterAndClearTimedBit(
1028     bool& timedWaiter,
1029     std::uintptr_t& previous) {
1030   // the previous value in the mutex can never be kTimedWaiter, timed waiters
1031   // always set (kTimedWaiter | kLocked) in the mutex word when they try and
1032   // acquire the mutex
1033   assert(previous != kTimedWaiter);
1034 
1035   if ((previous & kTimedWaiter)) {
1036     // record whether there was a timed waiter in the previous mutex state, and
1037     // clear the timed bit from the previous state
1038     timedWaiter = true;
1039     previous = previous & (~kTimedWaiter);
1040   }
1041 }
1042 
1043 template <typename Atomic>
1044 void wakeTimedWaiters(Atomic* state, bool timedWaiters) {
1045   if ((timedWaiters)) {
1046     atomic_notify_one(state);
1047   }
1048 }
1049 
1050 template <template <typename> class Atomic, bool TimePublishing>
1051 template <typename Func>
1052 auto DistributedMutex<Atomic, TimePublishing>::lock_combine(Func func)
1053     -> decltype(std::declval<const Func&>()()) {
1054   // invoke the lock implementation function and check whether we came out of
1055   // it with our task executed as a combined critical section.  This usually
1056   // happens when the mutex is contended.
1057   //
1058   // In the absence of contention, we just return from the try_lock() function
1059   // with the lock acquired.  So we need to invoke the task and unlock
1060   // the mutex before returning
1061   auto&& task = Request<Func>{func};
1062   auto&& state = lockImplementation(*this, state_, task);
1063   if (!state.combined_) {
1064     // to avoid having to play a return-value dance when the combinable
1065     // returns void, we use a scope exit to perform the unlock after the
1066     // function return has been processed
1067     SCOPE_EXIT {
1068       unlock(std::move(state));
1069     };
1070     return func();
1071   }
1072 
1073   // if we are here, that means we were able to get our request combined, we
1074   // can return the value that was transferred to us
1075   //
1076   // each thread that enqueues as a part of a contention chain takes up the
1077   // responsibility of any timed waiter that had come immediately before it,
1078   // so we wake up timed waiters before exiting the lock function.  Another
1079   // strategy might be to add the timed waiter information to the metadata and
1080   // let a single leader wake up a timed waiter for better concurrency.  But
1081   // this has proven not to be useful in benchmarks beyond a small 5% delta,
1082   // so we avoid taking the complexity hit and branch to wake up timed waiters
1083   // from each thread
1084   wakeTimedWaiters(&state_, state.timedWaiters_);
1085   return std::move(task).get();
1086 }
1087 
1088 template <template <typename> class Atomic, bool TimePublishing>
1089 typename DistributedMutex<Atomic, TimePublishing>::DistributedMutexStateProxy
1090 DistributedMutex<Atomic, TimePublishing>::lock() {
1091   auto null = nullptr;
1092   return lockImplementation(*this, state_, null);
1093 }
1094 
1095 template <typename Atomic, template <typename> class A, bool T>
1096 auto tryLockNoLoad(Atomic& atomic, DistributedMutex<A, T>&)
1097     -> typename DistributedMutex<A, T>::DistributedMutexStateProxy {
1098   // Try and set the least significant bit of the centralized lock state to 1,
1099   // if this succeeds, it must have been the case that we had a kUnlocked (or
1100   // 0) in the central storage before, since that is the only case where a 0
1101   // can be found in the least significant bit
1102   //
1103   // If this fails, then it is a no-op
1104   using Proxy = typename DistributedMutex<A, T>::DistributedMutexStateProxy;
1105   auto previous = atomic_fetch_set(atomic, 0, std::memory_order_acquire);
1106   if (!previous) {
1107     return Proxy{nullptr, kLocked};
1108   }
1109 
1110   return Proxy{nullptr, 0};
1111 }
1112 
1113 template <template <typename> class Atomic, bool TimePublishing>
1114 typename DistributedMutex<Atomic, TimePublishing>::DistributedMutexStateProxy
1115 DistributedMutex<Atomic, TimePublishing>::try_lock() {
1116   // The lock attempt below requires an expensive atomic fetch-and-mutate or
1117   // an even more expensive atomic compare-and-swap loop depending on the
1118   // platform.  These operations require pulling the lock cacheline into the
1119   // current core in exclusive mode and are therefore hard to parallelize
1120   //
1121   // This probabilistically avoids the expense by first checking whether the
1122   // mutex is currently locked
1123   if (state_.load(std::memory_order_relaxed) != kUnlocked) {
1124     return DistributedMutexStateProxy{nullptr, 0};
1125   }
1126 
1127   return tryLockNoLoad(state_, *this);
1128 }
1129 
1130 template <
1131     template <typename> class Atomic,
1132     bool TimePublishing,
1133     typename State,
1134     typename Request>
1135 typename DistributedMutex<Atomic, TimePublishing>::DistributedMutexStateProxy
1136 lockImplementation(
1137     DistributedMutex<Atomic, TimePublishing>& mutex,
1138     State& atomic,
1139     Request& request) {
1140   // first try and acquire the lock as a fast path, the underlying
1141   // implementation is slightly faster than using std::atomic::exchange() as
1142   // is used in this function.  So we get a small perf boost in the
1143   // uncontended case
1144   //
1145   // We only go through this fast path for the lock/unlock usage and avoid this
1146   // for combined critical sections.  This check adds unnecessary overhead in
1147   // that case as it causes an extra cacheline bounce
1148   constexpr auto combineRequested = !std::is_same<Request, std::nullptr_t>{};
1149   if (!combineRequested) {
1150     if (auto state = tryLockNoLoad(atomic, mutex)) {
1151       return state;
1152     }
1153   }
1154 
1155   auto previous = std::uintptr_t{0};
1156   auto waitMode = combineRequested ? kCombineUninitialized : kUninitialized;
1157   auto nextWaitMode = kAboutToWait;
1158   auto timedWaiter = false;
1159   Waiter<Atomic>* nextSleeper = nullptr;
1160   while (true) {
1161     // construct the state needed to wait
1162     //
1163     // We can't use auto here because MSVC errors out due to a missing copy
1164     // constructor
1165     Waiter<Atomic> state{};
1166     auto&& task = coalesce(request, state);
1167     auto&& storage = makeReturnValueStorageFor(task);
1168     auto&& address = folly::bit_cast<std::uintptr_t>(&state);
1169     attach(task, storage);
1170     state.initialize(waitMode, std::move(task));
1171     assert(!(address & 0b1));
1172 
1173     // set the locked bit in the address we will be persisting in the mutex
1174     address |= kLocked;
1175 
1176     // attempt to acquire the mutex, mutex acquisition is successful if the
1177     // previous value is zeroed out
1178     //
1179     // we use memory_order_acq_rel here because we want the read-modify-write
1180     // operation to be both acquire and release.  Acquire becasue if this is a
1181     // successful lock acquisition, we want to acquire state any other thread
1182     // has released from a prior unlock.  We want release semantics becasue
1183     // other threads that read the address of this value should see the full
1184     // well-initialized node we are going to wait on if the mutex acquisition
1185     // was unsuccessful
1186     previous = atomic.exchange(address, std::memory_order_acq_rel);
1187     recordTimedWaiterAndClearTimedBit(timedWaiter, previous);
1188     state.next_.store(previous, std::memory_order_relaxed);
1189     if (previous == kUnlocked) {
1190       return {/* next */ nullptr,
1191               /* expected */ address,
1192               /* timedWaiter */ timedWaiter,
1193               /* combined */ false,
1194               /* waker */ 0,
1195               /* waiters */ nullptr,
1196               /* ready */ nextSleeper};
1197     }
1198     assert(previous & kLocked);
1199 
1200     // wait until we get a signal from another thread, if this returns false,
1201     // we got skipped and had probably been scheduled out, so try again
1202     auto signal = kUninitialized;
1203     if (!wait(&state, waitMode, nextSleeper, signal)) {
1204       std::swap(waitMode, nextWaitMode);
1205       continue;
1206     }
1207 
1208     // at this point it is safe to access the other fields in the waiter state,
1209     // since the thread that woke us up is gone and nobody will be touching this
1210     // state again, note that this requires memory ordering, and this is why we
1211     // use memory_order_acquire (among other reasons) in the above wait
1212     //
1213     // first we see if the value we took off the mutex state was the thread that
1214     // initated the wakeups, if so, we are the terminal node of the current
1215     // contention chain.  If we are the terminal node, then we should expect to
1216     // see a kLocked in the mutex state when we unlock, if we see that, we can
1217     // commit the unlock to the centralized mutex state.  If not, we need to
1218     // continue wakeups
1219     //
1220     // a nice consequence of passing kLocked as the current address if we are
1221     // the terminal node is that it naturally just works with the algorithm.  If
1222     // we get a contention chain when coming out of a contention chain, the tail
1223     // of the new contention chain will have kLocked set as the previous, which,
1224     // as it happens "just works", since we have now established a recursive
1225     // relationship until broken
1226     auto next = previous;
1227     auto expected = address;
1228     if (previous == state.metadata_.waker_) {
1229       next = 0;
1230       expected = kLocked;
1231     }
1232 
1233     // if we were given a combine signal, detach the return value from the
1234     // wait struct into the request, so the current thread can access it
1235     // outside this function
1236     auto combined = (signal == kCombined);
1237     auto exceptionOccurred = (signal == kExceptionOccurred);
1238     if (combined || exceptionOccurred) {
1239       detach(request, state, exceptionOccurred, storage);
1240     }
1241 
1242     // if we are just coming out of a futex call, then it means that the next
1243     // waiter we are responsible for is also a waiter waiting on a futex, so
1244     // we return that list in the list of ready threads.  We wlil be waking up
1245     // the ready threads on unlock no matter what
1246     return {/* next */ extractPtr<Waiter<Atomic>>(next),
1247             /* expected */ expected,
1248             /* timedWaiter */ timedWaiter,
1249             /* combined */ combineRequested && (combined || exceptionOccurred),
1250             /* waker */ state.metadata_.waker_,
1251             /* waiters */ extractPtr<Waiter<Atomic>>(state.metadata_.waiters_),
1252             /* ready */ nextSleeper};
1253   }
1254 }
1255 
1256 inline bool preempted(std::uint64_t value, std::chrono::nanoseconds now) {
1257   auto currentTime = recover(strip(now));
1258   auto nodeTime = recover(value);
1259   auto preempted =
1260       (currentTime > nodeTime + kScheduledAwaySpinThreshold.count()) &&
1261       (nodeTime != recover(strip(std::chrono::nanoseconds::max())));
1262 
1263   // we say that the thread has been preempted if its timestamp says so, and
1264   // also if it is neither uninitialized nor skipped
1265   assert(value != kSkipped);
1266   return (preempted) && (value != kUninitialized) &&
1267       (value != kCombineUninitialized);
1268 }
1269 
1270 inline bool isSleeper(std::uintptr_t value) {
1271   return (value == kAboutToWait);
1272 }
1273 
1274 inline bool isInitialized(std::uintptr_t value) {
1275   return (value != kUninitialized) && (value != kCombineUninitialized);
1276 }
1277 
1278 inline bool isCombiner(std::uintptr_t value) {
1279   auto mode = (value & 0xff);
1280   return (mode == kCombineWaiting) || (mode == kCombineUninitialized);
1281 }
1282 
1283 inline bool isWaitingCombiner(std::uintptr_t value) {
1284   return (value & 0xff) == kCombineWaiting;
1285 }
1286 
1287 template <typename Waiter>
1288 CombineFunction loadTask(Waiter* current, std::uintptr_t value) {
1289   // if we know that the waiter is a combiner of some sort, it is safe to read
1290   // and copy the value of the function in the waiter struct, since we know
1291   // that a waiter would have set it before enqueueing
1292   if (isCombiner(value)) {
1293     return current->function_;
1294   }
1295 
1296   return nullptr;
1297 }
1298 
1299 template <typename Waiter>
1300 void transferCurrentException(Waiter* waiter) {
1301   assert(std::current_exception());
1302   new (&waiter->storage_) std::exception_ptr(std::current_exception());
1303   waiter->futex_.store(kExceptionOccurred, std::memory_order_release);
1304 }
1305 
1306 template <template <typename> class Atomic>
1307 inline std::uintptr_t tryCombine(
1308     Waiter<Atomic>* waiter,
1309     std::uintptr_t value,
1310     std::uintptr_t next,
1311     std::uint64_t iteration,
1312     std::chrono::nanoseconds now,
1313     CombineFunction task) {
1314 #ifndef ROCKSDB_LITE
1315   // if the waiter has asked for a combine operation, we should combine its
1316   // critical section and move on to the next waiter
1317   //
1318   // the waiter is combinable if the following conditions are satisfied
1319   //
1320   //  1) the state in the futex word is not uninitialized (kUninitialized)
1321   //  2) it has a valid combine function
1322   //  3) we are not past the limit of the number of combines we can perform
1323   //     or the waiter thread been preempted.  If the waiter gets preempted,
1324   //     its better to just execute their critical section before moving on.
1325   //     As they will have to re-queue themselves after preemption anyway,
1326   //     leading to further delays in critical section completion
1327   //
1328   // if all the above are satisfied, then we can combine the critical section.
1329   // Note that if the waiter is in a combineable state, that means that it had
1330   // finished its writes to both the task and the next_ value.  And observing
1331   // a waiting state also means that we have acquired the writes to the other
1332   // members of the waiter struct, so it's fine to use those values here
1333   if (isWaitingCombiner(value) &&
1334       (iteration <= kMaxCombineIterations || preempted(value, now))) {
1335     try {
1336       task();
1337       waiter->futex_.store(kCombined, std::memory_order_release);
1338     } catch (...) {
1339       transferCurrentException(waiter);
1340     }
1341     return next;
1342   }
1343 #endif  // ROCKSDB_LITE
1344   return 0;
1345 }
1346 
1347 template <typename Waiter>
1348 inline std::uintptr_t tryWake(
1349     bool publishing,
1350     Waiter* waiter,
1351     std::uintptr_t value,
1352     std::uintptr_t next,
1353     std::uintptr_t waker,
1354     Waiter*& sleepers,
1355     std::uint64_t iteration,
1356     CombineFunction task) {
1357   // try and combine the waiter's request first, if that succeeds that means
1358   // we have successfully executed their critical section and can move on to
1359   // the rest of the chain
1360   auto now = time();
1361   if (tryCombine(waiter, value, next, iteration, now, task)) {
1362     return next;
1363   }
1364 
1365   // first we see if we can wake the current thread that is spinning
1366   if ((!publishing || !preempted(value, now)) && !isSleeper(value)) {
1367     // the Metadata class should be trivially destructible as we use placement
1368     // new to set the relevant metadata without calling any destructor.  We
1369     // need to use placement new because the class contains a futex, which is
1370     // non-movable and non-copyable
1371     using Metadata = _t<std::decay<decltype(waiter->metadata_)>>;
1372     static_assert(std::is_trivially_destructible<Metadata>{}, "");
1373 
1374     // we need release here because of the write to waker_ and also because we
1375     // are unlocking the mutex, the thread we do the handoff to here should
1376     // see the modified data
1377     new (&waiter->metadata_) Metadata(waker, bit_cast<uintptr_t>(sleepers));
1378     waiter->futex_.store(kWake, std::memory_order_release);
1379     return 0;
1380   }
1381 
1382   // if the thread is not a sleeper, and we were not able to catch it before
1383   // preemption, we can just return a false, it is safe to read next_ because
1384   // the thread was preempted.  Preemption signals can only come after the
1385   // thread has set the next_ pointer, since the timestamp writes only start
1386   // occurring after that point
1387   //
1388   // if a thread was preempted it must have stored next_ in the waiter struct,
1389   // as the store to futex_ that resets the value from kUninitialized happens
1390   // after the write to next
1391   assert(publishing);
1392   if (!isSleeper(value)) {
1393     // go on to the next one
1394     //
1395     // Also, we need a memory_order_release here to prevent missed wakeups.  A
1396     // missed wakeup here can happen when we see that a thread had been
1397     // preempted and skip it.  Then go on to release the lock, and then when
1398     // the thread which got skipped does an exchange on the central storage,
1399     // still sees the locked bit, and never gets woken up
1400     //
1401     // Can we relax this?
1402     assert(preempted(value, now));
1403     assert(!isCombiner(value));
1404     next = waiter->next_.load(std::memory_order_relaxed);
1405     waiter->futex_.store(kSkipped, std::memory_order_release);
1406     return next;
1407   }
1408 
1409   // if we are here the thread is a sleeper
1410   //
1411   // we attempt to catch the thread before it goes to futex().  If we are able
1412   // to catch the thread before it sleeps on a futex, we are done, and don't
1413   // need to go any further
1414   //
1415   // if we are not able to catch the thread before it goes to futex, we
1416   // collect the current thread in the list of sleeping threads represented by
1417   // sleepers, and return the next thread in the list and return false along
1418   // with the previous next value
1419   //
1420   // it is safe to read the next_ pointer in the waiter struct if we were
1421   // unable to catch the thread before it went to futex() because we use
1422   // acquire-release ordering for the exchange operation below.  And if we see
1423   // that the thread was already sleeping, we have synchronized with the write
1424   // to next_ in the context of the sleeping thread
1425   //
1426   // Also we need to set the value of waiters_ and waker_ in the thread before
1427   // doing the exchange because we need to pass on the list of sleepers in the
1428   // event that we were able to catch the thread before it went to futex().
1429   // If we were unable to catch the thread before it slept, these fields will
1430   // be ignored when the thread wakes up anyway
1431   assert(isSleeper(value));
1432   waiter->metadata_.waker_ = waker;
1433   waiter->metadata_.waiters_ = folly::bit_cast<std::uintptr_t>(sleepers);
1434   auto pre =
1435       waiter->metadata_.sleeper_.exchange(kSleeping, std::memory_order_acq_rel);
1436 
1437   // we were able to catch the thread before it went to sleep, return true
1438   if (pre != kSleeping) {
1439     return 0;
1440   }
1441 
1442   // otherwise return false, with the value of next_, it is safe to read next
1443   // because of the same logic as when a thread was preempted
1444   //
1445   // we also need to collect this sleeper in the list of sleepers being built
1446   // up
1447   next = waiter->next_.load(std::memory_order_relaxed);
1448   auto head = folly::bit_cast<std::uintptr_t>(sleepers);
1449   waiter->next_.store(head, std::memory_order_relaxed);
1450   sleepers = waiter;
1451   return next;
1452 }
1453 
1454 template <typename Waiter>
1455 bool wake(
1456     bool publishing,
1457     Waiter& waiter,
1458     std::uintptr_t waker,
1459     Waiter*& sleepers,
1460     std::uint64_t iter) {
1461   // loop till we find a node that is either at the end of the list (as
1462   // specified by waker) or we find a node that is active (as specified by
1463   // the last published timestamp of the node)
1464   auto current = &waiter;
1465   while (current) {
1466     // it is important that we load the value of function and next_ after the
1467     // initial acquire load.  This is required because we need to synchronize
1468     // with the construction of the waiter struct before reading from it
1469     //
1470     // the load from the next_ variable is an optimistic load that assumes
1471     // that the waiting thread has probably gone to the waiting state.  If the
1472     // waiitng thread is in the waiting state (as revealed by the acquire load
1473     // from the futex word), we will see a well formed next_ value because it
1474     // happens-before the release store to the futex word.  The atomic load from
1475     // next_ is an optimization to avoid branching before loading and prevent
1476     // the compiler from eliding the load altogether (and using a pointer
1477     // dereference when needed)
1478     auto value = current->futex_.load(std::memory_order_acquire);
1479     auto next = current->next_.load(std::memory_order_relaxed);
1480     auto task = loadTask(current, value);
1481     next =
1482         tryWake(publishing, current, value, next, waker, sleepers, iter, task);
1483 
1484     // if there is no next node, we have managed to wake someone up and have
1485     // successfully migrated the lock to another thread
1486     if (!next) {
1487       return true;
1488     }
1489 
1490     // we need to read the value of the next node in the list before skipping
1491     // it, this is because after we skip it the node might wake up and enqueue
1492     // itself, and thereby gain a new next node
1493     assert(publishing);
1494     current = (next == waker) ? nullptr : extractPtr<Waiter>(next);
1495   }
1496 
1497   return false;
1498 }
1499 
1500 template <typename Atomic, typename Proxy, typename Sleepers>
1501 bool tryUnlockClean(Atomic& state, Proxy& proxy, Sleepers sleepers) {
1502   auto expected = proxy.expected_;
1503   while (true) {
1504     if (state.compare_exchange_strong(
1505             expected,
1506             kUnlocked,
1507             std::memory_order_release,
1508             std::memory_order_relaxed)) {
1509       // if we were able to commit an unlocked, we need to wake up the futex
1510       // waiters, if any
1511       doFutexWake(sleepers);
1512       return true;
1513     }
1514 
1515     // if we failed the compare_exchange_strong() above, we check to see if
1516     // the failure was because of the presence of a timed waiter.  If that
1517     // was the case then we try one more time with the kTimedWaiter bit set
1518     if (expected == (proxy.expected_ | kTimedWaiter)) {
1519       proxy.timedWaiters_ = true;
1520       continue;
1521     }
1522 
1523     // otherwise break, we have a contention chain
1524     return false;
1525   }
1526 }
1527 
1528 template <template <typename> class Atomic, bool Publish>
1529 void DistributedMutex<Atomic, Publish>::unlock(
1530     typename DistributedMutex::DistributedMutexStateProxy proxy) {
1531   // we always wake up ready threads and timed waiters if we saw either
1532   assert(proxy);
1533   assert(!proxy.combined_);
1534   SCOPE_EXIT {
1535     doFutexWake(proxy.ready_);
1536     wakeTimedWaiters(&state_, proxy.timedWaiters_);
1537   };
1538 
1539   // if there is a wait queue we are responsible for, try and start wakeups,
1540   // don't bother with the mutex state
1541   auto sleepers = proxy.waiters_;
1542   if (proxy.next_) {
1543     if (wake(Publish, *proxy.next_, proxy.waker_, sleepers, 0)) {
1544       return;
1545     }
1546 
1547     // At this point, if are in the if statement, we were not the terminal
1548     // node of the wakeup chain.  Terminal nodes have the next_ pointer set to
1549     // null in lock()
1550     //
1551     // So we need to pretend we were the end of the contention chain.  Coming
1552     // out of a contention chain always has the kLocked state set in the
1553     // mutex.  Unless there is another contention chain lined up, which does
1554     // not matter since we are the terminal node anyway
1555     proxy.expected_ = kLocked;
1556   }
1557 
1558   for (std::uint64_t i = 0; true; ++i) {
1559     // otherwise, since we don't have anyone we need to wake up, we try and
1560     // release the mutex just as is
1561     //
1562     // if this is successful, we can return, the unlock was successful, we have
1563     // committed a nice kUnlocked to the central storage, yay
1564     if (tryUnlockClean(state_, proxy, sleepers)) {
1565       return;
1566     }
1567 
1568     // here we have a contention chain built up on the mutex.  We grab the
1569     // wait queue and start executing wakeups.  We leave a locked bit on the
1570     // centralized storage and handoff control to the head of the queue
1571     //
1572     // we use memory_order_acq_rel here because we want to see the
1573     // full well-initialized node that the other thread is waiting on
1574     //
1575     // If we are unable to wake the contention chain, it is possible that when
1576     // we come back to looping here, a new contention chain will form.  In
1577     // that case we need to use kLocked as the waker_ value because the
1578     // terminal node of the new chain will see kLocked in the central storage
1579     auto head = state_.exchange(kLocked, std::memory_order_acq_rel);
1580     recordTimedWaiterAndClearTimedBit(proxy.timedWaiters_, head);
1581     auto next = extractPtr<Waiter<Atomic>>(head);
1582     auto expected = folly::exchange(proxy.expected_, kLocked);
1583     assert((head & kLocked) && (head != kLocked));
1584     if (wake(Publish, *next, expected, sleepers, i)) {
1585       break;
1586     }
1587   }
1588 }
1589 
1590 template <typename Atomic, typename Deadline, typename MakeProxy>
1591 auto timedLock(Atomic& state, Deadline deadline, MakeProxy proxy)
1592     -> decltype(std::declval<MakeProxy&>()(nullptr, kLocked, true)) {
1593   while (true) {
1594     // we put a bit on the central state to show that there is a timed waiter
1595     // and go to sleep on the central state
1596     //
1597     // when this thread goes to unlock the mutex, it will expect a 0b1 in the
1598     // mutex state (0b1, not 0b11), but then it will see that the value in the
1599     // mutex state is 0b11 and not 0b1, meaning that there might have been
1600     // another timed waiter.  Even though there might not have been another
1601     // timed waiter in the time being.  This sort of missed wakeup is
1602     // desirable for timed waiters; it helps avoid thundering herds of timed
1603     // waiters.  Because the mutex is packed in 8 bytes, and we need an
1604     // address to be stored in those 8 bytes, we don't have much room to play
1605     // with.  The only other solution is to issue a futexWake(INT_MAX) to wake
1606     // up all waiters when a clean unlock is committed, when a thread saw a
1607     // timed waiter in the mutex previously.
1608     //
1609     // putting a 0b11 here works for a set of reasons that is a superset of
1610     // the set of reasons that make it okay to put a kLocked (0b1) in the
1611     // mutex state.  Now that the thread has put (kTimedWaiter | kLocked)
1612     // (0b11) in the mutex state and it expects a kLocked (0b1), there are two
1613     // scenarios possible.  The first being when there is no contention chain
1614     // formation in the mutex from the time a timed waiter got a lock to
1615     // unlock.  In this case, the unlocker sees a 0b11 in the mutex state,
1616     // adjusts to the presence of a timed waiter and cleanly unlocks with a
1617     // kUnlocked (0b0).  The second is when there is a contention chain.
1618     // When a thread puts its address in the mutex and sees the timed bit, it
1619     // records the presence of a timed waiter, and then pretends as if it
1620     // hadn't seen the timed bit.  So future contention chain releases, will
1621     // terminate with a kLocked (0b1) and not a (kLocked | kTimedWaiter)
1622     // (0b11).  This just works naturally with the rest of the algorithm
1623     // without incurring a perf hit for the regular non-timed case
1624     //
1625     // this strategy does however mean, that when threads try to acquire the
1626     // mutex and all time out, there will be a wasteful syscall to issue wakeups
1627     // to waiting threads.  We don't do anything to try and minimize this
1628     //
1629     // we need to use a fetch_or() here because we need to convey two bits of
1630     // information - 1, whether the mutex is locked or not, and 2, whether
1631     // there is a timed waiter.  The alternative here is to use the second bit
1632     // to convey information only, we can use a fetch_set() on the second bit
1633     // to make this faster, but that comes at the expense of requiring regular
1634     // fast path lock attempts.  Which use a single bit read-modify-write for
1635     // better performance
1636     auto data = kTimedWaiter | kLocked;
1637     auto previous = state.fetch_or(data, std::memory_order_acquire);
1638     if (!(previous & 0b1)) {
1639       assert(!previous);
1640       return proxy(nullptr, kLocked, true);
1641     }
1642 
1643     // wait on the futex until signalled, if we get a timeout, the try_lock
1644     // fails
1645     auto result = atomic_wait_until(&state, previous | data, deadline);
1646     if (result == std::cv_status::timeout) {
1647       return proxy(nullptr, std::uintptr_t{0}, false);
1648     }
1649   }
1650 }
1651 
1652 template <template <typename> class Atomic, bool TimePublishing>
1653 template <typename Clock, typename Duration>
1654 typename DistributedMutex<Atomic, TimePublishing>::DistributedMutexStateProxy
1655 DistributedMutex<Atomic, TimePublishing>::try_lock_until(
1656     const std::chrono::time_point<Clock, Duration>& deadline) {
1657   // fast path for the uncontended case
1658   //
1659   // we get the time after trying to acquire the mutex because in the
1660   // uncontended case, the price of getting the time is about 1/3 of the
1661   // actual mutex acquisition.  So we only pay the price of that extra bit of
1662   // latency when needed
1663   //
1664   // this is even higher when VDSO is involved on architectures that do not
1665   // offer a direct interface to the timestamp counter
1666   if (auto state = try_lock()) {
1667     return state;
1668   }
1669 
1670   // fall back to the timed locking algorithm
1671   using Proxy = DistributedMutexStateProxy;
1672   return timedLock(
1673       state_,
1674       deadline,
1675       [](Waiter<Atomic>* next, std::uintptr_t expected, bool timedWaiter) {
1676         return Proxy{next, expected, timedWaiter};
1677       });
1678 }
1679 
1680 template <template <typename> class Atomic, bool TimePublishing>
1681 template <typename Rep, typename Period>
1682 typename DistributedMutex<Atomic, TimePublishing>::DistributedMutexStateProxy
1683 DistributedMutex<Atomic, TimePublishing>::try_lock_for(
1684     const std::chrono::duration<Rep, Period>& duration) {
1685   // fast path for the uncontended case.  Reasoning for doing this here is the
1686   // same as in try_lock_until()
1687   if (auto state = try_lock()) {
1688     return state;
1689   }
1690 
1691   // fall back to the timed locking algorithm
1692   using Proxy = DistributedMutexStateProxy;
1693   auto deadline = std::chrono::steady_clock::now() + duration;
1694   return timedLock(
1695       state_,
1696       deadline,
1697       [](Waiter<Atomic>* next, std::uintptr_t expected, bool timedWaiter) {
1698         return Proxy{next, expected, timedWaiter};
1699       });
1700 }
1701 } // namespace distributed_mutex
1702 } // namespace detail
1703 } // namespace folly
1704