refactor(#2286): make transient set/clear private — RAII guard is the only door
This commit is contained in:
parent
9c3884e031
commit
05c91245c7
1 changed files with 14 additions and 8 deletions
|
|
@ -338,6 +338,9 @@ pub struct TransientState {
|
||||||
/// drop — including drop-via-cancellation, the path that bare
|
/// drop — including drop-via-cancellation, the path that bare
|
||||||
/// `set_transient` / `clear_transient` pairs leaked through. Holds an
|
/// `set_transient` / `clear_transient` pairs leaked through. Holds an
|
||||||
/// `Arc<Coordinator>` so the guard is freely returnable / movable.
|
/// `Arc<Coordinator>` so the guard is freely returnable / movable.
|
||||||
|
#[must_use = "the guard clears the transient when dropped; bind it for the operation's \
|
||||||
|
duration (`let _guard = coord.transient_guard(...)`). An unbound call drops \
|
||||||
|
it immediately and un-sets the transient at once — the exact footgun this guards against."]
|
||||||
pub struct TransientGuard {
|
pub struct TransientGuard {
|
||||||
coord: Arc<Coordinator>,
|
coord: Arc<Coordinator>,
|
||||||
name: String,
|
name: String,
|
||||||
|
|
@ -1158,13 +1161,13 @@ impl Coordinator {
|
||||||
|
|
||||||
/// Mark an agent as in-progress (only one state per agent for now).
|
/// Mark an agent as in-progress (only one state per agent for now).
|
||||||
///
|
///
|
||||||
/// Prefer `transient_guard` when possible — it auto-clears on drop
|
/// Private on purpose: the RAII [`TransientGuard`] (via
|
||||||
/// even if the surrounding future is cancelled (HTTP request
|
/// [`Coordinator::transient_guard`]) is the only door, so the paired
|
||||||
/// aborted, runtime shutdown mid-rebuild, panic between set and
|
/// `clear_transient` always runs on drop even if the surrounding future
|
||||||
/// clear). The bare `set_transient` / `clear_transient` pair leaks
|
/// is cancelled (HTTP request aborted, runtime shutdown mid-rebuild,
|
||||||
/// the transient on any of those paths and the dashboard then
|
/// panic). A bare set with no guaranteed clear would leak the transient
|
||||||
/// shows the agent stuck in "rebuilding…" forever.
|
/// and leave the dashboard stuck in "rebuilding…" forever.
|
||||||
pub fn set_transient(&self, name: &str, kind: TransientKind) {
|
fn set_transient(&self, name: &str, kind: TransientKind) {
|
||||||
self.transient.lock().unwrap().insert(
|
self.transient.lock().unwrap().insert(
|
||||||
name.to_owned(),
|
name.to_owned(),
|
||||||
TransientState {
|
TransientState {
|
||||||
|
|
@ -1188,7 +1191,10 @@ impl Coordinator {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn clear_transient(&self, name: &str) {
|
/// Clear an agent's transient state. Private: only reachable through
|
||||||
|
/// [`TransientGuard`]'s `Drop`, which guarantees it runs (see
|
||||||
|
/// [`Coordinator::set_transient`]).
|
||||||
|
fn clear_transient(&self, name: &str) {
|
||||||
let removed = self.transient.lock().unwrap().remove(name);
|
let removed = self.transient.lock().unwrap().remove(name);
|
||||||
if let Some(state) = removed {
|
if let Some(state) = removed {
|
||||||
// Stamp the tombstone so the crash watcher can still see
|
// Stamp the tombstone so the crash watcher can still see
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue