ddn.hap

Module ddn.hap

Provides DDN Haps: synchronous, in-process, application-level notification primitives.

Haps are small multicast observer containers. A subject owns a hap, observers attach handlers to it, and the subject emits the hap when something meaningful "hap"-pens. (You should understand now why I named it "hap"?)

Types 20

private structRegistrationRecord(Handler)
Fields
size_t id
Handler handler
private classHapState(Handler)
Fields
RegistrationRecord!Handler[] records
size_t nextId
size_t dispatchDepth
bool alive
Methods
size_t activeCount() const @safe nothrow @nogc
void addRecord(size_t id, Handler handler) @safe
void removeRecordById(size_t id) @safe
void removeFirstByHandler(Handler handler) @safe
void removeAll() @safe nothrow
void markDead() @safe nothrow
private void removeAt(size_t index) @safe
structRegistration(Handler)

A registration handle returned by Hap, Hap.register or NothrowHap, NothrowHap.register.

Registration represents exactly one attached handler and provides explicit, idempotent detach. It is non-copyable and move-only.

A plain Registration does not auto-detach in its destructor; automatic cleanup belongs to scoped registration helpers and RegistrationBag.

Parameters

HandlerThe delegate handler type of the source hap.
Fields
HapState!Handler _state
size_t _id
bool _detached
Methods
bool attached() const @safe nothrow @nogcDetermine whether this registration's source hap is alive and this registration has not been detached.
bool detach() @safeDetach this registration from its source hap.
Constructors
this(HapState!Handler state, size_t id)
structScopedRegistration(Handler)

An RAII wrapper that detaches a Registration, registration automatically at scope exit.

ScopedRegistration is non-copyable and safely movable. When a scoped registration is destroyed, it calls detach() on the underlying Registration. Call release() to transfer ownership or disable automatic cleanup; release() returns the underlying Registration by move and leaves the scoped wrapper inert.

Parameters

HandlerThe delegate handler type of the source hap.
Fields
Registration!Handler _reg
Methods
bool attached() const @safe nothrow @nogcDetermine whether this scoped registration's underlying registration is still attached.
bool detach() @safeDetach the underlying registration manually.
Registration!Handler release() @safe nothrowRelease ownership of the underlying registration without detaching.
Constructors
this(Registration!Handler reg)
Destructors
~thisDestroy this scoped registration, detaching the underlying registration if it is still attached.
private structDetachRecord
Fields
bool delegate() @safe detach
private classOneShotState(Handler)
Fields
Handler userHandler
Registration!Handler reg
bool fired
Constructors
this(Handler h)
structOneShotRegistration(Handler)

A handle returned by registerOneShot that can cancel a pending one-shot registration.

OneShotRegistration is non-copyable and move-only. Call cancel() to prevent the one-shot handler from ever firing, or check attached() to see if it is still pending.

Parameters

HandlerThe delegate handler type of the source hap.
Fields
OneShotState!Handler _state
Methods
bool attached() const @safe nothrow @nogcDetermine whether the one-shot handler has not yet fired and has not been cancelled.
bool cancel() @safeCancel the one-shot registration.
Constructors
this(OneShotState!Handler state)
private classHeapRegistration(Handler)
Fields
Registration!Handler reg
Constructors
this(ref Registration!Handler r)

An owner-held container that detaches multiple heterogeneous registrations in bulk.

RegistrationBag stores registrations from any number of Hap or NothrowHap instances, regardless of their argument types, using internal type erasure. Call clear() or let the bag be destroyed to detach all live registrations.

Multiple private bags are expected in real programs: each component, library, plugin, controller, or test fixture that creates registrations should own its own bag. A global or shared bag is not recommended.

RegistrationBag is non-copyable and move-only.

