TLA Line data 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 MIS 0 : 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 0 : virtual void destroy()
93 : {
94 0 : if (func_)
95 0 : func_(nullptr, this, 0, 0);
96 0 : }
97 :
98 HIT 99785 : 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 99643 : 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
|