Seastar
High performance C++ framework for concurrent servers
abort_source.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 (C) 2017 ScyllaDB.
20 */
21
22#pragma once
23
24#include <seastar/util/modules.hh>
25#include <seastar/util/noncopyable_function.hh>
26#include <seastar/util/optimized_optional.hh>
27#include <seastar/util/std-compat.hh>
28
29#ifndef SEASTAR_MODULE
30#include <boost/intrusive/list.hpp>
31#include <exception>
32#include <optional>
33#include <type_traits>
34#include <utility>
35#endif
36
37namespace bi = boost::intrusive;
38
39namespace seastar {
40
41SEASTAR_MODULE_EXPORT_BEGIN
42
45
48class abort_requested_exception : public std::exception {
49public:
50 virtual const char* what() const noexcept override {
51 return "abort requested";
52 }
53};
54
59 using subscription_callback_type = noncopyable_function<void (const std::optional<std::exception_ptr>&) noexcept>;
61
62public:
66 class subscription : public bi::list_base_hook<bi::link_mode<bi::auto_unlink>> {
67 friend class abort_source;
68
70 bool _aborted = false;
71
73 : _target(std::move(target)) {
74 if (!as.abort_requested()) {
75 as._subscriptions.push_back(*this);
76 }
77 }
78
79 struct naive_cb_tag {}; // to disambiguate constructors
80 explicit subscription(naive_cb_tag, abort_source& as, naive_subscription_callback_type naive_cb)
81 : _target([cb = std::move(naive_cb)] (const std::optional<std::exception_ptr>&) noexcept { cb(); }) {
82 if (!as.abort_requested()) {
83 as._subscriptions.push_back(*this);
84 }
85 }
86
87 public:
93 void on_abort(const std::optional<std::exception_ptr>& ex) noexcept {
94 unlink();
95 if (!std::exchange(_aborted, true)) {
96 _target(ex);
97 }
98 }
99
100 public:
101 subscription() = default;
102
103 subscription(subscription&& other) noexcept(std::is_nothrow_move_constructible_v<subscription_callback_type>)
104 : _target(std::move(other._target))
105 , _aborted(std::exchange(other._aborted, true))
106 {
107 subscription_list_type::node_algorithms::swap_nodes(other.this_ptr(), this_ptr());
108 }
109
110 subscription& operator=(subscription&& other) noexcept(std::is_nothrow_move_assignable_v<subscription_callback_type>) {
111 if (this != &other) {
112 _target = std::move(other._target);
113 _aborted = std::exchange(other._aborted, true);
114 unlink();
115 subscription_list_type::node_algorithms::swap_nodes(other.this_ptr(), this_ptr());
116 }
117 return *this;
118 }
119
120 explicit operator bool() const noexcept {
121 return is_linked();
122 }
123 };
124
125private:
126 using subscription_list_type = bi::list<subscription, bi::constant_time_size<false>>;
127 subscription_list_type _subscriptions;
128 std::exception_ptr _ex;
129
130 void do_request_abort(std::optional<std::exception_ptr> ex) noexcept {
131 if (_ex) {
132 return;
133 }
134 _ex = ex.value_or(get_default_exception());
135 assert(_ex);
136 auto subs = std::move(_subscriptions);
137 while (!subs.empty()) {
138 subscription& s = subs.front();
139 s.on_abort(ex);
140 }
141 }
142
143public:
144 abort_source() = default;
145 virtual ~abort_source() = default;
146
147 abort_source(abort_source&&) = default;
148 abort_source& operator=(abort_source&&) = default;
149
165 template <typename Func>
166 requires (std::is_nothrow_invocable_r_v<void, Func, const std::optional<std::exception_ptr>&> ||
167 std::is_nothrow_invocable_r_v<void, Func>)
168 [[nodiscard]]
170 if constexpr (std::is_invocable_v<Func, std::exception_ptr>) {
171 return { subscription(*this, std::forward<Func>(f)) };
172 } else {
173 return { subscription(subscription::naive_cb_tag{}, *this, std::forward<Func>(f)) };
174 }
175 }
176
180 void request_abort() noexcept {
181 do_request_abort(std::nullopt);
182 }
183
187 void request_abort_ex(std::exception_ptr ex) noexcept {
188 do_request_abort(std::make_optional(std::move(ex)));
189 }
190
194 template <typename Exception>
195 void request_abort_ex(Exception&& e) noexcept {
196 do_request_abort(std::make_optional(std::make_exception_ptr(std::forward<Exception>(e))));
197 }
198
200 bool abort_requested() const noexcept {
201 return bool(_ex);
202 }
203
204
206 void check() const {
207 if (abort_requested()) {
208 std::rethrow_exception(_ex);
209 }
210 }
211
213 const std::exception_ptr& abort_requested_exception_ptr() const noexcept {
214 return _ex;
215 }
216
219 virtual std::exception_ptr get_default_exception() const noexcept {
220 return make_exception_ptr(abort_requested_exception());
221 }
222};
223
225
226SEASTAR_MODULE_EXPORT_END
227
228}
229
230#if FMT_VERSION < 100000
231// fmt v10 introduced formatter for std::exception
232template <>
233struct fmt::formatter<seastar::abort_requested_exception> : fmt::formatter<string_view> {
234 auto format(const seastar::abort_requested_exception& e, fmt::format_context& ctx) const {
235 return fmt::format_to(ctx.out(), "{}", e.what());
236 }
237};
238#endif
Definition: abort_source.hh:48
Definition: abort_source.hh:66
void on_abort(const std::optional< std::exception_ptr > &ex) noexcept
Definition: abort_source.hh:93
Definition: abort_source.hh:58
virtual std::exception_ptr get_default_exception() const noexcept
Definition: abort_source.hh:219
const std::exception_ptr & abort_requested_exception_ptr() const noexcept
Returns an exception with which an abort was requested.
Definition: abort_source.hh:213
void check() const
Throws a abort_requested_exception if cancellation has been requested.
Definition: abort_source.hh:206
void request_abort_ex(std::exception_ptr ex) noexcept
Definition: abort_source.hh:187
optimized_optional< subscription > subscribe(Func &&f)
Definition: abort_source.hh:169
void request_abort_ex(Exception &&e) noexcept
Definition: abort_source.hh:195
void request_abort() noexcept
Definition: abort_source.hh:180
bool abort_requested() const noexcept
Returns whether an abort has been requested.
Definition: abort_source.hh:200
Definition: optimized_optional.hh:48
Definition: stream.hh:127
Seastar API namespace.
Definition: abort_on_ebadf.hh:26
Definition: noncopyable_function.hh:37