capnproto

FORK: Cap'n Proto serialization/RPC system - core tools and C++ library
git clone https://git.neptards.moe/neptards/capnproto.git
Log | Files | Refs | README | LICENSE

mutex.h (28235B)


      1 // Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors
      2 // Licensed under the MIT License:
      3 //
      4 // Permission is hereby granted, free of charge, to any person obtaining a copy
      5 // of this software and associated documentation files (the "Software"), to deal
      6 // in the Software without restriction, including without limitation the rights
      7 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
      8 // copies of the Software, and to permit persons to whom the Software is
      9 // furnished to do so, subject to the following conditions:
     10 //
     11 // The above copyright notice and this permission notice shall be included in
     12 // all copies or substantial portions of the Software.
     13 //
     14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     17 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     19 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     20 // THE SOFTWARE.
     21 
     22 #pragma once
     23 
     24 #include "debug.h"
     25 #include "memory.h"
     26 #include <inttypes.h>
     27 #include "time.h"
     28 #include "source-location.h"
     29 #include "one-of.h"
     30 
     31 KJ_BEGIN_HEADER
     32 
     33 #if __linux__ && !defined(KJ_USE_FUTEX)
     34 #define KJ_USE_FUTEX 1
     35 #endif
     36 
     37 #if !KJ_USE_FUTEX && !_WIN32 && !__CYGWIN__
     38 // We fall back to pthreads when we don't have a better platform-specific primitive. pthreads
     39 // mutexes are bloated, though, so we like to avoid them. Hence on Linux we use futex(), and on
     40 // Windows we use SRW locks and friends. On Cygwin we prefer the Win32 primitives both because they
     41 // are more efficient and because I ran into problems with Cygwin's implementation of RW locks
     42 // seeming to allow multiple threads to lock the same mutex (but I didn't investigate very
     43 // closely).
     44 //
     45 // TODO(someday):  Write efficient low-level locking primitives for other platforms.
     46 #include <pthread.h>
     47 #endif
     48 
     49 // There are 3 macros controlling lock tracking:
     50 // KJ_TRACK_LOCK_BLOCKING will set up async signal safe TLS variables that can be used to identify
     51 // the KJ primitive blocking the current thread.
     52 // KJ_SAVE_ACQUIRED_LOCK_INFO will allow introspection of a Mutex to get information about what is
     53 // currently holding the lock.
     54 // KJ_TRACK_LOCK_ACQUISITION is automatically enabled by either one of them.
     55 
     56 #if KJ_TRACK_LOCK_BLOCKING
     57 // Lock tracking is required to keep track of what blocked.
     58 #define KJ_TRACK_LOCK_ACQUISITION 1
     59 #endif
     60 
     61 #if KJ_SAVE_ACQUIRED_LOCK_INFO
     62 #define KJ_TRACK_LOCK_ACQUISITION 1
     63 #include <unistd.h>
     64 #endif
     65 
     66 namespace kj {
     67 #if KJ_TRACK_LOCK_ACQUISITION
     68 #if !KJ_USE_FUTEX
     69 #error Lock tracking is only currently supported for futex-based mutexes.
     70 #endif
     71 
     72 #if !KJ_COMPILER_SUPPORTS_SOURCE_LOCATION
     73 #error C++20 or newer is required (or the use of clang/gcc).
     74 #endif
     75 
     76 using LockSourceLocation = SourceLocation;
     77 using LockSourceLocationArg = const SourceLocation&;
     78 // On x86-64 the codegen is optimal if the argument has type const& for the location. However,
     79 // since this conflicts with the optimal call signature for NoopSourceLocation,
     80 // LockSourceLocationArg is used to conditionally select the right type without polluting the usage
     81 // themselves. Interestingly this makes no difference on ARM.
     82 // https://godbolt.org/z/q6G8ee5a3
     83 #else
     84 using LockSourceLocation = NoopSourceLocation;
     85 using LockSourceLocationArg = NoopSourceLocation;
     86 #endif
     87 
     88 
     89 class Exception;
     90 
     91 // =======================================================================================
     92 // Private details -- public interfaces follow below.
     93 
     94 namespace _ {  // private
     95 
     96 #if KJ_SAVE_ACQUIRED_LOCK_INFO
     97 class HoldingExclusively {
     98   // The lock is being held in exclusive mode.
     99 public:
    100   constexpr HoldingExclusively(pid_t tid, const SourceLocation& location)
    101       : heldBy(tid), acquiredAt(location) {}
    102 
    103   pid_t threadHoldingLock() const { return heldBy; }
    104   const SourceLocation& lockAcquiredAt() const { return acquiredAt; }
    105 
    106 private:
    107   pid_t heldBy;
    108   SourceLocation acquiredAt;
    109 };
    110 
    111 class HoldingShared {
    112   // The lock is being held in shared mode currently. Which threads are holding this lock open
    113   // is unknown.
    114 public:
    115   constexpr HoldingShared(const SourceLocation& location) : acquiredAt(location) {}
    116 
    117   const SourceLocation& lockAcquiredAt() const { return acquiredAt; }
    118 
    119 private:
    120   SourceLocation acquiredAt;
    121 };
    122 #endif
    123 
    124 class Mutex {
    125   // Internal implementation details.  See `MutexGuarded<T>`.
    126 
    127   struct Waiter;
    128 
    129 public:
    130   Mutex();
    131   ~Mutex();
    132   KJ_DISALLOW_COPY(Mutex);
    133 
    134   enum Exclusivity {
    135     EXCLUSIVE,
    136     SHARED
    137   };
    138 
    139   bool lock(Exclusivity exclusivity, Maybe<Duration> timeout, LockSourceLocationArg location);
    140   void unlock(Exclusivity exclusivity, Waiter* waiterToSkip = nullptr);
    141 
    142   void assertLockedByCaller(Exclusivity exclusivity) const;
    143   // In debug mode, assert that the mutex is locked by the calling thread, or if that is
    144   // non-trivial, assert that the mutex is locked (which should be good enough to catch problems
    145   // in unit tests).  In non-debug builds, do nothing.
    146 
    147   class Predicate {
    148   public:
    149     virtual bool check() = 0;
    150   };
    151 
    152   void wait(Predicate& predicate, Maybe<Duration> timeout, LockSourceLocationArg location);
    153   // If predicate.check() returns false, unlock the mutex until predicate.check() returns true, or
    154   // when the timeout (if any) expires. The mutex is always re-locked when this returns regardless
    155   // of whether the timeout expired, and including if it throws.
    156   //
    157   // Requires that the mutex is already exclusively locked before calling.
    158 
    159   void induceSpuriousWakeupForTest();
    160   // Utility method for mutex-test.c++ which causes a spurious thread wakeup on all threads that
    161   // are waiting for a wait() condition. Assuming correct implementation, all those threads
    162   // should immediately go back to sleep.
    163 
    164 #if KJ_USE_FUTEX
    165   uint numReadersWaitingForTest() const;
    166   // The number of reader locks that are currently blocked on this lock (must be called while
    167   // holding the writer lock). This is really only a utility method for mutex-test.c++ so it can
    168   // validate certain invariants.
    169 #endif
    170 
    171 #if KJ_SAVE_ACQUIRED_LOCK_INFO
    172   using AcquiredMetadata = kj::OneOf<HoldingExclusively, HoldingShared>;
    173   KJ_DISABLE_TSAN AcquiredMetadata lockedInfo() const;
    174   // Returns metadata about this lock when its held. This method is async signal safe. It must also
    175   // be called in a state where it's guaranteed that the lock state won't be released by another
    176   // thread. In other words this has to be called from the signal handler within the thread that's
    177   // holding the lock.
    178 #endif
    179 
    180 private:
    181 #if KJ_USE_FUTEX
    182   uint futex;
    183   // bit 31 (msb) = set if exclusive lock held
    184   // bit 30 (msb) = set if threads are waiting for exclusive lock
    185   // bits 0-29 = count of readers; If an exclusive lock is held, this is the count of threads
    186   //   waiting for a read lock, otherwise it is the count of threads that currently hold a read
    187   //   lock.
    188 
    189 #ifdef KJ_CONTENTION_WARNING_THRESHOLD
    190   bool printContendedReader = false;
    191 #endif
    192 
    193   static constexpr uint EXCLUSIVE_HELD = 1u << 31;
    194   static constexpr uint EXCLUSIVE_REQUESTED = 1u << 30;
    195   static constexpr uint SHARED_COUNT_MASK = EXCLUSIVE_REQUESTED - 1;
    196 
    197 #elif _WIN32 || __CYGWIN__
    198   uintptr_t srwLock;  // Actually an SRWLOCK, but don't want to #include <windows.h> in header.
    199 
    200 #else
    201   mutable pthread_rwlock_t mutex;
    202 #endif
    203 
    204 #if KJ_SAVE_ACQUIRED_LOCK_INFO
    205   pid_t lockedExclusivelyByThread = 0;
    206   SourceLocation lockAcquiredLocation;
    207 
    208   KJ_DISABLE_TSAN void acquiredExclusive(pid_t tid, const SourceLocation& location) noexcept {
    209     lockAcquiredLocation = location;
    210     __atomic_store_n(&lockedExclusivelyByThread, tid, __ATOMIC_RELAXED);
    211   }
    212 
    213   KJ_DISABLE_TSAN void acquiredShared(const SourceLocation& location) noexcept {
    214     lockAcquiredLocation = location;
    215   }
    216 
    217   KJ_DISABLE_TSAN SourceLocation releasingExclusive() noexcept {
    218     auto tmp = lockAcquiredLocation;
    219     lockAcquiredLocation = SourceLocation{};
    220     lockedExclusivelyByThread = 0;
    221     return tmp;
    222   }
    223 #else
    224   static constexpr void acquiredExclusive(uint, LockSourceLocationArg) {}
    225   static constexpr void acquiredShared(LockSourceLocationArg) {}
    226   static constexpr NoopSourceLocation releasingExclusive() { return NoopSourceLocation{}; }
    227 #endif
    228   struct Waiter {
    229     kj::Maybe<Waiter&> next;
    230     kj::Maybe<Waiter&>* prev;
    231     Predicate& predicate;
    232     Maybe<Own<Exception>> exception;
    233 #if KJ_USE_FUTEX
    234     uint futex;
    235     bool hasTimeout;
    236 #elif _WIN32 || __CYGWIN__
    237     uintptr_t condvar;
    238     // Actually CONDITION_VARIABLE, but don't want to #include <windows.h> in header.
    239 #else
    240     pthread_cond_t condvar;
    241 
    242     pthread_mutex_t stupidMutex;
    243     // pthread condvars are only compatible with basic pthread mutexes, not rwlocks, for no
    244     // particularly good reason. To work around this, we need an extra mutex per condvar.
    245 #endif
    246   };
    247 
    248   kj::Maybe<Waiter&> waitersHead = nullptr;
    249   kj::Maybe<Waiter&>* waitersTail = &waitersHead;
    250   // linked list of waiters; can only modify under lock
    251 
    252   inline void addWaiter(Waiter& waiter);
    253   inline void removeWaiter(Waiter& waiter);
    254   bool checkPredicate(Waiter& waiter);
    255 #if _WIN32 || __CYGWIN__
    256   void wakeReadyWaiter(Waiter* waiterToSkip);
    257 #endif
    258 };
    259 
    260 class Once {
    261   // Internal implementation details.  See `Lazy<T>`.
    262 
    263 public:
    264 #if KJ_USE_FUTEX
    265   inline Once(bool startInitialized = false)
    266       : futex(startInitialized ? INITIALIZED : UNINITIALIZED) {}
    267 #else
    268   Once(bool startInitialized = false);
    269   ~Once();
    270 #endif
    271   KJ_DISALLOW_COPY(Once);
    272 
    273   class Initializer {
    274   public:
    275     virtual void run() = 0;
    276   };
    277 
    278   void runOnce(Initializer& init, LockSourceLocationArg location);
    279 
    280 #if _WIN32 || __CYGWIN__  // TODO(perf): Can we make this inline on win32 somehow?
    281   bool isInitialized() noexcept;
    282 
    283 #else
    284   inline bool isInitialized() noexcept {
    285     // Fast path check to see if runOnce() would simply return immediately.
    286 #if KJ_USE_FUTEX
    287     return __atomic_load_n(&futex, __ATOMIC_ACQUIRE) == INITIALIZED;
    288 #else
    289     return __atomic_load_n(&state, __ATOMIC_ACQUIRE) == INITIALIZED;
    290 #endif
    291   }
    292 #endif
    293 
    294   void reset();
    295   // Returns the state from initialized to uninitialized.  It is an error to call this when
    296   // not already initialized, or when runOnce() or isInitialized() might be called concurrently in
    297   // another thread.
    298 
    299 private:
    300 #if KJ_USE_FUTEX
    301   uint futex;
    302 
    303   enum State {
    304     UNINITIALIZED,
    305     INITIALIZING,
    306     INITIALIZING_WITH_WAITERS,
    307     INITIALIZED
    308   };
    309 
    310 #elif _WIN32 || __CYGWIN__
    311   uintptr_t initOnce;  // Actually an INIT_ONCE, but don't want to #include <windows.h> in header.
    312 
    313 #else
    314   enum State {
    315     UNINITIALIZED,
    316     INITIALIZED
    317   };
    318   State state;
    319   pthread_mutex_t mutex;
    320 #endif
    321 };
    322 
    323 }  // namespace _ (private)
    324 
    325 // =======================================================================================
    326 // Public interface
    327 
    328 template <typename T>
    329 class Locked {
    330   // Return type for `MutexGuarded<T>::lock()`.  `Locked<T>` provides access to the bounded object
    331   // and unlocks the mutex when it goes out of scope.
    332 
    333 public:
    334   KJ_DISALLOW_COPY(Locked);
    335   inline Locked(): mutex(nullptr), ptr(nullptr) {}
    336   inline Locked(Locked&& other): mutex(other.mutex), ptr(other.ptr) {
    337     other.mutex = nullptr;
    338     other.ptr = nullptr;
    339   }
    340   inline ~Locked() {
    341     if (mutex != nullptr) mutex->unlock(isConst<T>() ? _::Mutex::SHARED : _::Mutex::EXCLUSIVE);
    342   }
    343 
    344   inline Locked& operator=(Locked&& other) {
    345     if (mutex != nullptr) mutex->unlock(isConst<T>() ? _::Mutex::SHARED : _::Mutex::EXCLUSIVE);
    346     mutex = other.mutex;
    347     ptr = other.ptr;
    348     other.mutex = nullptr;
    349     other.ptr = nullptr;
    350     return *this;
    351   }
    352 
    353   inline void release() {
    354     if (mutex != nullptr) mutex->unlock(isConst<T>() ? _::Mutex::SHARED : _::Mutex::EXCLUSIVE);
    355     mutex = nullptr;
    356     ptr = nullptr;
    357   }
    358 
    359   inline T* operator->() { return ptr; }
    360   inline const T* operator->() const { return ptr; }
    361   inline T& operator*() { return *ptr; }
    362   inline const T& operator*() const { return *ptr; }
    363   inline T* get() { return ptr; }
    364   inline const T* get() const { return ptr; }
    365   inline operator T*() { return ptr; }
    366   inline operator const T*() const { return ptr; }
    367 
    368   template <typename Cond>
    369   void wait(Cond&& condition, Maybe<Duration> timeout = nullptr,
    370       LockSourceLocationArg location = {}) {
    371     // Unlocks the lock until `condition(state)` evaluates true (where `state` is type `const T&`
    372     // referencing the object protected by the lock).
    373 
    374     // We can't wait on a shared lock because the internal bookkeeping needed for a wait requires
    375     // the protection of an exclusive lock.
    376     static_assert(!isConst<T>(), "cannot wait() on shared lock");
    377 
    378     struct PredicateImpl final: public _::Mutex::Predicate {
    379       bool check() override {
    380         return condition(value);
    381       }
    382 
    383       Cond&& condition;
    384       const T& value;
    385 
    386       PredicateImpl(Cond&& condition, const T& value)
    387           : condition(kj::fwd<Cond>(condition)), value(value) {}
    388     };
    389 
    390     PredicateImpl impl(kj::fwd<Cond>(condition), *ptr);
    391     mutex->wait(impl, timeout, location);
    392   }
    393 
    394 private:
    395   _::Mutex* mutex;
    396   T* ptr;
    397 
    398   inline Locked(_::Mutex& mutex, T& value): mutex(&mutex), ptr(&value) {}
    399 
    400   template <typename U>
    401   friend class MutexGuarded;
    402   template <typename U>
    403   friend class ExternalMutexGuarded;
    404 
    405 #if KJ_MUTEX_TEST
    406 public:
    407 #endif
    408   void induceSpuriousWakeupForTest() { mutex->induceSpuriousWakeupForTest(); }
    409   // Utility method for mutex-test.c++ which causes a spurious thread wakeup on all threads that
    410   // are waiting for a when() condition. Assuming correct implementation, all those threads should
    411   // immediately go back to sleep.
    412 };
    413 
    414 template <typename T>
    415 class MutexGuarded {
    416   // An object of type T, bounded by a mutex.  In order to access the object, you must lock it.
    417   //
    418   // Write locks are not "recursive" -- trying to lock again in a thread that already holds a lock
    419   // will deadlock.  Recursive write locks are usually a sign of bad design.
    420   //
    421   // Unfortunately, **READ LOCKS ARE NOT RECURSIVE** either.  Common sense says they should be.
    422   // But on many operating systems (BSD, OSX), recursively read-locking a pthread_rwlock is
    423   // actually unsafe.  The problem is that writers are "prioritized" over readers, so a read lock
    424   // request will block if any write lock requests are outstanding.  So, if thread A takes a read
    425   // lock, thread B requests a write lock (and starts waiting), and then thread A tries to take
    426   // another read lock recursively, the result is deadlock.
    427 
    428 public:
    429   template <typename... Params>
    430   explicit MutexGuarded(Params&&... params);
    431   // Initialize the mutex-bounded object by passing the given parameters to its constructor.
    432 
    433   Locked<T> lockExclusive(LockSourceLocationArg location = {}) const;
    434   // Exclusively locks the object and returns it.  The returned `Locked<T>` can be passed by
    435   // move, similar to `Own<T>`.
    436   //
    437   // This method is declared `const` in accordance with KJ style rules which say that constness
    438   // should be used to indicate thread-safety.  It is safe to share a const pointer between threads,
    439   // but it is not safe to share a mutable pointer.  Since the whole point of MutexGuarded is to
    440   // be shared between threads, its methods should be const, even though locking it produces a
    441   // non-const pointer to the contained object.
    442 
    443   Locked<const T> lockShared(LockSourceLocationArg location = {}) const;
    444   // Lock the value for shared access.  Multiple shared locks can be taken concurrently, but cannot
    445   // be held at the same time as a non-shared lock.
    446 
    447   Maybe<Locked<T>> lockExclusiveWithTimeout(Duration timeout,
    448       LockSourceLocationArg location = {}) const;
    449   // Attempts to exclusively lock the object. If the timeout elapses before the lock is aquired,
    450   // this returns null.
    451 
    452   Maybe<Locked<const T>> lockSharedWithTimeout(Duration timeout,
    453       LockSourceLocationArg location = {}) const;
    454   // Attempts to lock the value for shared access. If the timeout elapses before the lock is aquired,
    455   // this returns null.
    456 
    457   inline const T& getWithoutLock() const { return value; }
    458   inline T& getWithoutLock() { return value; }
    459   // Escape hatch for cases where some external factor guarantees that it's safe to get the
    460   // value.  You should treat these like const_cast -- be highly suspicious of any use.
    461 
    462   inline const T& getAlreadyLockedShared() const;
    463   inline T& getAlreadyLockedShared();
    464   inline T& getAlreadyLockedExclusive() const;
    465   // Like `getWithoutLock()`, but asserts that the lock is already held by the calling thread.
    466 
    467   template <typename Cond, typename Func>
    468   auto when(Cond&& condition, Func&& callback, Maybe<Duration> timeout = nullptr,
    469       LockSourceLocationArg location = {}) const
    470       -> decltype(callback(instance<T&>())) {
    471     // Waits until condition(state) returns true, then calls callback(state) under lock.
    472     //
    473     // `condition`, when called, receives as its parameter a const reference to the state, which is
    474     // locked (either shared or exclusive). `callback` receives a mutable reference, which is
    475     // exclusively locked.
    476     //
    477     // `condition()` may be called multiple times, from multiple threads, while waiting for the
    478     // condition to become true. It may even return true once, but then be called more times.
    479     // It is guaranteed, though, that at the time `callback()` is finally called, `condition()`
    480     // would currently return true (assuming it is a pure function of the guarded data).
    481     //
    482     // If `timeout` is specified, then after the given amount of time, the callback will be called
    483     // regardless of whether the condition is true. In this case, when `callback()` is called,
    484     // `condition()` may in fact evaluate false, but *only* if the timeout was reached.
    485     //
    486     // TODO(cleanup): lock->wait() is a better interface. Can we deprecate this one?
    487 
    488     auto lock = lockExclusive();
    489     lock.wait(kj::fwd<Cond>(condition), timeout, location);
    490     return callback(value);
    491   }
    492 
    493 private:
    494   mutable _::Mutex mutex;
    495   mutable T value;
    496 };
    497 
    498 template <typename T>
    499 class MutexGuarded<const T> {
    500   // MutexGuarded cannot guard a const type.  This would be pointless anyway, and would complicate
    501   // the implementation of Locked<T>, which uses constness to decide what kind of lock it holds.
    502   static_assert(sizeof(T) < 0, "MutexGuarded's type cannot be const.");
    503 };
    504 
    505 template <typename T>
    506 class ExternalMutexGuarded {
    507   // Holds a value that can only be manipulated while some other mutex is locked.
    508   //
    509   // The ExternalMutexGuarded<T> lives *outside* the scope of any lock on the mutex, but ensures
    510   // that the value it holds can only be accessed under lock by forcing the caller to present a
    511   // lock before accessing the value.
    512   //
    513   // Additionally, ExternalMutexGuarded<T>'s destructor will take an exclusive lock on the mutex
    514   // while destroying the held value, unless the value has been release()ed before hand.
    515   //
    516   // The type T must have the following properties (which probably all movable types satisfy):
    517   // - T is movable.
    518   // - Immediately after any of the following has happened, T's destructor is effectively a no-op
    519   //   (hence certainly not requiring locks):
    520   //   - The value has been default-constructed.
    521   //   - The value has been initialized by-move from a default-constructed T.
    522   //   - The value has been moved away.
    523   // - If ExternalMutexGuarded<T> is ever moved, then T must have a move constructor and move
    524   //   assignment operator that do not follow any pointers, therefore do not need to take a lock.
    525   //
    526   // Inherits from LockSourceLocation to perform an empty base class optimization when lock tracking
    527   // is compiled out. Once the minimum C++ standard for the KJ library is C++20, this optimization
    528   // could be replaced by a member variable with a [[no_unique_address]] annotation.
    529 public:
    530   ExternalMutexGuarded(LockSourceLocationArg location = {})
    531       : location(location) {}
    532 
    533   template <typename U, typename... Params>
    534   ExternalMutexGuarded(Locked<U> lock, Params&&... params, LockSourceLocationArg location = {})
    535       : mutex(lock.mutex),
    536         value(kj::fwd<Params>(params)...),
    537         location(location) {}
    538   // Construct the value in-place. This constructor requires passing ownership of the lock into
    539   // the constructor. Normally this should be a lock that you take on the line calling the
    540   // constructor, like:
    541   //
    542   //     ExternalMutexGuarded<T> foo(someMutexGuarded.lockExclusive());
    543   //
    544   // The reason this constructor does not accept an lvalue reference to an existing lock is because
    545   // this would be deadlock-prone: If an exception were thrown immediately after the constructor
    546   // completed, then the destructor would deadlock, because the lock would still be held. An
    547   // ExternalMutexGuarded must live outside the scope of any locks to avoid such a deadlock.
    548 
    549   ~ExternalMutexGuarded() noexcept(false) {
    550     if (mutex != nullptr) {
    551       mutex->lock(_::Mutex::EXCLUSIVE, nullptr, location);
    552       KJ_DEFER(mutex->unlock(_::Mutex::EXCLUSIVE));
    553       value = T();
    554     }
    555   }
    556 
    557   ExternalMutexGuarded(ExternalMutexGuarded&& other)
    558       : mutex(other.mutex), value(kj::mv(other.value)), location(other.location) {
    559     other.mutex = nullptr;
    560   }
    561   ExternalMutexGuarded& operator=(ExternalMutexGuarded&& other) {
    562     mutex = other.mutex;
    563     value = kj::mv(other.value);
    564     location = other.location;
    565     other.mutex = nullptr;
    566     return *this;
    567   }
    568 
    569   template <typename U>
    570   void set(Locked<U>& lock, T&& newValue) {
    571     KJ_IREQUIRE(mutex == nullptr);
    572     mutex = lock.mutex;
    573     value = kj::mv(newValue);
    574   }
    575 
    576   template <typename U>
    577   T& get(Locked<U>& lock) {
    578     KJ_IREQUIRE(lock.mutex == mutex);
    579     return value;
    580   }
    581 
    582   template <typename U>
    583   const T& get(Locked<const U>& lock) const {
    584     KJ_IREQUIRE(lock.mutex == mutex);
    585     return value;
    586   }
    587 
    588   template <typename U>
    589   T release(Locked<U>& lock) {
    590     // Release (move away) the value. This allows the destructor to skip locking the mutex.
    591     KJ_IREQUIRE(lock.mutex == mutex);
    592     T result = kj::mv(value);
    593     mutex = nullptr;
    594     return result;
    595   }
    596 
    597 private:
    598   _::Mutex* mutex = nullptr;
    599   T value;
    600   KJ_NO_UNIQUE_ADDRESS LockSourceLocation location;
    601   // When built against C++20 (or clang >= 9.0), the overhead of this is elided. Otherwise this
    602   // struct will be 1 byte larger than it would otherwise be.
    603 };
    604 
    605 template <typename T>
    606 class Lazy {
    607   // A lazily-initialized value.
    608 
    609 public:
    610   template <typename Func>
    611   T& get(Func&& init, LockSourceLocationArg location = {});
    612   template <typename Func>
    613   const T& get(Func&& init, LockSourceLocationArg location = {}) const;
    614   // The first thread to call get() will invoke the given init function to construct the value.
    615   // Other threads will block until construction completes, then return the same value.
    616   //
    617   // `init` is a functor(typically a lambda) which takes `SpaceFor<T>&` as its parameter and returns
    618   // `Own<T>`.  If `init` throws an exception, the exception is propagated out of that thread's
    619   // call to `get()`, and subsequent calls behave as if `get()` hadn't been called at all yet --
    620   // in other words, subsequent calls retry initialization until it succeeds.
    621 
    622 private:
    623   mutable _::Once once;
    624   mutable SpaceFor<T> space;
    625   mutable Own<T> value;
    626 
    627   template <typename Func>
    628   class InitImpl;
    629 };
    630 
    631 // =======================================================================================
    632 // Inline implementation details
    633 
    634 template <typename T>
    635 template <typename... Params>
    636 inline MutexGuarded<T>::MutexGuarded(Params&&... params)
    637     : value(kj::fwd<Params>(params)...) {}
    638 
    639 template <typename T>
    640 inline Locked<T> MutexGuarded<T>::lockExclusive(LockSourceLocationArg location)
    641     const {
    642   mutex.lock(_::Mutex::EXCLUSIVE, nullptr, location);
    643   return Locked<T>(mutex, value);
    644 }
    645 
    646 template <typename T>
    647 inline Locked<const T> MutexGuarded<T>::lockShared(LockSourceLocationArg location) const {
    648   mutex.lock(_::Mutex::SHARED, nullptr, location);
    649   return Locked<const T>(mutex, value);
    650 }
    651 
    652 template <typename T>
    653 inline Maybe<Locked<T>> MutexGuarded<T>::lockExclusiveWithTimeout(Duration timeout,
    654     LockSourceLocationArg location) const {
    655   if (mutex.lock(_::Mutex::EXCLUSIVE, timeout, location)) {
    656     return Locked<T>(mutex, value);
    657   } else {
    658     return nullptr;
    659   }
    660 }
    661 
    662 template <typename T>
    663 inline Maybe<Locked<const T>> MutexGuarded<T>::lockSharedWithTimeout(Duration timeout,
    664     LockSourceLocationArg location) const {
    665   if (mutex.lock(_::Mutex::SHARED, timeout, location)) {
    666     return Locked<const T>(mutex, value);
    667   } else {
    668     return nullptr;
    669   }
    670 }
    671 
    672 template <typename T>
    673 inline const T& MutexGuarded<T>::getAlreadyLockedShared() const {
    674 #ifdef KJ_DEBUG
    675   mutex.assertLockedByCaller(_::Mutex::SHARED);
    676 #endif
    677   return value;
    678 }
    679 template <typename T>
    680 inline T& MutexGuarded<T>::getAlreadyLockedShared() {
    681 #ifdef KJ_DEBUG
    682   mutex.assertLockedByCaller(_::Mutex::SHARED);
    683 #endif
    684   return value;
    685 }
    686 template <typename T>
    687 inline T& MutexGuarded<T>::getAlreadyLockedExclusive() const {
    688 #ifdef KJ_DEBUG
    689   mutex.assertLockedByCaller(_::Mutex::EXCLUSIVE);
    690 #endif
    691   return const_cast<T&>(value);
    692 }
    693 
    694 template <typename T>
    695 template <typename Func>
    696 class Lazy<T>::InitImpl: public _::Once::Initializer {
    697 public:
    698   inline InitImpl(const Lazy<T>& lazy, Func&& func): lazy(lazy), func(kj::fwd<Func>(func)) {}
    699 
    700   void run() override {
    701     lazy.value = func(lazy.space);
    702   }
    703 
    704 private:
    705   const Lazy<T>& lazy;
    706   Func func;
    707 };
    708 
    709 template <typename T>
    710 template <typename Func>
    711 inline T& Lazy<T>::get(Func&& init, LockSourceLocationArg location) {
    712   if (!once.isInitialized()) {
    713     InitImpl<Func> initImpl(*this, kj::fwd<Func>(init));
    714     once.runOnce(initImpl, location);
    715   }
    716   return *value;
    717 }
    718 
    719 template <typename T>
    720 template <typename Func>
    721 inline const T& Lazy<T>::get(Func&& init, LockSourceLocationArg location) const {
    722   if (!once.isInitialized()) {
    723     InitImpl<Func> initImpl(*this, kj::fwd<Func>(init));
    724     once.runOnce(initImpl, location);
    725   }
    726   return *value;
    727 }
    728 
    729 #if KJ_TRACK_LOCK_BLOCKING
    730 struct BlockedOnMutexAcquisition {
    731   const _::Mutex& mutex;
    732   // The mutex we are blocked on.
    733 
    734   const SourceLocation& origin;
    735   // Where did the blocking operation originate from.
    736 };
    737 
    738 struct BlockedOnCondVarWait {
    739   const _::Mutex& mutex;
    740   // The mutex the condition variable is using (may or may not be locked).
    741 
    742   const void* waiter;
    743   // Pointer to the waiter that's being waited on.
    744 
    745   const SourceLocation& origin;
    746   // Where did the blocking operation originate from.
    747 };
    748 
    749 struct BlockedOnOnceInit {
    750   const _::Once& once;
    751 
    752   const SourceLocation& origin;
    753   // Where did the blocking operation originate from.
    754 };
    755 
    756 using BlockedOnReason = OneOf<BlockedOnMutexAcquisition, BlockedOnCondVarWait, BlockedOnOnceInit>;
    757 
    758 Maybe<const BlockedOnReason&> blockedReason() noexcept;
    759 // Returns the information about the reason the current thread is blocked synchronously on KJ
    760 // lock primitives. Returns nullptr if the current thread is not currently blocked on such
    761 // primitves. This is intended to be called from a signal handler to check whether the current
    762 // thread is blocked. Outside of a signal handler there is little value to this function. In those
    763 // cases by definition the thread is not blocked. This includes the callable used as part of a
    764 // condition variable since that happens after the lock is acquired & the current thread is no
    765 // longer blocked). The utility could be made useful for non-signal handler use-cases by being able
    766 // to fetch the pointer to the TLS variable directly (i.e. const BlockedOnReason&*). However, there
    767 // would have to be additional changes/complexity to try make that work since you'd need
    768 // synchronization to ensure that the memory you'd try to reference is still valid. The likely
    769 // solution would be to make these mutually exclusive options where you can use either the fast
    770 // async-safe option, or a mutex-guarded TLS variable you can get a reference to that isn't
    771 // async-safe. That being said, maybe someone can come up with a way to make something that works
    772 // in both use-cases which would of course be more preferable.
    773 #endif
    774 
    775 
    776 }  // namespace kj
    777 
    778 KJ_END_HEADER