nexus_timer/handle.rs
1//! Timer handle — proof that a timer was scheduled with the intent to cancel.
2//!
3//! A `TimerHandle<T>` is an 8-byte, move-only token returned by `schedule()`.
4//! It must be explicitly consumed via `wheel.cancel(handle)` or
5//! `wheel.free(handle)`. Dropping a handle without consuming it is a
6//! programming error (caught by `debug_assert!` in debug builds).
7
8use std::fmt;
9use std::marker::PhantomData;
10
11use crate::entry::EntryPtr;
12
13/// Handle to a scheduled timer.
14///
15/// Returned by [`TimerWheel::schedule`](crate::TimerWheel::schedule) and
16/// [`TimerWheel::try_schedule`](crate::TimerWheel::try_schedule). Must be
17/// consumed by one of:
18///
19/// - [`TimerWheel::cancel`](crate::TimerWheel::cancel) — unlinks from the
20/// wheel and extracts the value.
21/// - [`TimerWheel::free`](crate::TimerWheel::free) — releases the handle,
22/// converting to fire-and-forget.
23///
24/// Dropping without consuming is a programming error (`debug_assert!` fires).
25///
26/// # Size
27///
28/// 8 bytes (one pointer). `!Send`, `!Sync`, `!Clone`, `!Copy`.
29#[must_use = "handles must be consumed via cancel() or free(), dropping leaks the timer slot"]
30pub struct TimerHandle<T> {
31 pub(crate) ptr: EntryPtr<T>,
32 // !Send, !Sync
33 _marker: PhantomData<*const ()>,
34}
35
36impl<T> TimerHandle<T> {
37 /// Creates a new handle from an entry pointer.
38 #[inline]
39 pub(crate) fn new(ptr: EntryPtr<T>) -> Self {
40 TimerHandle {
41 ptr,
42 _marker: PhantomData,
43 }
44 }
45}
46
47impl<T> Drop for TimerHandle<T> {
48 fn drop(&mut self) {
49 debug_assert!(
50 false,
51 "TimerHandle dropped without being consumed — call wheel.cancel() or wheel.free()"
52 );
53 }
54}
55
56impl<T> fmt::Debug for TimerHandle<T> {
57 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
58 f.debug_struct("TimerHandle")
59 .field("ptr", &self.ptr)
60 .finish()
61 }
62}