//===--- Mutex.h - Mutex, ConditionVariable, & ReadWriteLock ----*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
// Mutex, ConditionVariable, Read/Write lock, and Scoped lock abstractions
// for use in Swift runtime.
//
//===----------------------------------------------------------------------===//

#ifndef SWIFT_RUNTIME_MUTEX_BACKDEPLOY56_H
#define SWIFT_RUNTIME_MUTEX_BACKDEPLOY56_H

#include <type_traits>
#include <utility>

#if __has_include(<unistd.h>)
#include <unistd.h>
#endif

#if defined(SWIFT_STDLIB_SINGLE_THREADED_RUNTIME) || defined(__wasi__)
#include "MutexSingleThreaded.h"
#elif defined(_POSIX_THREADS)
#include "MutexPThread.h"
#elif defined(_WIN32)
#include "MutexWin32.h"
#else
#error "Implement equivalent of MutexPThread.h/cpp for your platform."
#endif

namespace swift {

/// A stack based object that notifies one thread waiting on a condition
/// variable on destruction.
template <typename ConditionVariable>
class ScopedNotifyOneT {
  ScopedNotifyOneT() = delete;
  ScopedNotifyOneT(const ScopedNotifyOneT &) = delete;
  ScopedNotifyOneT &operator=(const ScopedNotifyOneT &) = delete;

  ConditionVariable &Condition;
public:
  explicit ScopedNotifyOneT(ConditionVariable &c) : Condition(c) {}

  ~ScopedNotifyOneT() {
    Condition.notifyOne();
  }
};

/// A stack based object that notifies all threads waiting on a condition
/// variable on destruction.
template <typename ConditionVariable>
class ScopedNotifyAllT {
  ScopedNotifyAllT() = delete;
  ScopedNotifyAllT(const ScopedNotifyAllT &) = delete;
  ScopedNotifyAllT &operator=(const ScopedNotifyAllT &) = delete;

  ConditionVariable &Condition;
public:
  explicit ScopedNotifyAllT(ConditionVariable &c) : Condition(c) {}

  ~ScopedNotifyAllT() {
    Condition.notifyAll();
  }
};

/// Compile time adjusted stack based object that locks/unlocks the supplied
/// Mutex type. Use the provided typedefs instead of this directly.
template <typename T, bool Inverted> class ScopedLockT {
  ScopedLockT() = delete;
  ScopedLockT(const ScopedLockT &) = delete;
  ScopedLockT &operator=(const ScopedLockT &) = delete;
  ScopedLockT(ScopedLockT &&) = delete;
  ScopedLockT &operator=(ScopedLockT &&) = delete;

public:
  explicit ScopedLockT(T &l) : Lock(l) {
    if (Inverted) {
      Lock.unlock();
    } else {
      Lock.lock();
    }
  }

  ~ScopedLockT() {
    if (Inverted) {
      Lock.lock();
    } else {
      Lock.unlock();
    }
  }

private:
  T &Lock;
};

/// A ConditionVariable that works with Mutex to allow -- as an example --
/// multi-threaded producers and consumers to signal each other in a safe way.
class ConditionVariable {
  friend class ConditionMutex;
  friend class StaticConditionVariable;

  ConditionVariable(const ConditionVariable &) = delete;
  ConditionVariable &operator=(const ConditionVariable &) = delete;
  ConditionVariable(ConditionVariable &&) = delete;
  ConditionVariable &operator=(ConditionVariable &&) = delete;

public:
  ConditionVariable() { ConditionPlatformHelper::init(Handle); }
  ~ConditionVariable() { ConditionPlatformHelper::destroy(Handle); }

  /// Notifies one waiter (if any exists) that the condition has been met.
  ///
  /// Note: To avoid missed notification it is best hold the related mutex
  //        lock when calling notifyOne.
  void notifyOne() { ConditionPlatformHelper::notifyOne(Handle); }

  /// Notifies all waiters (if any exists) that the condition has been met.
  ///
  /// Note: To avoid missed notification it is best hold the related mutex
  //        lock when calling notifyAll.
  void notifyAll() { ConditionPlatformHelper::notifyAll(Handle); }

private:
  ConditionHandle Handle;

public:
  /// A Mutex object that also supports ConditionVariables.
  ///
  /// This is NOT a recursive mutex.
  class Mutex {

    Mutex(const Mutex &) = delete;
    Mutex &operator=(const Mutex &) = delete;
    Mutex(Mutex &&) = delete;
    Mutex &operator=(Mutex &&) = delete;

  public:
    /// Constructs a non-recursive mutex.
    ///
    /// If `checked` is true the mutex will attempt to check for misuse and
    /// fatalError when detected. If `checked` is false (the default) the
    /// mutex will make little to no effort to check for misuse (more
    /// efficient).
    explicit Mutex(bool checked = false) {
      MutexPlatformHelper::init(Handle, checked);
    }
    ~Mutex() { MutexPlatformHelper::destroy(Handle); }

