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 Event(Event &&other) noexcept

Move constructor.

inline Event &operator=(Event &&other) noexcept

Move assignment operator.

inline time::TimePoint time() const noexcept

The event creation time.

inline EventId identifier() const noexcept

The event type.

inline const EventDataUniquePtr &data() const noexcept

Optional event data.

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 bool handleEvent(const Event &event) = 0

Handle an event owned by this backend.

Parameters:

event – The event to handle.

Returns:

true if this backend handled the event.

virtual std::optional<time::TimePoint> nextWakeTime() const = 0

Get the next time point at which this backend wants to be polled.

class EventBackendId

Represents an event backend type.

Public Types

using Value = uint32_t

The underlying value type.

Public Functions

constexpr EventBackendId() noexcept = default

Create a “no backend” type.

inline constexpr bool isValid() const noexcept

Test if this backend type is valid.

inline constexpr Value toRawValue() const noexcept

Get the raw value of this backend type.

class EventBackendIdInfo

Information about a backend id for diagnostics.

Public Functions

inline EventBackendIdInfo(text::String name, text::String description)

Create new backend identifier information.

inline const text::String &name() const noexcept

Get the reverse-DNS-style backend name.

inline const text::String &description() const noexcept

Get the backend description.

class EventBackendTarget

A target interface for event backends.

Subclassed by erbsland::event::impl::EventLoop

Public Functions

virtual void postFromBackend(Event event) = 0

Post an event generated by a backend.

Parameters:

event – The event to post.

virtual void wakeFromBackend() noexcept = 0

Wake this target after a backend state change.

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

Public Functions

inline virtual bool isHandled() const noexcept

Test if this event was handled and should not be propagated further.

inline void setHandled() noexcept

Mark this event as handled and should not be propagated further.

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.

class EventId

Represents a type of event.

Public Types

using Value = uint32_t

The underlying value type.

Public Functions

constexpr EventId() noexcept = default

Create a “no event” type.

inline constexpr bool isValid() const noexcept

Test if this event type is valid.

All types, except “no event” are valid.

inline constexpr Value toRawValue() const noexcept

Get the raw value of this event type.

class EventIdInfo

Information about an event id for diagnostics.

Public Functions

inline EventIdInfo(text::String name, text::String description)

Create new event identifier information.

inline const text::String &name() const noexcept

Get the reverse-DNS-style event name.

inline const text::String &description() const noexcept

Get the event description.

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:

true if 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:

true if 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.

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.

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.

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 EventId quit() noexcept

Get the quit event identifier.

static inline constexpr EventId invocation() noexcept

Get the invocation event identifier.

static inline constexpr EventId timer() noexcept

Get the timer event identifier.

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.

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.

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.

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, while EventEditor::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.

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.

class EventThread

A one-shot thread that owns and runs one event loop.

start() can succeed only once. quit() requests graceful termination, while join() 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() remains true for 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:

true after 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:

true only 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.

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 interval is 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 interval is 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 time::TimeDelta interval() const noexcept = 0

Get the current delay or interval.

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.

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::Application and are not quit by application shutdown.

Subclassed by erbsland::event::impl::UnmanagedEventThread

Public Static Functions

static UnmanagedEventThreadPtr create()

Create an unmanaged event thread.