Releases: lazily-hub/lazily-rs
lazily v0.12.0
lazily v0.12.0
Minor release over v0.11.1. Last published: crates.io 0.11.1.
Tag v0.12.0 points at this release commit on main.
Highlights
Correctness fixes to the networked str0m DataChannel backend (Str0mNet,
behind the opt-in webrtc-str0m feature). Two bugs that silently lost data are
fixed; one of them adds a new public error variant (the single breaking surface
in this release). The core reactive primitives (Context / ThreadSafeContext
/ AsyncContext, slots, cells, effects, signals) are unchanged.
Breaking (scoped to the webrtc-str0m feature)
- New
Str0mNetError::Backpressurevariant (#lzstr0mframe). The
Str0mNetErrorenum is not#[non_exhaustive], so adding a variant is a
breaking change for downstream code with an exhaustivematchon it. The
variant is returned byStr0mNetChannel::send_frameonce the driver's
outbound queue reachesMAX_PENDING_FRAMES(1024), so callers apply flow
control instead of growing memory without bound. Callers matching
Str0mNetErrorexhaustively must add aBackpressurearm (yield and retry
send_frame). Only thewebrtc-str0mfeature surface is affected; no other
public API changed.
Fixes
- #lzstr0mframe — silent frame loss under SCTP backpressure. The driver's
flush loop usedif ch.write(...).is_err() { break; }, which discarded the
boolreturned byChannel::write. str0m returnsOk(false)when the SCTP
send buffer is full (backpressure); the old code treated that identically to
Err, silently dropping every frame that exceeded the SCTP send window —
violating the ordered/reliable DataChannel invariantWebRtcSink/
WebRtcSourcerely on. Post-fix theOk(false)andErrpaths both
re-queue the frame and yield, letting the nextpoll_output/recv_from
cycle drain the window (forOk(false)) or detect a deadRtcon the next
is_alive()check (forErr). Combined with the new bounded queue +
Backpressurevariant, sustained bursts can no longer exhaust memory or lose
frames. Regression testburst_of_frames_arrives_in_order_under_backpressure
bursts 100 × 8 KiB frames through one channel and asserts all 100 arrive in
order, retrying onBackpressureas needed. - #lzstr0mpolldrive — surface driver I/O errors. The driver's UDP transmit
path waslet _ = socket.send_to(...), discarding every error:ENOBUFS
(send-buffer pressure),ECONNREFUSED(ICMP port-unreachable, peer down),
ENETUNREACH/EHOSTUNREACH(route flap),EBADF(socket closed). The
corresponding ICE/DTLS/SCTP packet was silently lost and the handshake / data
path stalled without diagnostics. Post-fix,WouldBlock/Interrupted
(retryable on a blocking socket)continuethe drain loop — str0m re-emits the
Transmiton a laterpoll_output; any other error breaks the driver
('outer), surfacingClosedso the caller re-signals. The read-timeout cap
is also documented as a command-poll interval (COMMAND_POLL_INTERVAL, 15 ms),
not a str0m timing parameter.
Verification
cargo build --all-features,cargo clippy --all-features --all-targets -- -D warnings,
cargo fmt --checkclean.cargo test --features "signaling-client webrtc-str0m" --test str0m_net --test webrtc_signaling --test webrtc_transport
— 7/7 pass, includingburst_of_frames_arrives_in_order_under_backpressure.- Full default + feature test suites pass (107 spec/integration tests + loom +
stress + tokio + async + ffi + ipc + websocket).
Pre-existing, unrelated: benchmark-check
make check ends in benchmark-check, which is red at this commit AND was red
at tag v0.11.1 (already shipped): stale thread_safe instrumentation
lock-acquisition budgets that pre-date this release's content. The four overshoots
are in thread_safe contention benchmarks (unrelated module); src/str0m_net.rs
is the only source touched by v0.12.0 and cannot affect those counts. Tracked
separately in tasks/software/lazily-rs.md as #lzbenchbudget.
Publish checklist
cargo publish(dry-run verified clean).gh release create v0.12.0 --notes-file RELEASE_NOTES_v0.12.0.md --title "lazily v0.12.0".- Rotate the crates.io token if expired before step 1.
lazily v0.11.1
lazily v0.11.1
Patch release over v0.11.0. Last published: crates.io 0.11.0.
Tag v0.11.1 points at this release commit on main.
Highlights
Hardening and verification-coverage fixes over the v0.11.0 reactive core. No
API changes, no new features, no breaking behavior — safe bump for downstream
users. All changes are patch-level and backward-compatible.
Fixes
- #lzrepourl —
Cargo.tomlrepositoryfield corrected from the stale
btakita/lazily-rsto the canonicallazily-hub/lazily-rs(matching
book.tomlandgit remote origin). Fixes the crates.io/docs.rs repository
link. 1cee080—EncodeError/DecodeErrorenums insrc/ipc.rsare now
gated on#[cfg(any(feature = "ffi", feature = "ipc-binary"))]to match their
impl blocks. Eliminates dead-code warnings when building withwebrtc-str0m
(which enablesipcwithoutffi/ipc-binary).8665e29— hardened all 12id.0 as usizecasts inthread_safe.rsto
usize::try_fromvia anode_index()helper, preventing silent truncation on
32-bit targets. AddedFrom<serde_json::Error>(ffi) and
From<postcard::Error>(ipc-binary) forEncodeError/DecodeErrorfor
ergonomic?propagation.- #lzloomtimeout — the
inline_seqlock_envelope_rejects_torn_and_stale_under_concurrent_publish
Loom exhaustive model never terminated (>5 min state-space blowup across the
full 6-atomic envelope + two threads + driving body). Switched to a
preemption-boundedloom::model::Builder(bound 4, 60 s duration cap). The
bound is validated by injecting a torn-read regression and confirming the
model still flags it. The two single-property seqlock models stay exhaustive;
SPEC and source doc-comments corrected from "exhaustive" to
"preemption-bounded" for the combined envelope model.
Verification
cargo build --all-features,cargo clippy --all-features --all-targets -- -D warnings,
cargo fmt --checkclean.- Full default + feature test suites pass.
cargo test --features loom --test thread_safe_loom: 15/15 in ~82 s (was
non-terminating).cargo publish --dry-run: 72 files, ~234 KiB compressed, clean.
Publish checklist
cargo publish(dry-run verified clean).gh release create v0.11.1 --notes-file RELEASE_NOTES_v0.11.1.md --title "lazily v0.11.1".- Rotate the crates.io token if expired before step 1.
lazily v0.11.0
lazily v0.11.0
Prepared for the operator publish (#12b1). Last published: crates.io 0.10.3.
Tag v0.11.0 is intended to point at this release commit on main.
Highlights
This minor lands eager Signal primitives across the single-threaded,
thread-safe, and async contexts, plus a full WebRTC DataChannel transport stack
on top of the v0.10.x reactive core: a sans-IO str0m backend, a real-socket
networked backend, a WebSocket fallback, signaling, and the glue that drives a
complete handshake end to end.
Features
- #3dmm / #x7sp - add
SignalHandle,ThreadSafeSignalHandle, and
AsyncSignalHandleas eager derived values backed by memo slots plus puller
effects. Signals provide always-materializedv1 -> v2updates with no
observable unset window, inherit memo equality suppression, and are documented
in the README, SPEC, PROTOCOL, and mdBook docs. - #lzwebrtcwire — wire
SignalingClienttoStr0mNet. New
webrtc_signalingmodule (offer_to_peer/answer_next_offer) owns the full
SDP offer/answer + trickled-ICE handshake overSignalingClient, pumping
frames intoaccept_answer/add_remote_candidateuntil the data channel
opens. Integration test brokers two realSignalingClientWebSocket peers
through an in-process #yxjw-protocol loopback relay and proves a
permission-filteredSnapshotcrosses the negotiated channel. - #lzwebrtcnet — networked str0m
DataChannelbackend (Str0mNet) over a
real UDP socket with the str0m DTLS/SCTP/ICE driver. - #97xn — multi-channel reactive bridge hub.
- #akp3 — WebSocket
DataChannelbackend (in-process loopback over a real
WS handshake). - #webrtcbackend — concrete sans-IO str0m
DataChannelbackend. - #webrtc2 / #webrtc3 — WebRTC
DataChannelIPC transport abstraction,
loopback integration tests, and Criterion benchmarks.
CI / tests
- #lzleanmodel - CI now builds the sibling Lean formal model so protocol
invariants stay checked alongside the Rust suite. - #lzspecconf — IPC conformance run against the canonical lazily-spec
fixtures. - #k03k / #lzasync — deterministic async resolve-loop window coverage.
Remaining (operator-gated)
- Live two-host / NAT validation of
Str0mNetthrough the deployed #yxjw
Cloudflare Worker (#lzwebrtcnet-e2e, part of #h6qb) — cannot be done in CI.
Publish checklist (#12b1)
cargo publish(dry-run verified clean: 72 files, 233 KiB compressed).gh release create v0.11.0 --notes-file RELEASE_NOTES_v0.11.0.md --title "lazily v0.11.0".- Rotate the crates.io token if expired before step 1.
v0.10.1 — fix async get_async benign-race panics
lazily 0.10.1
Patch release. Fixes a concurrency bug in the async feature (#k03k).
get_async no longer panics on benign races
ThreadSafeContext was unaffected; this is AsyncContext-only. Under contended set_cell + get_async, get_async asserted two states that are reachable under concurrency and panicked:
unreachable!("get() already checked Resolved")— the slot can transitionComputing → Resolvedbetween theget()fast-path check (which drops the lock) and the re-lock.panic!("get_async: notifier dropped unexpectedly")— a superseded (stale-revision) or invalidated compute drops allwatchsenders without a finalResolvedsend.
get_async is now an outer re-resolve loop that reads authoritative slot state and, on a dropped notifier, restarts to observe the current state instead of aborting. The published value was always correct — this only removes the panics.
Regression test (async_context_concurrent_set_and_get_async_never_panics_k03k, multi-thread tokio, 8 workers × 250 iters) fails deterministically against 0.10.0 and passes on 0.10.1. No thread_safe/API changes since 0.10.0.
v0.10.0 — selectable read strategy + inline small-Copy seqlock
lazily 0.10.0
Two ThreadSafeContext cached-read improvements.
Runtime-selectable read strategy (#rdstrat1)
Both the low-concurrency (parking_lot::RwLock) and high-concurrency (arc-swap) cached-read paths are compiled in and chosen at context construction via ThreadSafeContext::with_read_strategy.
Breaking default change: the default flips from arc-swap (0.9.0) to LowConcurrency — opt into HighConcurrency explicitly. arc-swap wins at 8+ cores but regresses uncontended; LowConcurrency is the better default for typical workloads.
Inline small-Copy seqlock fast path (#rdstrat2)
New CachedReadStorage::Inline variant stores small Copy slot values inline behind a wait-free single-writer / multi-reader seqlock ([AtomicU8; 24], relaxed per-byte atomics, crossbeam-style seq fences) — no heap Arc, no refcount traffic on read or publish, optimal under both read strategies.
- Opt-in via new
slot_copy/computed_copy/memo_copyconstructors. AutomaticCopydetection is impossible on stable Rust (pre-monomorphization method resolution can't select aCopy-gated impl without the bound; needs nightlyspecialization). - Transparent fallback to the strategy path above 24 bytes or alignment 16.
- Exhaustively Loom-modeled: torn-read refusal, full-envelope torn+stale refusal under concurrent publish, and post-invalidation stale rejection.
Full make check (fmt, clippy -D warnings, all feature suites, Loom 15/15) green.
v0.9.0
Full Changelog: v0.8.0...v0.9.0
v0.6.0
v0.6.0
Performance
- #lazyperf33: Hybrid InvalidationPlan — Vec for ≤16 nodes, HashMap above threshold
- #lazyperf34: Fix finish_batch O(n²) dedup — extend + sort_unstable + dedup
- #lazyperf35: Eliminate scratch buffer allocations on effect path
- #lazyperf36: Add std_sync_mutex feature for A/B benchmarking
- #lazyperf25–32: Vec node storage, SmallVec edges, Rc closures, parking_lot mutex, Vec-based invalidation plans, slot ID reuse
Features
- Handle-centric
getmethods forCellHandleandSlotHandle vec_edgesandstd_sync_mutexfeature flags for benchmarking- Thread-safe invalidation plan with latency evidence
- Contention regression budgets and effect contention profiles
Infrastructure
- GitHub Actions CI workflow
BENCHMARKS.mdextracted from READMEmake checkworkflow for local verification
lazily 0.5.0
Full Changelog: v0.4.0...v0.5.0
v0.1.1
v0.1.0
Initial release of lazily — lazy reactive signals for Rust.
Features
Context— container for all slots and cellsSlotHandle<T>— lazily-computed cached values with automatic dependency trackingCellHandle<T>— mutable values with PartialEq-guarded invalidation- Thread-local tracking stack for automatic dependency discovery
- Lazy semantics: dependents cleared on invalidation, recomputed only on access
- Dynamic dependency graphs (edges re-discovered on recomputation)