Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
25 changes: 25 additions & 0 deletions crates/uffs-daemon/src/handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -499,6 +499,31 @@ impl RequestHandler {

/// Handle `refresh` method — spawns refresh in background, returns
/// immediate ack.
///
/// # Task ownership
///
/// The spawned refresh task is **fire-and-forget** — the
/// `JoinHandle` is intentionally discarded. The closure captures
/// an `Arc<IndexManager>` clone, keeping the manager alive for the
/// refresh duration even if the original client connection drops.
///
/// * **Owner:** none held; the runtime owns the task.
/// * **Shutdown:** none — [`IndexManager::refresh`] runs to completion
/// regardless of daemon shutdown signal. Acceptable because refresh is
/// short-lived (seconds), and the daemon's
/// `await_shutdown_then_force_exit` watchdog will `process::exit` after
/// the load-task timeout, terminating any in-flight refresh with the
/// runtime.
/// * **Error obs.:** [`IndexManager::refresh`] logs internally; no upward
/// propagation. The immediate ack returned to the client asserts only
/// that the refresh was scheduled, not that it succeeded — clients poll
/// `status` for completion.
/// * **Cancel behavior:** not cancellable; runs to completion or process
/// exit.
///
/// See Phase 10c task-ownership inventory.
///
/// [`IndexManager::refresh`]: crate::index::IndexManager::refresh
fn handle_refresh(&self, id: u64, req: &RpcRequest) -> String {
let refresh_params: RefreshParams = req
.params
Expand Down
20 changes: 20 additions & 0 deletions crates/uffs-daemon/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -689,6 +689,26 @@ fn spawn_ipc_servers(
});
tracing::info!("IPC server task spawned");

// Task ownership (Phase 10c): the Windows named-pipe IPC server is
// **fire-and-forget** — the `_pipe_task` `JoinHandle` is bound but
// never `.abort()`-ed. Rationale (mirrors the parent fn rustdoc):
//
// * **Owner:** the binding `_pipe_task` lives until end-of-scope in
// `spawn_ipc_servers`; the task itself outlives the binding (Tokio detaches
// a spawned task once its `JoinHandle` drops).
// * **Shutdown:** none cooperative — the pipe `accept` loop has no
// cancellation hook; the `await_shutdown_then_force_exit` watchdog
// `process::exit`s the daemon, terminating the task with the runtime.
// * **Error obs.:** body logs via `tracing::error!`; outer `JoinHandle` drop
// discards the result.
// * **Cancel behavior:** process-exit only.
//
// The sibling AF_UNIX task IS returned + held + `.abort()`-ed —
// that's the "primary" IPC transport on Unix. On Windows, both
// transports coexist (AF_UNIX for tools using the cross-platform
// socket path; named-pipe for native Windows tooling); aborting
// just one and letting the other ride out process exit is a
// deliberate asymmetry.
#[cfg(windows)]
let _pipe_task = {
let pipe_index = Arc::clone(idx);
Expand Down
Loading