]> git.proxmox.com Git - ceph.git/blobdiff - ceph/src/seastar/include/seastar/core/gate.hh
import quincy beta 17.1.0
[ceph.git] / ceph / src / seastar / include / seastar / core / gate.hh
index 5d440c07fd6a5c2b60fb7c2088151ad7721f8d84..d99fe46338d98d151ef4bae4fe7ec0fe055a95d1 100644 (file)
@@ -48,6 +48,13 @@ class gate {
     size_t _count = 0;
     std::optional<promise<>> _stopped;
 public:
+    gate() = default;
+    gate(const gate&) = delete;
+    gate(gate&&) = default;
+    gate& operator=(gate&&) = default;
+    ~gate() {
+        assert(!_count && "gate destroyed with outstanding requests");
+    }
     /// Tries to register an in-progress request.
     ///
     /// If the gate is not closed, the request is registered and the function returns `true`,
@@ -115,6 +122,94 @@ public:
     bool is_closed() const noexcept {
         return bool(_stopped);
     }
+
+    /// Facility to hold a gate opened using RAII.
+    ///
+    /// A \ref gate::holder is usually obtained using \ref gate::get_holder.
+    ///
+    /// The \c gate is entered when the \ref gate::holder is constructed,
+    /// And the \c gate is left when the \ref gate::holder is destroyed.
+    ///
+    /// Copying the \ref gate::holder reenters the \c gate to keep an extra reference on it.
+    /// Moving the \ref gate::holder is supported and has no effect on the \c gate itself.
+    class holder {
+        gate* _g;
+
+    public:
+        /// Construct a default \ref holder, referencing no \ref gate.
+        /// Never throws.
+        holder() noexcept : _g(nullptr) { }
+
+        /// Construct a \ref holder by entering the \c gate.
+        /// May throw \ref gate_closed_exception if the gate is already closed.
+        explicit holder(gate& g) : _g(&g) {
+            _g->enter();
+        }
+
+        /// Construct a \ref holder by copying another \c holder.
+        /// Copying a holder never throws: The original holder has already entered the gate,
+        /// so even if later the gate was \ref close "close()d", the copy of the holder is also allowed to enter too.
+        /// Note that the fiber waiting for the close(), which until now was waiting for the one holder to leave,
+        /// will now wait for both copies to leave.
+        holder(const holder& x) noexcept : _g(x._g) {
+            if (_g) {
+                _g->_count++;
+            }
+        }
+
+        /// Construct a \ref holder by moving another \c holder.
+        /// The referenced \ref gate is unaffected, and so the
+        /// move-constructor must never throw.
+        holder(holder&& x) noexcept : _g(std::exchange(x._g, nullptr)) { }
+
+        /// Destroy a \ref holder and leave the referenced \ref gate.
+        ~holder() {
+            release();
+        }
+
+        /// Copy-assign another \ref holder.
+        /// \ref leave "Leave()" the current \ref gate before assigning the other one, if they are different.
+        /// Copying a holder never throws: The original holder has already entered the gate,
+        /// so even if later the gate was \ref close "close()d", the copy of the holder is also allowed to enter too.
+        /// Note that the fiber waiting for the close(), which until now was waiting for the one holder to leave,
+        /// will now wait for both copies to leave.
+        holder& operator=(const holder& x) noexcept {
+            if (x._g != _g) {
+                release();
+                _g = x._g;
+                if (_g) {
+                    _g->_count++;
+                }
+            }
+            return *this;
+        }
+
+        /// Move-assign another \ref holder.
+        /// The other \ref gate is unaffected,
+        /// and so the move-assign operator must always succeed.
+        /// Leave the current \ref gate before assigning the other one.
+        holder& operator=(holder&& x) noexcept {
+            if (&x != this) {
+                release();
+                _g = std::exchange(x._g, nullptr);
+            }
+            return *this;
+        }
+
+        /// Leave the held \c gate
+        void release() noexcept {
+            if (_g) {
+                _g->leave();
+                _g = nullptr;
+            }
+        }
+    };
+
+    /// Get a RAII-based gate::holder object that \ref enter "enter()s"
+    /// the gate when constructed and \ref leave "leave()s" it when destroyed.
+    holder hold() {
+        return holder(*this);
+    }
 };
 
 namespace internal {
@@ -160,7 +255,7 @@ inline
 auto
 try_with_gate(gate& g, Func&& func) noexcept {
     if (!g.try_enter()) {
-        using futurator = futurize<std::result_of_t<Func()>>;
+        using futurator = futurize<std::invoke_result_t<Func>>;
         return futurator::make_exception_future(gate_closed_exception());
     }
     return internal::invoke_func_with_gate(g, std::forward<Func>(func));