Skip to main content

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}