@@ -118,6 +118,8 @@ using sleep_duration = std::chrono::nanoseconds;
118118export class scheduler
119119{
120120public:
121+ using block_info = std::variant<std::monostate, sleep_duration, context*>;
122+
121123 /* *
122124 * @brief
123125 *
@@ -142,7 +144,7 @@ public:
142144 */
143145 void schedule (context& p_context,
144146 blocked_by p_block_state,
145- std::variant<sleep_duration, context*> p_block_info)
147+ block_info p_block_info) noexcept
146148 {
147149 return do_schedule (p_context, p_block_state, p_block_info);
148150 }
@@ -164,10 +166,9 @@ public:
164166 }
165167
166168private:
167- virtual void do_schedule (
168- context& p_context,
169- blocked_by p_block_state,
170- std::variant<sleep_duration, context*> p_block_info) = 0;
169+ virtual void do_schedule (context& p_context,
170+ blocked_by p_block_state,
171+ block_info p_block_info) noexcept = 0;
171172
172173 virtual std::pmr::memory_resource& do_get_allocator () noexcept = 0;
173174};
@@ -196,25 +197,38 @@ public:
196197 m_stack = { allocator.allocate_object <byte>(p_stack_size), p_stack_size };
197198 }
198199
199- void unblock ()
200+ void unblock () noexcept
200201 {
201- transition_to (blocked_by::nothing, default_timeout );
202+ transition_to (blocked_by::nothing);
202203 }
204+
203205 void unblock_without_notification ()
204206 {
205207 m_state = blocked_by::nothing;
206208 }
207- void block_by_time (sleep_duration p_duration)
209+
210+ std::suspend_always block_by_time (sleep_duration p_duration)
208211 {
209212 transition_to (blocked_by::time, p_duration);
213+ return {};
210214 }
211- void block_by_io (sleep_duration p_duration = default_timeout)
215+
216+ std::suspend_always block_by_io (sleep_duration p_duration = default_timeout)
212217 {
213218 transition_to (blocked_by::io, p_duration);
219+ return {};
214220 }
215- void block_by_sync (sleep_duration p_duration = default_timeout)
221+
222+ std::suspend_always block_by_sync (context* p_blocker)
216223 {
217- transition_to (blocked_by::sync, p_duration);
224+ transition_to (blocked_by::sync, p_blocker);
225+ return {};
226+ }
227+
228+ std::suspend_always block_by_external ()
229+ {
230+ transition_to (blocked_by::external, std::monostate{});
231+ return {};
218232 }
219233
220234 [[nodiscard]] constexpr std::coroutine_handle<> active_handle () const
@@ -224,7 +238,10 @@ public:
224238
225239 [[nodiscard]] auto state () const
226240 {
227- return std::get<1 >(m_state);
241+ if (std::holds_alternative<blocked_by>(m_state)) {
242+ return std::get<blocked_by>(m_state);
243+ }
244+ return blocked_by::nothing;
228245 }
229246
230247 constexpr void active_handle (std::coroutine_handle<> p_active_handle)
@@ -265,10 +282,28 @@ public:
265282
266283 constexpr auto last_allocation_size ()
267284 {
268- return std::get<usize>(m_state);
285+ if (std::holds_alternative<usize>(m_state)) {
286+ return std::get<usize>(m_state);
287+ }
288+ return 0uz;
269289 }
270290
271- void transition_to (blocked_by p_new_state, sleep_duration p_info)
291+ ~context ()
292+ {
293+ using poly_allocator = std::pmr::polymorphic_allocator<byte>;
294+ auto allocator = poly_allocator (&m_scheduler->get_allocator ());
295+ allocator.deallocate_object <byte>(m_stack.data (), m_stack.size ());
296+ };
297+
298+ private:
299+ friend class promise_base ;
300+ template <typename T>
301+ friend class future_promise_type ;
302+
303+ using context_state = std::variant<usize, blocked_by, std::exception_ptr>;
304+
305+ void transition_to (blocked_by p_new_state,
306+ scheduler::block_info p_info = std::monostate{}) noexcept
272307 {
273308 m_state = p_new_state;
274309 m_scheduler->schedule (*this , p_new_state, p_info);
@@ -296,17 +331,6 @@ public:
296331 m_state = p_exception;
297332 }
298333
299- ~context ()
300- {
301- using poly_allocator = std::pmr::polymorphic_allocator<byte>;
302- auto allocator = poly_allocator (&m_scheduler->get_allocator ());
303- allocator.deallocate_object <byte>(m_stack.data (), m_stack.size ());
304- };
305-
306- private:
307- friend class promise_base ;
308- using context_state = std::variant<usize, blocked_by, std::exception_ptr>;
309-
310334 // Should stay within a standard cache-line of 64 bytes (8 words)
311335 mem::strong_ptr<scheduler> m_scheduler; // word 1-2
312336 std::coroutine_handle<> m_active_handle = std::noop_coroutine(); // word 3
@@ -315,8 +339,82 @@ private:
315339 context_state m_state{ 0uz }; // word 7-8
316340};
317341
318- static_assert (sizeof (context) <=
319- std::hardware_constructive_interference_size * 2 );
342+ export class context_token
343+ {
344+ public:
345+ constexpr context_token () = default;
346+ constexpr context_token (context& p_capture) noexcept
347+ : m_context_address(std::bit_cast<std::uintptr_t >(&p_capture))
348+ {
349+ }
350+ constexpr context_token& operator =(context& p_capture) noexcept
351+ {
352+ m_context_address = std::bit_cast<std::uintptr_t >(&p_capture);
353+ return *this ;
354+ }
355+ constexpr context_token& operator =(nullptr_t ) noexcept
356+ {
357+ m_context_address = 0U ;
358+ return *this ;
359+ }
360+
361+ constexpr context_token (context_token const & p_capture) noexcept = default;
362+ constexpr context_token& operator =(context_token const & p_capture) noexcept =
363+ default ;
364+ constexpr context_token (context_token&& p_capture) noexcept = default;
365+ constexpr context_token& operator =(context_token& p_capture) noexcept =
366+ default ;
367+
368+ constexpr bool operator ==(context& p_context) noexcept
369+ {
370+ return m_context_address == std::bit_cast<std::uintptr_t >(&p_context);
371+ }
372+
373+ [[nodiscard]] constexpr bool in_use () const noexcept
374+ {
375+ return m_context_address != 0U ;
376+ }
377+
378+ [[nodiscard]] auto address () const noexcept
379+ {
380+ return m_context_address != 0U ;
381+ }
382+
383+ [[nodiscard]] constexpr operator bool () const noexcept
384+ {
385+ return in_use ();
386+ }
387+
388+ constexpr void lease (context& p_capture) noexcept
389+ {
390+ m_context_address = std::bit_cast<std::uintptr_t >(&p_capture);
391+ }
392+
393+ constexpr std::suspend_always set_as_block_by_sync (context& p_capture)
394+ {
395+ if (in_use ()) {
396+ auto * address = std::bit_cast<void *>(m_context_address);
397+ auto * inner_context = static_cast <context*>(address);
398+ p_capture.block_by_sync (inner_context);
399+ }
400+ return {};
401+ }
402+
403+ constexpr void unblock_and_clear () noexcept
404+ {
405+ if (in_use ()) {
406+ auto * address = std::bit_cast<void *>(m_context_address);
407+ auto * inner_context = static_cast <context*>(address);
408+ inner_context->unblock ();
409+ m_context_address = 0U ;
410+ }
411+ }
412+
413+ private:
414+ std::uintptr_t m_context_address = 0U ;
415+ };
416+
417+ static_assert (sizeof (context) <= std::hardware_constructive_interference_size);
320418
321419// =============================================================================
322420//
@@ -398,8 +496,7 @@ public:
398496 constexpr auto await_transform (
399497 std::chrono::duration<Rep, Ratio> p_sleep_duration) noexcept
400498 {
401- m_context->block_by_time (p_sleep_duration);
402- return std::suspend_always{};
499+ return m_context->block_by_time (p_sleep_duration);
403500 }
404501
405502 constexpr auto await_transform (pop_active_coroutine) noexcept
@@ -624,8 +721,7 @@ public:
624721
625722 constexpr void resume () const
626723 {
627- auto active = handle ().promise ().get_context ().active_handle ();
628- active.resume ();
724+ handle ().promise ().get_context ().active_handle ().resume ();
629725 }
630726
631727 /* *
0 commit comments