This is a history of changes to gateless/futurama
binding are now preserved across parking operations (<!, >!, alts! and the futurama !<! equivalents), even when a parked async/go block resumes on a different pool thread. Previously, a resume that fired on another thread before the parking thread finished saving its binding frame could observe a stale frame and lose bindings set inside the block. This is fixed by custom IOC "parking terminators" (futurama.impl/ioc-take!, ioc-put!, ioc-alts!) that snapshot the current thread binding frame before registering the resume callback. Validated against core.async 1.8 and 1.9.go coverage: async! uses the snapshotting terminators directly. Because plain core.async go blocks read their terminators from a hardcoded var at macroexpansion time (no injection point), futurama also alters the root of clojure.core.async.impl.ioc-macros/async-custom-terminators so go blocks compiled after the library loads get the same guarantee. This is a global, JVM-wide effect and a temporary measure pending an upstream core.async fix.ReadPort/take! logic into futurama.impl/async-read-port-take!, adding a poll! synchronous fast-path for ready core.async channels and committing the read handler up front. The up-front commit fixes a bug where reading a plain (non-async) value through an ->async-reader inside alts! could leave a phantom taker on the losing port that later consumed and dropped a value.async/go, verified over thousands of iterations with the park window deterministically widened) and ->async-reader/alts! read-port coverage. Replaced the bond get-pool spy — which was subject to cross-thread pollution — with pure, deterministic macroexpansion assertions, and replaced criterium benchmarks with a lightweight min-elapsed-ms helper.dev.weavejester/cljfmt 0.16.5 (from cljfmt/cljfmt 0.9.2), consolidated indentation config into a single .cljfmt.edn (unqualified symbol keys, so clojure-lsp and the CLI stay aligned), and removed the now-unused circleci/bond test dependency. Added an antq dependency-report tool (:deps-antq alias) with make deps-check / make deps-upgrade targets that exclude org.clojure/core.async (which is version-matrixed by hand), and bumped the CI actions (actions/checkout, jdx/mise-action).clj-kondo (2026.07.24), test.check (1.1.3), slf4j-simple (2.0.18), nrepl (1.7.0), cider-nrepl (0.62.2), graal-build-time (1.0.6), and deps-deploy (0.2.5). Dropped pjstadig/humane-test-output (no longer recommended for use) along with its activation in the test setup.satisfies? protocol checks behind inlining predicate macros in futurama.impl (async?, async-channel?, async-completable-reader?, async-completable-writer?, async-cancellable?), and switched futurama.core/futurama.impl call sites over to them. The public futurama.core/async? remains a function so it can still be passed as a higher-order value.!<! and !<!! now short-circuit non-async values, returning them directly without a channel round-trip; the argument expression is still evaluated exactly once.!<!/!<!! non-async fast path, including the single-evaluation guarantee for both non-async and async argument expressions.get-pool once again returns an ExecutorService (1.4.5 had narrowed the return type to Executor to track core.async 1.9's executor-for, which broke downstream consumers that called .submit/.invokeAll on the pool). When executor-for returns a plain Executor, the result is widened via a new futurama.impl/->executor-service proxy that forwards execute; real ExecutorService instances pass through unwrapped. The internal async-dispatch-task-handler continues to accept any Executor.UnsupportedOperationException from shutdown, shutdownNow, isShutdown, isTerminated, and awaitTermination — it does not own the underlying executor and refuses to lie about its lifecycle. Callers that need to manage a pool's lifecycle should hold a reference to the original Executor directly.->executor-service (passthrough vs. wrap, execute/submit routing, lifecycle behavior on both branches) and for the previously-untested with-async-factory and with-thread-factory macros (binding/restore, precedence, nesting, exception unwind).async-dispatch-task-handler now accepts any java.util.concurrent.Executor (previously required ExecutorService). Tasks are wrapped in a FutureTask so cancel-with-interrupt and exception-capture semantics are preserved. Required to support core.async 1.9's clojure.core.async.impl.dispatch/executor-for, which returns a plain Executor.get-pool now falls back to clojure.core.async.impl.dispatch/executor-for (was ForkJoinPool/commonPool). Out of the box, futurama dispatches over the same workload-aware pools as core.async — no futurama.executor-factory sysprop required for that behavior. The sysprop remains the override hook..mise.toml JDK to corretto-25 (latest LTS); updated .mise.toml to the new [tool_alias] schema; default TEST_CORE_ASYNC_ALIAS flipped to core.async-1.9 in the Makefile.->future function to easily convert values to future.1.8.735completable-future and fixed-threadpool1.8.730async macro, replace uses with async.thread, updated async, both macros route work to the appropriate thread pool, such as :io, :compute, or :mixed.*thread-factory* dynamic binding to allow separately defining a factory-fn for thread calls, distinct from async calls.completable-future macro, uses of completable-future should be replaced with thread.fixed-threadpool function, instead prefer to use Executors thread pools according to need.async-cancellable? fn to easily test if something can be cancelledasync-future and async-deferred macros to more easily create either.cancel! and cancelled? to async-cancel! and async-cancelled?fixed-threadpool method to create a FixedThreadPool*thread-pool* is now a FixedThreadPool which can be interrupted.async to be interrupted just like completable-future, add tests.with-pool macroFuture and IDeref.satisfies? to instance-satisfies? and class-satisfies?async-reduce, async-some, async-every?, async-walk/prewalk/postwalkasync-for so it uses less async macros and it is more flexibleasync-map so it leverages async-for behind the scenes.async-some and async-every? and instead added some new helpers.async-> and async->> threading macros to make it easier to thread async.async-map, async-some, async-every?async-for by just executing each iteration inside an async block and then collect afterasync-for comprehension which implicitly runs inside an async blockasync? helper function which is useful!<! and !<!!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 |