@@ -14,6 +14,7 @@ use std::rt::BlockedTask;
14
14
use std:: rt:: local:: Local ;
15
15
use std:: rt:: rtio:: RtioTimer ;
16
16
use std:: rt:: sched:: { Scheduler , SchedHandle } ;
17
+ use std:: util;
17
18
18
19
use uvll;
19
20
use super :: { Loop , UvHandle , ForbidUnwind , ForbidSwitch } ;
@@ -82,9 +83,13 @@ impl RtioTimer for TimerWatcher {
82
83
fn oneshot ( & mut self , msecs : u64 ) -> PortOne < ( ) > {
83
84
let ( port, chan) = oneshot ( ) ;
84
85
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
+ } ;
88
93
89
94
return port;
90
95
}
@@ -93,8 +98,14 @@ impl RtioTimer for TimerWatcher {
93
98
let ( port, chan) = stream ( ) ;
94
99
95
100
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
+ } ;
98
109
99
110
return port;
100
111
}
@@ -120,16 +131,24 @@ extern fn timer_cb(handle: *uvll::uv_timer_t, status: c_int) {
120
131
121
132
impl Drop for TimerWatcher {
122
133
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
+ } ;
127
145
}
128
146
}
129
147
130
148
#[ cfg( test) ]
131
149
mod test {
132
150
use super :: * ;
151
+ use std:: cell:: Cell ;
133
152
use std:: rt:: rtio:: RtioTimer ;
134
153
use super :: super :: local_loop;
135
154
@@ -205,6 +224,19 @@ mod test {
205
224
// which must wake up the task on the other end
206
225
}
207
226
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
+
208
240
#[ test]
209
241
fn sender_goes_away_oneshot ( ) {
210
242
let port = {
0 commit comments