Seastar
High performance C++ framework for concurrent servers
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#ifndef SEASTAR_MODULE
25#include <boost/intrusive/list.hpp>
26
27#include <seastar/core/future.hh>
28#include <seastar/util/std-compat.hh>
29#include <seastar/util/modules.hh>
30#include <cassert>
31#include <exception>
32#include <optional>
33#include <utility>
34#endif
35
36#ifdef SEASTAR_DEBUG
37#define SEASTAR_GATE_HOLDER_DEBUG
38#endif
39
40namespace seastar {
41
44
47SEASTAR_MODULE_EXPORT
48class gate_closed_exception : public std::exception {
49public:
50 virtual const char* what() const noexcept override {
51 return "gate closed";
52 }
53};
54
60SEASTAR_MODULE_EXPORT
61class gate {
62 size_t _count = 0;
63 std::optional<promise<>> _stopped;
64
65#ifdef SEASTAR_GATE_HOLDER_DEBUG
66 void assert_not_held_when_moved() const noexcept;
67 void assert_not_held_when_destroyed() const noexcept;
68#else // SEASTAR_GATE_HOLDER_DEBUG
69 void assert_not_held_when_moved() const noexcept {}
70 void assert_not_held_when_destroyed() const noexcept {}
71#endif // SEASTAR_GATE_HOLDER_DEBUG
72
73public:
74 // Implemented to force noexcept due to boost:intrusive::list
75 gate() noexcept {};
76 gate(const gate&) = delete;
77 gate(gate&& x) noexcept
78 : _count(std::exchange(x._count, 0)), _stopped(std::exchange(x._stopped, std::nullopt)) {
79 x.assert_not_held_when_moved();
80 }
81 gate& operator=(gate&& x) noexcept {
82 if (this != &x) {
83 assert(!_count && "gate reassigned with outstanding requests");
84 x.assert_not_held_when_moved();
85 _count = std::exchange(x._count, 0);
86 _stopped = std::exchange(x._stopped, std::nullopt);
87 }
88 return *this;
89 }
90 ~gate() {
91 assert(!_count && "gate destroyed with outstanding requests");
92 assert_not_held_when_destroyed();
93 }
98 bool try_enter() noexcept {
99 bool opened = !_stopped;
100 if (opened) {
101 ++_count;
102 }
103 return opened;
104 }
109 void enter() {
110 if (!try_enter()) {
111 throw gate_closed_exception();
112 }
113 }
118 void leave() noexcept {
119 --_count;
120 if (!_count && _stopped) {
121 _stopped->set_value();
122 }
123 }
133 void check() {
134 if (_stopped) {
135 throw gate_closed_exception();
136 }
137 }
143 future<> close() noexcept {
144 assert(!_stopped && "seastar::gate::close() cannot be called more than once");
145 _stopped = std::make_optional(promise<>());
146 if (!_count) {
147 _stopped->set_value();
148 }
149 return _stopped->get_future();
150 }
151
153 size_t get_count() const noexcept {
154 return _count;
155 }
156
158 bool is_closed() const noexcept {
159 return bool(_stopped);
160 }
161
171 class holder {
172 gate* _g;
173#ifdef SEASTAR_GATE_HOLDER_DEBUG
174 using member_hook_t = boost::intrusive::list_member_hook<boost::intrusive::link_mode<boost::intrusive::auto_unlink>>;
175 member_hook_t _link;
176
177 void debug_hold_gate() noexcept {
178 if (_g) {
179 _g->_holders.push_back(*this);
180 }
181 }
182
183 void debug_release_gate() noexcept {
184 _link.unlink();
185 }
186#else // SEASTAR_GATE_HOLDER_DEBUG
187 void debug_hold_gate() noexcept {}
188 void debug_release_gate() noexcept {}
189#endif // SEASTAR_GATE_HOLDER_DEBUG
190
191 // release the holder from the gate without leaving it.
192 // used for release and move.
193 gate* release_gate() noexcept {
194 auto* g = std::exchange(_g, nullptr);
195 if (g) {
196 debug_release_gate();
197 }
198 return g;
199 }
200
201 friend class gate;
202 public:
205 holder() noexcept : _g(nullptr) { }
206
209 explicit holder(gate& g) : _g(&g) {
210 _g->enter();
211 debug_hold_gate();
212 }
213
219 holder(const holder& x) noexcept : _g(x._g) {
220 if (_g) {
221 _g->_count++;
222 debug_hold_gate();
223 }
224 }
225
229 holder(holder&& x) noexcept : _g(std::move(x).release_gate()) {
230 debug_hold_gate();
231 }
232
235 release();
236 }
237
244 holder& operator=(const holder& x) noexcept {
245 if (x._g != _g) {
246 release();
247 _g = x._g;
248 if (_g) {
249 _g->_count++;
250 debug_hold_gate();
251 }
252 }
253 return *this;
254 }
255
260 holder& operator=(holder&& x) noexcept {
261 if (&x != this) {
262 release();
263 _g = std::move(x).release_gate();
264 debug_hold_gate();
265 }
266 return *this;
267 }
268
270 void release() noexcept {
271 if (auto g = release_gate()) {
272 g->leave();
273 }
274 }
275 };
276
280 return holder(*this);
281 }
282
286 std::optional<holder> try_hold() noexcept {
287 return is_closed() ? std::nullopt : std::make_optional<holder>(*this);
288 }
289
290private:
291#ifdef SEASTAR_GATE_HOLDER_DEBUG
292 using holders_list_t = boost::intrusive::list<holder,
293 boost::intrusive::member_hook<holder, holder::member_hook_t, &holder::_link>,
294 boost::intrusive::constant_time_size<false>>;
295
296 holders_list_t _holders;
297#endif // SEASTAR_GATE_HOLDER_DEBUG
298};
299
300#ifdef SEASTAR_GATE_HOLDER_DEBUG
301SEASTAR_MODULE_EXPORT
302inline void gate::assert_not_held_when_moved() const noexcept {
303 assert(_holders.empty() && "gate moved with outstanding holders");
304}
305inline void gate::assert_not_held_when_destroyed() const noexcept {
306 assert(_holders.empty() && "gate destroyed with outstanding holders");
307}
308#endif // SEASTAR_GATE_HOLDER_DEBUG
309
310namespace internal {
311
312template <typename Func>
313inline
314auto
315invoke_func_with_gate(gate::holder&& gh, Func&& func) noexcept {
316 return futurize_invoke(std::forward<Func>(func)).finally([gh = std::forward<gate::holder>(gh)] {});
317}
318
319} // namespace internal
320
329SEASTAR_MODULE_EXPORT
330template <typename Func>
331inline
332auto
333with_gate(gate& g, Func&& func) {
334 return internal::invoke_func_with_gate(g.hold(), std::forward<Func>(func));
335}
336
348SEASTAR_MODULE_EXPORT
349template <typename Func>
350inline
351auto
352try_with_gate(gate& g, Func&& func) noexcept {
353 if (g.is_closed()) {
354 using futurator = futurize<std::invoke_result_t<Func>>;
356 }
357 return internal::invoke_func_with_gate(g.hold(), std::forward<Func>(func));
358}
360
361
362}
A representation of a possibly not-yet-computed value.
Definition: future.hh:1240
Definition: gate.hh:171
holder(gate &g)
Definition: gate.hh:209
~holder()
Destroy a holder and leave the referenced gate.
Definition: gate.hh:234
holder & operator=(const holder &x) noexcept
Definition: gate.hh:244
holder(holder &&x) noexcept
Definition: gate.hh:229
holder(const holder &x) noexcept
Definition: gate.hh:219
void release() noexcept
Leave the held gate.
Definition: gate.hh:270
holder() noexcept
Definition: gate.hh:205
holder & operator=(holder &&x) noexcept
Definition: gate.hh:260
Definition: gate.hh:48
Definition: gate.hh:61
bool try_enter() noexcept
Definition: gate.hh:98
bool is_closed() const noexcept
Returns whether the gate is closed.
Definition: gate.hh:158
future close() noexcept
Definition: gate.hh:143
size_t get_count() const noexcept
Returns a current number of registered in-progress requests.
Definition: gate.hh:153
void check()
Definition: gate.hh:133
std::optional< holder > try_hold() noexcept
Definition: gate.hh:286
holder hold()
Definition: gate.hh:279
void enter()
Definition: gate.hh:109
void leave() noexcept
Definition: gate.hh:118
auto with_gate(gate &g, Func &&func)
Definition: gate.hh:333
auto try_with_gate(gate &g, Func &&func) noexcept
Definition: gate.hh:352
future< T > make_exception_future(std::exception_ptr &&value) noexcept
Creates a future in an available, failed state.
Definition: future.hh:1949
Seastar API namespace.
Definition: abort_on_ebadf.hh:26
Converts a type to a future type, if it isn't already.
Definition: future.hh:1853