include/boost/corosio/detail/scheduler_op.hpp

37.5% Lines (3/8) 60.0% Functions (3/5)
include/boost/corosio/detail/scheduler_op.hpp
Line Hits Source Code
1 //
2 // Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
3 // Copyright (c) 2026 Steve Gerbino
4 //
5 // Distributed under the Boost Software License, Version 1.0. (See accompanying
6 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7 //
8 // Official repository: https://github.com/cppalliance/corosio
9 //
10
11 #ifndef BOOST_COROSIO_DETAIL_SCHEDULER_OP_HPP
12 #define BOOST_COROSIO_DETAIL_SCHEDULER_OP_HPP
13
14 #include <boost/corosio/detail/config.hpp>
15 #include <boost/corosio/detail/intrusive.hpp>
16
17 #include <cstddef>
18 #include <cstdint>
19 #include <utility>
20
21 namespace boost::corosio::detail {
22
23 /** Base class for completion handlers using function pointer dispatch.
24
25 Handlers are continuations that execute after an asynchronous
26 operation completes. They can be queued for deferred invocation,
27 allowing callbacks and coroutine resumptions to be posted to an
28 executor.
29
30 This class uses a function pointer instead of virtual dispatch
31 to minimize overhead in the completion path. Each derived class
32 provides a static completion function that handles both normal
33 invocation and destruction.
34
35 @par Function Pointer Convention
36
37 The func_type signature is:
38 @code
39 void(*)(void* owner, scheduler_op* op, std::uint32_t bytes, std::uint32_t error);
40 @endcode
41
42 - When owner != nullptr: Normal completion. Process the operation.
43 - When owner == nullptr: Destroy mode. Clean up without invoking.
44
45 @par Ownership Contract
46
47 Callers must invoke exactly ONE of `complete()` or `destroy()`,
48 never both.
49
50 @see scheduler_op_queue
51 */
52 class scheduler_op : public intrusive_queue<scheduler_op>::node
53 {
54 public:
55 /** Function pointer type for completion handling.
56
57 @param owner Pointer to the scheduler (nullptr for destroy).
58 @param op The operation to complete or destroy.
59 @param bytes Bytes transferred (for I/O operations).
60 @param error Error code from the operation.
61 */
62 using func_type = void (*)(
63 void* owner,
64 scheduler_op* op,
65 std::uint32_t bytes,
66 std::uint32_t error);
67
68 /** Complete the operation via function pointer (IOCP path).
69
70 @param owner Pointer to the owning scheduler.
71 @param bytes Bytes transferred.
72 @param error Error code.
73 */
74 void complete(void* owner, std::uint32_t bytes, std::uint32_t error)
75 {
76 func_(owner, this, bytes, error);
77 }
78
79 /** Invoke the handler (epoll/select path).
80
81 Override in derived classes to handle operation completion.
82 Default implementation does nothing.
83 */
84 virtual void operator()() {}
85
86 /** Destroy without invoking the handler.
87
88 Called during shutdown or when discarding queued operations.
89 Override in derived classes if cleanup is needed.
90 Default implementation calls through func_ if set.
91 */
92 virtual void destroy()
93 {
94 if (func_)
95 func_(nullptr, this, 0, 0);
96 }
97
98 96766 virtual ~scheduler_op() = default;
99
100 protected:
101 /** Default constructor for derived classes using virtual dispatch.
102
103 Used by epoll/select backends that override operator() and destroy().
104 */
105 96624 scheduler_op() noexcept : func_(nullptr) {}
106
107 /** Construct with completion function for function pointer dispatch.
108
109 Used by IOCP backend for non-virtual completion.
110
111 @param func The static function to call for completion/destruction.
112 */
113 142 explicit scheduler_op(func_type func) noexcept : func_(func) {}
114
115 func_type func_;
116
117 // Pad to 32 bytes so derived structs (descriptor_state, epoll_op)
118 // keep hot fields on optimal cache line boundaries
119 std::byte reserved_[sizeof(void*)] = {};
120 };
121
122 using op_queue = intrusive_queue<scheduler_op>;
123
124 /** An intrusive FIFO queue of scheduler_ops.
125
126 This queue stores scheduler_ops using an intrusive linked list,
127 avoiding additional allocations for queue nodes. Scheduler_ops
128 are popped in the order they were pushed (first-in, first-out).
129
130 The destructor calls `destroy()` on any remaining scheduler_ops.
131
132 @note This is not thread-safe. External synchronization is
133 required for concurrent access.
134
135 @see scheduler_op
136 */
137 class scheduler_op_queue
138 {
139 op_queue q_;
140
141 public:
142 scheduler_op_queue() = default;
143
144 scheduler_op_queue(scheduler_op_queue&& other) noexcept
145 : q_(std::move(other.q_))
146 {
147 }
148
149 scheduler_op_queue(scheduler_op_queue const&) = delete;
150 scheduler_op_queue& operator=(scheduler_op_queue const&) = delete;
151 scheduler_op_queue& operator=(scheduler_op_queue&&) = delete;
152
153 ~scheduler_op_queue()
154 {
155 while (auto* h = q_.pop())
156 h->destroy();
157 }
158
159 bool empty() const noexcept
160 {
161 return q_.empty();
162 }
163 void push(scheduler_op* h) noexcept
164 {
165 q_.push(h);
166 }
167 void push(scheduler_op_queue& other) noexcept
168 {
169 q_.splice(other.q_);
170 }
171 scheduler_op* pop() noexcept
172 {
173 return q_.pop();
174 }
175 };
176
177 } // namespace boost::corosio::detail
178
179 #endif
180