Fields
DetachRecord[] _records
Methods
void add(Handler)(ref Registration!Handler reg) @safeAdd a Registration to this bag.
void add(Handler)(ref ScopedRegistration!Handler scoped) @safe nothrowAdd a ScopedRegistration to this bag.
void register(Args...)(ref Hap!Args hap, void delegate(Args) @safe handler) @safeRegister a handler on a hap and add the resulting registration to this bag.
void register(Args...)(ref Hap!Args hap, void function(Args) @safe handler) @safeditto
void register(Args...)(ref NothrowHap!Args hap, void delegate(Args) @safe nothrow handler) @safe nothrowditto
void register(Args...)(ref NothrowHap!Args hap, void function(Args) @safe nothrow handler) @safe nothrowditto
size_t length() const @safe nothrow @nogcDetermine the number of detach records in this bag.
void clear() @safeDetach all registrations held by this bag.
Destructors
~thisDestroy this bag, detaching all live registrations.
structHap(Args...)

A multicast hap that invokes all attached handlers synchronously.

Hap is the general-purpose, GC-backed variant for ordinary application code. It accepts @safe handlers, supports throwing handlers, and uses snapshot dispatch semantics: mutations made during emit affect later emits, not the handler slice currently being iterated.

Hap is intentionally not thread-safe. Use it as thread-confined owned state, or provide external synchronization when deliberately sharing an owning object.

Parameters

ArgsArgument types passed to attached handlers.
Fields
private HapState!Handler _state
Methods
bool hasHaps() const @safe nothrow @nogcDetermine whether at least one handler is attached.
size_t hapCount() const @safe nothrow @nogcGet the number of currently attached handlers.
void opOpAssign(string op : "~")(Handler handler) @safeAttach a delegate handler with `~=` syntax.
void opOpAssign(string op : "~")(FunctionHandler handler) @safeAttach a free-function handler with `~=` syntax.
void attach(Handler handler) @safeAttach a delegate handler.
void attach(FunctionHandler handler) @safeAttach a free-function handler.
void detach(Handler handler) @safeDetach the first matching delegate handler by delegate identity.
void detach(FunctionHandler handler) @safeDetach the first matching free-function handler.
void detachAll() @safe nothrowDetach all handlers.
Registration!Handler register(Handler handler) @safeRegister a delegate handler and return a detachable Registration.
Registration!Handler register(FunctionHandler handler) @safeRegister a free-function handler and return a detachable registration.
void opCall(Args args) @safeEmit this hap by invoking all handlers captured at the start of dispatch.
void emit(Args args) @safeNamed alternative to `opCall`.
void emitSafe(Args args, scope void delegate(Exception) @safe onError = null) @safeEmit this hap while reporting handler exceptions and continuing dispatch.
void ensureState() @safe nothrow
structNothrowHap(Args...)

A multicast hap restricted to handlers that cannot throw exceptions.

NothrowHap has the same ownership and snapshot semantics as Hap, but its handlers are nothrow, so emit is also nothrow.

Parameters

ArgsArgument types passed to attached handlers.
Fields
private HapState!Handler _state
Methods
bool hasHaps() const @safe nothrow @nogcDetermine whether at least one handler is attached.
size_t hapCount() const @safe nothrow @nogcGet the number of currently attached handlers.
void opOpAssign(string op : "~")(Handler handler) @safeAttach a nothrow delegate handler with `~=` syntax.
void opOpAssign(string op : "~")(FunctionHandler handler) @safeAttach a nothrow free-function handler with `~=` syntax.
void attach(Handler handler) @safeAttach a nothrow delegate handler.
void attach(FunctionHandler handler) @safeAttach a nothrow free-function handler.
void detach(Handler handler) @safeDetach the first matching delegate handler by delegate identity.
void detach(FunctionHandler handler) @safeDetach the first matching free-function handler.
void detachAll() @safe nothrowDetach all handlers.
Registration!Handler register(Handler handler) @safe nothrowRegister a nothrow delegate handler and return a detachable registration.
Registration!Handler register(FunctionHandler handler) @safe nothrowRegister a nothrow free-function handler and return a detachable registration.
void opCall(Args args) @safe nothrow Emit this hap by invoking all nothrow handlers captured at dispatch start. Params: args = Arguments forwarded to each handler.
void emit(Args args) @safe nothrowNamed alternative to `opCall`.
void ensureState() @safe nothrow
structStaticHap(size_t capacity, Args...)