    /// The lock() method has the following properties:
    /// - Behaves as an atomic operation.
    /// - Blocks the calling thread until exclusive ownership of the mutex
    ///   can be obtained.
    /// - Prior m.unlock() operations on the same mutex synchronize-with
    ///   this lock operation.
    /// - The behavior is undefined if the calling thread already owns
    ///   the mutex (likely a deadlock).
    /// - Does not throw exceptions but will halt on error (fatalError).
    void lock() { MutexPlatformHelper::lock(Handle); }

    /// The unlock() method has the following properties:
    /// - Behaves as an atomic operation.
    /// - Releases the calling thread's ownership of the mutex and
    ///   synchronizes-with the subsequent successful lock operations on
    ///   the same object.
    /// - The behavior is undefined if the calling thread does not own
    ///   the mutex.
    /// - Does not throw exceptions but will halt on error (fatalError).
    void unlock() { MutexPlatformHelper::unlock(Handle); }

    /// The try_lock() method has the following properties:
    /// - Behaves as an atomic operation.
    /// - Attempts to obtain exclusive ownership of the mutex for the calling
    ///   thread without blocking. If ownership is not obtained, returns
    ///   immediately. The function is allowed to spuriously fail and return
    ///   even if the mutex is not currently owned by another thread.
    /// - If try_lock() succeeds, prior unlock() operations on the same object
    ///   synchronize-with this operation. lock() does not synchronize with a
    ///   failed try_lock()
    /// - The behavior is undefined if the calling thread already owns
    ///   the mutex (likely a deadlock)?
    /// - Does not throw exceptions but will halt on error (fatalError).
    bool try_lock() { return MutexPlatformHelper::try_lock(Handle); }

    /// Releases lock, waits on supplied condition, and relocks before
    /// returning.
    ///
    /// Precondition: Mutex held by this thread, undefined otherwise.
    void wait(ConditionVariable &condition) {
      ConditionPlatformHelper::wait(condition.Handle, Handle);
    }

    /// Acquires lock before calling the supplied critical section and releases
    /// lock on return from critical section.
    ///
    /// This call can block while waiting for the lock to become available.
    ///
    /// For example the following mutates value while holding the mutex lock.
    ///
    /// ```
    ///   mutex.lock([&value] { value++; });
    /// ```
    ///
    /// Precondition: Mutex not held by this thread, undefined otherwise.
    template <typename CriticalSection>
    auto withLock(CriticalSection &&criticalSection)
        -> decltype(std::forward<CriticalSection>(criticalSection)()) {
      ScopedLock guard(*this);
      return std::forward<CriticalSection>(criticalSection)();
    }

    /// Acquires lock before calling the supplied critical section. If critical
    /// section returns `false` then it will wait on the supplied condition and
    /// call the critical section again when wait returns (after acquiring
    /// lock). If critical section returns `true` (done) it will no longer wait,
    /// it will release the lock and return (lockOrWait returns to caller).
    ///
    /// This call can block while waiting for the lock to become available.
    ///
    /// For example the following will loop waiting on the condition until
    /// `value > 0`. It will then "consume" that value and stop looping.
    /// ...all while being correctly protected by mutex.
    ///
    /// ```
    ///   mutex.withLockOrWait(condition, [&value] {
    ///     if (value > 0) {
    ///       value--;
    ///       return true;
    ///     }
    ///    return false;
    ///   });
    /// ```
    ///
    /// Precondition: Mutex not held by this thread, undefined otherwise.
    template <typename CriticalSection>
    void withLockOrWait(ConditionVariable &condition,
                        CriticalSection &&criticalSection) {
      withLock([&] {
        while (!criticalSection()) {
          wait(condition);
        }
      });
    }

    /// Acquires lock before calling the supplied critical section and on return
    /// from critical section it notifies one waiter of supplied condition and
    /// then releases the lock.
    ///
    /// This call can block while waiting for the lock to become available.
    ///
    /// For example the following mutates value while holding the mutex lock and
    /// then notifies one condition waiter about this change.
    ///
    /// ```
    ///   mutex.withLockThenNotifyOne(condition, [&value] { value++; });
    /// ```
    ///
    /// Precondition: Mutex not held by this thread, undefined otherwise.
    template <typename CriticalSection>
    auto withLockThenNotifyOne(ConditionVariable &condition,
                               CriticalSection &&criticalSection)
        -> decltype(std::forward<CriticalSection>(criticalSection)()) {
      return withLock([&] {
        ScopedNotifyOne guard(condition);
        return std::forward<CriticalSection>(criticalSection)();
      });
    }

