1  
//
1  
//
2  
// Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
2  
// Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
3  
// Copyright (c) 2026 Steve Gerbino
3  
// Copyright (c) 2026 Steve Gerbino
4  
//
4  
//
5  
// Distributed under the Boost Software License, Version 1.0. (See accompanying
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)
6  
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7  
//
7  
//
8  
// Official repository: https://github.com/cppalliance/corosio
8  
// Official repository: https://github.com/cppalliance/corosio
9  
//
9  
//
10  

10  

11  
#ifndef BOOST_COROSIO_DETAIL_SCHEDULER_OP_HPP
11  
#ifndef BOOST_COROSIO_DETAIL_SCHEDULER_OP_HPP
12  
#define BOOST_COROSIO_DETAIL_SCHEDULER_OP_HPP
12  
#define BOOST_COROSIO_DETAIL_SCHEDULER_OP_HPP
13  

13  

14  
#include <boost/corosio/detail/config.hpp>
14  
#include <boost/corosio/detail/config.hpp>
15  
#include <boost/corosio/detail/intrusive.hpp>
15  
#include <boost/corosio/detail/intrusive.hpp>
16  

16  

17  
#include <cstddef>
17  
#include <cstddef>
18  
#include <cstdint>
18  
#include <cstdint>
19  
#include <utility>
19  
#include <utility>
20  

20  

