src/corosio/src/detail/scheduler_op.hpp

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