    /// Acquires lock before calling the supplied critical section and on return
    /// from critical section it notifies all waiters of supplied condition and
    /// then releases the lock.
    ///
    /// This call can block while waiting for the lock to become available.
    ///
    /// For example the following mutates value while holding the mutex lock and
    /// then notifies all condition waiters about this change.
    ///
    /// ```
    ///   mutex.withLockThenNotifyAll(condition, [&value] { value++; });
    /// ```
    ///
    /// Precondition: Mutex not held by this thread, undefined otherwise.
    template <typename CriticalSection>
    auto withLockThenNotifyAll(ConditionVariable &condition,
                               CriticalSection &&criticalSection)
        -> decltype(std::forward<CriticalSection>(criticalSection)()) {
      return withLock([&] {
        ScopedNotifyAll guard(condition);
        return std::forward<CriticalSection>(criticalSection)();
      });
    }

    /// A stack based object that locks the supplied mutex on construction
    /// and unlocks it on destruction.
    ///
    /// Precondition: Mutex unlocked by this thread, undefined otherwise.
    typedef ScopedLockT<Mutex, false> ScopedLock;

    /// A stack based object that unlocks the supplied mutex on construction
    /// and relocks it on destruction.
    ///
    /// Precondition: Mutex locked by this thread, undefined otherwise.
    typedef ScopedLockT<Mutex, true> ScopedUnlock;

  private:
    ConditionMutexHandle Handle;
  };

  using ScopedNotifyOne = ScopedNotifyOneT<ConditionVariable>;
  using ScopedNotifyAll = ScopedNotifyAllT<ConditionVariable>;
};

/// A Mutex object that supports `BasicLockable` and `Lockable` C++ concepts.
/// See http://en.cppreference.com/w/cpp/concept/BasicLockable
/// See http://en.cppreference.com/w/cpp/concept/Lockable
///
/// This is NOT a recursive mutex.
class Mutex {

  Mutex(const Mutex &) = delete;
  Mutex &operator=(const Mutex &) = delete;
  Mutex(Mutex &&) = delete;
  Mutex &operator=(Mutex &&) = delete;

public:
  /// Constructs a non-recursive mutex.
  ///
  /// If `checked` is true the mutex will attempt to check for misuse and
  /// fatalError when detected. If `checked` is false (the default) the
  /// mutex will make little to no effort to check for misuse (more efficient).
  explicit Mutex(bool checked = false) {
    MutexPlatformHelper::init(Handle, checked);
  }
  ~Mutex() { MutexPlatformHelper::destroy(Handle); }

  /// The lock() method has the following properties:
  /// - Behaves as an atomic operation.
  /// - Blocks the calling thread until exclusive ownership of the mutex
  ///   can be obtained.
  /// - Prior m.unlock() operations on the same mutex synchronize-with
  ///   this lock operation.
  /// - The behavior is undefined if the calling thread already owns
  ///   the mutex (likely a deadlock).
  /// - Does not throw exceptions but will halt on error (fatalError).
  void lock() { MutexPlatformHelper::lock(Handle); }

  /// The unlock() method has the following properties:
  /// - Behaves as an atomic operation.
  /// - Releases the calling thread's ownership of the mutex and
  ///   synchronizes-with the subsequent successful lock operations on
  ///   the same object.
  /// - The behavior is undefined if the calling thread does not own
  ///   the mutex.
  /// - Does not throw exceptions but will halt on error (fatalError).
  void unlock() { MutexPlatformHelper::unlock(Handle); }

  /// The try_lock() method has the following properties:
  /// - Behaves as an atomic operation.
  /// - Attempts to obtain exclusive ownership of the mutex for the calling
  ///   thread without blocking. If ownership is not obtained, returns
  ///   immediately. The function is allowed to spuriously fail and return
  ///   even if the mutex is not currently owned by another thread.
  /// - If try_lock() succeeds, prior unlock() operations on the same object
  ///   synchronize-with this operation. lock() does not synchronize with a
  ///   failed try_lock()
  /// - The behavior is undefined if the calling thread already owns
  ///   the mutex (likely a deadlock)?
  /// - Does not throw exceptions but will halt on error (fatalError).
  bool try_lock() { return MutexPlatformHelper::try_lock(Handle); }

  /// Acquires lock before calling the supplied critical section and releases
  /// lock on return from critical section.
  ///
  /// This call can block while waiting for the lock to become available.
  ///
  /// For example the following mutates value while holding the mutex lock.
  ///
  /// ```
  ///   mutex.lock([&value] { value++; });
  /// ```
  ///
  /// Precondition: Mutex not held by this thread, undefined otherwise.
  template <typename CriticalSection>
  auto withLock(CriticalSection &&criticalSection)
      -> decltype(std::forward<CriticalSection>(criticalSection)()) {
    ScopedLock guard(*this);
    return std::forward<CriticalSection>(criticalSection)();
  }