A fixed-capacity multicast hap for @nogc nothrow dispatch paths.

StaticHap stores handlers inline, never allocates, and reports attachment failure when the configured capacity is reached.

StaticHap does not provide register because the registration lifetime model requires heap-backed state that would compromise @nogc dispatch. Use attach and detach for StaticHap handler management.

Parameters

capacityMaximum number of handlers stored by this hap.
ArgsArgument types passed to attached handlers.
Fields
private Handler[capacity] _handlers
private size_t _count
Methods
bool hasHaps() const @safe nothrow @nogcDetermine whether at least one handler is attached.
size_t hapCount() const @safe nothrow @nogcGet the number of currently attached handlers.
bool isFull() const @safe nothrow @nogcDetermine whether the fixed handler storage is full.
bool attach(Handler handler) @safe nothrow @nogcAttach a `@nogc nothrow` delegate handler.
bool attach(FunctionHandler handler) @safe nothrow @nogcAttach a `@nogc nothrow` free-function handler.
void opOpAssign(string op : "~")(Handler handler) @safe nothrow @nogcAttach a `@nogc nothrow` delegate handler with `~=` syntax.
void opOpAssign(string op : "~")(FunctionHandler handler) @safe nothrow @nogcAttach a `@nogc nothrow` free-function handler with `~=` syntax.
bool detach(Handler handler) @safe nothrow @nogcDetach the first matching delegate handler by delegate identity.
bool detach(FunctionHandler handler) @safe nothrow @nogcDetach the first matching free-function handler.
void detachAll() @safe nothrow @nogcDetach all handlers.
void opCall(Args args) @safe nothrow @nogcEmit this hap by invoking a stack-local snapshot of fixed handler storage.
void emit(Args args) @safe nothrow @nogcNamed alternative to `opCall`.
structProperty(T)

An observable value that emits when assignment changes the stored value.

Property!T is a small subject in observer-pattern terms: it owns one value of type T and emits onChanged with the old and new values whenever a successful assignment occurs. Unchanged assignments (where the new value compares equal to the old value) do not emit.

Property is non-copyable because the embedded onChanged hap is an owned notification channel. Move semantics are available via core.lifetime.move.

Parameters

TThe type of the stored value.
Fields
Hap!(T, T) onChangedEmitted with `(oldValue, newValue)` after an accepted assignment.
T _value
Methods
const(T) value() @property ref const return @safeGet the current value.
bool set(T newValue) @safeSet the current value.
void opAssign(T newValue) @safeAssign a new value, emitting `onChanged` when the value changes.
structSharedHap(Args...)

A thread-safe multicast hap that invokes all attached handlers synchronously.

SharedHap provides the same attach/detach/emit semantics as Hap but with internal synchronization for safe use across threads. Handlers are invoked outside the internal lock to prevent deadlocks during re-entrant calls.

Snapshot semantics: emit captures a snapshot of current handlers under the lock, then releases the lock before invoking handlers. Mutations during emit affect future snapshots, not the current one. A handler already captured in an in-flight snapshot may still run after detach or detachAll returns.

SharedHap is non-copyable and move-only. It uses reference-backed shared state so in-flight snapshots remain valid even if the public SharedHap value is moved or destroyed after snapshot capture.

Parameters

ArgsArgument types passed to attached handlers.
Fields
Methods
bool hasHaps() @safeDetermine whether at least one handler is attached.
size_t hapCount() @safeGet the number of currently attached handlers.
void opOpAssign(string op : "~")(Handler handler) @safeAttach a delegate handler with `~=` syntax.
void opOpAssign(string op : "~")(FunctionHandler handler) @safeAttach a free-function handler with `~=` syntax.
void attach(Handler handler) @safeAttach a delegate handler.
void attach(FunctionHandler handler) @safeAttach a free-function handler.
void detach(Handler handler) @safeDetach the first matching delegate handler by delegate identity.
void detach(FunctionHandler handler) @safeDetach the first matching free-function handler.
void detachAll() @safeDetach all handlers.
void opCall(Args args) @safeEmit this hap by invoking all handlers captured at dispatch start.
void emit(Args args) @safeNamed alternative to `opCall`.
void ensureState() @safe nothrow
void removeAt(size_t index) @safe
Nested Templates
SharedRecord
SharedState
structPriorityHap(Args...)

