Skip to content

Commit 066ca17

Browse files
committed
an attempt at a singleton pausible idle callback for each scheduler. suffers from nondeterministic deadlock and also pending scheduler messages on scheduler shutdown.
1 parent 680eb71 commit 066ca17

File tree

2 files changed

+83
-19
lines changed

2 files changed

+83
-19
lines changed

src/libstd/rt/sched.rs

Lines changed: 69 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ use cell::Cell;
3030
use rand::{XorShiftRng, RngUtil};
3131
use iterator::{range};
3232
use vec::{OwnedVector};
33+
use rt::uv::idle::IdleWatcher;
3334

3435
/// The Scheduler is responsible for coordinating execution of Coroutines
3536
/// on a single thread. When the scheduler is running it is owned by
@@ -76,8 +77,11 @@ pub struct Scheduler {
7677
/// them to.
7778
friend_handle: Option<SchedHandle>,
7879
/// A fast XorShift rng for scheduler use
79-
rng: XorShiftRng
80-
80+
rng: XorShiftRng,
81+
/// An IdleWatcher
82+
idle_watcher: IdleWatcher,
83+
/// A flag to indicate whether or not the idle callback is active.
84+
idle_flag: bool
8185
}
8286

8387
pub struct SchedHandle {
@@ -124,6 +128,9 @@ impl Scheduler {
124128
friend: Option<SchedHandle>)
125129
-> Scheduler {
126130

131+
let mut event_loop = event_loop;
132+
let idle_watcher = IdleWatcher::new(event_loop.uvio.uv_loop());
133+
127134
Scheduler {
128135
sleeper_list: sleeper_list,
129136
message_queue: MessageQueue::new(),
@@ -138,7 +145,9 @@ impl Scheduler {
138145
metrics: SchedMetrics::new(),
139146
run_anything: run_anything,
140147
friend_handle: friend,
141-
rng: XorShiftRng::new()
148+
rng: XorShiftRng::new(),
149+
idle_watcher: idle_watcher,
150+
idle_flag: true
142151
}
143152
}
144153

@@ -151,6 +160,8 @@ impl Scheduler {
151160
// scheduler task and bootstrap into it.
152161
pub fn bootstrap(~self, task: ~Task) {
153162

163+
let mut this = self;
164+
154165
// Initialize the TLS key.
155166
local_ptr::init_tls_key();
156167

@@ -161,10 +172,17 @@ impl Scheduler {
161172
// task, put it in TLS.
162173
Local::put::(sched_task);
163174

175+
// Before starting our first task, make sure the idle callback
176+
// is active. As we do not start in the sleep state this is
177+
// important.
178+
do this.idle_watcher.start |_idle_watcher, _status| {
179+
Scheduler::run_sched_once();
180+
}
181+
164182
// Now, as far as all the scheduler state is concerned, we are
165183
// inside the "scheduler" context. So we can act like the
166184
// scheduler and resume the provided task.
167-
self.resume_task_immediately(task);
185+
this.resume_task_immediately(task);
168186

169187
// Now we are back in the scheduler context, having
170188
// successfully run the input task. Start by running the
@@ -201,7 +219,7 @@ impl Scheduler {
201219
// Always run through the scheduler loop at least once so that
202220
// we enter the sleep state and can then be woken up by other
203221
// schedulers.
204-
self_sched.event_loop.callback(Scheduler::run_sched_once);
222+
// self_sched.event_loop.callback(Scheduler::run_sched_once);
205223

206224
// This is unsafe because we need to place the scheduler, with
207225
// the event_loop inside, inside our task. But we still need a
@@ -235,7 +253,11 @@ impl Scheduler {
235253
// already have a scheduler stored in our local task, so we
236254
// start off by taking it. This is the only path through the
237255
// scheduler where we get the scheduler this way.
238-
let sched = Local::take::<Scheduler>();
256+
let mut sched = Local::take::<Scheduler>();
257+
258+
// Assume that we need to continue idling unless we reach the
259+
// end of this function without performing an action.
260+
sched.activate_idle();
239261

240262
// Our first task is to read mail to see if we have important
241263
// messages.
@@ -282,15 +304,40 @@ impl Scheduler {
282304
sched.sleepy = true;
283305
let handle = sched.make_handle();
284306
sched.sleeper_list.push(handle);
307+
// Since we are sleeping, deactivate the idle callback.
308+
sched.pause_idle();
285309
} else {
286310
rtdebug!("not sleeping, already doing so or no_sleep set");
311+
// We may not be sleeping, but we still need to deactivate
312+
// the idle callback.
313+
sched.pause_idle();
287314
}
288315

289316
// Finished a cycle without using the Scheduler. Place it back
290317
// in TLS.
291318
Local::put(sched);
292319
}
293320

321+
fn activate_idle(&mut self) {
322+
if self.idle_flag {
323+
rtdebug!("idle flag already set, not reactivating idle watcher");
324+
} else {
325+
rtdebug!("idle flag was false, reactivating idle watcher");
326+
self.idle_flag = true;
327+
self.idle_watcher.restart();
328+
}
329+
}
330+
331+
fn pause_idle(&mut self) {
332+
if !self.idle_flag {
333+
rtdebug!("idle flag false, not stopping idle watcher");
334+
} else {
335+
rtdebug!("idle flag true, stopping idle watcher");
336+
self.idle_flag = false;
337+
self.idle_watcher.stop();
338+
}
339+
}
340+
294341
pub fn make_handle(&mut self) -> SchedHandle {
295342
let remote = self.event_loop.remote_callback(Scheduler::run_sched_once);
296343

@@ -312,7 +359,7 @@ impl Scheduler {
312359

313360
// We push the task onto our local queue clone.
314361
this.work_queue.push(task);
315-
this.event_loop.callback(Scheduler::run_sched_once);
362+
// this.event_loop.callback(Scheduler::run_sched_once);
316363

317364
// We've made work available. Notify a
318365
// sleeping scheduler.
@@ -346,30 +393,34 @@ impl Scheduler {
346393
// * Scheduler-context operations
347394

348395
// This function returns None if the scheduler is "used", or it
349-
// returns the still-available scheduler.
396+
// returns the still-available scheduler. Note: currently
397+
// considers *any* message receive a use and returns None.
350398
fn interpret_message_queue(~self) -> Option<~Scheduler> {
351399

352400
let mut this = self;
353401
match this.message_queue.pop() {
354402
Some(PinnedTask(task)) => {
355-
this.event_loop.callback(Scheduler::run_sched_once);
403+
// this.event_loop.callback(Scheduler::run_sched_once);
356404
let mut task = task;
357405
task.give_home(Sched(this.make_handle()));
358406
this.resume_task_immediately(task);
359407
return None;
360408
}
361409
Some(TaskFromFriend(task)) => {
362-
this.event_loop.callback(Scheduler::run_sched_once);
410+
// this.event_loop.callback(Scheduler::run_sched_once);
363411
rtdebug!("got a task from a friend. lovely!");
364-
return this.sched_schedule_task(task);
412+
this.sched_schedule_task(task).map_move(Local::put);
413+
return None;
365414
}
366415
Some(Wake) => {
367-
this.event_loop.callback(Scheduler::run_sched_once);
416+
// this.event_loop.callback(Scheduler::run_sched_once);
368417
this.sleepy = false;
369-
return Some(this);
418+
Local::put(this);
419+
return None;
420+
// return Some(this);
370421
}
371422
Some(Shutdown) => {
372-
this.event_loop.callback(Scheduler::run_sched_once);
423+
// this.event_loop.callback(Scheduler::run_sched_once);
373424
if this.sleepy {
374425
// There may be an outstanding handle on the
375426
// sleeper list. Pop them all to make sure that's
@@ -388,11 +439,10 @@ impl Scheduler {
388439
// event loop references we will shut down.
389440
this.no_sleep = true;
390441
this.sleepy = false;
391-
// YYY: Does a shutdown count as a "use" of the
392-
// scheduler? This seems to work - so I'm leaving it
393-
// this way despite not having a solid rational for
394-
// why I should return the scheduler here.
395-
return Some(this);
442+
443+
Local::put(this);
444+
return None;
445+
// return Some(this);
396446
}
397447
None => {
398448
return Some(this);

src/libstd/rt/uv/idle.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,20 @@ impl IdleWatcher {
4848
}
4949
}
5050

51+
pub fn restart(&mut self) {
52+
unsafe {
53+
assert!(0 == uvll::idle_start(self.native_handle(), idle_cb))
54+
};
55+
56+
extern fn idle_cb(handle: *uvll::uv_idle_t, status: c_int) {
57+
let mut idle_watcher: IdleWatcher = NativeHandle::from_native_handle(handle);
58+
let data = idle_watcher.get_watcher_data();
59+
let cb: &IdleCallback = data.idle_cb.get_ref();
60+
let status = status_to_maybe_uv_error(idle_watcher, status);
61+
(*cb)(idle_watcher, status);
62+
}
63+
}
64+
5165
pub fn stop(&mut self) {
5266
// NB: Not resetting the Rust idle_cb to None here because `stop` is
5367
// likely called from *within* the idle callback, causing a use after

0 commit comments

Comments
 (0)