  /// A stack based object that locks the supplied mutex on construction
  /// and unlocks it on destruction.
  ///
  /// Precondition: Mutex unlocked by this thread, undefined otherwise.
  typedef ScopedLockT<Mutex, false> ScopedLock;

  /// A stack based object that unlocks the supplied mutex on construction
  /// and relocks it on destruction.
  ///
  /// Precondition: Mutex locked by this thread, undefined otherwise.
  typedef ScopedLockT<Mutex, true> ScopedUnlock;

private:
  MutexHandle Handle;
};

/// Compile time adjusted stack based object that locks/unlocks the supplied
/// ReadWriteLock type. Use the provided typedefs instead of this directly.
template <typename T, bool Read, bool Inverted> class ScopedRWLockT {

  ScopedRWLockT() = delete;
  ScopedRWLockT(const ScopedRWLockT &) = delete;
  ScopedRWLockT &operator=(const ScopedRWLockT &) = delete;
  ScopedRWLockT(ScopedRWLockT &&) = delete;
  ScopedRWLockT &operator=(ScopedRWLockT &&) = delete;

public:
  explicit ScopedRWLockT(T &l) : Lock(l) {
    if (Inverted) {
      if (Read) {
        Lock.readUnlock();
      } else {
        Lock.writeUnlock();
      }
    } else {
      if (Read) {
        Lock.readLock();
      } else {
        Lock.writeLock();
      }
    }
  }

  ~ScopedRWLockT() {
    if (Inverted) {
      if (Read) {
        Lock.readLock();
      } else {
        Lock.writeLock();
      }
    } else {
      if (Read) {
        Lock.readUnlock();
      } else {
        Lock.writeUnlock();
      }
    }
  }

private:
  T &Lock;
};

class ReadWriteLock;
class StaticReadWriteLock;

/// A stack based object that unlocks the supplied ReadWriteLock on
/// construction and locks it for reading on destruction.
///
/// Precondition: ReadWriteLock unlocked by this thread, undefined otherwise.
typedef ScopedRWLockT<ReadWriteLock, true, false> ScopedReadLock;
typedef ScopedRWLockT<StaticReadWriteLock, true, false> StaticScopedReadLock;

/// A stack based object that unlocks the supplied ReadWriteLock on
/// construction and locks it for reading on destruction.
///
/// Precondition: ReadWriteLock unlocked by this thread, undefined
/// otherwise.
typedef ScopedRWLockT<ReadWriteLock, true, true> ScopedReadUnlock;
typedef ScopedRWLockT<StaticReadWriteLock, true, true> StaticScopedReadUnlock;

/// A stack based object that unlocks the supplied ReadWriteLock on
/// construction and locks it for reading on destruction.
///
/// Precondition: ReadWriteLock unlocked by this thread, undefined otherwise.
typedef ScopedRWLockT<ReadWriteLock, false, false> ScopedWriteLock;
typedef ScopedRWLockT<StaticReadWriteLock, false, false> StaticScopedWriteLock;

/// A stack based object that unlocks the supplied ReadWriteLock on
/// construction and locks it for writing on destruction.
///
/// Precondition: ReadWriteLock unlocked by this thread, undefined otherwise.
typedef ScopedRWLockT<ReadWriteLock, false, true> ScopedWriteUnlock;
typedef ScopedRWLockT<StaticReadWriteLock, false, true> StaticScopedWriteUnlock;

/// A Read / Write lock object that has semantics similar to `BasicLockable`
/// and `Lockable` C++ concepts however it supports multiple concurrent
/// threads holding the reader lock as long as the write lock isn't held and
/// only one thread can hold the write local at the same time.
///
/// If you need static allocated ReadWriteLock use StaticReadWriteLock.
///
/// See http://en.cppreference.com/w/cpp/concept/BasicLockable
/// See http://en.cppreference.com/w/cpp/concept/Lockable
///
/// This is NOT a recursive mutex.
class ReadWriteLock {

  ReadWriteLock(const ReadWriteLock &) = delete;
  ReadWriteLock &operator=(const ReadWriteLock &) = delete;
  ReadWriteLock(ReadWriteLock &&) = delete;
  ReadWriteLock &operator=(ReadWriteLock &&) = delete;

public:
  ReadWriteLock() { ReadWriteLockPlatformHelper::init(Handle); }
  ~ReadWriteLock() { ReadWriteLockPlatformHelper::destroy(Handle); }

  /// The readLock() method has the following properties:
  /// - Behaves as an atomic operation.
  /// - Blocks the calling thread while the write lock is held by another 
  ///   thread and once the read lock is acquired by the calling thread
  ///   other threads are prevented from acquiring the write lock.
  /// - Multiple threads can hold the read lock at the same time.
  /// - Prior unlock() operations on the same lock synchronize-with
  ///   this lock operation.
  /// - The behavior is undefined if the calling thread already owns
  ///   the read or write lock (likely a deadlock).
  /// - Does not throw exceptions but will halt on error (fatalError).
  ///
  /// Callers must not mutate the data protected by the ReadWriteLock while
  /// holding the read lock, the write lock must be used.
  void readLock() { ReadWriteLockPlatformHelper::readLock(Handle); }

