Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
f3354f6
feat(platform-wallet)!: shutdown() joins coordinator threads and retu…
lklimek Jun 22, 2026
261178e
fix(platform-wallet): RAII-guard is_syncing so a coordinator panic ca…
lklimek Jun 22, 2026
42d734d
refactor(rs-dash-async): add AtomicFlagGuard RAII helper
lklimek Jun 23, 2026
6e78b77
fix(platform-wallet): refine CoordinatorThreadStatus variants + tight…
lklimek Jun 23, 2026
5f80450
test(rs-dash-async): assert AtomicFlagGuard contract + add #[must_use]
lklimek Jun 23, 2026
6b2cd39
fix(platform-wallet): make coordinator passes cancellable + converge …
lklimek Jun 23, 2026
13a22dd
fix(platform-wallet): bound clear_shielded + tidy shutdown docs/logging
lklimek Jun 23, 2026
93b8954
fix(platform-wallet-ffi): timeout-bound the shielded sync stop bridge
lklimek Jun 23, 2026
747f5f0
Merge branch 'v3.1-dev' into feat/platform-wallet-shutdown-join
lklimek Jun 23, 2026
2bd9501
fix(platform-wallet)!: close residual coordinator-thread UAF on shutdown
lklimek Jun 23, 2026
7c975ed
fix(platform-wallet)!: surface non-clean shielded drain on clear/stop
lklimek Jun 23, 2026
5f63c95
fix(platform-wallet): reap prior coordinator thread outside backgroun…
lklimek Jun 23, 2026
2b068ba
fix(platform-wallet): close shielded epilogue TOCTOU + pin restart reap
lklimek Jun 24, 2026
5017ba1
fix(swift-sdk): retain wallet callback context on incomplete shutdown
lklimek Jun 24, 2026
b491773
test(platform-wallet): bound cleanup quiesce in restart-reap regressi…
lklimek Jun 24, 2026
76c8bee
fix(platform-wallet): track detached coordinator threads so shutdown(…
lklimek Jun 24, 2026
3cca1cf
perf(platform-wallet): drain coordinators concurrently in shutdown() …
lklimek Jun 24, 2026
8c52811
feat(dash-async): add shared ThreadRegistry worker-lifecycle engine
lklimek Jun 24, 2026
ac9a51a
feat(dash-async): key-scope parked orphans for any_alive_for()
lklimek Jun 24, 2026
d20aed0
refactor(platform-wallet): migrate sync coordinators onto shared Thre…
lklimek Jun 24, 2026
d190f29
test(dash-async): anchor DrainHook compile_fail doctest to E0277 + no…
lklimek Jun 24, 2026
3e81fc1
fix(dash-async,platform-wallet): harden ThreadRegistry lifecycle + do…
lklimek Jun 24, 2026
911f99f
refactor(platform-wallet): extract CoordinatorLifecycle to dedup the …
lklimek Jun 24, 2026
22647a7
fix(platform-wallet): raise quiescing gate in CoordinatorLifecycle::q…
lklimek Jun 25, 2026
7f3aeb5
fix(dash-async): park a restarted worker's prior under the slot lock …
lklimek Jun 25, 2026
41791c0
fix(platform-wallet-ffi): gate shielded_sync_stop success on orphan l…
lklimek Jun 25, 2026
4b099a9
fix(platform-wallet): bound clear_shielded's drain and hold its quies…
lklimek Jun 25, 2026
7be68c5
refactor(dash-async): full spawn-failure rollback + drop stale doc hi…
lklimek Jun 25, 2026
3821389
docs(swift-sdk): broaden deinit comment for shielded_sync_stop's orph…
lklimek Jun 25, 2026
748c4f8
fix(platform-wallet): make the quiescing<->is_syncing handshake self-…
lklimek Jun 25, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 9 additions & 1 deletion packages/rs-dash-async/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,20 @@ authors = ["Dash Core Team"]
license = "MIT"
description = "Async-sync bridging utilities for Dash Platform"

[features]
# Exposes cross-crate test seams (e.g. `ThreadRegistry::park_orphan_for_test`)
# so downstream crates can drive registry regression tests without shipping
# the seam in their production builds.
test-util = []

[dependencies]
thiserror = "2.0"
tracing = "0.1.41"

[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
tokio = { version = "1.40", features = ["rt", "rt-multi-thread", "time", "net"] }
tokio-util = { version = "0.7.12" }
futures = { version = "0.3.30" }

[dev-dependencies]
tokio = { version = "1.40", features = ["macros", "rt-multi-thread", "sync"] }
tokio = { version = "1.40", features = ["macros", "rt-multi-thread", "sync", "time"] }
64 changes: 64 additions & 0 deletions packages/rs-dash-async/src/atomic.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
use std::sync::atomic::{AtomicBool, Ordering};

/// RAII guard that clears an [`AtomicBool`] flag to `false` on drop.
///
/// Callers set the flag to `true` before constructing the guard (typically
/// via a `compare_exchange`); the guard resets it on every exit path,
/// including panics, so a panicked holder can never leave the flag wedged.
///
/// **Panic-strategy caveat:** the clear-on-panic guarantee relies on
/// destructors running while the stack unwinds, so it holds under
/// `panic = "unwind"` (the default). Under `panic = "abort"` — e.g. the
/// iOS release profiles — a panic aborts the process immediately and no
/// `Drop` runs; there is simply no "after" left for the flag to gate.
#[must_use = "AtomicFlagGuard clears the flag on drop; binding to `_` or using as a statement drops it immediately"]
pub struct AtomicFlagGuard<'a>(&'a AtomicBool);
Comment thread
Claudius-Maginificent marked this conversation as resolved.

impl<'a> AtomicFlagGuard<'a> {
/// Wrap `flag`. Does **not** set it to `true` — the caller is
/// responsible for doing that before constructing the guard.
pub fn new(flag: &'a AtomicBool) -> Self {
Comment thread
Claudius-Maginificent marked this conversation as resolved.
Self(flag)
}
}

impl Drop for AtomicFlagGuard<'_> {
fn drop(&mut self) {
self.0.store(false, Ordering::Release);
Comment thread
Claudius-Maginificent marked this conversation as resolved.
}
}

#[cfg(test)]
mod tests {
use super::*;
use std::panic::{catch_unwind, AssertUnwindSafe};

/// A guard constructed over a `true` flag holds it while in scope and
/// clears it to `false` on a normal scope exit.
#[test]
fn clears_flag_on_normal_drop() {
let flag = AtomicBool::new(true);
{
let _guard = AtomicFlagGuard::new(&flag);
assert!(flag.load(Ordering::Acquire), "flag stays set while held");
}
assert!(!flag.load(Ordering::Acquire), "flag cleared on drop");
}

/// The clear also runs while unwinding a panic — the load-bearing
/// property the sync coordinators lean on so a panicked pass can't
/// leave `is_syncing` latched and wedge `quiesce()`'s drain.
#[test]
fn clears_flag_while_unwinding_panic() {
let flag = AtomicBool::new(true);
let result = catch_unwind(AssertUnwindSafe(|| {
let _guard = AtomicFlagGuard::new(&flag);
panic!("boom while holding the guard");
}));
assert!(result.is_err(), "the panic propagated out of catch_unwind");
assert!(
!flag.load(Ordering::Acquire),
"Drop ran during unwinding and cleared the flag"
);
}
}
13 changes: 13 additions & 0 deletions packages/rs-dash-async/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,20 @@
//!
//! Provides [`block_on`] -- a function that bridges async futures into sync code,
//! handling multiple tokio runtime flavors (no runtime, current-thread, multi-thread).
//!
//! Also provides [`AtomicFlagGuard`] — a RAII guard for panic-safe `AtomicBool` flag resets,
//! and [`ThreadRegistry`] — a shared lifecycle engine for background OS-thread / tokio-task
//! workers (start, cancel, weight-ordered quiesce + join, orphan reap).

mod atomic;
mod block_on;
#[cfg(not(target_arch = "wasm32"))]
mod registry;

pub use atomic::AtomicFlagGuard;
pub use block_on::{block_on, AsyncError};
#[cfg(not(target_arch = "wasm32"))]
pub use registry::{
DrainHook, RegistryKey, ShutdownReport, ShutdownWeight, ThreadRegistry, WorkerConfig,
WorkerStatus, DEFAULT_JOIN_BUDGET, DEFAULT_REAP_BACKSTOP,
};
Loading
Loading