Replace address-only trust and pushed peer state with installation identities, SPKI-pinned QUIC, candidate-only discovery, and bounded responder-owned protocol-8 pulls. The runtime now owns each network generation and all admitted work through shutdown. Add exact bundled content identities, reproducible manifest publishing, capability-confined downloads, streaming BLAKE3 verification, quarantine and retry, and crash-recoverable download and install transactions. Ship generated fixture catalogs and fail closed when production manifests are absent. The Tauri backend exposes durable sharing policy, redacted identity state, and attempt-keyed transfer snapshots. Frontend consumption follows in the next commit. Repository-wide test certificates and protocol-7 paths are removed. BREAKING CHANGE: peers must use protocol 8 and exact catalog content artifacts; protocol-7 frames and shared-certificate identities are no longer accepted. Test Plan: - `just test` -- passed on the completed stack (708 workspace tests) - `just clippy` -- passed on the completed stack - `just build` -- passed with fixture catalogs on the completed stack - `just catalog-check-production` -- failed closed because the external production manifest corpus is absent - `git diff --cached --check` -- passed
143 lines
4.9 KiB
Rust
143 lines
4.9 KiB
Rust
//! Lexically scoped execution for finite blocking work.
|
|
|
|
use tokio::runtime::{Handle, RuntimeFlavor};
|
|
|
|
/// Runs finite blocking work without detaching it from the calling task.
|
|
///
|
|
/// On a multi-thread Tokio runtime, `block_in_place` lets the executor hand the
|
|
/// caller's other asynchronous work to another worker while this closure runs.
|
|
/// A current-thread runtime cannot make that handoff, so it executes the closure
|
|
/// directly, as does code running outside a Tokio runtime.
|
|
///
|
|
/// This function deliberately has no cancellation point: when it returns, the
|
|
/// closure has completed and all values owned by the closure have been dropped.
|
|
pub fn scoped_blocking<F, R>(work: F) -> R
|
|
where
|
|
F: FnOnce() -> R,
|
|
{
|
|
if matches!(
|
|
Handle::try_current().map(|handle| handle.runtime_flavor()),
|
|
Ok(RuntimeFlavor::MultiThread)
|
|
) {
|
|
tokio::task::block_in_place(work)
|
|
} else {
|
|
work()
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use std::{
|
|
sync::{Arc, Condvar, Mutex, mpsc},
|
|
thread,
|
|
time::Duration,
|
|
};
|
|
|
|
use tokio::runtime::{Handle, RuntimeFlavor};
|
|
|
|
use super::scoped_blocking;
|
|
|
|
struct DropSignal(mpsc::Sender<()>);
|
|
|
|
impl Drop for DropSignal {
|
|
fn drop(&mut self) {
|
|
let _ = self.0.send(());
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn runs_directly_without_a_runtime() {
|
|
let calling_thread = thread::current().id();
|
|
let execution_thread = scoped_blocking(|| thread::current().id());
|
|
|
|
assert_eq!(execution_thread, calling_thread);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn runs_directly_on_a_current_thread_runtime() {
|
|
assert_eq!(
|
|
Handle::current().runtime_flavor(),
|
|
RuntimeFlavor::CurrentThread
|
|
);
|
|
|
|
let calling_thread = thread::current().id();
|
|
let execution_thread = scoped_blocking(|| thread::current().id());
|
|
|
|
assert_eq!(execution_thread, calling_thread);
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
|
|
async fn abort_waits_for_in_place_work_to_finish() {
|
|
assert_eq!(
|
|
Handle::current().runtime_flavor(),
|
|
RuntimeFlavor::MultiThread
|
|
);
|
|
|
|
let gate = Arc::new((Mutex::new(false), Condvar::new()));
|
|
let gate_for_task = Arc::clone(&gate);
|
|
let gate_for_release_thread = Arc::clone(&gate);
|
|
let (entered_tx, entered_rx) = tokio::sync::oneshot::channel();
|
|
let (dropped_tx, dropped_rx) = mpsc::channel();
|
|
let (request_release, release_requested) = mpsc::channel();
|
|
|
|
// The timeout also makes this test fail safe: if an assertion panics,
|
|
// dropping `request_release` wakes this thread so the runtime can shut down.
|
|
let release_thread = thread::spawn(move || {
|
|
let _ = release_requested.recv_timeout(Duration::from_secs(2));
|
|
let (gate_open, wake) = &*gate_for_release_thread;
|
|
let mut gate_open = gate_open.lock().expect("release gate must not be poisoned");
|
|
*gate_open = true;
|
|
wake.notify_one();
|
|
});
|
|
|
|
let mut task = tokio::spawn(async move {
|
|
scoped_blocking(move || {
|
|
let _drop_signal = DropSignal(dropped_tx);
|
|
entered_tx
|
|
.send(())
|
|
.expect("test task must report entering blocking work");
|
|
|
|
let (gate_open, wake) = &*gate_for_task;
|
|
let gate_open = gate_open.lock().expect("task gate must not be poisoned");
|
|
let _gate_open = wake
|
|
.wait_while(gate_open, |gate_open| !*gate_open)
|
|
.expect("task gate must not be poisoned");
|
|
});
|
|
});
|
|
|
|
tokio::time::timeout(Duration::from_secs(2), entered_rx)
|
|
.await
|
|
.expect("blocking work must start")
|
|
.expect("blocking task must retain the entry sender");
|
|
|
|
task.abort();
|
|
|
|
assert!(
|
|
tokio::time::timeout(Duration::from_millis(50), &mut task)
|
|
.await
|
|
.is_err(),
|
|
"aborting the task must not complete it while in-place work is blocked"
|
|
);
|
|
assert_eq!(dropped_rx.try_recv(), Err(mpsc::TryRecvError::Empty));
|
|
|
|
request_release
|
|
.send(())
|
|
.expect("release thread must remain available");
|
|
release_thread
|
|
.join()
|
|
.expect("release thread must not panic");
|
|
|
|
let completion = tokio::time::timeout(Duration::from_secs(2), &mut task)
|
|
.await
|
|
.expect("task must finish after its blocking work is released");
|
|
match completion {
|
|
Ok(()) => {}
|
|
Err(error) if error.is_cancelled() => {}
|
|
Err(error) => panic!("blocking task failed unexpectedly: {error}"),
|
|
}
|
|
dropped_rx
|
|
.recv_timeout(Duration::from_secs(1))
|
|
.expect("blocking closure values must be dropped before task completion");
|
|
}
|
|
}
|