21  
namespace boost::corosio::detail {
21  
namespace boost::corosio::detail {
22  

22  

23  
/** Base class for completion handlers using function pointer dispatch.
23  
/** Base class for completion handlers using function pointer dispatch.
24  

24  

25  
    Handlers are continuations that execute after an asynchronous
25  
    Handlers are continuations that execute after an asynchronous
26  
    operation completes. They can be queued for deferred invocation,
26  
    operation completes. They can be queued for deferred invocation,
27  
    allowing callbacks and coroutine resumptions to be posted to an
27  
    allowing callbacks and coroutine resumptions to be posted to an
28  
    executor.
28  
    executor.
29  

29  

30  
    This class uses a function pointer instead of virtual dispatch
30  
    This class uses a function pointer instead of virtual dispatch
31  
    to minimize overhead in the completion path. Each derived class
31  
    to minimize overhead in the completion path. Each derived class
32  
    provides a static completion function that handles both normal
32  
    provides a static completion function that handles both normal
33  
    invocation and destruction.
33  
    invocation and destruction.
34  

34  

35  
    @par Function Pointer Convention
35  
    @par Function Pointer Convention
36  

36  

37  
    The func_type signature is:
37  
    The func_type signature is:
38  
    @code
38  
    @code
39  
    void(*)(void* owner, scheduler_op* op, std::uint32_t bytes, std::uint32_t error);
39  
    void(*)(void* owner, scheduler_op* op, std::uint32_t bytes, std::uint32_t error);
40  
    @endcode
40  
    @endcode
41  

41  

42  
    - When owner != nullptr: Normal completion. Process the operation.
42  
    - When owner != nullptr: Normal completion. Process the operation.
43  
    - When owner == nullptr: Destroy mode. Clean up without invoking.
43  
    - When owner == nullptr: Destroy mode. Clean up without invoking.
44  

44  

45  
    @par Ownership Contract
45  
    @par Ownership Contract
46  

46  

47  
    Callers must invoke exactly ONE of `complete()` or `destroy()`,
47  
    Callers must invoke exactly ONE of `complete()` or `destroy()`,
48  
    never both.
48  
    never both.
49  

49  

50  
    @see scheduler_op_queue
50  
    @see scheduler_op_queue
51  
*/
51  
*/
52  
class scheduler_op : public intrusive_queue<scheduler_op>::node
52  
class scheduler_op : public intrusive_queue<scheduler_op>::node
53  
{
53  
{
54  
public:
54  
public:
55  
    /** Function pointer type for completion handling.
55  
    /** Function pointer type for completion handling.
56  

56  

57  
        @param owner Pointer to the scheduler (nullptr for destroy).
57  
        @param owner Pointer to the scheduler (nullptr for destroy).
58  
        @param op The operation to complete or destroy.
58  
        @param op The operation to complete or destroy.
59  
        @param bytes Bytes transferred (for I/O operations).
59  
        @param bytes Bytes transferred (for I/O operations).
60  
        @param error Error code from the operation.
60  
        @param error Error code from the operation.
61  
    */
61  
    */
62  
    using func_type = void (*)(
62  
    using func_type = void (*)(
63  
        void* owner,
63  
        void* owner,
64  
        scheduler_op* op,
64  
        scheduler_op* op,
65  
        std::uint32_t bytes,
65  
        std::uint32_t bytes,
66  
        std::uint32_t error);
66  
        std::uint32_t error);
67  

67  

68  
    /** Complete the operation via function pointer (IOCP path).
68  
    /** Complete the operation via function pointer (IOCP path).
69  

69  

70  
        @param owner Pointer to the owning scheduler.
70  
        @param owner Pointer to the owning scheduler.
71  
        @param bytes Bytes transferred.
71  
        @param bytes Bytes transferred.
72  
        @param error Error code.
72  
        @param error Error code.
73  
    */
73  
    */
74  
    void complete(void* owner, std::uint32_t bytes, std::uint32_t error)
74  
    void complete(void* owner, std::uint32_t bytes, std::uint32_t error)
75  
    {
75  
    {
76  
        func_(owner, this, bytes, error);
76  
        func_(owner, this, bytes, error);
77  
    }
77  
    }
78  

78  

79  
    /** Invoke the handler (epoll/select path).
79  
    /** Invoke the handler (epoll/select path).
80  

80  

81  
        Override in derived classes to handle operation completion.
81  
        Override in derived classes to handle operation completion.
82  
        Default implementation does nothing.
82  
        Default implementation does nothing.
83  
    */
83  
    */
84  
    virtual void operator()() {}
84  
    virtual void operator()() {}
85  

85  

86  
    /** Destroy without invoking the handler.
86  
    /** Destroy without invoking the handler.
87  

87  

88  
        Called during shutdown or when discarding queued operations.
88  
        Called during shutdown or when discarding queued operations.
89  
        Override in derived classes if cleanup is needed.
89  
        Override in derived classes if cleanup is needed.
90  
        Default implementation calls through func_ if set.
90  
        Default implementation calls through func_ if set.
91  
    */
91  
    */
92  
    virtual void destroy()
92  
    virtual void destroy()
93  
    {
93  
    {
94  
        if (func_)
94  
        if (func_)
95  
            func_(nullptr, this, 0, 0);
95  
            func_(nullptr, this, 0, 0);
96  
    }
96  
    }
97  

97  

98  
    virtual ~scheduler_op() = default;
98  
    virtual ~scheduler_op() = default;
99  

99  

100  
protected:
100  
protected:
101  
    /** Default constructor for derived classes using virtual dispatch.
101  
    /** Default constructor for derived classes using virtual dispatch.
102  

102  

103  
        Used by epoll/select backends that override operator() and destroy().
103  
        Used by epoll/select backends that override operator() and destroy().
104  
    */
104  
    */
105  
    scheduler_op() noexcept : func_(nullptr) {}
105  
    scheduler_op() noexcept : func_(nullptr) {}
106  

106  

107  
    /** Construct with completion function for function pointer dispatch.
107  
    /** Construct with completion function for function pointer dispatch.
108  

108  

109  
        Used by IOCP backend for non-virtual completion.
109  
        Used by IOCP backend for non-virtual completion.
110  

110  

111  
        @param func The static function to call for completion/destruction.
111  
        @param func The static function to call for completion/destruction.
112  
    */
112  
    */
113  
    explicit scheduler_op(func_type func) noexcept : func_(func) {}
113  
    explicit scheduler_op(func_type func) noexcept : func_(func) {}
114  

114  

115  
    func_type func_;
115  
    func_type func_;
116  

116  

117  
    // Pad to 32 bytes so derived structs (descriptor_state, epoll_op)
117  
    // Pad to 32 bytes so derived structs (descriptor_state, epoll_op)
118  
    // keep hot fields on optimal cache line boundaries
118  
    // keep hot fields on optimal cache line boundaries
119  
    std::byte reserved_[sizeof(void*)] = {};
119  
    std::byte reserved_[sizeof(void*)] = {};
120  
};
120  
};
121  

121  

122  
using op_queue = intrusive_queue<scheduler_op>;
122  
using op_queue = intrusive_queue<scheduler_op>;
123  

123  

124  
/** An intrusive FIFO queue of scheduler_ops.
124  
/** An intrusive FIFO queue of scheduler_ops.
125  

125  

126  
    This queue stores scheduler_ops using an intrusive linked list,
126  
    This queue stores scheduler_ops using an intrusive linked list,
127  
    avoiding additional allocations for queue nodes. Scheduler_ops
127  
    avoiding additional allocations for queue nodes. Scheduler_ops
128  
    are popped in the order they were pushed (first-in, first-out).
128  
    are popped in the order they were pushed (first-in, first-out).
129  

129  

130  
    The destructor calls `destroy()` on any remaining scheduler_ops.
130  
    The destructor calls `destroy()` on any remaining scheduler_ops.
131  

131  

132  
    @note This is not thread-safe. External synchronization is
132  
    @note This is not thread-safe. External synchronization is
133  
    required for concurrent access.
133  
    required for concurrent access.
134  

134  

135  
    @see scheduler_op
135  
    @see scheduler_op
136  
*/
136  
*/
137  
class scheduler_op_queue
137  
class scheduler_op_queue
138  
{
138  
{
139  
    op_queue q_;
139  
    op_queue q_;
140  

140  

141  
public:
141  
public:
142  
    scheduler_op_queue() = default;
142  
    scheduler_op_queue() = default;
143  

143  

144  
    scheduler_op_queue(scheduler_op_queue&& other) noexcept
144  
    scheduler_op_queue(scheduler_op_queue&& other) noexcept
145  
        : q_(std::move(other.q_))
145  
        : q_(std::move(other.q_))
146  
    {
146  
    {
147  
    }
147  
    }
148  

148  

149  
    scheduler_op_queue(scheduler_op_queue const&)            = delete;
149  
    scheduler_op_queue(scheduler_op_queue const&)            = delete;
150  
    scheduler_op_queue& operator=(scheduler_op_queue const&) = delete;
150  
    scheduler_op_queue& operator=(scheduler_op_queue const&) = delete;
151  
    scheduler_op_queue& operator=(scheduler_op_queue&&)      = delete;
151  
    scheduler_op_queue& operator=(scheduler_op_queue&&)      = delete;
152  

152  

153  
    ~scheduler_op_queue()
153  
    ~scheduler_op_queue()
154  
    {
154  
    {
155  
        while (auto* h = q_.pop())
155  
        while (auto* h = q_.pop())
156  
            h->destroy();
156  
            h->destroy();
157  
    }
157  
    }
158  

158  

159  
    bool empty() const noexcept
159  
    bool empty() const noexcept
160  
    {
160  
    {
161  
        return q_.empty();
161  
        return q_.empty();
162  
    }
162  
    }
163  
    void push(scheduler_op* h) noexcept
163  
    void push(scheduler_op* h) noexcept
164  
    {
164  
    {
165  
        q_.push(h);
165  
        q_.push(h);
166  
    }
166  
    }
167  
    void push(scheduler_op_queue& other) noexcept
167  
    void push(scheduler_op_queue& other) noexcept
168  
    {
168  
    {
169  
        q_.splice(other.q_);
169  
        q_.splice(other.q_);
170  
    }
170  
    }
171  
    scheduler_op* pop() noexcept
171  
    scheduler_op* pop() noexcept
172  
    {
172  
    {
173  
        return q_.pop();
173  
        return q_.pop();
174  
    }
174  
    }
175  
};
175  
};
176  

176  

177  
} // namespace boost::corosio::detail
177  
} // namespace boost::corosio::detail
178  

178  

179  
#endif
179  
#endif