Skip to content

Commit 173365c

Browse files
committed
Rework the idle callback to have a safer interface
It turns out that the uv implementation would cause use-after-free if the idle callback was used after the call to `close`, and additionally nothing would ever really work that well if `start()` were called twice. To change this, the `start` and `close` methods were removed in favor of specifying the callback at creation, and allowing destruction to take care of closing the watcher.
1 parent c654b0e commit 173365c

File tree

5 files changed

+79
-86
lines changed

5 files changed

+79
-86
lines changed

src/librustuv/idle.rs

Lines changed: 66 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,11 @@ pub struct IdleWatcher {
1919
handle: *uvll::uv_idle_t,
2020
idle_flag: bool,
2121
closed: bool,
22-
callback: Option<~Callback>,
22+
callback: ~Callback,
2323
}
2424

2525
impl IdleWatcher {
26-
pub fn new(loop_: &mut Loop) -> ~IdleWatcher {
26+
pub fn new(loop_: &mut Loop, cb: ~Callback) -> ~IdleWatcher {
2727
let handle = UvHandle::alloc(None::<IdleWatcher>, uvll::UV_IDLE);
2828
assert_eq!(unsafe {
2929
uvll::uv_idle_init(loop_.handle, handle)
@@ -32,7 +32,7 @@ impl IdleWatcher {
3232
handle: handle,
3333
idle_flag: false,
3434
closed: false,
35-
callback: None,
35+
callback: cb,
3636
};
3737
return me.install();
3838
}
@@ -64,12 +64,6 @@ impl IdleWatcher {
6464
}
6565

6666
impl PausibleIdleCallback for IdleWatcher {
67-
fn start(&mut self, cb: ~Callback) {
68-
assert!(self.callback.is_none());
69-
self.callback = Some(cb);
70-
assert_eq!(unsafe { uvll::uv_idle_start(self.handle, idle_cb) }, 0)
71-
self.idle_flag = true;
72-
}
7367
fn pause(&mut self) {
7468
if self.idle_flag == true {
7569
assert_eq!(unsafe {uvll::uv_idle_stop(self.handle) }, 0);
@@ -82,84 +76,93 @@ impl PausibleIdleCallback for IdleWatcher {
8276
self.idle_flag = true;
8377
}
8478
}
85-
fn close(&mut self) {
86-
self.pause();
87-
if !self.closed {
88-
self.closed = true;
89-
self.close_async_();
90-
}
91-
}
9279
}
9380

9481
impl UvHandle<uvll::uv_idle_t> for IdleWatcher {
9582
fn uv_handle(&self) -> *uvll::uv_idle_t { self.handle }
9683
}
9784

9885
extern fn idle_cb(handle: *uvll::uv_idle_t, status: c_int) {
86+
if status == uvll::ECANCELED { return }
9987
assert_eq!(status, 0);
10088
let idle: &mut IdleWatcher = unsafe { UvHandle::from_uv_handle(&handle) };
101-
assert!(idle.callback.is_some());
102-
idle.callback.get_mut_ref().call();
89+
idle.callback.call();
90+
}
91+
92+
impl Drop for IdleWatcher {
93+
fn drop(&mut self) {
94+
self.pause();
95+
self.close_async_();
96+
}
10397
}
10498

10599
#[cfg(test)]
106100
mod test {
107-
108-
use Loop;
109101
use super::*;
110-
use std::unstable::run_in_bare_thread;
102+
use std::rt::tube::Tube;
103+
use std::rt::rtio::{Callback, PausibleIdleCallback};
104+
use super::super::run_uv_loop;
105+
106+
struct MyCallback(Tube<int>, int);
107+
impl Callback for MyCallback {
108+
fn call(&mut self) {
109+
match *self {
110+
MyCallback(ref mut tube, val) => tube.send(val)
111+
}
112+
}
113+
}
111114

112115
#[test]
113-
#[ignore(reason = "valgrind - loop destroyed before watcher?")]
114-
fn idle_new_then_close() {
115-
do run_in_bare_thread {
116-
let mut loop_ = Loop::new();
117-
let idle_watcher = { IdleWatcher::new(&mut loop_) };
118-
idle_watcher.close(||());
116+
fn not_used() {
117+
do run_uv_loop |l| {
118+
let cb = ~MyCallback(Tube::new(), 1);
119+
let _idle = IdleWatcher::new(l, cb as ~Callback);
119120
}
120121
}
121122

122123
#[test]
123-
fn idle_smoke_test() {
124-
do run_in_bare_thread {
125-
let mut loop_ = Loop::new();
126-
let mut idle_watcher = { IdleWatcher::new(&mut loop_) };
127-
let mut count = 10;
128-
let count_ptr: *mut int = &mut count;
129-
do idle_watcher.start |idle_watcher, status| {
130-
let mut idle_watcher = idle_watcher;
131-
assert!(status.is_none());
132-
if unsafe { *count_ptr == 10 } {
133-
idle_watcher.stop();
134-
idle_watcher.close(||());
135-
} else {
136-
unsafe { *count_ptr = *count_ptr + 1; }
137-
}
138-
}
139-
loop_.run();
140-
loop_.close();
141-
assert_eq!(count, 10);
124+
fn smoke_test() {
125+
do run_uv_loop |l| {
126+
let mut tube = Tube::new();
127+
let cb = ~MyCallback(tube.clone(), 1);
128+
let mut idle = IdleWatcher::new(l, cb as ~Callback);
129+
idle.resume();
130+
tube.recv();
142131
}
143132
}
144133

145134
#[test]
146-
fn idle_start_stop_start() {
147-
do run_in_bare_thread {
148-
let mut loop_ = Loop::new();
149-
let mut idle_watcher = { IdleWatcher::new(&mut loop_) };
150-
do idle_watcher.start |idle_watcher, status| {
151-
let mut idle_watcher = idle_watcher;
152-
assert!(status.is_none());
153-
idle_watcher.stop();
154-
do idle_watcher.start |idle_watcher, status| {
155-
assert!(status.is_none());
156-
let mut idle_watcher = idle_watcher;
157-
idle_watcher.stop();
158-
idle_watcher.close(||());
159-
}
160-
}
161-
loop_.run();
162-
loop_.close();
135+
fn fun_combinations_of_methods() {
136+
do run_uv_loop |l| {
137+
let mut tube = Tube::new();
138+
let cb = ~MyCallback(tube.clone(), 1);
139+
let mut idle = IdleWatcher::new(l, cb as ~Callback);
140+
idle.resume();
141+
tube.recv();
142+
idle.pause();
143+
idle.resume();
144+
idle.resume();
145+
tube.recv();
146+
idle.pause();
147+
idle.pause();
148+
idle.resume();
149+
tube.recv();
150+
}
151+
}
152+
153+
#[test]
154+
fn pause_pauses() {
155+
do run_uv_loop |l| {
156+
let mut tube = Tube::new();
157+
let cb = ~MyCallback(tube.clone(), 1);
158+
let mut idle1 = IdleWatcher::new(l, cb as ~Callback);
159+
let cb = ~MyCallback(tube.clone(), 2);
160+
let mut idle2 = IdleWatcher::new(l, cb as ~Callback);
161+
idle2.resume();
162+
assert_eq!(tube.recv(), 2);
163+
idle2.pause();
164+
idle1.resume();
165+
assert_eq!(tube.recv(), 1);
163166
}
164167
}
165168
}

src/librustuv/uvio.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -148,8 +148,8 @@ impl EventLoop for UvEventLoop {
148148
IdleWatcher::onetime(self.uvio.uv_loop(), f);
149149
}
150150

151-
fn pausible_idle_callback(&mut self) -> ~PausibleIdleCallback {
152-
IdleWatcher::new(self.uvio.uv_loop()) as ~PausibleIdleCallback
151+
fn pausible_idle_callback(&mut self, cb: ~Callback) -> ~PausibleIdleCallback {
152+
IdleWatcher::new(self.uvio.uv_loop(), cb) as ~PausibleIdleCallback
153153
}
154154

155155
fn remote_callback(&mut self, f: ~Callback) -> ~RemoteCallback {

src/libstd/rt/basic.rs

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ impl BasicLoop {
107107
match self.idle {
108108
Some(idle) => {
109109
if (*idle).active {
110-
(*idle).work.get_mut_ref().call();
110+
(*idle).work.call();
111111
}
112112
}
113113
None => {}
@@ -150,8 +150,8 @@ impl EventLoop for BasicLoop {
150150
}
151151

152152
// XXX: Seems like a really weird requirement to have an event loop provide.
153-
fn pausible_idle_callback(&mut self) -> ~PausibleIdleCallback {
154-
let callback = ~BasicPausible::new(self);
153+
fn pausible_idle_callback(&mut self, cb: ~Callback) -> ~PausibleIdleCallback {
154+
let callback = ~BasicPausible::new(self, cb);
155155
rtassert!(self.idle.is_none());
156156
unsafe {
157157
let cb_ptr: &*mut BasicPausible = cast::transmute(&callback);
@@ -204,36 +204,27 @@ impl Drop for BasicRemote {
204204

205205
struct BasicPausible {
206206
eloop: *mut BasicLoop,
207-
work: Option<~Callback>,
207+
work: ~Callback,
208208
active: bool,
209209
}
210210

211211
impl BasicPausible {
212-
fn new(eloop: &mut BasicLoop) -> BasicPausible {
212+
fn new(eloop: &mut BasicLoop, cb: ~Callback) -> BasicPausible {
213213
BasicPausible {
214214
active: false,
215-
work: None,
215+
work: cb,
216216
eloop: eloop,
217217
}
218218
}
219219
}
220220

221221
impl PausibleIdleCallback for BasicPausible {
222-
fn start(&mut self, f: ~Callback) {
223-
rtassert!(!self.active && self.work.is_none());
224-
self.active = true;
225-
self.work = Some(f);
226-
}
227222
fn pause(&mut self) {
228223
self.active = false;
229224
}
230225
fn resume(&mut self) {
231226
self.active = true;
232227
}
233-
fn close(&mut self) {
234-
self.active = false;
235-
self.work = None;
236-
}
237228
}
238229

239230
impl Drop for BasicPausible {

src/libstd/rt/rtio.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ pub trait Callback {
3131
pub trait EventLoop {
3232
fn run(&mut self);
3333
fn callback(&mut self, proc());
34-
fn pausible_idle_callback(&mut self) -> ~PausibleIdleCallback;
34+
fn pausible_idle_callback(&mut self, ~Callback) -> ~PausibleIdleCallback;
3535
fn remote_callback(&mut self, ~Callback) -> ~RemoteCallback;
3636

3737
/// The asynchronous I/O services. Not all event loops may provide one
@@ -226,10 +226,8 @@ pub trait RtioTTY {
226226
}
227227

228228
pub trait PausibleIdleCallback {
229-
fn start(&mut self, f: ~Callback);
230229
fn pause(&mut self);
231230
fn resume(&mut self);
232-
fn close(&mut self);
233231
}
234232

235233
pub trait RtioSignal {}

src/libstd/rt/sched.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,8 @@ impl Scheduler {
169169
pub fn bootstrap(mut ~self, task: ~Task) {
170170

171171
// Build an Idle callback.
172-
self.idle_callback = Some(self.event_loop.pausible_idle_callback());
172+
let cb = ~SchedRunner as ~Callback;
173+
self.idle_callback = Some(self.event_loop.pausible_idle_callback(cb));
173174

174175
// Initialize the TLS key.
175176
local_ptr::init_tls_key();
@@ -184,7 +185,7 @@ impl Scheduler {
184185
// Before starting our first task, make sure the idle callback
185186
// is active. As we do not start in the sleep state this is
186187
// important.
187-
self.idle_callback.get_mut_ref().start(~SchedRunner as ~Callback);
188+
self.idle_callback.get_mut_ref().resume();
188189

189190
// Now, as far as all the scheduler state is concerned, we are
190191
// inside the "scheduler" context. So we can act like the
@@ -202,7 +203,7 @@ impl Scheduler {
202203

203204
// Close the idle callback.
204205
let mut sched: ~Scheduler = Local::take();
205-
sched.idle_callback.get_mut_ref().close();
206+
sched.idle_callback.take();
206207
// Make one go through the loop to run the close callback.
207208
sched.run();
208209

0 commit comments

Comments
 (0)