All notable changes to this project are documented here. The format loosely follows Keep a Changelog.
- ROADMAP.md — maintenance posture, P1/P2/P3 backlog, adoption class, and explicit exclusions.
- CHANGELOG.md — this file.
- SECURITY.md and CONTRIBUTING.md.
- GitHub Actions CI (
.github/workflows/ci.yml) runningcargo test,cargo test --features=test_runtime,cargo test --doc,cargo test --examples, and clippy on stable. - Runnable
examples/*.rs:monadio,publisher,cor,do_notation,fp,actor,actor_ask(compile-checked and run under--features=test_runtime). - rustdoc coverage across all public modules/items, plus doctests for
fpandmaybeAPIs. - Latent-defect regression tests for the async primitives:
WillAsyncandCountDownLatchmulti-waker and poll-before-start cases, andBlockingQueue::{take,poll}_result_as_future(previously untested). - Runnable doctests for the
foldl!/foldr!macros (completing example coverage across all 11fpmacros) and for the opt-in async entry pointsMonadIO::to_futureandPublisher::subscribe_as_stream. - README CI status badge (renders live once the workflow runs on the default branch); README now states edition 2021 / MSRV 1.56.
- docs/PROJECT_NOTES.md — project origins (three-era history), cross-language family (
fpGo/fpEs), and a catalog of less-known/"leaked" public APIs (observe_on/subscribe_onscheduler routing + its no-op trap,subscribe_blocking_queuepush→pull bridge,do_m_pattern!,map_insert!, coroutine macros) with scenarios and anti-patterns. README gained a "Less-known / advanced capabilities" section pointing to it. - Two runnable examples for previously undemonstrated "leaked" capabilities:
examples/scheduler.rs(MonadIO::observe_on/subscribe_onthread-hopping, with thesubscribe_on-alone no-op trap proven by asserting the deliveringThreadId) andexamples/publisher_queue.rs(Publisher::as_blocking_queuepush→pull bridge).examples/cor.rsgained comments documenting the sync/asyncyield_fromdeadlock invariant.
- Cargo.toml —
includenow packagesLICENSEandREADME.md; maintenance badge set topassively-maintained. - README.md — removed stale Travis CI badge and custom "docs-online" shield.
- README.md — replaced long, non-compiling inline Rust blocks with
cargo run --example <name> --features=test_runtimecommands pointing at maintained example targets. - README.md — documented feature-flag layering (
pure,for_futures,test_runtime, module gates,WillAsyncFuturerequirements). - README.md — documented known behavior: deferred shutdown redesign,
Corsync deadlock invariant, and thatthread::sleepis not synchronization. - README.md — stated that pattern matching is a deferred non-goal (no strikethrough placeholder).
- clippy.sh / publish.sh — made reproducible on stable; publish now dry-runs, requires an existing release tag, and confirms before publishing.
- Removed legacy
.travis.yml. - Cargo.toml — adopted edition 2021 and pinned MSRV
rust-version = "1.56"; removed the dead commentedtokiodependency and feature line. - CI — added
rustfmt --checkandrustdoc -D warningsgates, an MSRV (1.56) job, and a feature-combination build matrix;clippy.shnow fails on warnings (-D warnings);publish.shrefuses a dirty working tree (dropped--allow-dirty). - src/fp.rs — reimplemented
compose!,pipe!, andcompose_twofrom first principles, removing a CC BY-SA-licensed StackOverflow snippet (semantics unchanged). - src/lib.rs — crate-level docs now list the full 8-module
purestack (previously mis-stated asfp+maybeonly). - Deleted seven vestigial
thread::sleepsynchronization hacks from tests (each was already gated by a latch/flag/waker/queue); mechanical clippy idiom cleanup acrossactor,common,maybe,publisher.
WillAsync/CountDownLatchwoke only one awaiter — the singleOption<Waker>slot dropped all but the last-registered waker, hanging concurrent awaiters; now storesVec<Waker>and wakes all (draining before waking to stay reentrancy-safe).WillAsync::start()now also wakes wakers registered beforestart(), so poll-before-start no longer hangs.SubscriptionFuncbuffered every value forever (memory leak) — underfor_futures,on_nextunconditionally pushed each delivered value into an internalcachedstream, but that stream was open from construction (LinkedListAsync::new()startsalive=true). A callback-only subscription (the commonsubscribe_fncase) has no stream consumer, so the buffer grew without bound. Thecachedstream now starts closed and only begins buffering onceas_stream()opens it — matching the documented open/close intent. Streaming (subscribe_as_stream) is unaffected. Regression-guarded bytest_common_subscription_func_no_buffer_until_stream_opened.BlockingQueue::stopdoc/dead-code mismatch — the method and module docs claimed it "drops the sender side" to close the queue, but the code didlet sender = …lock().unwrap(); drop(sender);which dropped theMutexGuard, not theSender(which lives behindArc<Mutex<..>>and cannot be dropped there). Removed the misleading no-op and corrected both docs to state the real mechanism:stop()flips thealiveflag (gating lateroffer/take), and a consumer already blocked intake()stays blocked until its owntimeout. No behavior change (verified bytest_sync_blocking_queue_is_alive_and_stop).HandlerThreadInner::postaccepted work after stop —Handler::stoppromised "stop accepting new jobs," butpost()always enqueued into the innerBlockingQueue. A post after stop could leak forever into an un-drained queue or wake a worker blocked intake()and execute one more job after stop.post()now rejects only the stopped state (started && !alive), preserving pre-start queueing. Regression-guarded bytest_handler_inner_post_after_stop_is_rejectedandtest_handler_post_before_start_is_preserved.ActorAsync::stopleft mailbox handles accepting sends — stopping an actor cleared the actor alive flag but left the mailboxBlockingQueuealive, soHandleAsync::sendafter stop still queued messages that would never be processed.stop()now also stops the mailbox queue so later sends are rejected, while still not interrupting a thread already blocked intake()(shutdown redesign remains deferred). Regression-guarded bytest_actor_handle_send_after_stop_is_rejected.CountDownLatch::waitreleased only one blocking waiter —countdown()usedCondvar::notify_one(), so when the count reached zero only one waiting thread was guaranteed to wake. It now usesnotify_all()at zero, matching JavaCountDownLatchsemantics and the existing async multi-waker behavior. Regression-guarded bytest_sync_countdownlatch_releases_all_waiters.- Reentrant waker deadlock risks —
WillAsync,CountDownLatch, andLinkedListAsyncnow release state/waker locks before invokingwake()/wake_all_wakers()/notify_all(), so synchronous re-polling wakers cannot block on the same non-reentrant mutexes during wakeup. Verified by existing async stream/future tests. Cor::stopvestigial no-op —stop()randrop(self.op_ch_sender.lock().unwrap());, which (like the oldBlockingQueue::stop) dropped theMutexGuard, not the sharedSender, so it never closed the op channel. Removed the no-op and clarified that cooperative stop is delivered solely by thealiveflag (ayield_fromto a stoppedCorreturnsNonebecausereceive()early-returns). Behavior-preserving; also removes one lock held understarted_alive. Verified bytest_cor_*(pure +test_runtime).Publisher::delete_observerclosed streams for non-members and duplicates too early — stream closure happened before confirming the subscription belonged to that publisher, so deleting a non-member could close another publisher's stream. With duplicate registrations, removing one occurrence closed the shared stream while another observer still remained.delete_observernow closes only after removing a matching observer, and only when no matching observer remains in that publisher. Regression-guarded bytest_publisher_delete_non_member_does_not_close_subscription_streamandtest_publisher_delete_duplicate_keeps_stream_open_until_last_observer.
0.3.5 — 2021-08-21
- Feature sync isolations —
syncmodule wiring aligned with feature flags.
- Improved
Actor::for_each_child(id, handle)API for iterating child actors during shutdown-style flows.
0.3.4 — 2021-08-16
- Better APIs for accessing actor children (
get_handle_child, related helpers).
0.3.3 — 2021-08-15
- Actor ask-style patterns (Akka/Erlang inspired) with tests.
paralleltest.shfor repeated concurrent test runs.
- Reduced flaky tests caused by overly short
thread::sleeptiming.
0.3.2 — 2021-08-14
- Reduced unnecessary
clone()andlock()usage in hot paths. LinkedListAsynclocking improvements.
0.3.1 — 2021-08-13
- Potential leaked
Future/Streamwaker wakeups. - Publisher
subscribe_blocking_queueimplementation issues. CountDownLatchfuture implementation bugs (0.3.0 follow-up).
Publishersubscriptions useArc<SubscriptionFunc<T>>instead ofArc<Mutex<…>>.LinkedListAsyncextracted and made stream-capable behindfor_futures.- Feature tags reorganized for more independent module builds.
0.3.0 — 2021-08-11
- Actor module —
ActorAsyncwith spawn, context state, parent/child handles. - Expanded tests across actor interfaces.
- Major version bump reflecting actor model and feature-flag restructuring.
BlockingQueuetake_result/poll_resultasFuture(withfor_futures).CountDownLatchfuture support and bug fixes.- Publisher and MonadIO futures/stream hooks.
- Ongoing reduction of
Arc<Mutex<…>>usage and subscription rework.
- Initial crate:
MonadIO,Publisher,Cor/ do-notation macros,HandlerThread,WillAsync,fpmacros,Maybe. - Progressive addition of sync primitives, publisher observers, and test coverage.