JH-Toolkit v1.4.1
An engineering-oriented C++20 toolkit with duck-typed concepts, static design, async coroutines, and semantic containers — header-only, RTTI-free, and concurrency-friendly.
Loading...
Searching...
No Matches
slot.h File Reference

Coroutine-based asynchronous slot/listener/event_signal system with One-Slot-Per-Hub semantics, multi-listener fan-in, and user-controlled fan-out logic inside the slot coroutine. More...

#include <coroutine>
#include <optional>
#include <mutex>
#include <chrono>
#include <functional>
#include "jh/typed"

Go to the source code of this file.

Classes

class  jh::async::slot
 Coroutine representing the user-defined asynchronous state machine. More...
struct  jh::async::slot::promise_type
 Slot coroutine promise type. More...
class  jh::async::slot_hub
 Synchronization domain managing timed mutex acquisition and binding exactly one slot. More...
class  jh::async::listener< T >
 A one-shot inbox that serves as the fan-in aggregation point. More...
class  jh::async::event_signal< T >
 Lightweight push-only event emitter. More...

Namespaces

namespace  jh::async
 Aggregated entry point for coroutine-based asynchronous facilities.

Detailed Description

Coroutine-based asynchronous slot/listener/event_signal system with One-Slot-Per-Hub semantics, multi-listener fan-in, and user-controlled fan-out logic inside the slot coroutine.

Author
JeongHan-Bae <mastropseudo@gmail.com>

Overview

This module defines a minimal coroutine-driven event dispatch mechanism. A slot is a coroutine that represents arbitrary user-defined behavior. A listener<T> is an awaitable endpoint that provides values of type T into the slot when an event is emitted.

A slot_hub manages synchronization, timeout behavior, and the One-to-One binding between the hub and a single slot . All listeners created from a slot_hub forward events only to the slot bound to that slot_hub.

Design Notes

  • slot_hubslot is strictly One-to-One.
    A slot_hub can bind exactly one slot. All listeners made from that slot_hub always deliver values to that same slot.
    hub stands for "synchronization domain/kernel," and slot stands for "unique control coroutine."
    We allow multiple pairs of hubs and slots, but there is a One-to-One correspondence between hubs and slots.
  • One slot → many listeners.
    Multiple listeners are being monitored at different stages or under different conditions. Listening to multiple listeners within the same stage constitutes a synchronization barrier (semantically incorrect).
  • One listener → many event_signals (fan-in).
    A listeners may be connected to multiple event_signal objects. All signals write into the same inbox and attempt to resume the slot. The user can distinguish signal sources by encoding IDs/tags in the payload.
  • No fan-out in signals — fan-out belongs to the slot.
    event_signal performs only "push to listener". Routing, filtering, branching, switching, multi-stage flow control, and fan-out behavior are entirely user-defined inside the slot coroutine.
  • Listener inbox is a one-shot handoff slot , not a buffer.
    A value is written into the inbox only if the listener succeeds in acquiring the hub's mutex within the timeout window. Once written, the slot is resumed immediately, consumes the value during await_resume(), and the inbox is cleared.
    Because the write and the slot resumption occur while holding the same lock, the inbox is never overwritten, never accumulates unread entries, and never loses values due to replacement. If the lock cannot be acquired, nothing is written at all.
    This implements a high-pressure fuse: either the event is fully delivered
    write → resume → consume → clear
    or not delivered at all.
  • spawn() binds the slot to the calling thread.
    After spawn(), all event-triggered resumes occur on that same thread.
  • Unified lifetime.
    slot, slot_hub, and all listeners are expected to share the same lifetime. Moving a slot after binding/spawn may break this constraint and must be avoided. event_signal must not outlive its mapped listener.

Usage Model

The usage pattern is conceptually divided into two independent parts. Each part has its own internal ordering constraints, while the two parts themselves do not impose ordering constraints on each other.

Part 1 — Infrastructure Construction

These steps must occur in the following order:

  1. Create a slot_hub.
  2. Create one or more listeners from the hub.
  3. Create a slot coroutine that observes these listeners.
  4. Bind the slot to the hub via bind_slot().
  5. Call spawn() to start the coroutine and bind it to the calling thread.
Part 2 — Event Binding and Dispatching
  1. A listener must already exist before connecting event_signals to it.
  2. event_signal::connect(listener*) must be called before the first emit() targeting that listener.
  3. emit() should be called after slot.spawn() was called.

Advanced patterns (multi-signal, switching, state machines, routing, phase transitions, conditional awaits) are implemented entirely in the slot coroutine. The library provides only the suspension/resume primitives.

Mechanics of Slot Coroutines

  1. The slot is created and suspended at initial_suspend().
  2. The slot_hub binds exactly one slot; listeners use this binding.
  3. spawn() resumes the coroutine for the first time.
  4. co_await listener suspends the coroutine and registers its handle.
  5. An event_signal writes into the listener inbox and resumes the slot after acquiring the hub's timed mutex.
  6. co_yield {} yields a monostate and resumes execution immediately — useful for deterministic scheduling points.
  7. Normal completion enters final_suspend().

Queuing and Backpressure

Traditional Buffer Queues are presenting a synchronous mechanism, not an asynchronous one, which is why we reject them.
std::timed_mutex is essentially a timed queuing aid with timeout (overpressure) circuit breaking to ensure that emit is invoked according to the order of calls.

AspectLock QueuesBuffer Queues
Data BufferingNo — values are written only if the lock is acquired; at most one in-flight valueYes — values are enqueued and buffered until consumed
Queuing MechanismFIFO lock waiters — emitters are queued implicitly via the mutexExplicit data queue — typically implemented via ring buffer or linked list
Overflow ControlTimeout — emit is rejected if lock cannot be acquired in timeUnbounded or bounded buffer — may require manual pressure control
Resume SemanticsInlineslot is resumed immediately after value is writtenOut-of-band — consumer must poll or wait
Synchronization RoleIntegral — part of the event delivery protocolSeparate — often requires condition variables or manual signaling
Fan-in BehaviorYes — multiple signals may target a single listener via mutex arbitrationPossible — but usually requires a multiplexer
Fan-out SupportNo — deliberate omission; routing is done inside slotOptional — may push to multiple consumers

Important Constraints

  • slot_hub may bind only one slot.
  • event_signal must not outlive the listener it is connected to.
  • slot, slot_hub, and listeners must share the same overall lifetime.
  • slot alone is responsible for any fan-out or complex routing logic.

Example Usage

jh::async::slot_hub hub(std::chrono::milliseconds(TIMEOUT_MS));
auto make_slot = [...](jh::async::listener<T> &li, ...) -> jh::async::slot {
for (;;) {
auto v1 = co_await li;
...
co_yield {};
}
...
};
auto listener1 = hub.make_listener<T>();
sig1.connect(&listener1);
jh::async::slot s = make_slot(listener1, ...);
hub.bind_slot(s);
s.spawn();
// now, emitting events will resume the <tt>slot</tt> coroutine
Lightweight push-only event emitter.
Definition slot.h:672
void connect(listener< T > *l) noexcept
Connect this signal to a listener.
Definition slot.h:680
A one-shot inbox that serves as the fan-in aggregation point.
Definition slot.h:563
Synchronization domain managing timed mutex acquisition and binding exactly one slot.
Definition slot.h:457
Coroutine representing the user-defined asynchronous state machine.
Definition slot.h:334
void spawn()
Start the coroutine associated with this slot.
Definition slot.h:367
Version
1.4.x
Date
2025