All notable changes to this crate are documented here. The format is loosely based on Keep a Changelog, and this crate adheres to Semantic Versioning.
wait_blocking()no longer breaks builds onwasm32targets.EventListener::wait()(blocking) does not exist ontarget_family = "wasm"— the method is now gated with#[cfg(all(feature = "std", not(target_family = "wasm")))]socargo check --all-features --target wasm32-unknown-unknownsucceeds.alloc + futureis now a testable configuration.tests/future.rspreviously importedstd::time,std::sync::atomic, and runtime crates unconditionally, socargo test --no-default-features --features alloc,futurefailed to compile. Std-only tests are now gated behind#[cfg(feature = "std")], and the manual-poll / remaining / clone / fmt / over-done / add-assign tests usecore::*/alloc::*so they compile and run under alloc-only.- Counter overflow in
add()is now caught in release builds. All three WaitGroup variants (sync,spin,future) useddebug_assert!or unchecked+=/fetch_addfor the overflow check, which silently wrapped in release mode. A wrap fromusize::MAX + 1 → 0could reset the counter and letwait()return prematurely or hang.sync::WaitGroupnow useschecked_add(...).expect(...), andspin/futureusefetch_updatewithchecked_addso the atomic is never corrupted.
- CI now tests feature pairs and wasm with
--all-features. Added explicitcargo build/test --no-default-features --features alloc,futureandcargo build/test --all-featuressteps alongside the existingcargo hack --each-featureruns. The cross-build job now also runscargo check --all-featuresper target, catching platform-specific conditional-compilation misses like the wasmwait_blockingissue above.
First stable release. Significant breaking changes from 0.9.x — see Migration from 0.9 below before upgrading.
The crate now commits to Semantic Versioning: breaking changes to the public API or a bump of the declared MSRV will require a major version bump.
-
done()now returns the remaining count, not the previous value. In 0.9.x,future::WaitGroup::done()/no_std::WaitGroup::done()returned the counter value before the decrement, which contradicted the documented behaviour. It now consistently returns the count after decrementing. The blockingWaitGroupwas already correct. -
waitings()renamed toremaining()on all variants. The new name reads naturally (if wg.remaining() > 0 { … }) and is consistent across variants. -
Old
no_stdmodule replaced byspin. The lock-free, atomic-counter variant now lives atwg::spin::WaitGroup. Inno_stdbuilds,wg::WaitGroupis a re-export ofwg::spin::WaitGroup(backward-compatible forno_stdusers). -
future::AsyncWaitGrouprenamed tofuture::WaitGroup. Use the module-qualified name:wg::future::WaitGroup. -
The
allocfeature no longer pulls incrossbeam-utils; the spin backoff is now inlined into the crate.
- Busy-loop in
future::WaitGroup::poll. The previousPendingbranch calledwake_by_ref()before returningPending, causing the executor to re-poll the future continuously and burn 100% CPU untildone()reached zero. The waker is now only registered once (via the listener) and the future properly yields. - Wrong return value from
done()(see breaking changes above). std::sync::Mutexpoisoning no longer panics. The blockingWaitGroupnow recovers the guard on poisoning viaPoisonError::into_inner— a poisoned counter is not a memory-safety concern, and cascading panics across every thread touching the group were an over-reaction.- Redundant
notify(usize::MAX)on everydone(). The async variant now only notifies waiters when the counter actually reaches zero, instead of on every decrement. crossbeam_utils::Backoff::new()was being reset inside the wait loop, so it never escalated past its first spin budget. Replaced with an inline adaptive backoff that spins and — onstd— yields the OS thread once the spin budget is exhausted.- Silent over-done no longer triggers the misleading
assert_eq!(x, 0). Callingdone()on a zero counter remains a silent no-op returning0. - The
required-features = ["tokio"]ontests/future.rspointed at a non-existent feature, so the integration test never ran in CI. Fixed torequired-features = ["future"], and the CI job now exercises it.
- Memory ordering tightened:
addusesRelease,doneusesAcqRel/Acquire,remaining/waitloads useAcquire. The previousSeqCsteverywhere was unnecessarily strong —Release/Acquireprovides the required happens-before edges for this structure. #![forbid(unsafe_code)]is now enforced at the crate level.
wg::spin::WaitGroup— a lock-free, atomic-counter WaitGroup available on bothstdandno_std. Uses an inline adaptive backoff that yields the OS thread onstdand spins on pureno_std.- Dedicated integration tests for each variant.
- Compile-time
Send + Syncassertions for all three variants. - Declared MSRV (
rust-version = "1.76.0", driven byparking_lotandtriomphefloor requirements).
- Replace
wg::AsyncWaitGroupwithwg::future::WaitGroup. - Replace
.waitings()with.remaining(). - If you relied on
done()returning the pre-decrement value on the async / no_std variants, adjust your code — it now returns the post-decrement value, matching the blocking variant and the documented contract. required-features = ["tokio"]in consumer code should berequired-features = ["future"].
0.9.2 and earlier
See the git history for pre-1.0 releases.