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