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/concepts.hh>
25 #include <seastar/util/modules.hh>
26 #include <seastar/util/noncopyable_function.hh>
27 #include <seastar/util/optimized_optional.hh>
28 #include <seastar/util/std-compat.hh>
29 
30 #ifndef SEASTAR_MODULE
31 #include <boost/intrusive/list.hpp>
32 #include <exception>
33 #include <optional>
34 #include <type_traits>
35 #include <utility>
36 #endif
37 
38 namespace bi = boost::intrusive;
39 
40 namespace seastar {
41 
42 SEASTAR_MODULE_EXPORT_BEGIN
43 
46 
49 class abort_requested_exception : public std::exception {
50 public:
51  virtual const char* what() const noexcept override {
52  return "abort requested";
53  }
54 };
55 
59 class abort_source {
60  using subscription_callback_type = noncopyable_function<void (const std::optional<std::exception_ptr>&) noexcept>;
62 
63 public:
67  class subscription : public bi::list_base_hook<bi::link_mode<bi::auto_unlink>> {
68  friend class abort_source;
69 
71 
73  : _target(std::move(target)) {
74  as._subscriptions.push_back(*this);
75  }
76 
77  struct naive_cb_tag {}; // to disambiguate constructors
78  explicit subscription(naive_cb_tag, abort_source& as, naive_subscription_callback_type naive_cb)
79  : _target([cb = std::move(naive_cb)] (const std::optional<std::exception_ptr>&) noexcept { cb(); }) {
80  as._subscriptions.push_back(*this);
81  }
82 
83  void on_abort(const std::optional<std::exception_ptr>& ex) noexcept {
84  _target(ex);
85  }
86 
87  public:
88  subscription() = default;
89 
90  subscription(subscription&& other) noexcept(std::is_nothrow_move_constructible_v<subscription_callback_type>)
91  : _target(std::move(other._target)) {
92  subscription_list_type::node_algorithms::swap_nodes(other.this_ptr(), this_ptr());
93  }
94 
95  subscription& operator=(subscription&& other) noexcept(std::is_nothrow_move_assignable_v<subscription_callback_type>) {
96  if (this != &other) {
97  _target = std::move(other._target);
98  unlink();
99  subscription_list_type::node_algorithms::swap_nodes(other.this_ptr(), this_ptr());
100  }
101  return *this;
102  }
103 
104  explicit operator bool() const noexcept {
105  return is_linked();
106  }
107  };
108 
109 private:
110  using subscription_list_type = bi::list<subscription, bi::constant_time_size<false>>;
111  subscription_list_type _subscriptions;
112  std::exception_ptr _ex;
113 
114  void do_request_abort(std::optional<std::exception_ptr> ex) noexcept {
115  if (_ex) {
116  return;
117  }
118  _ex = ex.value_or(get_default_exception());
119  assert(_ex);
120  auto subs = std::move(_subscriptions);
121  while (!subs.empty()) {
122  subscription& s = subs.front();
123  s.unlink();
124  s.on_abort(ex);
125  }
126  }
127 
128 public:
129  abort_source() = default;
130  virtual ~abort_source() = default;
131 
132  abort_source(abort_source&&) = default;
133  abort_source& operator=(abort_source&&) = default;
134 
139  template <typename Func>
140  SEASTAR_CONCEPT(
141  requires (std::is_nothrow_invocable_r_v<void, Func, const std::optional<std::exception_ptr>&> ||
142  std::is_nothrow_invocable_r_v<void, Func>))
143  [[nodiscard]]
145  if (abort_requested()) {
146  return { };
147  }
148  if constexpr (std::is_invocable_v<Func, std::exception_ptr>) {
149  return { subscription(*this, std::forward<Func>(f)) };
150  } else {
151  return { subscription(subscription::naive_cb_tag{}, *this, std::forward<Func>(f)) };
152  }
153  }
154 
158  void request_abort() noexcept {
159  do_request_abort(std::nullopt);
160  }
161 
165  void request_abort_ex(std::exception_ptr ex) noexcept {
166  do_request_abort(std::make_optional(std::move(ex)));
167  }
168 
172  template <typename Exception>
173  void request_abort_ex(Exception&& e) noexcept {
174  do_request_abort(std::make_optional(std::make_exception_ptr(std::forward<Exception>(e))));
175  }
176 
178  bool abort_requested() const noexcept {
179  return bool(_ex);
180  }
181 
182 
184  void check() const {
185  if (abort_requested()) {
186  std::rethrow_exception(_ex);
187  }
188  }
189 
192  virtual std::exception_ptr get_default_exception() const noexcept {
193  return make_exception_ptr(abort_requested_exception());
194  }
195 };
196 
198 
199 SEASTAR_MODULE_EXPORT_END
200 
201 }
Definition: abort_source.hh:49
Definition: abort_source.hh:67
Definition: abort_source.hh:59
virtual std::exception_ptr get_default_exception() const noexcept
Definition: abort_source.hh:192
void check() const
Throws a abort_requested_exception if cancellation has been requested.
Definition: abort_source.hh:184
void request_abort_ex(std::exception_ptr ex) noexcept
Definition: abort_source.hh:165
void request_abort_ex(Exception &&e) noexcept
Definition: abort_source.hh:173
optimized_optional< subscription > subscribe(Func &&f)
Definition: abort_source.hh:144
void request_abort() noexcept
Definition: abort_source.hh:158
bool abort_requested() const noexcept
Returns whether an abort has been requested.
Definition: abort_source.hh:178
Definition: optimized_optional.hh:53
Definition: stream.hh:128
Seastar API namespace.
Definition: abort_on_ebadf.hh:26
Definition: noncopyable_function.hh:37