1+ use std:: sync:: mpsc:: { self , RecvTimeoutError } ;
12use std:: time:: Duration ;
23
34use spawned_rt:: threads:: { self as rt, CancellationToken , JoinHandle } ;
@@ -9,29 +10,58 @@ pub struct TimerHandle {
910 pub cancellation_token : CancellationToken ,
1011}
1112
12- // Sends a message after a given period to the specified Actor. The task terminates
13- // once the send has completed
13+ /// Sends a message after a given period to the specified Actor.
14+ ///
15+ /// The timer respects both its own cancellation token and the Actor's
16+ /// cancellation token. If either is cancelled, the timer wakes up immediately
17+ /// and exits without sending the message.
1418pub fn send_after < T > ( period : Duration , mut handle : ActorRef < T > , message : T :: Message ) -> TimerHandle
1519where
1620 T : Actor + ' static ,
1721{
1822 let cancellation_token = CancellationToken :: new ( ) ;
19- let mut cloned_token = cancellation_token. clone ( ) ;
20- let mut actor_cancellation_token = handle. cancellation_token ( ) ;
23+ let timer_token = cancellation_token. clone ( ) ;
24+ let actor_token = handle. cancellation_token ( ) ;
25+
26+ // Channel to wake the timer thread on cancellation
27+ let ( wake_tx, wake_rx) = mpsc:: channel :: < ( ) > ( ) ;
28+
29+ // Register wake-up on timer cancellation
30+ let wake_tx1 = wake_tx. clone ( ) ;
31+ timer_token. on_cancel ( Box :: new ( move || {
32+ let _ = wake_tx1. send ( ( ) ) ;
33+ } ) ) ;
34+
35+ // Register wake-up on actor cancellation
36+ actor_token. on_cancel ( Box :: new ( move || {
37+ let _ = wake_tx. send ( ( ) ) ;
38+ } ) ) ;
39+
2140 let join_handle = rt:: spawn ( move || {
22- rt:: sleep ( period) ;
23- // Only send if neither the timer nor the actor has been cancelled
24- if !cloned_token. is_cancelled ( ) && !actor_cancellation_token. is_cancelled ( ) {
25- let _ = handle. send ( message) ;
26- } ;
41+ match wake_rx. recv_timeout ( period) {
42+ Err ( RecvTimeoutError :: Timeout ) => {
43+ // Timer expired - send if still valid
44+ if !timer_token. is_cancelled ( ) && !actor_token. is_cancelled ( ) {
45+ let _ = handle. send ( message) ;
46+ }
47+ }
48+ Ok ( ( ) ) | Err ( RecvTimeoutError :: Disconnected ) => {
49+ // Woken early by cancellation - exit without sending
50+ }
51+ }
2752 } ) ;
53+
2854 TimerHandle {
2955 join_handle,
3056 cancellation_token,
3157 }
3258}
3359
34- // Sends a message to the specified Actor repeatedly after `Time` milliseconds.
60+ /// Sends a message to the specified Actor repeatedly at the given interval.
61+ ///
62+ /// The timer respects both its own cancellation token and the Actor's
63+ /// cancellation token. If either is cancelled, the timer wakes up immediately
64+ /// and exits.
3565pub fn send_interval < T > (
3666 period : Duration ,
3767 mut handle : ActorRef < T > ,
@@ -41,17 +71,34 @@ where
4171 T : Actor + ' static ,
4272{
4373 let cancellation_token = CancellationToken :: new ( ) ;
44- let mut cloned_token = cancellation_token. clone ( ) ;
45- let mut actor_cancellation_token = handle. cancellation_token ( ) ;
46- let join_handle = rt:: spawn ( move || loop {
47- rt:: sleep ( period) ;
48- // Stop if either the timer or the actor has been cancelled
49- if cloned_token. is_cancelled ( ) || actor_cancellation_token. is_cancelled ( ) {
50- break ;
51- } else {
74+ let timer_token = cancellation_token. clone ( ) ;
75+ let actor_token = handle. cancellation_token ( ) ;
76+
77+ // Channel to wake the timer thread on cancellation
78+ let ( wake_tx, wake_rx) = mpsc:: channel :: < ( ) > ( ) ;
79+
80+ // Register wake-up on timer cancellation
81+ let wake_tx1 = wake_tx. clone ( ) ;
82+ timer_token. on_cancel ( Box :: new ( move || {
83+ let _ = wake_tx1. send ( ( ) ) ;
84+ } ) ) ;
85+
86+ // Register wake-up on actor cancellation
87+ actor_token. on_cancel ( Box :: new ( move || {
88+ let _ = wake_tx. send ( ( ) ) ;
89+ } ) ) ;
90+
91+ let join_handle = rt:: spawn ( move || {
92+ while let Err ( RecvTimeoutError :: Timeout ) = wake_rx. recv_timeout ( period) {
93+ // Timer expired - send if still valid
94+ if timer_token. is_cancelled ( ) || actor_token. is_cancelled ( ) {
95+ break ;
96+ }
5297 let _ = handle. send ( message. clone ( ) ) ;
53- } ;
98+ }
99+ // If we exit the loop via Ok(()) or Disconnected, cancellation occurred
54100 } ) ;
101+
55102 TimerHandle {
56103 join_handle,
57104 cancellation_token,
0 commit comments