Fixed a memory leak caused by the singleton idle callback failing to close correctly. The problem was that the close function requires running inside a callback in the event loop, but we were trying to close the idle watcher after the loop returned from run. The fix was to just call run again to process this callback. There is an additional tweak to move the initialization logic fully into bootstrap, so tasks that do not ever call run do not have problems destructing.

This commit is contained in:
toddaaro
2013-08-20 15:33:20 -07:00
parent e5ccf13668
commit 352ac2efdf
2 changed files with 17 additions and 14 deletions
+16 -13
View File
@@ -80,7 +80,7 @@ pub struct Scheduler {
/// A fast XorShift rng for scheduler use
rng: XorShiftRng,
/// A toggleable idle callback
idle_callback: ~PausibleIdleCallback
idle_callback: Option<~PausibleIdleCallback>
}
impl Scheduler {
@@ -107,9 +107,6 @@ pub fn new_special(event_loop: ~EventLoopObject,
friend: Option<SchedHandle>)
-> Scheduler {
let mut event_loop = event_loop;
let idle_callback = event_loop.pausible_idle_callback();
Scheduler {
sleeper_list: sleeper_list,
message_queue: MessageQueue::new(),
@@ -125,7 +122,7 @@ pub fn new_special(event_loop: ~EventLoopObject,
run_anything: run_anything,
friend_handle: friend,
rng: XorShiftRng::new(),
idle_callback: idle_callback
idle_callback: None
}
}
@@ -140,6 +137,9 @@ pub fn bootstrap(~self, task: ~Task) {
let mut this = self;
// Build an Idle callback.
this.idle_callback = Some(this.event_loop.pausible_idle_callback());
// Initialize the TLS key.
local_ptr::init_tls_key();
@@ -153,7 +153,7 @@ pub fn bootstrap(~self, task: ~Task) {
// Before starting our first task, make sure the idle callback
// is active. As we do not start in the sleep state this is
// important.
this.idle_callback.start(Scheduler::run_sched_once);
this.idle_callback.get_mut_ref().start(Scheduler::run_sched_once);
// Now, as far as all the scheduler state is concerned, we are
// inside the "scheduler" context. So we can act like the
@@ -169,6 +169,12 @@ pub fn bootstrap(~self, task: ~Task) {
rtdebug!("starting scheduler %u", sched.sched_id());
sched.run();
// Close the idle callback.
let mut sched = Local::take::<Scheduler>();
sched.idle_callback.get_mut_ref().close();
// Make one go through the loop to run the close callback.
sched.run();
// Now that we are done with the scheduler, clean up the
// scheduler task. Do so by removing it from TLS and manually
// cleaning up the memory it uses. As we didn't actually call
@@ -182,9 +188,6 @@ pub fn bootstrap(~self, task: ~Task) {
let message = stask.sched.get_mut_ref().message_queue.pop();
assert!(message.is_none());
// Close the idle callback.
stask.sched.get_mut_ref().idle_callback.close();
stask.destroyed = true;
}
@@ -230,7 +233,7 @@ fn run_sched_once() {
// Assume that we need to continue idling unless we reach the
// end of this function without performing an action.
sched.idle_callback.resume();
sched.idle_callback.get_mut_ref().resume();
// First we check for scheduler messages, these are higher
// priority than regular tasks.
@@ -257,12 +260,12 @@ fn run_sched_once() {
let handle = sched.make_handle();
sched.sleeper_list.push(handle);
// Since we are sleeping, deactivate the idle callback.
sched.idle_callback.pause();
sched.idle_callback.get_mut_ref().pause();
} else {
rtdebug!("not sleeping, already doing so or no_sleep set");
// We may not be sleeping, but we still need to deactivate
// the idle callback.
sched.idle_callback.pause();
sched.idle_callback.get_mut_ref().pause();
}
// Finished a cycle without using the Scheduler. Place it back
@@ -461,7 +464,7 @@ pub fn enqueue_task(&mut self, task: ~Task) {
// We push the task onto our local queue clone.
this.work_queue.push(task);
this.idle_callback.resume();
this.idle_callback.get_mut_ref().resume();
// We've made work available. Notify a
// sleeping scheduler.
+1 -1
View File
@@ -176,7 +176,7 @@ pub fn close(&mut self) {
self.pause();
if !self.closed {
self.closed = true;
self.watcher.close(||());
self.watcher.close(||{});
}
}
}