|
| 1 | +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- |
| 2 | +// vim: ts=8 sw=2 smarttab ft=cpp |
| 3 | +/* |
| 4 | + * Ceph - scalable distributed file system |
| 5 | + */ |
| 6 | + |
| 7 | +// Copyright: 2025 Contributors to the Ceph Project |
| 8 | +// Based on boost/asio/redirect_error.hpp and |
| 9 | +// boost/asio/impl/redirect_error.hpp which are |
| 10 | +// Copyright (c) 2003-2024 Christopher M. Kohlhoff (chris at kohlhoff dot com) |
| 11 | +// |
| 12 | +// Distributed under the Boost Software License, Version 1.0. (See |
| 13 | +// accompanying copy at http://www.boost.org/LICENSE_1_0.txt) |
| 14 | + |
| 15 | +#pragma once |
| 16 | + |
| 17 | +#include <type_traits> |
| 18 | + |
| 19 | +#include <boost/asio/associated_executor.hpp> |
| 20 | +#include <boost/asio/async_result.hpp> |
| 21 | +#include <boost/asio/default_completion_token.hpp> |
| 22 | +#include <boost/asio/disposition.hpp> |
| 23 | +#include <boost/asio/handler_continuation_hook.hpp> |
| 24 | + |
| 25 | +/// \file common/async/redirect_error.h |
| 26 | +/// |
| 27 | +/// \brief `redirect_error` that knows about `dispositions`. |
| 28 | +/// |
| 29 | +/// Asio has a very useful concept called a `disposition` that |
| 30 | +/// generalizes the notion of an error code. Unfortunately |
| 31 | +/// `redirect_error` doesn't know about dispositions, making it less |
| 32 | +/// useful. |
| 33 | + |
| 34 | +namespace ceph::async { |
| 35 | + |
| 36 | +/// A @ref completion_token adapter used to specify that an error |
| 37 | +/// produced by an asynchronous operation is captured to a specified |
| 38 | +/// variable. The variable must be of a `disposition` type. |
| 39 | +/** |
| 40 | + * The redirect_error_t class is used to indicate that any disposition produced |
| 41 | + * by an asynchronous operation is captured to a specified variable. |
| 42 | + */ |
| 43 | +template<typename CompletionToken, |
| 44 | + boost::asio::disposition Disposition> |
| 45 | +class redirect_error_t { |
| 46 | +public: // Sigh |
| 47 | + CompletionToken token; |
| 48 | + Disposition& disposition; |
| 49 | + |
| 50 | + template<typename CT> |
| 51 | + redirect_error_t(CT&& token, Disposition& disposition) |
| 52 | + : token(std::forward<CT>(token)), disposition(disposition) {} |
| 53 | +}; |
| 54 | + |
| 55 | +/// A function object type that adapts a @ref completion_token to capture |
| 56 | +/// disposition values to a variable. |
| 57 | +/** |
| 58 | + * May also be used directly as a completion token, in which case it adapts the |
| 59 | + * asynchronous operation's default completion token (or boost::asio::deferred |
| 60 | + * if no default is available). |
| 61 | + */ |
| 62 | +template<boost::asio::disposition Disposition> |
| 63 | +class partial_redirect_error { |
| 64 | +public: |
| 65 | + Disposition& disposition; |
| 66 | + |
| 67 | + /// Constructor that specifies the variable used to capture disposition values. |
| 68 | + explicit partial_redirect_error(Disposition& disposition) |
| 69 | + : disposition(disposition) {} |
| 70 | + |
| 71 | + /// Adapt a @ref completion_token to specify that the completion handler |
| 72 | + /// should capture disposition values to a variable. |
| 73 | + template<typename CompletionToken> |
| 74 | + [[nodiscard]] constexpr inline auto |
| 75 | + operator ()(CompletionToken&& token) const { |
| 76 | + return redirect_error_t<std::decay_t<CompletionToken>, Disposition>{ |
| 77 | + std::forward<CompletionToken>(token), disposition}; |
| 78 | + } |
| 79 | +}; |
| 80 | + |
| 81 | +/// Create a partial completion token adapter that captures disposition values |
| 82 | +/// to a variable. |
| 83 | +template<boost::asio::disposition Disposition> |
| 84 | +[[nodiscard]] inline auto redirect_error(Disposition& d) |
| 85 | +{ |
| 86 | + return partial_redirect_error<std::decay_t<Disposition>>{d}; |
| 87 | +} |
| 88 | + |
| 89 | +/// Adapt a @ref completion_token to capture disposition values to a variable. |
| 90 | +template<typename CompletionToken, boost::asio::disposition Disposition> |
| 91 | +[[nodiscard]] inline auto redirect_error(CompletionToken&& token, |
| 92 | + Disposition& d) |
| 93 | +{ |
| 94 | + return redirect_error_t<std::decay_t<CompletionToken>, |
| 95 | + std::decay_t<Disposition>>{ |
| 96 | + std::forward<CompletionToken>(token), d}; |
| 97 | +} |
| 98 | + |
| 99 | +namespace detail { |
| 100 | +template<typename Handler, boost::asio::disposition Disposition> |
| 101 | +class redirect_error_handler { |
| 102 | +public: |
| 103 | + Disposition& disposition; |
| 104 | + // Essentially a call-once function, invoked as an rvalue. |
| 105 | + Handler handler; |
| 106 | + |
| 107 | + using result_type = void; |
| 108 | + template<typename CompletionToken> |
| 109 | + redirect_error_handler( |
| 110 | + redirect_error_t<std::decay_t<CompletionToken>, |
| 111 | + std::decay_t<Disposition>> re) |
| 112 | + : disposition(re.disposition), handler(std::move(re.token)) {} |
| 113 | + |
| 114 | + template<typename RedirectedHandler> |
| 115 | + redirect_error_handler(Disposition &disposition, RedirectedHandler&& handler) |
| 116 | + : disposition(disposition), |
| 117 | + handler(std::forward<RedirectedHandler>(handler)) {} |
| 118 | + |
| 119 | + |
| 120 | + void operator ()() { |
| 121 | + std::move(handler)(); |
| 122 | + } |
| 123 | + |
| 124 | + template<typename Arg0, typename ...Args> |
| 125 | + std::enable_if_t<!std::is_same_v<std::decay_t<Arg0>, |
| 126 | + Disposition>> |
| 127 | + operator ()(Arg0&& arg0, Args ...args) { |
| 128 | + std::move(handler)(std::forward<Arg0>(arg0), std::forward<Args>(args)...); |
| 129 | + } |
| 130 | + |
| 131 | + template<typename... Args> |
| 132 | + void operator ()(const Disposition& d, Args&& ...args) { |
| 133 | + disposition = d; |
| 134 | + std::move(handler)(std::forward<Args>(args)...); |
| 135 | + } |
| 136 | +}; |
| 137 | + |
| 138 | +template<boost::asio::disposition Disposition, typename Handler> |
| 139 | +inline bool asio_handler_is_continuation( |
| 140 | + redirect_error_handler<Disposition, Handler>* this_handler) |
| 141 | +{ |
| 142 | + using boost::asio::asio_handler_is_continuation; |
| 143 | + return asio_handler_is_continuation(&this_handler->handler); |
| 144 | +} |
| 145 | + |
| 146 | +template<typename Signature> |
| 147 | +struct redirect_error_signature |
| 148 | +{ |
| 149 | + using type = Signature; |
| 150 | +}; |
| 151 | + |
| 152 | +template<typename R, boost::asio::disposition Disposition, typename... Args> |
| 153 | +struct redirect_error_signature<R(Disposition, Args...)> |
| 154 | +{ |
| 155 | + typedef R type(Args...); |
| 156 | +}; |
| 157 | + |
| 158 | +template<typename R, boost::asio::disposition Disposition, typename... Args> |
| 159 | +struct redirect_error_signature<R(const Disposition&, Args...)> |
| 160 | +{ |
| 161 | + typedef R type(Args...); |
| 162 | +}; |
| 163 | + |
| 164 | +template<typename R, boost::asio::disposition Disposition, typename... Args> |
| 165 | +struct redirect_error_signature<R(Disposition, Args...) &> |
| 166 | +{ |
| 167 | + typedef R type(Args...) &; |
| 168 | +}; |
| 169 | + |
| 170 | +template<typename R, boost::asio::disposition Disposition, typename... Args> |
| 171 | +struct redirect_error_signature<R(const Disposition&, Args...) &> |
| 172 | +{ |
| 173 | + typedef R type(Args...) &; |
| 174 | +}; |
| 175 | + |
| 176 | +template<typename R, boost::asio::disposition Disposition, typename... Args> |
| 177 | +struct redirect_error_signature<R(Disposition, Args...) &&> |
| 178 | +{ |
| 179 | + typedef R type(Args...) &&; |
| 180 | +}; |
| 181 | + |
| 182 | +template<typename R, boost::asio::disposition Disposition, typename... Args> |
| 183 | +struct redirect_error_signature<R(const Disposition&, Args...) &&> |
| 184 | +{ |
| 185 | + typedef R type(Args...) &&; |
| 186 | +}; |
| 187 | + |
| 188 | +template<typename R, boost::asio::disposition Disposition, typename... Args> |
| 189 | +struct redirect_error_signature<R(Disposition, Args...) noexcept> |
| 190 | +{ |
| 191 | + typedef R type(Args...) & noexcept; |
| 192 | +}; |
| 193 | + |
| 194 | +template<typename R, boost::asio::disposition Disposition, typename... Args> |
| 195 | +struct redirect_error_signature<R(const Disposition&, Args...) noexcept> |
| 196 | +{ |
| 197 | + typedef R type(Args...) & noexcept; |
| 198 | +}; |
| 199 | + |
| 200 | +template<typename R, boost::asio::disposition Disposition, typename... Args> |
| 201 | +struct redirect_error_signature<R(Disposition, Args...) & noexcept> |
| 202 | +{ |
| 203 | + typedef R type(Args...) & noexcept; |
| 204 | +}; |
| 205 | + |
| 206 | +template <typename R, boost::asio::disposition Disposition, typename... Args> |
| 207 | +struct redirect_error_signature<R(const Disposition&, Args...) & noexcept> |
| 208 | +{ |
| 209 | + typedef R type(Args...) & noexcept; |
| 210 | +}; |
| 211 | + |
| 212 | +template<typename R, boost::asio::disposition Disposition, typename... Args> |
| 213 | +struct redirect_error_signature<R(Disposition, Args...) && noexcept> |
| 214 | +{ |
| 215 | + typedef R type(Args...) && noexcept; |
| 216 | +}; |
| 217 | + |
| 218 | +template <typename R, boost::asio::disposition Disposition, typename... Args> |
| 219 | +struct redirect_error_signature<R(const Disposition&, Args...) && noexcept> |
| 220 | +{ |
| 221 | + typedef R type(Args...) && noexcept; |
| 222 | +}; |
| 223 | + |
| 224 | +template<typename Initiation, typename = void> |
| 225 | +class initiation_base : public Initiation |
| 226 | +{ |
| 227 | +public: |
| 228 | + template<typename I> |
| 229 | + explicit initiation_base(I&& initiation) |
| 230 | + : Initiation(std::forward<I>(initiation)) {} |
| 231 | +}; |
| 232 | + |
| 233 | +template<typename Initiation> |
| 234 | +class initiation_base<Initiation, |
| 235 | + std::enable_if_t<!std::is_class_v<Initiation>>> |
| 236 | +{ |
| 237 | +public: |
| 238 | + template<typename I> |
| 239 | + explicit initiation_base(I&& initiation) |
| 240 | + : initiation(std::forward<I>(initiation)) {} |
| 241 | + |
| 242 | + template<typename... Args> |
| 243 | + void operator()(Args&&... args) const |
| 244 | + { |
| 245 | + initiation(std::forward<Args>(args)...); |
| 246 | + } |
| 247 | + |
| 248 | +private: |
| 249 | + Initiation initiation; |
| 250 | +}; |
| 251 | +} // namespace detail |
| 252 | +} // namespace ceph::async |
| 253 | + |
| 254 | +namespace boost::asio { |
| 255 | + |
| 256 | +template<boost::asio::disposition Disposition, typename CompletionToken, |
| 257 | + typename Signature> |
| 258 | +struct async_result<::ceph::async::redirect_error_t<CompletionToken, |
| 259 | + Disposition>, Signature> |
| 260 | + : async_result<CompletionToken, |
| 261 | + typename ::ceph::async::detail::redirect_error_signature< |
| 262 | + Signature>::type> |
| 263 | +{ |
| 264 | + template<typename Initiation> |
| 265 | + struct init_wrapper : ::ceph::async::detail::initiation_base<Initiation> |
| 266 | + { |
| 267 | + using ::ceph::async::detail::initiation_base<Initiation>::initiation_base; |
| 268 | + |
| 269 | + template<typename Handler, typename... Args> |
| 270 | + void operator ()(Handler&& handler, Disposition* d, Args&&... args) && |
| 271 | + { |
| 272 | + static_cast<Initiation&&>(*this)( |
| 273 | + ::ceph::async::detail::redirect_error_handler< |
| 274 | + decay_t<Handler>, Disposition>( |
| 275 | + *d, std::forward<Handler>(handler)), |
| 276 | + std::forward<Args>(args)...); |
| 277 | + } |
| 278 | + |
| 279 | + template<typename Handler, typename... Args> |
| 280 | + void operator ()(Handler&& handler, Disposition* d, Args&&... args) const & |
| 281 | + { |
| 282 | + static_cast<const Initiation&>(*this)( |
| 283 | + ::ceph::async::detail::redirect_error_handler< |
| 284 | + decay_t<Handler>, Disposition>( |
| 285 | + *d, std::forward<Handler>(handler)), |
| 286 | + std::forward<Args>(args)...); |
| 287 | + } |
| 288 | + }; |
| 289 | + |
| 290 | + template<typename Initiation, typename RawCompletionToken, typename... Args> |
| 291 | + static auto initiate(Initiation&& initiation, |
| 292 | + RawCompletionToken&& token, Args&&... args) |
| 293 | + { |
| 294 | + return async_initiate< |
| 295 | + std::conditional_t< |
| 296 | + std::is_const_v<remove_reference_t<RawCompletionToken>>, |
| 297 | + const CompletionToken, CompletionToken>, |
| 298 | + typename ::ceph::async::detail::redirect_error_signature<Signature>::type>( |
| 299 | + init_wrapper<std::decay_t<Initiation>>( |
| 300 | + std::forward<Initiation>(initiation)), |
| 301 | + token.token, &token.disposition, std::forward<Args>(args)...); |
| 302 | + } |
| 303 | +}; |
| 304 | + |
| 305 | +template<template<typename, typename> class Associator, typename Handler, |
| 306 | + typename DefaultCandidate, typename Disposition> |
| 307 | +struct associator<Associator, |
| 308 | + ::ceph::async::detail::redirect_error_handler<Handler, |
| 309 | + Disposition>, |
| 310 | + DefaultCandidate> |
| 311 | + : Associator<Handler, DefaultCandidate> |
| 312 | +{ |
| 313 | + static auto get(const ::ceph::async::detail::redirect_error_handler< |
| 314 | + Handler, Disposition>& h) noexcept |
| 315 | + { |
| 316 | + return Associator<Handler, DefaultCandidate>::get(h.handler); |
| 317 | + } |
| 318 | + |
| 319 | + static auto get(const ::ceph::async::detail::redirect_error_handler< |
| 320 | + Handler, Disposition>& h, |
| 321 | + const DefaultCandidate& c) noexcept |
| 322 | + { |
| 323 | + return Associator<Handler, DefaultCandidate>::get(h.handler, c); |
| 324 | + } |
| 325 | +}; |
| 326 | + |
| 327 | +template<boost::asio::disposition Disposition, typename... Signatures> |
| 328 | +struct async_result<::ceph::async::partial_redirect_error<Disposition>, |
| 329 | + Signatures...> |
| 330 | +{ |
| 331 | + template <typename Initiation, typename RawCompletionToken, typename... Args> |
| 332 | + static auto initiate(Initiation&& initiation, |
| 333 | + RawCompletionToken&& token, Args&&... args) |
| 334 | + { |
| 335 | + return async_initiate<Signatures...>( |
| 336 | + std::forward<Initiation>(initiation), |
| 337 | + ::ceph::async::redirect_error_t< |
| 338 | + default_completion_token_t<associated_executor_t<Initiation>>, |
| 339 | + Disposition>( |
| 340 | + default_completion_token_t<associated_executor_t<Initiation>>{}, |
| 341 | + token.disposition), std::forward<Args>(args)...); |
| 342 | + } |
| 343 | +}; |
| 344 | +} // namespace boost::asio |
0 commit comments