]> git.proxmox.com Git - ceph.git/blame - ceph/src/seastar/include/seastar/core/abort_source.hh
update ceph source to reef 18.1.2
[ceph.git] / ceph / src / seastar / include / seastar / core / abort_source.hh
CommitLineData
11fdf7f2
TL
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/noncopyable_function.hh>
25#include <seastar/util/optimized_optional.hh>
26#include <seastar/util/std-compat.hh>
27
28#include <boost/intrusive/list.hpp>
29
30#include <exception>
31
32namespace bi = boost::intrusive;
33
34namespace seastar {
35
36/// \addtogroup fiber-module
37/// @{
38
39/// Exception thrown when an \ref abort_source object has been
40/// notified by the \ref abort_source::request_abort() method.
41class abort_requested_exception : public std::exception {
42public:
43 virtual const char* what() const noexcept override {
44 return "abort requested";
45 }
46};
47
48/// Facility to communicate a cancellation request to a fiber.
49/// Callbacks can be registered with the \c abort_source, which are called
50/// atomically with a call to request_abort().
51class abort_source {
1e59de90
TL
52 using subscription_callback_type = noncopyable_function<void (const std::optional<std::exception_ptr>&) noexcept>;
53 using naive_subscription_callback_type = noncopyable_function<void() noexcept>;
11fdf7f2
TL
54
55public:
56 /// Represents a handle to the callback registered by a given fiber. Ending the
57 /// lifetime of the \c subscription will unregister the callback, if it hasn't
58 /// been invoked yet.
59 class subscription : public bi::list_base_hook<bi::link_mode<bi::auto_unlink>> {
60 friend class abort_source;
61
11fdf7f2
TL
62 subscription_callback_type _target;
63
64 explicit subscription(abort_source& as, subscription_callback_type target)
20effc67 65 : _target(std::move(target)) {
11fdf7f2
TL
66 as._subscriptions->push_back(*this);
67 }
68
1e59de90
TL
69 struct naive_cb_tag {}; // to disambiguate constructors
70 explicit subscription(naive_cb_tag, abort_source& as, naive_subscription_callback_type naive_cb)
71 : _target([cb = std::move(naive_cb)] (const std::optional<std::exception_ptr>&) noexcept { cb(); }) {
72 as._subscriptions->push_back(*this);
73 }
74
75 void on_abort(const std::optional<std::exception_ptr>& ex) noexcept {
76 _target(ex);
11fdf7f2
TL
77 }
78
79 public:
80 subscription() = default;
81
82 subscription(subscription&& other) noexcept(std::is_nothrow_move_constructible<subscription_callback_type>::value)
20effc67 83 : _target(std::move(other._target)) {
11fdf7f2
TL
84 subscription_list_type::node_algorithms::swap_nodes(other.this_ptr(), this_ptr());
85 }
86
87 subscription& operator=(subscription&& other) noexcept(std::is_nothrow_move_assignable<subscription_callback_type>::value) {
88 if (this != &other) {
89 _target = std::move(other._target);
1e59de90 90 unlink();
11fdf7f2
TL
91 subscription_list_type::node_algorithms::swap_nodes(other.this_ptr(), this_ptr());
92 }
93 return *this;
94 }
95
96 explicit operator bool() const noexcept {
20effc67 97 return is_linked();
11fdf7f2
TL
98 }
99 };
100
101private:
102 using subscription_list_type = bi::list<subscription, bi::constant_time_size<false>>;
f67539c2 103 std::optional<subscription_list_type> _subscriptions = subscription_list_type();
1e59de90
TL
104 std::exception_ptr _ex;
105
106 void do_request_abort(std::optional<std::exception_ptr> ex) noexcept {
107 assert(_subscriptions);
108 _ex = ex.value_or(get_default_exception());
109 auto subs = std::exchange(_subscriptions, std::nullopt);
110 while (!subs->empty()) {
111 subscription& s = subs->front();
112 s.unlink();
113 s.on_abort(ex);
114 }
115 }
11fdf7f2
TL
116
117public:
1e59de90
TL
118 abort_source() = default;
119 virtual ~abort_source() = default;
120
121 abort_source(abort_source&&) = default;
122 abort_source& operator=(abort_source&&) = default;
123
11fdf7f2
TL
124 /// Delays the invocation of the callback \c f until \ref request_abort() is called.
125 /// \returns an engaged \ref optimized_optional containing a \ref subscription that can be used to control
126 /// the lifetime of the callback \c f, if \ref abort_requested() is \c false. Otherwise,
127 /// returns a disengaged \ref optimized_optional.
1e59de90
TL
128 template <typename Func>
129 SEASTAR_CONCEPT(requires
130 requires (Func f, const std::optional<std::exception_ptr>& opt_ex) { { f(opt_ex) } noexcept -> std::same_as<void>; }
131 || requires (Func f) { { f() } noexcept -> std::same_as<void>; }
132 )
20effc67 133 [[nodiscard]]
1e59de90 134 optimized_optional<subscription> subscribe(Func&& f) {
11fdf7f2
TL
135 if (abort_requested()) {
136 return { };
137 }
1e59de90
TL
138 if constexpr (std::is_invocable_v<Func, std::exception_ptr>) {
139 return { subscription(*this, std::forward<Func>(f)) };
140 } else {
141 return { subscription(subscription::naive_cb_tag{}, *this, std::forward<Func>(f)) };
142 }
11fdf7f2
TL
143 }
144
145 /// Requests that the target operation be aborted. Current subscriptions
1e59de90
TL
146 /// are invoked inline with this call with a disengaged optional<std::exception_ptr>,
147 /// and no new ones can be registered.
148 /// Must be called exactly once, otherwise the program will be aborted.
149 void request_abort() noexcept {
150 do_request_abort(std::nullopt);
151 }
152
153 /// Requests that the target operation be aborted with a given \c exception_ptr.
154 /// Current subscriptions are invoked inline with this exception,
155 /// and no new ones can be registered.
156 /// Must be called exactly once, otherwise the program will be aborted.
157 void request_abort_ex(std::exception_ptr ex) noexcept {
158 do_request_abort(std::make_optional(std::move(ex)));
159 }
160
161 /// Requests that the target operation be aborted with a given \c Exception object.
162 /// Current subscriptions are invoked inline with this exception, converted to std::exception_ptr,
163 /// and no new ones can be registered.
164 /// Must be called exactly once, otherwise the program will be aborted.
165 template <typename Exception>
166 void request_abort_ex(Exception&& e) noexcept {
167 do_request_abort(std::make_optional(std::make_exception_ptr(std::forward<Exception>(e))));
11fdf7f2
TL
168 }
169
170 /// Returns whether an abort has been requested.
171 bool abort_requested() const noexcept {
172 return !_subscriptions;
173 }
174
175
f67539c2 176 /// Throws a \ref abort_requested_exception if cancellation has been requested.
11fdf7f2
TL
177 void check() const {
178 if (abort_requested()) {
1e59de90 179 std::rethrow_exception(_ex);
11fdf7f2
TL
180 }
181 }
1e59de90
TL
182
183 /// Returns the default exception type (\ref abort_requested_exception) for this abort source.
184 /// Overridable by derived classes.
185 virtual std::exception_ptr get_default_exception() const noexcept {
186 return make_exception_ptr(abort_requested_exception());
187 }
11fdf7f2
TL
188};
189
190/// @}
191
192}