  /// The try_readLock() method has the following properties:
  /// - Behaves as an atomic operation.
  /// - Attempts to obtain the read lock without blocking the calling thread.
  ///   If ownership is not obtained, returns immediately. The function is
  ///   allowed to spuriously fail and return even if the lock is not
  ///   currently owned by another thread.
  /// - If try_readLock() succeeds, prior unlock() operations on the same
  ///   object synchronize-with this operation. unlock() does not synchronize
  ///   with a failed try_readLock().
  /// - The behavior is undefined if the calling thread already owns
  ///   the read or write lock (likely a deadlock)?
  /// - Does not throw exceptions but will halt on error (fatalError).
  ///
  /// Callers must not mutate the data protected by the ReadWriteLock while
  /// holding the read lock, the write lock must be used.
  bool try_readLock() {
    return ReadWriteLockPlatformHelper::try_readLock(Handle);
  }

  /// The readUnlock() method has the following properties:
  /// - Behaves as an atomic operation.
  /// - Releases the calling thread's ownership of the read lock
  ///   and synchronizes-with the subsequent successful lock operations on
  ///   the same object.
  /// - The behavior is undefined if the calling thread does not own
  ///   the read lock.
  /// - Does not throw exceptions but will halt on error (fatalError).
  void readUnlock() { ReadWriteLockPlatformHelper::readUnlock(Handle); }

  /// The writeLock() method has the following properties:
  /// - Behaves as an atomic operation.
  /// - Blocks the calling thread while the write lock or a read lock is held
  ///   by another thread and once the write lock is acquired by the calling
  ///   thread other threads are prevented from acquiring the write lock or a
  ///   read lock.
  /// - Only one thread can hold the write lock at the same time.
  /// - Prior unlock() operations on the same lock synchronize-with
  ///   this lock operation.
  /// - The behavior is undefined if the calling thread already owns
  ///   the read or write lock (likely a deadlock).
  /// - Does not throw exceptions but will halt on error (fatalError).
  void writeLock() { ReadWriteLockPlatformHelper::writeLock(Handle); }

  /// The try_writeLock() method has the following properties:
  /// - Behaves as an atomic operation.
  /// - Attempts to obtain the write lock without blocking the calling thread.
  ///   If ownership is not obtained, returns immediately. The function is
  ///   allowed to spuriously fail and return even if the lock is not
  ///   currently owned by another thread.
  /// - If try_writeLock() succeeds, prior unlock() operations on the same
  ///   object synchronize-with this operation. unlock() does not synchronize
  ///   with a failed try_writeLock().
  /// - The behavior is undefined if the calling thread already owns
  ///   the read or write lock (likely a deadlock)?
  /// - Does not throw exceptions but will halt on error (fatalError).
  bool try_writeLock() {
    return ReadWriteLockPlatformHelper::try_writeLock(Handle);
  }

  /// The writeUnlock() method has the following properties:
  /// - Behaves as an atomic operation.
  /// - Releases the calling thread's ownership of the write lock
  ///   and synchronizes-with the subsequent successful lock operations on
  ///   the same object.
  /// - The behavior is undefined if the calling thread does not own
  ///   the write lock.
  /// - Does not throw exceptions but will halt on error (fatalError).
  void writeUnlock() { ReadWriteLockPlatformHelper::writeUnlock(Handle); }

  /// Acquires read lock before calling the supplied critical section and
  /// releases lock on return from critical section. Callers must not mutate
  /// the data protected by the ReadWriteLock while holding the read lock, the
  /// write lock must be used.
  ///
  /// This call can block while waiting for the lock to become available.
  ///
  /// For example the following reads the cached value while holding
  /// the read lock.
  ///
  /// ```
  ///   rw.withReadLock([&value] { value = cachedValue; });
  /// ```
  ///
  /// Precondition: ReadWriteLock not held by this thread, undefined otherwise.
  template <typename CriticalSection>
  auto withReadLock(CriticalSection &&criticalSection)
      -> decltype(std::forward<CriticalSection>(criticalSection)()) {
    ScopedReadLock guard(*this);
    return std::forward<CriticalSection>(criticalSection)();
  }

