]> git.proxmox.com Git - ceph.git/blob - ceph/src/seastar/include/seastar/core/abort_source.hh
import quincy beta 17.1.0
[ceph.git] / ceph / src / seastar / include / seastar / core / 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/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
32 namespace bi = boost::intrusive;
33
34 namespace 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.
41 class abort_requested_exception : public std::exception {
42 public:
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().
51 class abort_source {
52 using subscription_callback_type = noncopyable_function<void() noexcept>;
53
54 public:
55 /// Represents a handle to the callback registered by a given fiber. Ending the
56 /// lifetime of the \c subscription will unregister the callback, if it hasn't
57 /// been invoked yet.
58 class subscription : public bi::list_base_hook<bi::link_mode<bi::auto_unlink>> {
59 friend class abort_source;
60
61 subscription_callback_type _target;
62
63 explicit subscription(abort_source& as, subscription_callback_type target)
64 : _target(std::move(target)) {
65 as._subscriptions->push_back(*this);
66 }
67
68 void on_abort() {
69 _target();
70 }
71
72 public:
73 subscription() = default;
74
75 subscription(subscription&& other) noexcept(std::is_nothrow_move_constructible<subscription_callback_type>::value)
76 : _target(std::move(other._target)) {
77 subscription_list_type::node_algorithms::swap_nodes(other.this_ptr(), this_ptr());
78 }
79
80 subscription& operator=(subscription&& other) noexcept(std::is_nothrow_move_assignable<subscription_callback_type>::value) {
81 if (this != &other) {
82 _target = std::move(other._target);
83 if (is_linked()) {
84 subscription_list_type::node_algorithms::unlink(this_ptr());
85 }
86 subscription_list_type::node_algorithms::swap_nodes(other.this_ptr(), this_ptr());
87 }
88 return *this;
89 }
90
91 explicit operator bool() const noexcept {
92 return is_linked();
93 }
94 };
95
96 private:
97 using subscription_list_type = bi::list<subscription, bi::constant_time_size<false>>;
98 std::optional<subscription_list_type> _subscriptions = subscription_list_type();
99
100 public:
101 /// Delays the invocation of the callback \c f until \ref request_abort() is called.
102 /// \returns an engaged \ref optimized_optional containing a \ref subscription that can be used to control
103 /// the lifetime of the callback \c f, if \ref abort_requested() is \c false. Otherwise,
104 /// returns a disengaged \ref optimized_optional.
105 [[nodiscard]]
106 optimized_optional<subscription> subscribe(subscription_callback_type f) noexcept(std::is_nothrow_move_constructible<subscription_callback_type>::value) {
107 if (abort_requested()) {
108 return { };
109 }
110 return { subscription(*this, std::move(f)) };
111 }
112
113 /// Requests that the target operation be aborted. Current subscriptions
114 /// are invoked inline with this call, and no new ones can be registered.
115 void request_abort() {
116 _subscriptions->clear_and_dispose([] (subscription* s) { s->on_abort(); });
117 _subscriptions = { };
118 }
119
120 /// Returns whether an abort has been requested.
121 bool abort_requested() const noexcept {
122 return !_subscriptions;
123 }
124
125
126 /// Throws a \ref abort_requested_exception if cancellation has been requested.
127 void check() const {
128 if (abort_requested()) {
129 throw abort_requested_exception();
130 }
131 }
132 };
133
134 /// @}
135
136 }