pub struct HandleGuard { /* private fields */ }Expand description
Per-handle quiescing core. Lives inline inside each handle
struct. try_enter returns a guard that prevents _free from
completing until dropped; begin_free quiesces in-flight ops
and prevents new ones.
Implementations§
Source§impl HandleGuard
impl HandleGuard
Sourcepub fn try_enter(&self) -> Option<HandleOp<'_>>
pub fn try_enter(&self) -> Option<HandleOp<'_>>
Try to enter an FFI operation against this handle.
Increments active_ops first so a concurrent begin_free
is forced to observe the increment OR to set freeing first
(they synchronize via SeqCst). After the increment, we
re-check freeing: if free is in progress, the op cannot
proceed and we decrement back out. Otherwise we return a
guard whose Drop decrements.
Returns None if _free has already started — the caller
must surface a typed “shutting down / freed” error code and
MUST NOT touch any fields of the handle except this
HandleGuard (which lives in still-valid leaked memory).
Sourcepub fn begin_free(&self, deadline: Duration) -> bool
pub fn begin_free(&self, deadline: Duration) -> bool
Mark the handle as freeing and wait for in-flight ops to
drain. Returns true if THIS call won the race to flip
freeing AND in-flight ops drained within
FFI_HANDLE_FREE_DEADLINE. Returns false on timeout
OR if a prior caller already flipped freeing.
Single-winner contract. Only ONE caller across the
lifetime of this guard ever sees true. That winning
caller is the one that owns the right to take the inner
out of ManuallyDrop exactly once. Subsequent callers
(whether concurrent or strictly after) see false and
MUST NOT touch the inner — the winner has it (or had it,
and dropped it).
This is what makes _free idempotent: a second _free
call gates the ManuallyDrop::take behind this method’s
true return, so it bails before the double-take that
would UAF the inner allocation.
On timeout (winner observed freeing=false→true but
drain didn’t complete), the caller must NOT take the
inner — concurrent ops may still be holding it. Leak
inner along with the box.
Future try_enter calls will see freeing=true and bail,
regardless of whether the winner’s drain succeeded, timed
out, or this caller is the loser. “No NEW ops will start”
is set as soon as the winner flips the flag.