diff options
| author | toddaaro <github@opprobrio.us> | 2013-08-15 11:13:41 -0700 |
|---|---|---|
| committer | toddaaro <github@opprobrio.us> | 2013-08-16 16:37:09 -0700 |
| commit | 066ca17eaae239a666579fc80fe9047aaac0599d (patch) | |
| tree | 1f8fac52d7e580e9bc2eafcb5366d31146aa43f1 /src/libstd | |
| parent | 680eb71564ebba5e76ce1e1a8287b30042332cc5 (diff) | |
| download | rust-066ca17eaae239a666579fc80fe9047aaac0599d.tar.gz rust-066ca17eaae239a666579fc80fe9047aaac0599d.zip | |
an attempt at a singleton pausible idle callback for each scheduler. suffers from nondeterministic deadlock and also pending scheduler messages on scheduler shutdown.
Diffstat (limited to 'src/libstd')
| -rw-r--r-- | src/libstd/rt/sched.rs | 88 | ||||
| -rw-r--r-- | src/libstd/rt/uv/idle.rs | 14 |
2 files changed, 83 insertions, 19 deletions
diff --git a/src/libstd/rt/sched.rs b/src/libstd/rt/sched.rs index ce4e64c47d2..58e1676b424 100644 --- a/src/libstd/rt/sched.rs +++ b/src/libstd/rt/sched.rs @@ -30,6 +30,7 @@ use cell::Cell; use rand::{XorShiftRng, RngUtil}; use iterator::{range}; use vec::{OwnedVector}; +use rt::uv::idle::IdleWatcher; /// The Scheduler is responsible for coordinating execution of Coroutines /// on a single thread. When the scheduler is running it is owned by @@ -76,8 +77,11 @@ pub struct Scheduler { /// them to. friend_handle: Option<SchedHandle>, /// A fast XorShift rng for scheduler use - rng: XorShiftRng - + rng: XorShiftRng, + /// An IdleWatcher + idle_watcher: IdleWatcher, + /// A flag to indicate whether or not the idle callback is active. + idle_flag: bool } pub struct SchedHandle { @@ -124,6 +128,9 @@ impl Scheduler { friend: Option<SchedHandle>) -> Scheduler { + let mut event_loop = event_loop; + let idle_watcher = IdleWatcher::new(event_loop.uvio.uv_loop()); + Scheduler { sleeper_list: sleeper_list, message_queue: MessageQueue::new(), @@ -138,7 +145,9 @@ impl Scheduler { metrics: SchedMetrics::new(), run_anything: run_anything, friend_handle: friend, - rng: XorShiftRng::new() + rng: XorShiftRng::new(), + idle_watcher: idle_watcher, + idle_flag: true } } @@ -151,6 +160,8 @@ impl Scheduler { // scheduler task and bootstrap into it. pub fn bootstrap(~self, task: ~Task) { + let mut this = self; + // Initialize the TLS key. local_ptr::init_tls_key(); @@ -161,10 +172,17 @@ impl Scheduler { // task, put it in TLS. Local::put::(sched_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. + do this.idle_watcher.start |_idle_watcher, _status| { + 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 // scheduler and resume the provided task. - self.resume_task_immediately(task); + this.resume_task_immediately(task); // Now we are back in the scheduler context, having // successfully run the input task. Start by running the @@ -201,7 +219,7 @@ impl Scheduler { // Always run through the scheduler loop at least once so that // we enter the sleep state and can then be woken up by other // schedulers. - self_sched.event_loop.callback(Scheduler::run_sched_once); +// self_sched.event_loop.callback(Scheduler::run_sched_once); // This is unsafe because we need to place the scheduler, with // the event_loop inside, inside our task. But we still need a @@ -235,7 +253,11 @@ impl Scheduler { // already have a scheduler stored in our local task, so we // start off by taking it. This is the only path through the // scheduler where we get the scheduler this way. - let sched = Local::take::<Scheduler>(); + let mut sched = Local::take::<Scheduler>(); + + // Assume that we need to continue idling unless we reach the + // end of this function without performing an action. + sched.activate_idle(); // Our first task is to read mail to see if we have important // messages. @@ -282,8 +304,13 @@ impl Scheduler { sched.sleepy = true; let handle = sched.make_handle(); sched.sleeper_list.push(handle); + // Since we are sleeping, deactivate the idle callback. + sched.pause_idle(); } 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.pause_idle(); } // Finished a cycle without using the Scheduler. Place it back @@ -291,6 +318,26 @@ impl Scheduler { Local::put(sched); } + fn activate_idle(&mut self) { + if self.idle_flag { + rtdebug!("idle flag already set, not reactivating idle watcher"); + } else { + rtdebug!("idle flag was false, reactivating idle watcher"); + self.idle_flag = true; + self.idle_watcher.restart(); + } + } + + fn pause_idle(&mut self) { + if !self.idle_flag { + rtdebug!("idle flag false, not stopping idle watcher"); + } else { + rtdebug!("idle flag true, stopping idle watcher"); + self.idle_flag = false; + self.idle_watcher.stop(); + } + } + pub fn make_handle(&mut self) -> SchedHandle { let remote = self.event_loop.remote_callback(Scheduler::run_sched_once); @@ -312,7 +359,7 @@ impl Scheduler { // We push the task onto our local queue clone. this.work_queue.push(task); - this.event_loop.callback(Scheduler::run_sched_once); +// this.event_loop.callback(Scheduler::run_sched_once); // We've made work available. Notify a // sleeping scheduler. @@ -346,30 +393,34 @@ impl Scheduler { // * Scheduler-context operations // This function returns None if the scheduler is "used", or it - // returns the still-available scheduler. + // returns the still-available scheduler. Note: currently + // considers *any* message receive a use and returns None. fn interpret_message_queue(~self) -> Option<~Scheduler> { let mut this = self; match this.message_queue.pop() { Some(PinnedTask(task)) => { - this.event_loop.callback(Scheduler::run_sched_once); +// this.event_loop.callback(Scheduler::run_sched_once); let mut task = task; task.give_home(Sched(this.make_handle())); this.resume_task_immediately(task); return None; } Some(TaskFromFriend(task)) => { - this.event_loop.callback(Scheduler::run_sched_once); +// this.event_loop.callback(Scheduler::run_sched_once); rtdebug!("got a task from a friend. lovely!"); - return this.sched_schedule_task(task); + this.sched_schedule_task(task).map_move(Local::put); + return None; } Some(Wake) => { - this.event_loop.callback(Scheduler::run_sched_once); +// this.event_loop.callback(Scheduler::run_sched_once); this.sleepy = false; - return Some(this); + Local::put(this); + return None; +// return Some(this); } Some(Shutdown) => { - this.event_loop.callback(Scheduler::run_sched_once); +// this.event_loop.callback(Scheduler::run_sched_once); if this.sleepy { // There may be an outstanding handle on the // sleeper list. Pop them all to make sure that's @@ -388,11 +439,10 @@ impl Scheduler { // event loop references we will shut down. this.no_sleep = true; this.sleepy = false; - // YYY: Does a shutdown count as a "use" of the - // scheduler? This seems to work - so I'm leaving it - // this way despite not having a solid rational for - // why I should return the scheduler here. - return Some(this); + + Local::put(this); + return None; +// return Some(this); } None => { return Some(this); diff --git a/src/libstd/rt/uv/idle.rs b/src/libstd/rt/uv/idle.rs index b73be9f7250..a21146620ca 100644 --- a/src/libstd/rt/uv/idle.rs +++ b/src/libstd/rt/uv/idle.rs @@ -48,6 +48,20 @@ impl IdleWatcher { } } + pub fn restart(&mut self) { + unsafe { + assert!(0 == uvll::idle_start(self.native_handle(), idle_cb)) + }; + + extern fn idle_cb(handle: *uvll::uv_idle_t, status: c_int) { + let mut idle_watcher: IdleWatcher = NativeHandle::from_native_handle(handle); + let data = idle_watcher.get_watcher_data(); + let cb: &IdleCallback = data.idle_cb.get_ref(); + let status = status_to_maybe_uv_error(idle_watcher, status); + (*cb)(idle_watcher, status); + } + } + pub fn stop(&mut self) { // NB: Not resetting the Rust idle_cb to None here because `stop` is // likely called from *within* the idle callback, causing a use after |
