Event System
Usage Notes
Events::post is for raw event objects.
Use Events::invoke to execute a callback in the target event loop, including thread-to-thread callback messages.
Use Events::invokeAfter for fire-and-forget delayed callbacks.
Use EventTimer when the caller needs to keep a cancellation object for one-shot or repeated scheduled work.
Keep the returned EventTimer pointer for as long as the scheduled work shall remain active.
Use currentEvents() from domain event editors to attach to the event loop currently running on the thread.
EventLoopDriver provides the single native wait and wake path and can be injected for tests or custom reactors.
Use ManagedEventThread for application-owned worker event loops and UnmanagedEventThread for standalone worker
event loops.
Event Thread Lifecycle
Event threads are one-shot objects.
After start() succeeds, isStarted() remains true even after the worker terminates and is joined.
isRunning() only observes whether the event loop is currently executing and is not a synchronization barrier for
startup or completion.
quit() is an idempotent asynchronous request.
It can be issued before startup and lets events queued before the quit request run before the loop terminates.
join() is only a wait operation: it never requests termination, is a no-op before startup or after a previous join,
and must not be called from the event thread itself.
It does not transport exceptions from the worker thread; an exception that escapes the event loop terminates the
process.
Destroying an event thread requests termination and joins a started worker. Therefore, the final owning pointer must be released from another thread, never from the event thread itself.
Source-Owned Event Editors
An EventSource owns its handlers and one stable EventEditor.
Call the source’s events() method on its owner loop and use the typed on...() methods to replace handlers.
Passing an empty callback clears the corresponding handler.
The returned reference is borrowed and remains valid only while the source remains alive.
Returning a reference makes the editor’s management role explicit: it is neither a callback subscription nor an
independently retained object.
It also keeps fluent setup natural, for example lookup->events().onResolved(...).onError(...).
An implementation can construct its editor together with the source or lazily on the first events() call, but every
call returns the same editor.
EventEditor::source() and EventEditor::target() form the small common interface needed by generic code that
works with different editor types.
source() returns a shared pointer so such code can deliberately keep the source alive.
The common implementation stores that source weakly to avoid a source/editor ownership cycle and treats an expired
source as an internal logic error.
target() retains the event collection that dispatches the callbacks.
Most application code does not need either accessor and works directly with the typed editor methods.
The name events() also appears on Application and EventThread, where it returns an EventsPtr event-loop
target.
Those classes are not event sources, so there is no editor involved and the existing name keeps its distinct meaning.
Observer Subscriptions
Events that naturally have multiple independent observers use add...() methods.
Each registration returns a move-only EventSubscription.
Retain that handle for as long as the callback shall remain active; destroying or cancelling it prevents future callback
invocations.
A callback that has already started may finish.
An event offers either a single replaceable on...() handler or multiple add...() subscriptions.
It does not combine both semantics for the same notification.
Interface
-
EventsPtr erbsland::event::currentEvents()
Access the events interface for the event loop currently running on this thread.
- Throws:
err::LogicError – If the current thread is not running an event loop.
- Returns:
The events interface for the loop bound to the current thread.
-
class Event
A single event.
Public Functions
-
inline explicit Event(const EventId identifier) noexcept
Create an event of a given type.
- Parameters:
identifier – The event identifier.
-
inline Event(const EventId identifier, EventDataUniquePtr data) noexcept
Create an event of a given type with optional data.
- Parameters:
identifier – The event identifier.
data – Optional event data.
-
inline const EventDataUniquePtr &data() const noexcept
Optional event data.
-
inline explicit Event(const EventId identifier) noexcept
-
class EventBackend
A backend for event loops.
Backends provide external event sources such as timers, files or network IO.
Subclassed by erbsland::event::impl::SchedulerBackend, erbsland::network::impl::NetworkBackend
Public Functions
-
virtual EventBackendId backendId() const noexcept = 0
Get the unique backend identifier.
-
virtual void attach(EventBackendTargetWeakPtr target, EventLoopDriverWeakPtr driver) = 0
Attach this backend to a backend target.
- Parameters:
target – The target for events generated by this backend.
driver – The native wait and wake driver for attached event sources.
-
virtual void poll(time::TimePoint now) = 0
Poll the backend for due events.
- Parameters:
now – The current time point used by the loop for this poll cycle.
-
virtual EventBackendId backendId() const noexcept = 0
-
class EventBackendId
Represents an event backend type.
Public Types
-
using Value = uint32_t
The underlying value type.
-
using Value = uint32_t
-
class EventBackendIdInfo
Information about a backend id for diagnostics.
-
class EventBackendTarget
A target interface for event backends.
Subclassed by erbsland::event::impl::EventLoop
-
using erbsland::event::EventCallback = std::function<void()>
A callback that is executed by an event loop.
-
class EventData
The base class for event data.
If an event has additional data, it is stored as a subclass of this class.
Subclassed by erbsland::event::impl::CallbackEventData, erbsland::event::impl::TimerEventData
-
class EventEditor
Common interface for editing handlers owned by an event source.
Sources own their editors and return them by reference. The source and target accessors exist primarily for generic code that works with heterogeneous editors; regular code uses the typed
on...()methods.Subclassed by erbsland::event::impl::CommonEventEditor
Public Functions
-
virtual ~EventEditor()
Destroy the event editor.
-
virtual EventSourcePtr source() const = 0
Retain and access the source that owns this editor.
- Throws:
err::LogicError – If the editor cannot provide its source because of an internal lifetime error.
- Returns:
The owning event source.
-
virtual EventsPtr target() const noexcept = 0
Retain and access the target on which callbacks are executed.
- Returns:
The callback target.
-
virtual ~EventEditor()
-
class EventId
Represents a type of event.
Public Types
-
using Value = uint32_t
The underlying value type.
-
using Value = uint32_t
-
class EventIdInfo
Information about an event id for diagnostics.
-
class EventLoop : public erbsland::event::Events
An event loop.
Each event loop owns its queue and backend registry.
Subclassed by erbsland::event::impl::EventLoop
Public Functions
-
virtual void run() = 0
Run this event loop until stopped.
-
virtual bool runOnce() = 0
Run one event-loop cycle without a maximum wait time.
- Returns:
trueif an event was processed.
-
virtual bool runOnce(time::TimeDelta maximumWait) = 0
Run one event-loop cycle with a maximum wait time.
- Parameters:
maximumWait – The maximum time to wait for an event.
- Returns:
trueif an event was processed.
-
virtual std::size_t runUntilIdle() = 0
Run event-loop cycles until there is no immediate work left.
- Returns:
The number of processed events.
-
virtual void stop() noexcept = 0
Stop this event loop.
-
virtual void quit() noexcept = 0
Quit this event loop by posting a quit event.
-
virtual bool isRunning() const noexcept = 0
Test if this event loop is running.
-
virtual bool isQuitRequested() const noexcept = 0
Test if this event loop was requested to quit.
-
virtual bool hasError() const noexcept = 0
Test if a callback exception was captured.
-
virtual std::exception_ptr takeError() noexcept = 0
Take the oldest captured callback exception.
-
virtual void setErrorHandler(EventLoopErrorHandler handler) = 0
Set the error handler for callback and backend exceptions.
- Parameters:
handler – The handler to call after capturing an exception.
-
virtual void registerBackend(EventBackendPtr backend) = 0
Register a backend.
- Parameters:
backend – The backend to register.
Public Static Functions
-
static EventLoopPtr create()
Create an event loop with the default backend.
- Returns:
The new event loop.
-
static EventLoopPtr create(EventBackendPtr backend)
Create an event loop with one initial backend.
- Parameters:
backend – The backend to register.
- Returns:
The new event loop.
-
static EventLoopPtr create(EventLoopDriverPtr driver)
Create an event loop with a custom native driver.
- Parameters:
driver – The non-null driver to use.
- Returns:
The new event loop.
-
virtual void run() = 0
-
class EventLoopDriver
The native wait and wake driver for an event loop.
A driver is called only by its owning event loop, except for
wake(), which is thread-safe. Native event sources use platform-specific implementation interfaces derived from this class.Subclassed by erbsland::event::impl::EpollEventLoopDriver, erbsland::event::impl::KqueueEventLoopDriver, erbsland::event::impl::WindowsEventLoopDriver
Public Functions
-
virtual void wait() = 0
Wait until the driver is woken or a native source is ready.
-
virtual void wait(time::TimeDelta maximumWait) = 0
Wait until the driver is woken, a native source is ready, or the maximum wait elapsed.
A zero or negative duration only polls immediately available native events.
- Parameters:
maximumWait – The maximum wait duration.
-
virtual void wake() noexcept = 0
Wake the driver.
This method is thread-safe.
Public Static Functions
-
static EventLoopDriverPtr createDefault()
Create the default driver for the current platform.
- Returns:
A kqueue, epoll, or IOCP based driver.
-
virtual void wait() = 0
-
enum class erbsland::event::EventLoopErrorAction : uint8_t
The action to take after an event-loop error.
Values:
-
enumerator Continue
Continue the event loop.
-
enumerator Stop
Stop this event loop.
-
enumerator Continue
-
using erbsland::event::EventLoopErrorHandler = std::function<EventLoopErrorAction(std::exception_ptr error)>
A callback that decides how an event loop reacts to an exception.
-
class EventRegistry
The registry to manage event and backend ids.
Public Functions
-
explicit EventRegistry(PrivateTag)
Create the event registry.
-
EventId registerEvent(text::String name, text::String description = {})
Register a custom event type.
- Parameters:
name – Must be a reverse domain name. E.g.
com.example.myapp.MyEvent. Valid characters are-_.a-zA-Z0-9. The maximum length is 200 code-points.description – A description of the event type. The maximum length is 2000 code-points.
-
bool isRegistered(EventId identifier) const noexcept
Test if an event is registered.
- Parameters:
identifier – The event identifier.
-
bool isRegistered(const text::String &name) const noexcept
Test if an event is registered.
- Parameters:
name – The event name.
-
EventId getEventId(const text::String &name) const noexcept
Get the event identifier from a given ID.
- Parameters:
name – The event name.
- Returns:
The event identifier or
noEvent()if the event is not registered.
-
EventIdInfo getEventInfo(EventId identifier) const noexcept
Access event information for a given ID.
- Parameters:
identifier – The event identifier.
- Returns:
The event information or empty event information if the event is not registered.
-
EventBackendId registerBackend(text::String name, text::String description = {})
Register a custom backend type.
- Parameters:
name – Must be a reverse domain name. E.g.
com.example.myapp.MyBackend. Valid characters are-_.a-zA-Z0-9. The maximum length is 200 code-points.description – A description of the backend type. The maximum length is 2000 code-points.
-
bool isRegistered(EventBackendId identifier) const noexcept
Test if a backend is registered.
- Parameters:
identifier – The backend identifier.
-
bool isBackendRegistered(const text::String &name) const noexcept
Test if a backend is registered.
- Parameters:
name – The backend name.
-
EventBackendId getBackendId(const text::String &name) const noexcept
Get the backend identifier from a given ID.
- Parameters:
name – The backend name.
- Returns:
The backend identifier or
noBackend()if the backend is not registered.
-
EventBackendIdInfo getBackendInfo(EventBackendId identifier) const noexcept
Access backend information for a given ID.
- Parameters:
identifier – The backend identifier.
- Returns:
The backend information or empty backend information if the backend is not registered.
Public Static Functions
-
static inline constexpr EventId noEvent() noexcept
Get the no-event identifier.
This is an alias for
EventId{}for making code more readable.
-
static inline constexpr EventBackendId noBackend() noexcept
Get the no-backend identifier.
This is an alias for
EventBackendId{}for making code more readable.
-
static inline constexpr EventBackendId schedulerBackend() noexcept
Get the scheduler backend identifier.
-
static inline constexpr EventBackendId networkBackend() noexcept
Get the network backend identifier.
-
explicit EventRegistry(PrivateTag)
-
class Events
A target for thread-safe event posting and callback invocation.
Subclassed by erbsland::event::EventLoop
Public Functions
-
virtual void post(Event event) = 0
Post an event to this target.
- Parameters:
event – The event to post.
-
virtual void invoke(EventCallback callback) = 0
Invoke a callback on this target.
- Parameters:
callback – The callback to execute on the target event loop.
-
virtual void invokeAfter(time::TimeDelta delay, EventCallback callback) = 0
Invoke a callback on this target after a delay.
A zero or negative delay queues the callback like
invoke().- Parameters:
delay – The delay before queuing the callback.
callback – The callback to execute on the target event loop.
-
template<typename T>
inline T &get() Access a backend frontend interface.
- Template Parameters:
T – The public backend frontend interface.
- Throws:
err::ParameterError – If the backend is not available or has the wrong type.
- Returns:
The requested backend frontend.
-
inline EventTimerPtr createTimer(EventCallback callback)
Create an inactive timer for this target.
- Parameters:
callback – The callback to execute when the timer fires.
-
virtual void post(Event event) = 0
-
class EventScheduler
The public scheduler frontend for an events interface.
Subclassed by erbsland::event::impl::SchedulerBackend
Public Functions
-
virtual void invokeAfter(time::TimeDelta delay, EventCallback callback) = 0
Invoke a callback after a delay.
A zero or negative delay queues the callback immediately.
- Parameters:
delay – The delay before queuing the callback.
callback – The callback to execute on the target event loop.
-
virtual EventTimerPtr createTimer(EventCallback callback) = 0
Create an inactive timer.
- Parameters:
callback – The callback to execute when the timer fires.
Public Static Functions
-
static inline constexpr EventBackendId backendId() noexcept
The backend identifier for scheduler implementations.
-
virtual void invokeAfter(time::TimeDelta delay, EventCallback callback) = 0
-
class EventSource : public std::enable_shared_from_this<EventSource>
Base class for shared event sources owned by one event loop.
Each concrete source owns one stable event editor and exposes it through
events(). Returning a reference makes the editor’s non-owning role explicit, whileEventEditor::source()lets generic code retain the source if needed.Subclassed by erbsland::network::Connection, erbsland::network::HostLookup, erbsland::network::HttpClientRequest, erbsland::network::HttpClientResponse, erbsland::network::HttpClientSession, erbsland::network::HttpServer, erbsland::network::HttpServerRequest, erbsland::network::HttpServerSession, erbsland::network::TcpListener, erbsland::network::UdpSocket
Public Functions
-
const EventsPtr &ownerEvents() const noexcept
Access the event loop that owns this source.
-
virtual EventEditor &events() = 0
Access the stable editor owned by this source.
- Throws:
err::LogicError – If called outside the owner event loop.
- Returns:
The source-owned editor.
-
const EventsPtr &ownerEvents() const noexcept
-
class EventSubscription
A move-only lifetime handle for one registered event callback.
Destroying or cancelling this handle prevents future callback invocations. A callback that already started may finish. Destroying the publisher automatically invalidates the subscription.
Public Functions
-
EventSubscription() = default
Create an inactive subscription.
-
inline ~EventSubscription()
Cancel this subscription.
-
inline void cancel() noexcept
Cancel this subscription.
-
inline bool isActive() const noexcept
Test whether this subscription is still active.
-
inline explicit EventSubscription(impl::EventSubscriptionControlPtr control) noexcept
Create a subscription from its internal control.
- Parameters:
control – The control owned by this subscription.
-
EventSubscription() = default
-
class EventThread
A one-shot thread that owns and runs one event loop.
start()can succeed only once.quit()requests graceful termination, whilejoin()only waits and never requests termination itself. Concrete implementations stop and join a started worker before destruction. The final owning pointer must not be released from the event thread itself.Subclassed by erbsland::event::ManagedEventThread, erbsland::event::UnmanagedEventThread
Public Functions
-
virtual void start() = 0
Start the event loop on a new worker thread.
The call returns after creating the worker; the event loop may not yet report itself as running. After a successful call,
isStarted()remainstruefor the lifetime of this object.- Throws:
err::LogicError – If this thread was started before, including after it was joined.
std::system_error – If the native worker thread cannot be created.
-
virtual void quit() noexcept = 0
Request graceful termination of the event loop.
This operation is idempotent, returns without waiting, and can be called before
start(). Events queued before the quit request are processed before the loop terminates.
-
virtual void join() = 0
Wait until the thread has finished.
This operation does not request termination. It blocks while the worker is running, returns immediately if the thread was never started or was already joined, and leaves
isStarted()unchanged. Exceptions escaping the worker are not transported through this call and terminate the process instead.- Throws:
err::LogicError – If called from this event thread.
std::system_error – If the native join operation fails.
-
virtual bool isStarted() const noexcept = 0
Test whether
start()completed successfully at least once.- Returns:
trueafter the one successful start, including after the worker terminated and was joined.
-
virtual bool isRunning() const noexcept = 0
Test whether the event loop is currently running on the worker.
- Returns:
trueonly while the event loop is executing; this is not a start or completion barrier.
-
virtual EventLoop &eventLoop() noexcept = 0
Access the event loop owned by this thread.
- Returns:
A reference that remains valid for the lifetime of this thread object.
-
virtual EventsPtr events() noexcept = 0
Access the event-target interface of the owned loop.
- Returns:
A shared event target suitable for posting work to this thread.
-
virtual void start() = 0
-
class EventTimer
A thread-safe timer for scheduled event callbacks.
Subclassed by erbsland::event::impl::EventTimer
Public Functions
-
virtual void startOnce(time::TimeDelta delay) = 0
Start this timer once after
delay.A zero or negative delay schedules the timer for immediate execution.
- Parameters:
delay – The delay before executing the callback.
-
virtual void startFixedDelay(time::TimeDelta interval) = 0
Start this timer repeatedly with a fixed delay after each callback finishes.
- Parameters:
interval – The positive interval between callback completion and the next execution.
- Throws:
err::ParameterError – If
intervalis zero or negative.
-
virtual void startFixedRate(time::TimeDelta interval) = 0
Start this timer repeatedly with a fixed rate.
Missed ticks are skipped and never queued as catch-up bursts.
- Parameters:
interval – The positive interval between scheduled callback times.
- Throws:
err::ParameterError – If
intervalis zero or negative.
-
virtual void stop() noexcept = 0
Stop this timer.
-
virtual bool isActive() const noexcept = 0
Test if this timer is active or has a callback queued for execution.
-
virtual EventTimerMode mode() const noexcept = 0
Get the current timer mode.
-
virtual void startOnce(time::TimeDelta delay) = 0
-
enum class erbsland::event::EventTimerMode : uint8_t
The current scheduling mode of an event timer.
Values:
-
enumerator Inactive
The timer is inactive.
-
enumerator Once
The timer runs once.
-
enumerator FixedDelay
The timer repeats with a delay after each callback.
-
enumerator FixedRate
The timer repeats on a fixed cadence.
-
enumerator Inactive
-
class ManagedEventThread : public erbsland::event::EventThread
An application-managed thread that runs an event loop.
Managed event threads are created by
core::Application::createEventThread()and are quit together with the application event loop.Subclassed by erbsland::event::impl::ManagedEventThread
-
class UnmanagedEventThread : public erbsland::event::EventThread
A standalone thread that runs an event loop.
Unmanaged event threads are not registered with
core::Applicationand are not quit by application shutdown.Subclassed by erbsland::event::impl::UnmanagedEventThread
Public Static Functions
-
static UnmanagedEventThreadPtr create()
Create an unmanaged event thread.
-
static UnmanagedEventThreadPtr create()