Line data Source code
1 : //
2 : // Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot com)
3 : //
4 : // Distributed under the Boost Software License, Version 1.0. (See accompanying
5 : // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6 : //
7 : // Official repository: https://github.com/cppalliance/beast2
8 : //
9 :
10 : #ifndef BOOST_BEAST2_WRAP_EXECUTOR_HPP
11 : #define BOOST_BEAST2_WRAP_EXECUTOR_HPP
12 :
13 : #include <boost/beast2/detail/config.hpp>
14 : #include <boost/capy/executor.hpp>
15 : #include <boost/asio/associated_allocator.hpp>
16 : #include <boost/asio/post.hpp>
17 : #include <cstddef>
18 : #include <memory>
19 : #include <new>
20 : #include <type_traits>
21 : #include <utility>
22 :
23 : namespace boost {
24 : namespace beast2 {
25 :
26 : namespace detail {
27 :
28 : // Metadata stored before each work allocation
29 : struct work_metadata
30 : {
31 : void* raw;
32 : std::size_t total_size;
33 : };
34 :
35 : template<class AsioExecutor>
36 : struct asio_executor_impl
37 : {
38 : friend struct capy::executor::access;
39 :
40 : using allocator_type = typename
41 : asio::associated_allocator<AsioExecutor>::type;
42 :
43 : AsioExecutor exec_;
44 : allocator_type alloc_;
45 :
46 : explicit
47 21 : asio_executor_impl(AsioExecutor ex)
48 21 : : exec_(std::move(ex))
49 21 : , alloc_(asio::get_associated_allocator(exec_))
50 : {
51 21 : }
52 :
53 : // Move constructor
54 39 : asio_executor_impl(asio_executor_impl&& other) noexcept
55 39 : : exec_(std::move(other.exec_))
56 39 : , alloc_(std::move(other.alloc_))
57 : {
58 39 : }
59 :
60 : // Move assignment
61 : asio_executor_impl& operator=(asio_executor_impl&& other) noexcept
62 : {
63 : if(this != &other)
64 : {
65 : exec_ = std::move(other.exec_);
66 : alloc_ = std::move(other.alloc_);
67 : }
68 : return *this;
69 : }
70 :
71 : // Delete copy operations
72 : asio_executor_impl(asio_executor_impl const&) = delete;
73 : asio_executor_impl& operator=(asio_executor_impl const&) = delete;
74 :
75 : private:
76 : void*
77 123 : allocate(std::size_t size, std::size_t align)
78 : {
79 : // Rebind allocator to char for byte-level allocation
80 : using char_alloc = typename std::allocator_traits<
81 : allocator_type>::template rebind_alloc<char>;
82 123 : char_alloc a(alloc_);
83 :
84 : // We need space for:
85 : // - metadata struct (aligned to work_metadata alignment)
86 : // - padding for work alignment
87 : // - work object
88 123 : std::size_t const meta_size = sizeof(work_metadata);
89 123 : std::size_t const total_size = meta_size + align + size;
90 :
91 : // Allocate raw storage
92 123 : char* raw = std::allocator_traits<char_alloc>::allocate(a, total_size);
93 :
94 : // Compute aligned pointer for work after metadata
95 123 : char* after_meta = raw + meta_size;
96 123 : void* aligned = after_meta;
97 123 : std::size_t space = total_size - meta_size;
98 123 : aligned = std::align(align, size, aligned, space);
99 :
100 : // Store metadata immediately before the aligned work region
101 123 : work_metadata* meta = reinterpret_cast<work_metadata*>(
102 : static_cast<char*>(aligned) - sizeof(work_metadata));
103 123 : meta->raw = raw;
104 123 : meta->total_size = total_size;
105 :
106 245 : return aligned;
107 1 : }
108 :
109 : void
110 0 : deallocate(void* p, std::size_t /*size*/, std::size_t /*align*/)
111 : {
112 : using char_alloc = typename std::allocator_traits<
113 : allocator_type>::template rebind_alloc<char>;
114 0 : char_alloc a(alloc_);
115 :
116 : // Retrieve metadata stored before p
117 0 : work_metadata* meta = reinterpret_cast<work_metadata*>(
118 : static_cast<char*>(p) - sizeof(work_metadata));
119 :
120 0 : std::allocator_traits<char_alloc>::deallocate(
121 0 : a, static_cast<char*>(meta->raw), meta->total_size);
122 0 : }
123 :
124 : void
125 123 : submit(capy::executor::work* w)
126 : {
127 : // Capture a copy of allocator for the lambda
128 1 : allocator_type alloc_copy = alloc_;
129 123 : asio::post(exec_,
130 247 : [w, alloc_copy]() mutable
131 : {
132 : using char_alloc = typename std::allocator_traits<
133 : allocator_type>::template rebind_alloc<char>;
134 123 : char_alloc a(alloc_copy);
135 :
136 : // Retrieve metadata stored before w
137 123 : work_metadata* meta = reinterpret_cast<work_metadata*>(
138 : reinterpret_cast<char*>(w) - sizeof(work_metadata));
139 123 : void* raw = meta->raw;
140 123 : std::size_t total_size = meta->total_size;
141 :
142 123 : w->invoke();
143 123 : w->~work();
144 :
145 1 : std::allocator_traits<char_alloc>::deallocate(
146 : a, static_cast<char*>(raw), total_size);
147 1 : });
148 123 : }
149 : };
150 :
151 : } // detail
152 :
153 : /** Return a capy::executor from an Asio executor.
154 :
155 : This function wraps an Asio executor in a capy::executor,
156 : mapping the capy::executor implementation API to the
157 : corresponding Asio executor operations.
158 :
159 : The returned executor uses get_associated_allocator on
160 : the Asio executor to obtain the allocator for work items.
161 :
162 : @param ex The Asio executor to wrap.
163 :
164 : @return A capy::executor that submits work via the
165 : provided Asio executor.
166 : */
167 : template<class AsioExecutor>
168 : capy::executor
169 19 : wrap_executor(AsioExecutor ex)
170 : {
171 : return capy::executor::wrap(
172 38 : detail::asio_executor_impl<
173 : typename std::decay<AsioExecutor>::type>(
174 57 : std::move(ex)));
175 : }
176 :
177 : } // beast2
178 : } // boost
179 :
180 : #endif
181 :
|