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