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 +
    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 +
    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 +
    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