A multicast hap that dispatches handlers in priority order.

PriorityHap associates each handler with an integer priority. Handlers with lower priority values run first. Handlers with equal priority run in stable attachment order. This is a separate type from Hap to keep normal attachment-order dispatch simple and fast.

PriorityHap supports attach, detach, detachAll, emit, and hasHaps/hapCount. It is non-copyable and move-only.

Parameters

ArgsArgument types passed to attached handlers.
Fields
private PriorityRecord[] _records
private size_t _nextOrder
private size_t _dispatchDepth
Methods
bool hasHaps() const @safe nothrow @nogcDetermine whether at least one handler is attached.
size_t hapCount() const @safe nothrow @nogcGet the number of currently attached handlers.
void attach(Handler handler, int priority = 0) @safeAttach a delegate handler with the given priority.
void attach(FunctionHandler handler, int priority = 0) @safeAttach a free-function handler with the given priority.
void opOpAssign(string op : "~")(Handler handler) @safeAttach a delegate handler with `~=` syntax at default priority 0.
void opOpAssign(string op : "~")(FunctionHandler handler) @safeAttach a free-function handler with `~=` syntax at default priority 0.
void detach(Handler handler) @safeDetach the first matching delegate handler by delegate identity.
void detach(FunctionHandler handler) @safeDetach the first matching free-function handler.
void detachAll() @safe nothrowDetach all handlers.
void opCall(Args args) @safeEmit this hap by invoking all handlers in priority order.
void emit(Args args) @safeNamed alternative to `opCall`.
void sortRecords() @safe
void removeAt(size_t index) @safe
Nested Templates
PriorityRecord

Cancellation state passed to handlers during a CancellableHap emit.

A handler calls cancel() to stop later handlers in the same emit from running. Cancellation is one-way: once cancelled, it cannot be undone.

Fields
bool _cancelled
Methods
void cancel() @safe nothrowCancel propagation. Later handlers in this emit will not run.
bool cancelled() const @safe nothrow @nogcCheck whether propagation has been cancelled.
structCancellableHap(Args...)

A multicast hap that supports propagation-stopping workflows.

CancellableHap passes a CancelToken as the first argument to each handler. When a handler calls token.cancel(), subsequent handlers in that emit are skipped. Handlers before cancellation run in attachment order.

This is a separate type from Hap to keep normal dispatch simple and to make cancellation explicit at the handler signature level.

Parameters

ArgsArgument types passed to attached handlers (excluding CancelToken).
Fields
private Handler[] _handlers
private size_t _dispatchDepth
Methods
bool hasHaps() const @safe nothrow @nogcDetermine whether at least one handler is attached.
size_t hapCount() const @safe nothrow @nogcGet the number of currently attached handlers.
void opOpAssign(string op : "~")(Handler handler) @safeAttach a delegate handler with `~=` syntax.
void attach(Handler handler) @safeAttach a delegate handler.
void detach(Handler handler) @safeDetach the first matching delegate handler.
void detachAll() @safe nothrowDetach all handlers.
void emit(Args args) @safeEmit this hap. Handlers receive a `CancelToken` followed by `args`.
void removeAt(size_t index) @safe
structQueryHap(Result, Args...)
Fields
private Handler[] _handlers
private size_t _dispatchDepth
Methods
bool hasHaps() const @safe nothrow @nogc
size_t hapCount() const @safe nothrow @nogc
void opOpAssign(string op : "~")(Handler handler) @safe
void attach(Handler handler) @safe
void detach(Handler handler) @safe
void detachAll() @safe nothrow
Result[] emit(Args args) @safe
void removeAt(size_t index) @safe
structReplayHap(Args...)

