@@ -14,6 +14,7 @@ use std::rt::BlockedTask;
1414use std:: rt:: local:: Local ;
1515use std:: rt:: rtio:: RtioTimer ;
1616use std:: rt:: sched:: { Scheduler , SchedHandle } ;
17+ use std:: util;
1718
1819use uvll;
1920use super :: { Loop , UvHandle , ForbidUnwind , ForbidSwitch } ;
@@ -82,9 +83,13 @@ impl RtioTimer for TimerWatcher {
8283 fn oneshot ( & mut self , msecs : u64 ) -> PortOne < ( ) > {
8384 let ( port, chan) = oneshot ( ) ;
8485
85- let _m = self . fire_homing_missile ( ) ;
86- self . action = Some ( SendOnce ( chan) ) ;
87- self . start ( msecs, 0 ) ;
86+ // similarly to the destructor, we must drop the previous action outside
87+ // of the homing missile
88+ let _prev_action = {
89+ let _m = self . fire_homing_missile ( ) ;
90+ self . start ( msecs, 0 ) ;
91+ util:: replace ( & mut self . action , Some ( SendOnce ( chan) ) )
92+ } ;
8893
8994 return port;
9095 }
@@ -93,8 +98,14 @@ impl RtioTimer for TimerWatcher {
9398 let ( port, chan) = stream ( ) ;
9499
95100 let _m = self . fire_homing_missile ( ) ;
96- self . action = Some ( SendMany ( chan) ) ;
97- self . start ( msecs, msecs) ;
101+
102+ // similarly to the destructor, we must drop the previous action outside
103+ // of the homing missile
104+ let _prev_action = {
105+ let _m = self . fire_homing_missile ( ) ;
106+ self . start ( msecs, msecs) ;
107+ util:: replace ( & mut self . action , Some ( SendMany ( chan) ) )
108+ } ;
98109
99110 return port;
100111 }
@@ -120,16 +131,24 @@ extern fn timer_cb(handle: *uvll::uv_timer_t, status: c_int) {
120131
121132impl Drop for TimerWatcher {
122133 fn drop ( & mut self ) {
123- let _m = self . fire_homing_missile ( ) ;
124- self . action = None ;
125- self . stop ( ) ;
126- self . close_async_ ( ) ;
134+ // note that this drop is a little subtle. Dropping a channel which is
135+ // held internally may invoke some scheduling operations. We can't take
136+ // the channel unless we're on the home scheduler, but once we're on the
137+ // home scheduler we should never move. Hence, we take the timer's
138+ // action item and then move it outside of the homing block.
139+ let _action = {
140+ let _m = self . fire_homing_missile ( ) ;
141+ self . stop ( ) ;
142+ self . close_async_ ( ) ;
143+ self . action . take ( )
144+ } ;
127145 }
128146}
129147
130148#[ cfg( test) ]
131149mod test {
132150 use super :: * ;
151+ use std:: cell:: Cell ;
133152 use std:: rt:: rtio:: RtioTimer ;
134153 use super :: super :: local_loop;
135154
@@ -205,6 +224,19 @@ mod test {
205224 // which must wake up the task on the other end
206225 }
207226
227+ #[ test]
228+ fn reset_doesnt_switch_tasks ( ) {
229+ // similar test to the one above.
230+ let mut timer = TimerWatcher :: new ( local_loop ( ) ) ;
231+ let timer_port = Cell :: new ( timer. period ( 1000 ) ) ;
232+
233+ do spawn {
234+ timer_port. take ( ) . try_recv ( ) ;
235+ }
236+
237+ timer. oneshot ( 1 ) ;
238+ }
239+
208240 #[ test]
209241 fn sender_goes_away_oneshot ( ) {
210242 let port = {
0 commit comments