21
21
//!
22
22
//! Each timer has a state field associated with it. This field contains either
23
23
//! the current scheduled time, or a special flag value indicating its state.
24
- //! This state can either indicate that the timer is firing (and thus will be fired
25
- //! with an `Ok(())` result soon) or that it has already been fired/deregistered.
24
+ //! This state can either indicate that the timer is on the 'pending' queue (and
25
+ //! thus will be fired with an `Ok(())` result soon) or that it has already been
26
+ //! fired/deregistered.
26
27
//!
27
28
//! This single state field allows for code that is firing the timer to
28
29
//! synchronize with any racing `reset` calls reliably.
48
49
//! There is of course a race condition between timer reset and timer
49
50
//! expiration. If the driver fails to observe the updated expiration time, it
50
51
//! could trigger expiration of the timer too early. However, because
51
- //! [`mark_firing `][mark_firing ] performs a compare-and-swap, it will identify this race and
52
- //! refuse to mark the timer as firing .
52
+ //! [`mark_pending `][mark_pending ] performs a compare-and-swap, it will identify this race and
53
+ //! refuse to mark the timer as pending .
53
54
//!
54
- //! [mark_firing ]: TimerHandle::mark_firing
55
+ //! [mark_pending ]: TimerHandle::mark_pending
55
56
56
57
use crate :: loom:: cell:: UnsafeCell ;
57
58
use crate :: loom:: sync:: atomic:: AtomicU64 ;
@@ -69,9 +70,9 @@ use std::{marker::PhantomPinned, pin::Pin, ptr::NonNull};
69
70
70
71
type TimerResult = Result < ( ) , crate :: time:: error:: Error > ;
71
72
72
- pub ( super ) const STATE_DEREGISTERED : u64 = u64:: MAX ;
73
- const STATE_FIRING : u64 = STATE_DEREGISTERED - 1 ;
74
- const STATE_MIN_VALUE : u64 = STATE_FIRING ;
73
+ const STATE_DEREGISTERED : u64 = u64:: MAX ;
74
+ const STATE_PENDING_FIRE : u64 = STATE_DEREGISTERED - 1 ;
75
+ const STATE_MIN_VALUE : u64 = STATE_PENDING_FIRE ;
75
76
/// The largest safe integer to use for ticks.
76
77
///
77
78
/// This value should be updated if any other signal values are added above.
@@ -122,6 +123,10 @@ impl StateCell {
122
123
}
123
124
}
124
125
126
+ fn is_pending ( & self ) -> bool {
127
+ self . state . load ( Ordering :: Relaxed ) == STATE_PENDING_FIRE
128
+ }
129
+
125
130
/// Returns the current expiration time, or None if not currently scheduled.
126
131
fn when ( & self ) -> Option < u64 > {
127
132
let cur_state = self . state . load ( Ordering :: Relaxed ) ;
@@ -157,28 +162,26 @@ impl StateCell {
157
162
}
158
163
}
159
164
160
- /// Marks this timer firing, if its scheduled time is not after `not_after`.
165
+ /// Marks this timer as being moved to the pending list, if its scheduled
166
+ /// time is not after `not_after`.
161
167
///
162
168
/// If the timer is scheduled for a time after `not_after`, returns an Err
163
169
/// containing the current scheduled time.
164
170
///
165
171
/// SAFETY: Must hold the driver lock.
166
- unsafe fn mark_firing ( & self , not_after : u64 ) -> Result < ( ) , u64 > {
172
+ unsafe fn mark_pending ( & self , not_after : u64 ) -> Result < ( ) , u64 > {
167
173
// Quick initial debug check to see if the timer is already fired. Since
168
174
// firing the timer can only happen with the driver lock held, we know
169
175
// we shouldn't be able to "miss" a transition to a fired state, even
170
176
// with relaxed ordering.
171
177
let mut cur_state = self . state . load ( Ordering :: Relaxed ) ;
178
+
172
179
loop {
173
- // Because its state is STATE_DEREGISTERED, it has been fired.
174
- if cur_state == STATE_DEREGISTERED {
175
- break Err ( cur_state) ;
176
- }
177
180
// improve the error message for things like
178
181
// https://github.com/tokio-rs/tokio/issues/3675
179
182
assert ! (
180
183
cur_state < STATE_MIN_VALUE ,
181
- "mark_firing called when the timer entry is in an invalid state"
184
+ "mark_pending called when the timer entry is in an invalid state"
182
185
) ;
183
186
184
187
if cur_state > not_after {
@@ -187,7 +190,7 @@ impl StateCell {
187
190
188
191
match self . state . compare_exchange_weak (
189
192
cur_state,
190
- STATE_FIRING ,
193
+ STATE_PENDING_FIRE ,
191
194
Ordering :: AcqRel ,
192
195
Ordering :: Acquire ,
193
196
) {
@@ -334,6 +337,11 @@ pub(crate) struct TimerShared {
334
337
/// Only accessed under the entry lock.
335
338
pointers : linked_list:: Pointers < TimerShared > ,
336
339
340
+ /// The expiration time for which this entry is currently registered.
341
+ /// Generally owned by the driver, but is accessed by the entry when not
342
+ /// registered.
343
+ cached_when : AtomicU64 ,
344
+
337
345
/// Current state. This records whether the timer entry is currently under
338
346
/// the ownership of the driver, and if not, its current state (not
339
347
/// complete, fired, error, etc).
@@ -348,6 +356,7 @@ unsafe impl Sync for TimerShared {}
348
356
impl std:: fmt:: Debug for TimerShared {
349
357
fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
350
358
f. debug_struct ( "TimerShared" )
359
+ . field ( "cached_when" , & self . cached_when . load ( Ordering :: Relaxed ) )
351
360
. field ( "state" , & self . state )
352
361
. finish ( )
353
362
}
@@ -365,12 +374,40 @@ impl TimerShared {
365
374
pub ( super ) fn new ( shard_id : u32 ) -> Self {
366
375
Self {
367
376
shard_id,
377
+ cached_when : AtomicU64 :: new ( 0 ) ,
368
378
pointers : linked_list:: Pointers :: new ( ) ,
369
379
state : StateCell :: default ( ) ,
370
380
_p : PhantomPinned ,
371
381
}
372
382
}
373
383
384
+ /// Gets the cached time-of-expiration value.
385
+ pub ( super ) fn cached_when ( & self ) -> u64 {
386
+ // Cached-when is only accessed under the driver lock, so we can use relaxed
387
+ self . cached_when . load ( Ordering :: Relaxed )
388
+ }
389
+
390
+ /// Gets the true time-of-expiration value, and copies it into the cached
391
+ /// time-of-expiration value.
392
+ ///
393
+ /// SAFETY: Must be called with the driver lock held, and when this entry is
394
+ /// not in any timer wheel lists.
395
+ pub ( super ) unsafe fn sync_when ( & self ) -> u64 {
396
+ let true_when = self . true_when ( ) ;
397
+
398
+ self . cached_when . store ( true_when, Ordering :: Relaxed ) ;
399
+
400
+ true_when
401
+ }
402
+
403
+ /// Sets the cached time-of-expiration value.
404
+ ///
405
+ /// SAFETY: Must be called with the driver lock held, and when this entry is
406
+ /// not in any timer wheel lists.
407
+ unsafe fn set_cached_when ( & self , when : u64 ) {
408
+ self . cached_when . store ( when, Ordering :: Relaxed ) ;
409
+ }
410
+
374
411
/// Returns the true time-of-expiration value, with relaxed memory ordering.
375
412
pub ( super ) fn true_when ( & self ) -> u64 {
376
413
self . state . when ( ) . expect ( "Timer already fired" )
@@ -383,6 +420,7 @@ impl TimerShared {
383
420
/// in the timer wheel.
384
421
pub ( super ) unsafe fn set_expiration ( & self , t : u64 ) {
385
422
self . state . set_expiration ( t) ;
423
+ self . cached_when . store ( t, Ordering :: Relaxed ) ;
386
424
}
387
425
388
426
/// Sets the true time-of-expiration only if it is after the current.
@@ -552,8 +590,16 @@ impl TimerEntry {
552
590
}
553
591
554
592
impl TimerHandle {
555
- pub ( super ) unsafe fn true_when ( & self ) -> u64 {
556
- unsafe { self . inner . as_ref ( ) . true_when ( ) }
593
+ pub ( super ) unsafe fn cached_when ( & self ) -> u64 {
594
+ unsafe { self . inner . as_ref ( ) . cached_when ( ) }
595
+ }
596
+
597
+ pub ( super ) unsafe fn sync_when ( & self ) -> u64 {
598
+ unsafe { self . inner . as_ref ( ) . sync_when ( ) }
599
+ }
600
+
601
+ pub ( super ) unsafe fn is_pending ( & self ) -> bool {
602
+ unsafe { self . inner . as_ref ( ) . state . is_pending ( ) }
557
603
}
558
604
559
605
/// Forcibly sets the true and cached expiration times to the given tick.
@@ -564,16 +610,27 @@ impl TimerHandle {
564
610
self . inner . as_ref ( ) . set_expiration ( tick) ;
565
611
}
566
612
567
- /// Attempts to mark this entry as firing . If the expiration time is after
613
+ /// Attempts to mark this entry as pending . If the expiration time is after
568
614
/// `not_after`, however, returns an Err with the current expiration time.
569
615
///
570
616
/// If an `Err` is returned, the `cached_when` value will be updated to this
571
617
/// new expiration time.
572
618
///
573
619
/// SAFETY: The caller must ensure that the handle remains valid, the driver
574
620
/// lock is held, and that the timer is not in any wheel linked lists.
575
- pub ( super ) unsafe fn mark_firing ( & self , not_after : u64 ) -> Result < ( ) , u64 > {
576
- self . inner . as_ref ( ) . state . mark_firing ( not_after)
621
+ /// After returning Ok, the entry must be added to the pending list.
622
+ pub ( super ) unsafe fn mark_pending ( & self , not_after : u64 ) -> Result < ( ) , u64 > {
623
+ match self . inner . as_ref ( ) . state . mark_pending ( not_after) {
624
+ Ok ( ( ) ) => {
625
+ // mark this as being on the pending queue in cached_when
626
+ self . inner . as_ref ( ) . set_cached_when ( u64:: MAX ) ;
627
+ Ok ( ( ) )
628
+ }
629
+ Err ( tick) => {
630
+ self . inner . as_ref ( ) . set_cached_when ( tick) ;
631
+ Err ( tick)
632
+ }
633
+ }
577
634
}
578
635
579
636
/// Attempts to transition to a terminal state. If the state is already a
0 commit comments