summaryrefslogtreecommitdiffstats
path: root/src/common/async/forward_handler.h
blob: ae88cc83f46478ed99eb8298ce8f4a6b21b3c075 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
// vim: ts=8 sw=2 smarttab
/*
 * Ceph - scalable distributed file system
 *
 * Copyright (C) 2018 Red Hat
 *
 * This is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License version 2.1, as published by the Free Software
 * Foundation. See file COPYING.
 *
 */

#ifndef CEPH_ASYNC_FORWARD_HANDLER_H
#define CEPH_ASYNC_FORWARD_HANDLER_H

#include <boost/asio.hpp>

namespace ceph::async {

/**
 * A forwarding completion handler for use with boost::asio.
 *
 * A completion handler wrapper that invokes the handler's operator() as an
 * rvalue, regardless of whether the wrapper is invoked as an lvalue or rvalue.
 * This operation is potentially destructive to the wrapped handler, so is only
 * suitable for single-use handlers.
 *
 * This is useful when combined with bind_handler() and move-only arguments,
 * because executors will always call the lvalue overload of operator().
 *
 * The original Handler's associated allocator and executor are maintained.
 *
 * @see forward_handler
 */
template <typename Handler>
struct ForwardingHandler {
  Handler handler;

  ForwardingHandler(Handler&& handler)
    : handler(std::move(handler))
  {}

  template <typename ...Args>
  void operator()(Args&& ...args) {
    std::move(handler)(std::forward<Args>(args)...);
  }

  using allocator_type = boost::asio::associated_allocator_t<Handler>;
  allocator_type get_allocator() const noexcept {
    return boost::asio::get_associated_allocator(handler);
  }
};

} // namespace ceph::async

namespace boost::asio {

// specialize boost::asio::associated_executor<> for ForwardingHandler
template <typename Handler, typename Executor>
struct associated_executor<ceph::async::ForwardingHandler<Handler>, Executor> {
  using type = boost::asio::associated_executor_t<Handler, Executor>;

  static type get(const ceph::async::ForwardingHandler<Handler>& handler,
                  const Executor& ex = Executor()) noexcept {
    return boost::asio::get_associated_executor(handler.handler, ex);
  }
};

} // namespace boost::asio

namespace ceph::async {

/**
 * Returns a single-use completion handler that always forwards on operator().
 *
 * Wraps a completion handler such that it is always invoked as an rvalue. This
 * is necessary when combining executors and bind_handler() with move-only
 * argument types.
 *
 * Example use:
 *
 *   auto callback = [] (std::unique_ptr<int>&& p) {};
 *   auto bound_handler = bind_handler(callback, std::make_unique<int>(5));
 *   auro handler = forward_handler(std::move(bound_handler));
 *
 *   // execute the forwarding handler on an io_context:
 *   boost::asio::io_context context;
 *   boost::asio::post(context, std::move(handler));
 *   context.run();
 *
 * @see ForwardingHandler
 */
template <typename Handler>
auto forward_handler(Handler&& h)
{
  return ForwardingHandler{std::forward<Handler>(h)};
}

} // namespace ceph::async

#endif // CEPH_ASYNC_FORWARD_HANDLER_H