  /// Acquires write lock before calling the supplied critical section and
  /// releases lock on return from critical section.
  ///
  /// This call can block while waiting for the lock to become available.
  ///
  /// For example the following updates the cached value while holding
  /// the write lock.
  ///
  /// ```
  ///   rw.withWriteLock([&newValue] { cachedValue = newValue });
  /// ```
  ///
  /// Precondition: ReadWriteLock not held by this thread, undefined otherwise.
  template <typename CriticalSection>
  auto withWriteLock(CriticalSection &&criticalSection)
      -> decltype(std::forward<CriticalSection>(criticalSection)()) {
    ScopedWriteLock guard(*this);
    return std::forward<CriticalSection>(criticalSection)();
  }

private:
  ReadWriteLockHandle Handle;
};

/// A static allocation variant of ConditionVariable.
///
/// Use ConditionVariable instead unless you need static allocation.
class StaticConditionVariable {
  StaticConditionVariable(const StaticConditionVariable &) = delete;
  StaticConditionVariable &operator=(const StaticConditionVariable &) = delete;
  StaticConditionVariable(StaticConditionVariable &&) = delete;
  StaticConditionVariable &operator=(StaticConditionVariable &&) = delete;

public:
#if SWIFT_CONDITION_SUPPORTS_CONSTEXPR
  constexpr
#endif
      StaticConditionVariable()
      : Handle(ConditionPlatformHelper::staticInit()) {
  }

  /// See ConditionVariable::notifyOne
  void notifyOne() { ConditionPlatformHelper::notifyOne(Handle); }

  /// See ConditionVariable::notifyAll
  void notifyAll() { ConditionPlatformHelper::notifyAll(Handle); }

  using ScopedNotifyOne = ScopedNotifyOneT<StaticConditionVariable>;
  using ScopedNotifyAll = ScopedNotifyAllT<StaticConditionVariable>;

  /// A static allocation variant of ConditionVariable::Mutex.
  ///
  /// Use ConditionVariable::Mutex instead unless you need static allocation.
  class StaticMutex {

    StaticMutex(const StaticMutex &) = delete;
    StaticMutex &operator=(const StaticMutex &) = delete;
    StaticMutex(StaticMutex &&) = delete;
    StaticMutex &operator=(StaticMutex &&) = delete;

  public:
#if SWIFT_MUTEX_SUPPORTS_CONSTEXPR
    constexpr
#endif
    StaticMutex()
        : Handle(MutexPlatformHelper::conditionStaticInit()) {
    }

    /// See Mutex::lock
    void lock() { MutexPlatformHelper::lock(Handle); }

    /// See Mutex::unlock
    void unlock() { MutexPlatformHelper::unlock(Handle); }

    /// See Mutex::try_lock
    bool try_lock() { return MutexPlatformHelper::try_lock(Handle); }

    /// See Mutex::wait
    void wait(StaticConditionVariable &condition) {
      ConditionPlatformHelper::wait(condition.Handle, Handle);
    }
    void wait(ConditionVariable &condition) {
      ConditionPlatformHelper::wait(condition.Handle, Handle);
    }

    /// See Mutex::lock
    template <typename CriticalSection>
    auto withLock(CriticalSection &&criticalSection)
        -> decltype(std::forward<CriticalSection>(criticalSection)()) {
      ScopedLock guard(*this);
      return std::forward<CriticalSection>(criticalSection)();
    }

    /// See Mutex::withLockOrWait
    template <typename CriticalSection>
    void withLockOrWait(StaticConditionVariable &condition,
                        CriticalSection &&criticalSection) {
      withLock([&] {
        while (!criticalSection()) {
          wait(condition);
        }
      });
    }

    /// See Mutex::withLockThenNotifyOne
    template <typename CriticalSection>
    auto withLockThenNotifyOne(StaticConditionVariable &condition,
                               CriticalSection &&criticalSection)
        -> decltype(std::forward<CriticalSection>(criticalSection)()) {
      return withLock([&] {
        StaticConditionVariable::ScopedNotifyOne guard(condition);
        return std::forward<CriticalSection>(criticalSection)();
      });
    }

    /// See Mutex::withLockThenNotifyAll
    template <typename CriticalSection>
    auto withLockThenNotifyAll(StaticConditionVariable &condition,
                               CriticalSection &&criticalSection)
        -> decltype(std::forward<CriticalSection>(criticalSection)()) {
      return withLock([&] {
        StaticConditionVariable::ScopedNotifyAll guard(condition);
        return std::forward<CriticalSection>(criticalSection)();
      });
    }

    /// A stack based object that locks the supplied mutex on construction
    /// and unlocks it on destruction.
    ///
    /// Precondition: Mutex unlocked by this thread, undefined otherwise.
    typedef ScopedLockT<StaticMutex, false> ScopedLock;

    /// A stack based object that unlocks the supplied mutex on construction
    /// and relocks it on destruction.
    ///
    /// Precondition: Mutex locked by this thread, undefined otherwise.
    typedef ScopedLockT<StaticMutex, true> ScopedUnlock;

