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
size_t idHandler handlervoid addRecord(size_t id, Handler handler) @safevoid removeRecordById(size_t id) @safevoid removeFirstByHandler(Handler handler) @safeA 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
Handler | The delegate handler type of the source hap. |
bool attached() const @safe nothrow @nogcDetermine whether this registration's source hap is alive and this registration has not been detached.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
Handler | The delegate handler type of the source hap. |
Registration!Handler _regbool attached() const @safe nothrow @nogcDetermine whether this scoped registration's underlying registration is still attached.Registration!Handler release() @safe nothrowRelease ownership of the underlying registration without detaching.this(Registration!Handler reg)~thisDestroy this scoped registration, detaching the underlying registration if it is still attached.bool delegate() @safe detachthis(Handler h)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
Handler | The delegate handler type of the source hap. |
OneShotState!Handler _statebool attached() const @safe nothrow @nogcDetermine whether the one-shot handler has not yet fired and has not been cancelled.this(OneShotState!Handler state)Registration!Handler regthis(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.
DetachRecord[] _recordsvoid 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 NothrowHap!Args hap, void delegate(Args) @safe nothrow handler) @safe nothrowdittovoid register(Args...)(ref NothrowHap!Args hap, void function(Args) @safe nothrow handler) @safe nothrowditto~thisDestroy this bag, detaching all live registrations.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
Args | Argument types passed to attached handlers. |
private HapState!Handler _statevoid opOpAssign(string op : "~")(FunctionHandler handler) @safeAttach a free-function handler with `~=` syntax.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 emitSafe(Args args, scope void delegate(Exception) @safe onError = null) @safeEmit this hap while reporting handler exceptions and continuing dispatch.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
Args | Argument types passed to attached handlers. |
private HapState!Handler _statevoid 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.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.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
capacity | Maximum number of handlers stored by this hap. |
Args | Argument types passed to attached handlers. |
private Handler[capacity] _handlersprivate size_t _countbool 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 opCall(Args args) @safe nothrow @nogcEmit this hap by invoking a stack-local snapshot of fixed handler storage.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
T | The type of the stored value. |
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
Args | Argument types passed to attached handlers. |
SharedState _statevoid opOpAssign(string op : "~")(FunctionHandler handler) @safeAttach a free-function handler with `~=` syntax.void removeAt(size_t index) @safeSharedRecordSharedStateA 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
Args | Argument types passed to attached handlers. |
private PriorityRecord[] _recordsprivate size_t _nextOrderprivate size_t _dispatchDepthvoid 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 sortRecords() @safevoid removeAt(size_t index) @safePriorityRecordCancellation 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.
bool _cancelledA 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
Args | Argument types passed to attached handlers (excluding CancelToken). |
private Handler[] _handlersprivate size_t _dispatchDepthvoid removeAt(size_t index) @safeprivate Handler[] _handlersprivate size_t _dispatchDepthvoid opOpAssign(string op : "~")(Handler handler) @safevoid attach(Handler handler) @safevoid detach(Handler handler) @safevoid removeAt(size_t index) @safeA 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
Args | Argument types for the hap. |
private Handler[] _handlersprivate Tuple!Args _lastValueprivate bool _hasValueprivate size_t _dispatchDepthvoid opOpAssign(string op : "~")(Handler handler) @safevoid 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) @safevoid 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) @safeAggregate 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.
DetachRecord[] _recordsFunctions 13
auto scopedRegister(Args...)(ref Hap!Args hap, void delegate(Args) @safe handler) @safeCreate a scoped registration that detaches automatically at scope exit.auto scopedRegister(Args...)(ref NothrowHap!Args hap, void delegate(Args) @safe nothrow handler) @safe nothrowdittoauto scopedRegister(Args...)(ref NothrowHap!Args hap, void function(Args) @safe nothrow handler) @safe nothrowdittoauto 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.auto registerOneShot(Args...)(ref NothrowHap!Args hap, void delegate(Args) @safe nothrow handler) @safe nothrowdittovoid 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.void registerInto(Args...)(ref RegistrationBag bag, ref Hap!Args hap, void function(Args) @safe handler) @safedittovoid registerInto(Args...)(ref RegistrationBag bag, ref NothrowHap!Args hap, void delegate(Args) @safe nothrow handler) @safe nothrowdittovoid registerInto(Args...)(ref RegistrationBag bag, ref NothrowHap!Args hap, void function(Args) @safe nothrow handler) @safe nothrowdittoauto 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.auto registerFiltered(Args...)(ref NothrowHap!Args hap,
scope bool delegate(Args) @safe nothrow predicate,
void delegate(Args) @safe nothrow handler) @safe nothrowditto