mirror of
https://gitlab.com/niansa/libcrosscoro.git
synced 2025-03-06 20:53:32 +01:00
106 lines
3.4 KiB
C++
106 lines
3.4 KiB
C++
#pragma once
|
|
|
|
#include <atomic>
|
|
#include <coroutine>
|
|
|
|
namespace coro
|
|
{
|
|
/**
|
|
* Event is a manully triggered thread safe signal that can be co_await()'ed by multiple awaiters.
|
|
* Each awaiter should co_await the event and upon the event being set each awaiter will have their
|
|
* coroutine resumed.
|
|
*
|
|
* The event can be manually reset to the un-set state to be re-used.
|
|
* \code
|
|
t1: coro::event e;
|
|
...
|
|
t2: func(coro::event& e) { ... co_await e; ... }
|
|
...
|
|
t1: do_work();
|
|
t1: e.set();
|
|
...
|
|
t2: resume()
|
|
* \endcode
|
|
*/
|
|
class event
|
|
{
|
|
public:
|
|
/**
|
|
* Creates an event with the given initial state of being set or not set.
|
|
* @param initially_set By default all events start as not set, but if needed this parameter can
|
|
* set the event to already be triggered.
|
|
*/
|
|
explicit event(bool initially_set = false) noexcept;
|
|
~event() = default;
|
|
|
|
event(const event&) = delete;
|
|
event(event&&) = delete;
|
|
auto operator=(const event&) -> event& = delete;
|
|
auto operator=(event &&) -> event& = delete;
|
|
|
|
/**
|
|
* @return True if this event is currently in the set state.
|
|
*/
|
|
auto is_set() const noexcept -> bool { return m_state.load(std::memory_order_acquire) == this; }
|
|
|
|
/**
|
|
* Sets this event and resumes all awaiters.
|
|
*/
|
|
auto set() noexcept -> void;
|
|
|
|
struct awaiter
|
|
{
|
|
/**
|
|
* @param e The event to wait for it to be set.
|
|
*/
|
|
awaiter(const event& e) noexcept : m_event(e) {}
|
|
|
|
/**
|
|
* @return True if the event is already set, otherwise false to suspend this coroutine.
|
|
*/
|
|
auto await_ready() const noexcept -> bool { return m_event.is_set(); }
|
|
|
|
/**
|
|
* Adds this coroutine to the list of awaiters in a thread safe fashion. If the event
|
|
* is set while attempting to add this coroutine to the awaiters then this will return false
|
|
* to resume execution immediately.
|
|
* @return False if the event is already set, otherwise true to suspend this coroutine.
|
|
*/
|
|
auto await_suspend(std::coroutine_handle<> awaiting_coroutine) noexcept -> bool;
|
|
|
|
/**
|
|
* Nothing to do on resume.
|
|
*/
|
|
auto await_resume() noexcept {}
|
|
|
|
/// Refernce to the event that this awaiter is waiting on.
|
|
const event& m_event;
|
|
/// The awaiting continuation coroutine handle.
|
|
std::coroutine_handle<> m_awaiting_coroutine;
|
|
/// The next awaiter in line for this event, nullptr if this is the end.
|
|
awaiter* m_next{nullptr};
|
|
};
|
|
|
|
/**
|
|
* @return An awaiter struct to suspend and resume this coroutine for when the event is set.
|
|
*/
|
|
auto operator co_await() const noexcept -> awaiter { return awaiter(*this); }
|
|
|
|
/**
|
|
* Resets the event from set to not set so it can be re-used. If the event is not currently
|
|
* set then this function has no effect.
|
|
*/
|
|
auto reset() noexcept -> void;
|
|
|
|
protected:
|
|
/// For access to m_state.
|
|
friend struct awaiter;
|
|
/// The state of the event, nullptr is not set with zero awaiters. Set to an awaiter* there are
|
|
/// coroutines awaiting the event to be set, and set to this the event has triggered.
|
|
/// 1) nullptr == not set
|
|
/// 2) awaiter* == linked list of awaiters waiting for the event to trigger.
|
|
/// 3) this == The event is triggered and all awaiters are resumed.
|
|
mutable std::atomic<void*> m_state;
|
|
};
|
|
|
|
} // namespace coro
|