  private:
    ConditionMutexHandle Handle;
  };

private:
  ConditionHandle Handle;
};

/// A static allocation variant of Mutex.
///
/// Use Mutex instead unless you need static allocation.
class StaticMutex {

  StaticMutex(const StaticMutex &) = delete;
  StaticMutex &operator=(const StaticMutex &) = delete;
  StaticMutex(StaticMutex &&) = delete;
  StaticMutex &operator=(StaticMutex &&) = delete;

public:
#if SWIFT_MUTEX_SUPPORTS_CONSTEXPR
  constexpr
#endif
  StaticMutex()
      : Handle(MutexPlatformHelper::staticInit()) {
  }

  /// See Mutex::lock
  void lock() { MutexPlatformHelper::lock(Handle); }

  /// See Mutex::unlock
  void unlock() { MutexPlatformHelper::unlock(Handle); }

  /// See Mutex::try_lock
  bool try_lock() { return MutexPlatformHelper::try_lock(Handle); }

  /// See Mutex::lock
  template <typename CriticalSection>
  auto withLock(CriticalSection &&criticalSection)
      -> decltype(std::forward<CriticalSection>(criticalSection)()) {
    ScopedLock guard(*this);
    return std::forward<CriticalSection>(criticalSection)();
  }

  /// A stack based object that locks the supplied mutex on construction
  /// and unlocks it on destruction.
  ///
  /// Precondition: Mutex unlocked by this thread, undefined otherwise.
  typedef ScopedLockT<StaticMutex, false> ScopedLock;

  /// A stack based object that unlocks the supplied mutex on construction
  /// and relocks it on destruction.
  ///
  /// Precondition: Mutex locked by this thread, undefined otherwise.
  typedef ScopedLockT<StaticMutex, true> ScopedUnlock;

private:
  MutexHandle Handle;
};

/// A static allocation variant of ReadWriteLock.
///
/// Use ReadWriteLock instead unless you need static allocation.
class StaticReadWriteLock {

  StaticReadWriteLock(const StaticReadWriteLock &) = delete;
  StaticReadWriteLock &operator=(const StaticReadWriteLock &) = delete;
  StaticReadWriteLock(StaticReadWriteLock &&) = delete;
  StaticReadWriteLock &operator=(StaticReadWriteLock &&) = delete;

public:
#if SWIFT_READWRITELOCK_SUPPORTS_CONSTEXPR
  constexpr
#endif
  StaticReadWriteLock()
      : Handle(ReadWriteLockPlatformHelper::staticInit()) {
  }

  /// See ReadWriteLock::readLock
  void readLock() { ReadWriteLockPlatformHelper::readLock(Handle); }

  /// See ReadWriteLock::try_readLock
  bool try_readLock() {
    return ReadWriteLockPlatformHelper::try_readLock(Handle);
  }

  /// See ReadWriteLock::readUnlock
  void readUnlock() { ReadWriteLockPlatformHelper::readUnlock(Handle); }

  /// See ReadWriteLock::writeLock
  void writeLock() { ReadWriteLockPlatformHelper::writeLock(Handle); }

  /// See ReadWriteLock::try_writeLock
  bool try_writeLock() {
    return ReadWriteLockPlatformHelper::try_writeLock(Handle);
  }

  /// See ReadWriteLock::writeUnlock
  void writeUnlock() { ReadWriteLockPlatformHelper::writeUnlock(Handle); }

  /// See ReadWriteLock::withReadLock
  template <typename CriticalSection>
  auto withReadLock(CriticalSection &&criticalSection)
      -> decltype(std::forward<CriticalSection>(criticalSection)()) {
    StaticScopedReadLock guard(*this);
    return std::forward<CriticalSection>(criticalSection)();
  }

  /// See ReadWriteLock::withWriteLock
  template <typename CriticalSection>
  auto withWriteLock(CriticalSection &&criticalSection)
      -> decltype(std::forward<CriticalSection>(criticalSection)()) {
    StaticScopedWriteLock guard(*this);
    return std::forward<CriticalSection>(criticalSection)();
  }

private:
  ReadWriteLockHandle Handle;
};

/// A Mutex object that supports `BasicLockable` C++ concepts. It is
/// considered
/// unsafe to use because it doesn't do any error checking. It is only for
/// use in pathways that deal with reporting fatalErrors to avoid the
/// potential
/// for recursive fatalErrors that could happen if you used Mutex.
///
/// Always use Mutex, unless in the above mentioned error pathway situation.
class StaticUnsafeMutex {

  StaticUnsafeMutex(const StaticUnsafeMutex &) = delete;
  StaticUnsafeMutex &operator=(const StaticUnsafeMutex &) = delete;
  StaticUnsafeMutex(StaticUnsafeMutex &&) = delete;
  StaticUnsafeMutex &operator=(StaticUnsafeMutex &&) = delete;

public:
#if SWIFT_MUTEX_SUPPORTS_CONSTEXPR
  constexpr
#endif
  StaticUnsafeMutex()
      : Handle(MutexPlatformHelper::staticInit()) {
  }

  /// The lock() method has the following properties:
  /// - Behaves as an atomic operation.
  /// - Blocks the calling thread until exclusive ownership of the mutex
  ///   can be obtained.
  /// - Prior m.unlock() operations on the same mutex synchronize-with
  ///   this lock operation.
  /// - The behavior is undefined if the calling thread already owns
  ///   the mutex (likely a deadlock).
  /// - Ignores errors that may happen, undefined when an error happens.
  void lock() { MutexPlatformHelper::unsafeLock(Handle); }

  /// The unlock() method has the following properties:
  /// - Behaves as an atomic operation.
  /// - Releases the calling thread's ownership of the mutex and
  ///   synchronizes-with the subsequent successful lock operations on
  ///   the same object.
  /// - The behavior is undefined if the calling thread does not own
  ///   the mutex.
  /// - Ignores errors that may happen, undefined when an error happens.
  void unlock() { MutexPlatformHelper::unsafeUnlock(Handle); }

  template <typename CriticalSection>
  auto withLock(CriticalSection &&criticalSection)
      -> decltype(std::forward<CriticalSection>(criticalSection)()) {
    ScopedLock guard(*this);
    return std::forward<CriticalSection>(criticalSection)();
  }

  typedef ScopedLockT<StaticUnsafeMutex, false> ScopedLock;
  typedef ScopedLockT<StaticUnsafeMutex, true> ScopedUnlock;

private:
  MutexHandle Handle;
};

/// An indirect variant of a Mutex. This allocates the mutex on the heap, for
/// places where having the mutex inline takes up too much space. Used for
/// SmallMutex on platforms where Mutex is large.
class IndirectMutex {
  IndirectMutex(const IndirectMutex &) = delete;
  IndirectMutex &operator=(const IndirectMutex &) = delete;
  IndirectMutex(IndirectMutex &&) = delete;
  IndirectMutex &operator=(IndirectMutex &&) = delete;

public:
  explicit IndirectMutex(bool checked = false) { Ptr = new Mutex(checked); }
  ~IndirectMutex() { delete Ptr; }

  void lock() { Ptr->lock(); }

  void unlock() { Ptr->unlock(); }

  bool try_lock() { return Ptr->try_lock(); }

  template <typename CriticalSection>
  auto withLock(CriticalSection &&criticalSection)
      -> decltype(criticalSection()) {
    return Ptr->withLock(std::forward<CriticalSection>(criticalSection));
  }

  /// A stack based object that locks the supplied mutex on construction
  /// and unlocks it on destruction.
  ///
  /// Precondition: Mutex unlocked by this thread, undefined otherwise.
  typedef ScopedLockT<IndirectMutex, false> ScopedLock;

  /// A stack based object that unlocks the supplied mutex on construction
  /// and relocks it on destruction.
  ///
  /// Precondition: Mutex locked by this thread, undefined otherwise.
  typedef ScopedLockT<IndirectMutex, true> ScopedUnlock;

private:
  Mutex *Ptr;
};

/// A "small" mutex, which is pointer sized or smaller, for places where the
/// mutex is stored inline with limited storage space. This uses a normal Mutex
/// when that is small, and otherwise uses IndirectMutex.
using SmallMutex =
    std::conditional_t<sizeof(Mutex) <= sizeof(void *), Mutex, IndirectMutex>;

// Enforce literal requirements for static variants.
#if SWIFT_MUTEX_SUPPORTS_CONSTEXPR
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
static_assert(std::is_literal_type<StaticMutex>::value,
              "StaticMutex must be literal type");
static_assert(std::is_literal_type<StaticUnsafeMutex>::value,
              "StaticUnsafeMutex must be literal type");
#pragma clang diagnostic pop
#else
// Your platform doesn't currently support statically allocated Mutex
// you will possibly see global-constructors warnings
#endif

#if SWIFT_CONDITION_SUPPORTS_CONSTEXPR
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
static_assert(std::is_literal_type<StaticConditionVariable>::value,
              "StaticConditionVariable must be literal type");
#pragma clang diagnostic pop
#else
// Your platform doesn't currently support statically allocated ConditionVar
// you will possibly see global-constructors warnings
#endif

#if SWIFT_READWRITELOCK_SUPPORTS_CONSTEXPR
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
static_assert(std::is_literal_type<StaticReadWriteLock>::value,
              "StaticReadWriteLock must be literal type");
#pragma clang diagnostic pop
#else
// Your platform doesn't currently support statically allocated ReadWriteLocks
// you will possibly see global-constructors warnings
#endif
}

#endif // SWIFT_RUNTIME_MUTEX_BACKDEPLOY56_H
