@@ -327,6 +327,11 @@ pub(crate) struct TimerHandle {
327327 inner : NonNull < TimerShared > ,
328328}
329329
330+ // SAFETY: TimerHandle is a pointer to TimerShared, which is Send + Sync.
331+ // The handle can be safely sent across threads.
332+ unsafe impl Send for TimerHandle { }
333+ unsafe impl Sync for TimerHandle { }
334+
330335pub ( super ) type EntryList = crate :: util:: linked_list:: LinkedList < TimerShared , TimerShared > ;
331336
332337/// The shared state structure of a timer. This structure is shared between the
@@ -356,6 +361,12 @@ pub(crate) struct TimerShared {
356361 /// complete, fired, error, etc).
357362 state : StateCell ,
358363
364+ /// Tracks whether this timer is in the buckets (true) or wheel (false).
365+ /// This is used during cleanup to determine where to remove the timer from.
366+ /// Accessed with relaxed ordering as it's only modified during registration
367+ /// under either the bucket lock or driver lock.
368+ in_buckets : crate :: loom:: sync:: atomic:: AtomicBool ,
369+
359370 _p : PhantomPinned ,
360371}
361372
@@ -388,6 +399,7 @@ impl TimerShared {
388399 registered_when : AtomicU64 :: new ( 0 ) ,
389400 pointers : linked_list:: Pointers :: new ( ) ,
390401 state : StateCell :: default ( ) ,
402+ in_buckets : crate :: loom:: sync:: atomic:: AtomicBool :: new ( false ) ,
391403 _p : PhantomPinned ,
392404 }
393405 }
@@ -415,7 +427,7 @@ impl TimerShared {
415427 ///
416428 /// SAFETY: Must be called with the driver lock held, and when this entry is
417429 /// not in any timer wheel lists.
418- unsafe fn set_registered_when ( & self , when : u64 ) {
430+ pub ( super ) unsafe fn set_registered_when ( & self , when : u64 ) {
419431 self . registered_when . store ( when, Ordering :: Relaxed ) ;
420432 }
421433
@@ -453,6 +465,17 @@ impl TimerShared {
453465 pub ( super ) fn might_be_registered ( & self ) -> bool {
454466 self . state . might_be_registered ( )
455467 }
468+
469+ /// Returns true if this timer is registered in the buckets (vs the wheel).
470+ pub ( super ) fn is_in_buckets ( & self ) -> bool {
471+ self . in_buckets . load ( Ordering :: Relaxed )
472+ }
473+
474+ /// Marks this timer as being in the buckets.
475+ /// SAFETY: Must be called while holding the bucket lock during insertion.
476+ pub ( super ) unsafe fn mark_in_buckets ( & self ) {
477+ self . in_buckets . store ( true , Ordering :: Relaxed ) ;
478+ }
456479}
457480
458481unsafe impl linked_list:: Link for TimerShared {
@@ -583,11 +606,19 @@ impl TimerEntry {
583606 }
584607 } ;
585608
586- if inner. extend_expiration ( tick) . is_ok ( ) {
609+ // For bucket timers, we cannot use extend_expiration because the timer handle
610+ // is physically located in a specific bucket. Changing the expiration would
611+ // leave the handle in the wrong bucket. So we skip extend_expiration and go
612+ // straight to reregister, which will insert into the correct new bucket.
613+ //
614+ // NOTE: Even when reregister=false (e.g., reset_without_reregister used by Interval),
615+ // bucket timers MUST still reregister to move the handle to the new bucket.
616+ // The reregister=false case is only valid for wheel timers which can use extend_expiration.
617+ if !inner. is_in_buckets ( ) && inner. extend_expiration ( tick) . is_ok ( ) {
587618 return ;
588619 }
589620
590- if reregister {
621+ if reregister || inner . is_in_buckets ( ) {
591622 unsafe {
592623 self . driver ( )
593624 . reregister ( & self . driver . driver ( ) . io , tick, inner. into ( ) ) ;
@@ -641,8 +672,14 @@ impl TimerHandle {
641672
642673 /// Forcibly sets the true and cached expiration times to the given tick.
643674 ///
644- /// SAFETY: The caller must ensure that the handle remains valid, the driver
645- /// lock is held, and that the timer is not in any wheel linked lists.
675+ /// SAFETY: The caller must ensure that the handle remains valid and that
676+ /// the timer is not in any wheel linked lists. Additionally, either:
677+ /// - The driver lock is held (for wheel-based timers), OR
678+ /// - The appropriate bucket lock is held (for bucket-based timers)
679+ ///
680+ /// The lock requirement ensures proper memory synchronization between the
681+ /// thread setting the expiration and the driver thread that will later
682+ /// fire the timer.
646683 pub ( super ) unsafe fn set_expiration ( & self , tick : u64 ) {
647684 self . inner . as_ref ( ) . set_expiration ( tick) ;
648685 }
@@ -670,6 +707,32 @@ impl TimerHandle {
670707 }
671708 }
672709
710+ /// Marks this timer as being in the buckets.
711+ /// SAFETY: Must be called while holding the bucket lock during insertion.
712+ pub ( super ) unsafe fn mark_in_buckets ( & self ) {
713+ self . inner . as_ref ( ) . mark_in_buckets ( )
714+ }
715+
716+ /// Unmarks this timer as being in the buckets.
717+ pub ( super ) unsafe fn unmark_in_buckets ( & self ) {
718+ self . inner
719+ . as_ref ( )
720+ . in_buckets
721+ . store ( false , crate :: loom:: sync:: atomic:: Ordering :: Relaxed ) ;
722+ }
723+
724+ /// Returns true if this timer is in the buckets (vs the wheel).
725+ /// SAFETY: The handle must be valid.
726+ pub ( super ) unsafe fn is_in_buckets_unsafe ( & self ) -> bool {
727+ unsafe { self . inner . as_ref ( ) . is_in_buckets ( ) }
728+ }
729+
730+ /// Returns true if this timer might still be registered (not yet fired).
731+ /// SAFETY: The handle must be valid.
732+ pub ( super ) unsafe fn might_be_registered ( & self ) -> bool {
733+ unsafe { self . inner . as_ref ( ) . might_be_registered ( ) }
734+ }
735+
673736 /// Attempts to transition to a terminal state. If the state is already a
674737 /// terminal state, does nothing.
675738 ///
0 commit comments