A hap that replays the last emitted value to newly attached handlers.

ReplayHap stores the most recent arguments from the last emit call. When a handler is attached, it immediately receives the stored value if one exists. Subsequent emit calls update the stored value and invoke all handlers as usual.

This is useful for state-like events such as settings, selection state, or configuration values where new observers need the current value immediately.

Parameters

ArgsArgument types for the hap.
Fields
private Handler[] _handlers
private Tuple!Args _lastValue
private bool _hasValue
private size_t _dispatchDepth
Methods
bool hasHaps() const @safe nothrow @nogcDetermine whether at least one handler is attached.
size_t hapCount() const @safe nothrow @nogc
bool hasValue() const @safe nothrow @nogcDetermine whether a value has been emitted.
void opOpAssign(string op : "~")(Handler handler) @safe
void attach(Handler handler) @safeAttach a handler. If a value has been emitted, the handler is invoked immediately with the stored value.
void detach(Handler handler) @safe
void detachAll() @safe nothrow
void emit(Args args) @safeEmit and store the value. All handlers are invoked, and the value is stored for replay to future attach calls.
void reset() @safe nothrowReset the stored value. Future attach calls will not replay until emit is called again.
void removeAt(size_t index) @safe
structAggregate

Aggregate multiple upstream haps into a single callback.

Aggregate registers on each upstream hap and invokes the callback whenever any upstream fires. Cleanup happens through clear() or destruction.

Fields
DetachRecord[] _records
Methods
void add(Args...)(ref Hap!Args hap, void delegate(Args) @safe handler) @safeConnect an upstream hap to this aggregate.
void add(Args...)(ref Hap!Args hap, void function(Args) @safe handler) @safeditto
void clear() @safeDisconnect all upstream registrations.
Destructors

Functions 13

fnauto scopedRegister(Args...)(ref Hap!Args hap, void delegate(Args) @safe handler) @safeCreate a scoped registration that detaches automatically at scope exit.
fnauto scopedRegister(Args...)(ref Hap!Args hap, void function(Args) @safe handler) @safeditto
fnauto scopedRegister(Args...)(ref NothrowHap!Args hap, void delegate(Args) @safe nothrow handler) @safe nothrowditto
fnauto scopedRegister(Args...)(ref NothrowHap!Args hap, void function(Args) @safe nothrow handler) @safe nothrowditto
fnauto registerOneShot(Args...)(ref Hap!Args hap, void delegate(Args) @safe handler) @safeRegister a one-shot delegate handler on a Hap that detaches itself after the first invocation.
fnauto registerOneShot(Args...)(ref NothrowHap!Args hap, void delegate(Args) @safe nothrow handler) @safe nothrowditto
fnvoid registerInto(Args...)(ref RegistrationBag bag, ref Hap!Args hap, void delegate(Args) @safe handler) @safeRegister a handler on a hap and add the resulting registration to a bag.
fnvoid registerInto(Args...)(ref RegistrationBag bag, ref Hap!Args hap, void function(Args) @safe handler) @safeditto
fnvoid registerInto(Args...)(ref RegistrationBag bag, ref NothrowHap!Args hap, void delegate(Args) @safe nothrow handler) @safe nothrowditto
fnvoid registerInto(Args...)(ref RegistrationBag bag, ref NothrowHap!Args hap, void function(Args) @safe nothrow handler) @safe nothrowditto
fnauto registerFiltered(Args...)(ref Hap!Args hap, scope bool delegate(Args) @safe predicate, void delegate(Args) @safe handler) @safeRegister a filtered delegate handler that invokes only when a predicate matches.
fnauto registerFiltered(Args...)(ref NothrowHap!Args hap, scope bool delegate(Args) @safe nothrow predicate, void delegate(Args) @safe nothrow handler) @safe nothrowditto
fnauto pipe(SourceArgs, TargetArgs)( ref Hap!SourceArgs source, ref Hap!TargetArgs target, void delegate(SourceArgs, ref Hap!TargetArgs) @safe transform) @safePipe events from a source hap through a transform function to a target hap.