Performance and correctness pass across channels, the multiplexer, and the pipeline. No public API changes.
put! and take! no longer
allocate a closure per operation, and take! no longer allocates a result
tuple or sequences it. JFR confirms the hot paths allocate no application
objects.[:block ...] or
:closed is returned unchanged by take!. Previously a value whose first
element was :block was mistaken for an internal marker and crashed the
take.Phaser barrier were replaced by sequential blocking put! on
the dispatcher thread. Every tap still receives every value, in order, with
the same strict backpressure.n=1 fix. nil is the only EOF, so boolean false now passes
through, and closing the output channel stops the loop.n=1. Boolean false inputs are no longer treated as EOF, and
the loop stops when the output channel is closed.Measured on OpenJDK 25.0.4.1, Clojure 1.12.4, on Linux; min of N runs. The multiplexer figures are an interleaved A/B against the previous revision on the same machine. Absolute numbers vary by hardware.
Channels (200k put!/take! pairs, one producer thread and one consumer):
| Configuration | Before | After | Change |
|---|---|---|---|
| unbuffered | 315.9 ms | 315.9 ms | 0% |
| buffered cap 5 | 89.7 ms | 89.9 ms | ~0% |
| buffered cap 100 | 32.0 ms | 21.9 ms | -31% |
Multiplexer (one source, buffered taps, one consumer per tap):
| Configuration | Before | After | Change |
|---|---|---|---|
| dispatch-only 5 taps, 50k | 140.5 ms | 17.8 ms | -87% |
| dispatch-only 20 taps, 50k | 321.8 ms | 33.1 ms | -90% |
| full 5 taps, 100k | 387.3 ms | 314.2 ms | -19% |
| full 20 taps, 25k | 274.6 ms | 241.8 ms | -12% |
JFR (settings=profile) shows the multiplexer hot path no longer spends time
in Phaser.internalAwaitAdvance or ForkJoinPool/virtual-thread scheduling, and
the channel put!/take! fast paths allocate no application objects.
Correctness is covered by the full test suite: 28 tests, 792 assertions, 0 failures.
org.clojure/core.async 1.7.701,
criterium/criterium 0.4.6, circleci/bond 0.6.0, and the Cognitect
test-runner v0.5.1.# Functional tests
clojure -M:test:test-runner
# Performance benchmarks (criterium quick-bench)
clojure -M:test:test-performance
Bug-fix, compatibility, and performance pass across channels, the multiplexer,
the pipeline, pub/sub, and the waiter machinery. One breaking change to the
pub! API.
pub! gained an :ex-handler for publish
failures, and an exception thrown by an :ex-handler now closes the
associated resources regardless of the failure. The multiplexer and pub/sub
paths catch Throwable instead of Exception, so errors are reported to the
handler instead of escaping the dispatch loop.park-and-wait checks for interruption before its timeout check, and the
multiplexer calls shutdownNow on cleanup rather than close.unsub-add! and sub!, and a
conflict between zombied AltsWaiters and real waiters.pub! changed arity to accept the publish functions and an
:ex-handler for publish failures; update call sites accordingly.put! and take! return false/nil instead of :interrupted when the
operation is interrupted (the channel is not closed).nil on close!.pub throws when its topic function returns nil instead of silently
dropping the value.n=1 fast path runs on a single virtual thread.Initial release.
multiplex for fan-out with per-tap backpressure.pub/sub.select over multiple channel operations.csp-clj.core namespace.Can you improve this documentation?Edit on GitHub
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |