]> git.proxmox.com Git - ceph.git/blobdiff - ceph/src/rocksdb/third-party/folly/folly/synchronization/Baton.h
update ceph source to reef 18.1.2
[ceph.git] / ceph / src / rocksdb / third-party / folly / folly / synchronization / Baton.h
diff --git a/ceph/src/rocksdb/third-party/folly/folly/synchronization/Baton.h b/ceph/src/rocksdb/third-party/folly/folly/synchronization/Baton.h
deleted file mode 100644 (file)
index bf156ec..0000000
+++ /dev/null
@@ -1,328 +0,0 @@
-//  Copyright (c) 2011-present, Facebook, Inc.  All rights reserved.
-//  This source code is licensed under both the GPLv2 (found in the
-//  COPYING file in the root directory) and Apache 2.0 License
-//  (found in the LICENSE.Apache file in the root directory).
-
-#pragma once
-
-#include <assert.h>
-#include <errno.h>
-#include <stdint.h>
-#include <atomic>
-#include <thread>
-
-#include <folly/detail/Futex.h>
-#include <folly/portability/Asm.h>
-#include <folly/synchronization/WaitOptions.h>
-#include <folly/synchronization/detail/Spin.h>
-
-namespace folly {
-
-/// A Baton allows a thread to block once and be awoken. Captures a
-/// single handoff, and during its lifecycle (from construction/reset
-/// to destruction/reset) a baton must either be post()ed and wait()ed
-/// exactly once each, or not at all.
-///
-/// Baton includes no internal padding, and is only 4 bytes in size.
-/// Any alignment or padding to avoid false sharing is up to the user.
-///
-/// This is basically a stripped-down semaphore that supports only a
-/// single call to sem_post and a single call to sem_wait.
-///
-/// The non-blocking version (MayBlock == false) provides more speed
-/// by using only load acquire and store release operations in the
-/// critical path, at the cost of disallowing blocking.
-///
-/// The current posix semaphore sem_t isn't too bad, but this provides
-/// more a bit more speed, inlining, smaller size, a guarantee that
-/// the implementation won't change, and compatibility with
-/// DeterministicSchedule.  By having a much more restrictive
-/// lifecycle we can also add a bunch of assertions that can help to
-/// catch race conditions ahead of time.
-template <bool MayBlock = true, template <typename> class Atom = std::atomic>
-class Baton {
- public:
-  static constexpr WaitOptions wait_options() {
-    return {};
-  }
-
-  constexpr Baton() noexcept : state_(INIT) {}
-
-  Baton(Baton const&) = delete;
-  Baton& operator=(Baton const&) = delete;
-
-  /// It is an error to destroy a Baton on which a thread is currently
-  /// wait()ing.  In practice this means that the waiter usually takes
-  /// responsibility for destroying the Baton.
-  ~Baton() noexcept {
-    // The docblock for this function says that it can't be called when
-    // there is a concurrent waiter.  We assume a strong version of this
-    // requirement in which the caller must _know_ that this is true, they
-    // are not allowed to be merely lucky.  If two threads are involved,
-    // the destroying thread must actually have synchronized with the
-    // waiting thread after wait() returned.  To convey causality the the
-    // waiting thread must have used release semantics and the destroying
-    // thread must have used acquire semantics for that communication,
-    // so we are guaranteed to see the post-wait() value of state_,
-    // which cannot be WAITING.
-    //
-    // Note that since we only care about a single memory location,
-    // the only two plausible memory orders here are relaxed and seq_cst.
-    assert(state_.load(std::memory_order_relaxed) != WAITING);
-  }
-
-  bool ready() const noexcept {
-    auto s = state_.load(std::memory_order_acquire);
-    assert(s == INIT || s == EARLY_DELIVERY);
-    return (s == EARLY_DELIVERY);
-  }
-
-  /// Equivalent to destroying the Baton and creating a new one.  It is
-  /// a bug to call this while there is a waiting thread, so in practice
-  /// the waiter will be the one that resets the baton.
-  void reset() noexcept {
-    // See ~Baton for a discussion about why relaxed is okay here
-    assert(state_.load(std::memory_order_relaxed) != WAITING);
-
-    // We use a similar argument to justify the use of a relaxed store
-    // here.  Since both wait() and post() are required to be called
-    // only once per lifetime, no thread can actually call those methods
-    // correctly after a reset() unless it synchronizes with the thread
-    // that performed the reset().  If a post() or wait() on another thread
-    // didn't synchronize, then regardless of what operation we performed
-    // here there would be a race on proper use of the Baton's spec
-    // (although not on any particular load and store).  Put another way,
-    // we don't need to synchronize here because anybody that might rely
-    // on such synchronization is required by the baton rules to perform
-    // an additional synchronization that has the desired effect anyway.
-    //
-    // There is actually a similar argument to be made about the
-    // constructor, in which the fenceless constructor initialization
-    // of state_ is piggybacked on whatever synchronization mechanism
-    // distributes knowledge of the Baton's existence
-    state_.store(INIT, std::memory_order_relaxed);
-  }
-
-  /// Causes wait() to wake up.  For each lifetime of a Baton (where a
-  /// lifetime starts at construction or reset() and ends at
-  /// destruction or reset()) there can be at most one call to post(),
-  /// in the single poster version.  Any thread may call post().
-  void post() noexcept {
-    if (!MayBlock) {
-      /// Spin-only version
-      ///
-      assert(
-          ((1 << state_.load(std::memory_order_relaxed)) &
-           ((1 << INIT) | (1 << EARLY_DELIVERY))) != 0);
-      state_.store(EARLY_DELIVERY, std::memory_order_release);
-      return;
-    }
-
-    /// May-block versions
-    ///
-    uint32_t before = state_.load(std::memory_order_acquire);
-
-    assert(before == INIT || before == WAITING || before == TIMED_OUT);
-
-    if (before == INIT &&
-        state_.compare_exchange_strong(
-            before,
-            EARLY_DELIVERY,
-            std::memory_order_release,
-            std::memory_order_relaxed)) {
-      return;
-    }
-
-    assert(before == WAITING || before == TIMED_OUT);
-
-    if (before == TIMED_OUT) {
-      return;
-    }
-
-    assert(before == WAITING);
-    state_.store(LATE_DELIVERY, std::memory_order_release);
-    detail::futexWake(&state_, 1);
-  }
-
-  /// Waits until post() has been called in the current Baton lifetime.
-  /// May be called at most once during a Baton lifetime (construction
-  /// |reset until destruction|reset).  If post is called before wait in
-  /// the current lifetime then this method returns immediately.
-  ///
-  /// The restriction that there can be at most one wait() per lifetime
-  /// could be relaxed somewhat without any perf or size regressions,
-  /// but by making this condition very restrictive we can provide better
-  /// checking in debug builds.
-  void wait(const WaitOptions& opt = wait_options()) noexcept {
-    if (try_wait()) {
-      return;
-    }
-
-    auto const deadline = std::chrono::steady_clock::time_point::max();
-    tryWaitSlow(deadline, opt);
-  }
-
-  /// Similar to wait, but doesn't block the thread if it hasn't been posted.
-  ///
-  /// try_wait has the following semantics:
-  /// - It is ok to call try_wait any number times on the same baton until
-  ///   try_wait reports that the baton has been posted.
-  /// - It is ok to call timed_wait or wait on the same baton if try_wait
-  ///   reports that baton hasn't been posted.
-  /// - If try_wait indicates that the baton has been posted, it is invalid to
-  ///   call wait, try_wait or timed_wait on the same baton without resetting
-  ///
-  /// @return       true if baton has been posted, false othewise
-  bool try_wait() const noexcept {
-    return ready();
-  }
-
-  /// Similar to wait, but with a timeout. The thread is unblocked if the
-  /// timeout expires.
-  /// Note: Only a single call to wait/try_wait_for/try_wait_until is allowed
-  /// during a baton's life-cycle (from ctor/reset to dtor/reset). In other
-  /// words, after try_wait_for the caller can't invoke
-  /// wait/try_wait/try_wait_for/try_wait_until
-  /// again on the same baton without resetting it.
-  ///
-  /// @param  timeout       Time until which the thread can block
-  /// @return               true if the baton was posted to before timeout,
-  ///                       false otherwise
-  template <typename Rep, typename Period>
-  bool try_wait_for(
-      const std::chrono::duration<Rep, Period>& timeout,
-      const WaitOptions& opt = wait_options()) noexcept {
-    if (try_wait()) {
-      return true;
-    }
-
-    auto const deadline = std::chrono::steady_clock::now() + timeout;
-    return tryWaitSlow(deadline, opt);
-  }
-
-  /// Similar to wait, but with a deadline. The thread is unblocked if the
-  /// deadline expires.
-  /// Note: Only a single call to wait/try_wait_for/try_wait_until is allowed
-  /// during a baton's life-cycle (from ctor/reset to dtor/reset). In other
-  /// words, after try_wait_until the caller can't invoke
-  /// wait/try_wait/try_wait_for/try_wait_until
-  /// again on the same baton without resetting it.
-  ///
-  /// @param  deadline      Time until which the thread can block
-  /// @return               true if the baton was posted to before deadline,
-  ///                       false otherwise
-  template <typename Clock, typename Duration>
-  bool try_wait_until(
-      const std::chrono::time_point<Clock, Duration>& deadline,
-      const WaitOptions& opt = wait_options()) noexcept {
-    if (try_wait()) {
-      return true;
-    }
-
-    return tryWaitSlow(deadline, opt);
-  }
-
-  /// Alias to try_wait_for. Deprecated.
-  template <typename Rep, typename Period>
-  bool timed_wait(
-      const std::chrono::duration<Rep, Period>& timeout) noexcept {
-    return try_wait_for(timeout);
-  }
-
-  /// Alias to try_wait_until. Deprecated.
-  template <typename Clock, typename Duration>
-  bool timed_wait(
-      const std::chrono::time_point<Clock, Duration>& deadline) noexcept {
-    return try_wait_until(deadline);
-  }
-
- private:
-  enum State : uint32_t {
-    INIT = 0,
-    EARLY_DELIVERY = 1,
-    WAITING = 2,
-    LATE_DELIVERY = 3,
-    TIMED_OUT = 4,
-  };
-
-  template <typename Clock, typename Duration>
-  bool tryWaitSlow(
-      const std::chrono::time_point<Clock, Duration>& deadline,
-      const WaitOptions& opt) noexcept {
-    switch (
-        detail::spin_pause_until(deadline, opt, [this] { return ready(); })) {
-      case detail::spin_result::success:
-        return true;
-      case detail::spin_result::timeout:
-        return false;
-      case detail::spin_result::advance:
-        break;
-    }
-
-    if (!MayBlock) {
-      switch (detail::spin_yield_until(deadline, [this] { return ready(); })) {
-        case detail::spin_result::success:
-          return true;
-        case detail::spin_result::timeout:
-          return false;
-        case detail::spin_result::advance:
-          break;
-      }
-    }
-
-    // guess we have to block :(
-    uint32_t expected = INIT;
-    if (!state_.compare_exchange_strong(
-            expected,
-            WAITING,
-            std::memory_order_relaxed,
-            std::memory_order_relaxed)) {
-      // CAS failed, last minute reprieve
-      assert(expected == EARLY_DELIVERY);
-      // TODO: move the acquire to the compare_exchange failure load after C++17
-      std::atomic_thread_fence(std::memory_order_acquire);
-      return true;
-    }
-
-    while (true) {
-      auto rv = detail::futexWaitUntil(&state_, WAITING, deadline);
-
-      // Awoken by the deadline passing.
-      if (rv == detail::FutexResult::TIMEDOUT) {
-        assert(deadline != (std::chrono::time_point<Clock, Duration>::max()));
-        state_.store(TIMED_OUT, std::memory_order_release);
-        return false;
-      }
-
-      // Probably awoken by a matching wake event, but could also by awoken
-      // by an asynchronous signal or by a spurious wakeup.
-      //
-      // state_ is the truth even if FUTEX_WAIT reported a matching
-      // FUTEX_WAKE, since we aren't using type-stable storage and we
-      // don't guarantee reuse.  The scenario goes like this: thread
-      // A's last touch of a Baton is a call to wake(), which stores
-      // LATE_DELIVERY and gets an unlucky context switch before delivering
-      // the corresponding futexWake.  Thread B sees LATE_DELIVERY
-      // without consuming a futex event, because it calls futexWait
-      // with an expected value of WAITING and hence doesn't go to sleep.
-      // B returns, so the Baton's memory is reused and becomes another
-      // Baton (or a reuse of this one).  B calls futexWait on the new
-      // Baton lifetime, then A wakes up and delivers a spurious futexWake
-      // to the same memory location.  B's futexWait will then report a
-      // consumed wake event even though state_ is still WAITING.
-      //
-      // It would be possible to add an extra state_ dance to communicate
-      // that the futexWake has been sent so that we can be sure to consume
-      // it before returning, but that would be a perf and complexity hit.
-      uint32_t s = state_.load(std::memory_order_acquire);
-      assert(s == WAITING || s == LATE_DELIVERY);
-      if (s == LATE_DELIVERY) {
-        return true;
-      }
-    }
-  }
-
-  detail::Futex<Atom> state_;
-};
-
-} // namespace folly