Skip to content

feat: async dispatch support - #1

Merged
Big-Iron-Cheems merged 5 commits into
mainfrom
feat/async-dispatch
Mar 5, 2026
Merged

feat: async dispatch support#1
Big-Iron-Cheems merged 5 commits into
mainfrom
feat/async-dispatch

Conversation

@Big-Iron-Cheems

Copy link
Copy Markdown
Owner

Adds opt-in per-handler async execution via Executor injection,
preserving all existing dispatch guarantees (priority ordering, mutation visibility, cancellation) across thread boundaries.

What's new

  • EventBus(executor): async-capable bus construction
  • @Subscribe(async = true) and subscribe(..., async = true): per-handler opt-in
  • postAsync(event): non-blocking dispatch returning CompletableFuture<T>
  • post(event): unchanged contract; blocks if async handlers are present
  • TypedEventBus.postAsync: delegation to underlying bus
  • AsyncCancellableGuard: automatic thread-safe cancellation
  • AsyncDispatchBenchmark: postAsync vs sync post across all-sync, all-async, mixed scenarios

Guarantees preserved

  • Priority ordering is deterministic across sync/async handler mixes
  • Sync handlers following async ones observe all prior mutations via thenRun chaining
  • Cancellation is automatically thread-safe for the duration of the chain
  • No executor configured -> async = true falls back silently to sync

Out of scope

  • Parallel handler execution (incompatible with mutation visibility)
  • async flag on subscribeAll (wildcards are always sync by design)
  • kanal-coroutines submodule (possibly in the future)

- Add async: Boolean = false to @subscribe annotation
- Add asyncExecutor: Executor? constructor param to SimpleEventBus
- Add async flag to ListenerEntry and lambda subscribe overloads
- Add postAsync(event): CompletableFuture<T> to EventBus and TypedEventBus
- Implement buildDispatchChain with priority-ordered CompletableFuture chaining;
  async entries via thenApplyAsync, sync entries via thenApply after async steps
- Add AsyncCancellableGuard: makes cancellation thread-safe for async dispatch
  without requiring @volatile on user isCancelled fields; uses happens-before
  polling via the future chain and flushes back to the event after join
- Add factory overloads: EventBus(executor), EventBus(executor, handler),
  createWithHandler(executor, handler)
- Delegate postAsync and async subscribe through TypedEventBusAdapter
- Add AsyncDispatchTest (28 tests) covering all async scenarios
- Add AsyncDispatchBenchmark
- Update ABI snapshot, Cancellable KDoc, postAsync KDoc, README
- postAsync: short-circuit to sync loop when no async handlers present
- post KDoc: document CompletionException on executor rejection
- AsyncDispatchBenchmark: fix syncPostWithExecutorConfigured to use a
  dedicated sync-only bus; add asyncFallbackToSync benchmark
- AsyncJavaInteropTest: add Java-facing async API coverage
- AsyncCancellableGuard: remove trailing blank lines
- post: route CompletionException from .join() to exceptionHandler;
  no-throw contract preserved; postAsync still propagates (documented)
- Subscription.cancel(): private cancelled flag prevents spurious
  dispatchCache.clear() on double-cancel
- DispatchList: pre-compute hasAnyAsync at cache-build time; removes
  list.any scan on every dispatch
- chainAsync/chainSync: thenApply -> thenRun; removes redundant Unit
- Executor-rejection tests, subscription idempotency tests
- Split JavaUsageExample into per-feature files matching Kotlin layout
- README: linked example tables, Maven badge -> Meteor Maven,
  @volatile mentions removed
Apply consistent doc structure to all public-facing files:
- One-sentence imperative lead on every method
- @param on all non-obvious parameters, @return on all non-void methods
- Kotlin snippet first, Java snippet only where the call site differs
- Removed internal implementation details from user-facing docs
- Subscription.cancel clarifies idempotency; close clarifies alias role
- TypedEventBus methods aligned with EventBus counterparts
- subscribeAll KDoc (EventBus + TypedEventBus): wildcards are always sync;
  when following an async step they run on the completing async thread, not
  the posting thread; thread-local state (e.g. MDC) will silently see the
  wrong context on a bus with async handlers
- AsyncDispatchTest: wildcard between async typed handlers
  preserves mutation visibility; wildcard fires in priority order within the
  chain; wildcard in the sync prefix runs on the posting thread
@Big-Iron-Cheems
Big-Iron-Cheems merged commit dd21aea into main Mar 5, 2026
1 check passed
@Big-Iron-Cheems
Big-Iron-Cheems deleted the feat/async-dispatch branch March 5, 2026 18:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant