]> git.proxmox.com Git - ceph.git/blob - ceph/src/seastar/include/seastar/core/gate.hh
update source to Ceph Pacific 16.2.2
[ceph.git] / ceph / src / seastar / include / seastar / core / gate.hh
1 /*
2 * This file is open source software, licensed to you under the terms
3 * of the Apache License, Version 2.0 (the "License"). See the NOTICE file
4 * distributed with this work for additional information regarding copyright
5 * ownership. You may not use this file except in compliance with the License.
6 *
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing,
12 * software distributed under the License is distributed on an
13 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14 * KIND, either express or implied. See the License for the
15 * specific language governing permissions and limitations
16 * under the License.
17 */
18 /*
19 * Copyright 2014 Cloudius Systems
20 */
21
22 #pragma once
23
24 #include <seastar/core/future.hh>
25 #include <seastar/util/std-compat.hh>
26 #include <exception>
27
28 namespace seastar {
29
30 /// \addtogroup fiber-module
31 /// @{
32
33 /// Exception thrown when a \ref gate object has been closed
34 /// by the \ref gate::close() method.
35 class gate_closed_exception : public std::exception {
36 public:
37 virtual const char* what() const noexcept override {
38 return "gate closed";
39 }
40 };
41
42 /// Facility to stop new requests, and to tell when existing requests are done.
43 ///
44 /// When stopping a service that serves asynchronous requests, we are faced with
45 /// two problems: preventing new requests from coming in, and knowing when existing
46 /// requests have completed. The \c gate class provides a solution.
47 class gate {
48 size_t _count = 0;
49 std::optional<promise<>> _stopped;
50 public:
51 /// Tries to register an in-progress request.
52 ///
53 /// If the gate is not closed, the request is registered and the function returns `true`,
54 /// Otherwise the function just returns `false` and has no other effect.
55 bool try_enter() noexcept {
56 bool opened = !_stopped;
57 if (opened) {
58 ++_count;
59 }
60 return opened;
61 }
62 /// Registers an in-progress request.
63 ///
64 /// If the gate is not closed, the request is registered. Otherwise,
65 /// a \ref gate_closed_exception is thrown.
66 void enter() {
67 if (!try_enter()) {
68 throw gate_closed_exception();
69 }
70 }
71 /// Unregisters an in-progress request.
72 ///
73 /// If the gate is closed, and there are no more in-progress requests,
74 /// the `_stopped` promise will be fulfilled.
75 void leave() noexcept {
76 --_count;
77 if (!_count && _stopped) {
78 _stopped->set_value();
79 }
80 }
81 /// Potentially stop an in-progress request.
82 ///
83 /// If the gate is already closed, a \ref gate_closed_exception is thrown.
84 /// By using \ref enter() and \ref leave(), the program can ensure that
85 /// no further requests are serviced. However, long-running requests may
86 /// continue to run. The check() method allows such a long operation to
87 /// voluntarily stop itself after the gate is closed, by making calls to
88 /// check() in appropriate places. check() with throw an exception and
89 /// bail out of the long-running code if the gate is closed.
90 void check() {
91 if (_stopped) {
92 throw gate_closed_exception();
93 }
94 }
95 /// Closes the gate.
96 ///
97 /// Future calls to \ref enter() will fail with an exception, and when
98 /// all current requests call \ref leave(), the returned future will be
99 /// made ready.
100 future<> close() noexcept {
101 assert(!_stopped && "seastar::gate::close() cannot be called more than once");
102 _stopped = std::make_optional(promise<>());
103 if (!_count) {
104 _stopped->set_value();
105 }
106 return _stopped->get_future();
107 }
108
109 /// Returns a current number of registered in-progress requests.
110 size_t get_count() const noexcept {
111 return _count;
112 }
113
114 /// Returns whether the gate is closed.
115 bool is_closed() const noexcept {
116 return bool(_stopped);
117 }
118 };
119
120 namespace internal {
121
122 template <typename Func>
123 inline
124 auto
125 invoke_func_with_gate(gate& g, Func&& func) noexcept {
126 return futurize_invoke(std::forward<Func>(func)).finally([&g] { g.leave(); });
127 }
128
129 } // namespace intgernal
130
131 /// Executes the function \c func making sure the gate \c g is properly entered
132 /// and later on, properly left.
133 ///
134 /// \param func function to be executed
135 /// \param g the gate. Caller must make sure that it outlives this function.
136 /// \returns whatever \c func returns
137 ///
138 /// \relates gate
139 template <typename Func>
140 inline
141 auto
142 with_gate(gate& g, Func&& func) {
143 g.enter();
144 return internal::invoke_func_with_gate(g, std::forward<Func>(func));
145 }
146
147 /// Executes the function \c func if the gate \c g can be entered
148 /// and later on, properly left.
149 ///
150 /// \param func function to be executed
151 /// \param g the gate. Caller must make sure that it outlives this function.
152 ///
153 /// If the gate is already closed, an exception future holding
154 /// \ref gate_closed_exception is returned, otherwise
155 /// \returns whatever \c func returns.
156 ///
157 /// \relates gate
158 template <typename Func>
159 inline
160 auto
161 try_with_gate(gate& g, Func&& func) noexcept {
162 if (!g.try_enter()) {
163 using futurator = futurize<std::result_of_t<Func()>>;
164 return futurator::make_exception_future(gate_closed_exception());
165 }
166 return internal::invoke_func_with_gate(g, std::forward<Func>(func));
167 }
168 /// @}
169
170 }