Skip to content

Commit f83835b

Browse files
committed
Moved the logic for a pausible idle callback into a new type - PausibleIdleCallback and placed the appropriate signatures in rtio and implementation into uvio.
1 parent 88d8baa commit f83835b

File tree

3 files changed

+103
-96
lines changed

3 files changed

+103
-96
lines changed

src/libstd/rt/rtio.rs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,22 +24,25 @@ pub type RtioTcpStreamObject = uvio::UvTcpStream;
2424
pub type RtioTcpListenerObject = uvio::UvTcpListener;
2525
pub type RtioUdpSocketObject = uvio::UvUdpSocket;
2626
pub type RtioTimerObject = uvio::UvTimer;
27+
pub type PausibleIdleCallback = uvio::UvPausibleIdleCallback;
2728

2829
pub trait EventLoop {
2930
fn run(&mut self);
3031
fn callback(&mut self, ~fn());
32+
fn pausible_idle_callback(&mut self) -> ~PausibleIdleCallback;
3133
fn callback_ms(&mut self, ms: u64, ~fn());
3234
fn remote_callback(&mut self, ~fn()) -> ~RemoteCallbackObject;
3335
/// The asynchronous I/O services. Not all event loops may provide one
3436
fn io<'a>(&'a mut self) -> Option<&'a mut IoFactoryObject>;
3537
}
3638

3739
pub trait RemoteCallback {
38-
/// Trigger the remote callback. Note that the number of times the callback
39-
/// is run is not guaranteed. All that is guaranteed is that, after calling 'fire',
40-
/// the callback will be called at least once, but multiple callbacks may be coalesced
41-
/// and callbacks may be called more often requested. Destruction also triggers the
42-
/// callback.
40+
/// Trigger the remote callback. Note that the number of times the
41+
/// callback is run is not guaranteed. All that is guaranteed is
42+
/// that, after calling 'fire', the callback will be called at
43+
/// least once, but multiple callbacks may be coalesced and
44+
/// callbacks may be called more often requested. Destruction also
45+
/// triggers the callback.
4346
fn fire(&mut self);
4447
}
4548

src/libstd/rt/sched.rs

Lines changed: 23 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,13 @@ use super::message_queue::MessageQueue;
2323
use rt::kill::BlockedTask;
2424
use rt::local_ptr;
2525
use rt::local::Local;
26-
use rt::rtio::RemoteCallback;
26+
use rt::rtio::{RemoteCallback, PausibleIdleCallback};
2727
use rt::metrics::SchedMetrics;
2828
use borrow::{to_uint};
2929
use cell::Cell;
3030
use rand::{XorShiftRng, RngUtil};
3131
use iterator::{range};
3232
use vec::{OwnedVector};
33-
use rt::uv::idle::IdleWatcher;
3433

3534
/// The Scheduler is responsible for coordinating execution of Coroutines
3635
/// on a single thread. When the scheduler is running it is owned by
@@ -78,10 +77,13 @@ pub struct Scheduler {
7877
friend_handle: Option<SchedHandle>,
7978
/// A fast XorShift rng for scheduler use
8079
rng: XorShiftRng,
81-
/// An IdleWatcher
82-
idle_watcher: Option<IdleWatcher>,
83-
/// A flag to indicate whether or not the idle callback is active.
84-
idle_flag: bool
80+
/// A toggleable idle callback
81+
idle_callback: ~PausibleIdleCallback
82+
}
83+
84+
enum CleanupJob {
85+
DoNothing,
86+
GiveTask(~Task, UnsafeTaskReceiver)
8587
}
8688

8789
pub struct SchedHandle {
@@ -97,11 +99,6 @@ pub enum SchedMessage {
9799
TaskFromFriend(~Task)
98100
}
99101

100-
enum CleanupJob {
101-
DoNothing,
102-
GiveTask(~Task, UnsafeTaskReceiver)
103-
}
104-
105102
impl Scheduler {
106103

107104
pub fn sched_id(&self) -> uint { to_uint(self) }
@@ -126,7 +123,10 @@ impl Scheduler {
126123
sleeper_list: SleeperList,
127124
run_anything: bool,
128125
friend: Option<SchedHandle>)
129-
-> Scheduler {
126+
-> Scheduler {
127+
128+
let mut event_loop = event_loop;
129+
let idle_callback = event_loop.pausible_idle_callback();
130130

131131
Scheduler {
132132
sleeper_list: sleeper_list,
@@ -142,9 +142,8 @@ impl Scheduler {
142142
metrics: SchedMetrics::new(),
143143
run_anything: run_anything,
144144
friend_handle: friend,
145-
rng: XorShiftRng::new(),
146-
idle_watcher: None,
147-
idle_flag: false
145+
rng: XorShiftRng::new(),
146+
idle_callback: idle_callback
148147
}
149148
}
150149

@@ -172,7 +171,7 @@ impl Scheduler {
172171
// Before starting our first task, make sure the idle callback
173172
// is active. As we do not start in the sleep state this is
174173
// important.
175-
this.activate_idle();
174+
this.idle_callback.start(Scheduler::run_sched_once);
176175

177176
// Now, as far as all the scheduler state is concerned, we are
178177
// inside the "scheduler" context. So we can act like the
@@ -194,14 +193,17 @@ impl Scheduler {
194193
// cleaning up the memory it uses. As we didn't actually call
195194
// task.run() on the scheduler task we never get through all
196195
// the cleanup code it runs.
197-
let mut stask = Local::take::<Task>();
196+
let mut stask = Local::take::<Task>();
198197

199198
rtdebug!("stopping scheduler %u", stask.sched.get_ref().sched_id());
200199

201200
// Should not have any messages
202201
let message = stask.sched.get_mut_ref().message_queue.pop();
203202
assert!(message.is_none());
204203

204+
// Close the idle callback.
205+
stask.sched.get_mut_ref().idle_callback.close();
206+
205207
stask.destroyed = true;
206208
}
207209

@@ -211,11 +213,6 @@ impl Scheduler {
211213

212214
let mut self_sched = self;
213215

214-
// Always run through the scheduler loop at least once so that
215-
// we enter the sleep state and can then be woken up by other
216-
// schedulers.
217-
// self_sched.event_loop.callback(Scheduler::run_sched_once);
218-
219216
// This is unsafe because we need to place the scheduler, with
220217
// the event_loop inside, inside our task. But we still need a
221218
// mutable reference to the event_loop to give it the "run"
@@ -252,7 +249,7 @@ impl Scheduler {
252249

253250
// Assume that we need to continue idling unless we reach the
254251
// end of this function without performing an action.
255-
sched.activate_idle();
252+
sched.idle_callback.resume();
256253

257254
// Our first task is to read mail to see if we have important
258255
// messages.
@@ -300,59 +297,19 @@ impl Scheduler {
300297
let handle = sched.make_handle();
301298
sched.sleeper_list.push(handle);
302299
// Since we are sleeping, deactivate the idle callback.
303-
sched.pause_idle();
300+
sched.idle_callback.pause();
304301
} else {
305302
rtdebug!("not sleeping, already doing so or no_sleep set");
306303
// We may not be sleeping, but we still need to deactivate
307304
// the idle callback.
308-
sched.pause_idle();
305+
sched.idle_callback.pause();
309306
}
310307

311308
// Finished a cycle without using the Scheduler. Place it back
312309
// in TLS.
313310
Local::put(sched);
314311
}
315312

316-
fn activate_idle(&mut self) {
317-
rtdebug!("activating idle");
318-
if self.idle_flag {
319-
rtassert!(self.idle_watcher.is_some());
320-
rtdebug!("idle flag already set, not reactivating idle watcher");
321-
} else {
322-
rtdebug!("idle flag was false, reactivating idle watcher");
323-
self.idle_flag = true;
324-
if self.idle_watcher.is_none() {
325-
// There's no idle handle yet. Create one
326-
let mut idle_watcher = IdleWatcher::new(self.event_loop.uvio.uv_loop());
327-
do idle_watcher.start |_idle_watcher, _status| {
328-
Scheduler::run_sched_once();
329-
}
330-
self.idle_watcher = Some(idle_watcher);
331-
} else {
332-
self.idle_watcher.get_mut_ref().restart();
333-
}
334-
}
335-
}
336-
337-
fn pause_idle(&mut self) {
338-
rtassert!(self.idle_watcher.is_some());
339-
rtassert!(self.idle_flag);
340-
341-
rtdebug!("stopping idle watcher");
342-
343-
self.idle_flag = false;
344-
if !self.no_sleep {
345-
self.idle_watcher.get_mut_ref().stop();
346-
} else {
347-
rtdebug!("closing idle watcher");
348-
// The scheduler is trying to exit. Destroy the idle watcher
349-
// to drop the reference to the event loop.
350-
let mut idle_watcher = self.idle_watcher.take_unwrap();
351-
idle_watcher.stop();
352-
idle_watcher.close(||());
353-
}
354-
}
355-
356313
pub fn make_handle(&mut self) -> SchedHandle {
357314
let remote = self.event_loop.remote_callback(Scheduler::run_sched_once);
358315

@@ -376,10 +333,9 @@ impl Scheduler {
376333

377334
// We push the task onto our local queue clone.
378335
this.work_queue.push(task);
379-
// this.event_loop.callback(Scheduler::run_sched_once);
380336

381337
// There is definitely work to be done later. Make sure we wake up for it.
382-
this.activate_idle();
338+
this.idle_callback.resume();
383339

384340
// We've made work available. Notify a
385341
// sleeping scheduler.
@@ -420,28 +376,23 @@ impl Scheduler {
420376
let mut this = self;
421377
match this.message_queue.pop() {
422378
Some(PinnedTask(task)) => {
423-
// this.event_loop.callback(Scheduler::run_sched_once);
424379
let mut task = task;
425380
task.give_home(Sched(this.make_handle()));
426381
this.resume_task_immediately(task);
427382
return None;
428383
}
429384
Some(TaskFromFriend(task)) => {
430-
// this.event_loop.callback(Scheduler::run_sched_once);
431385
rtdebug!("got a task from a friend. lovely!");
432386
this.sched_schedule_task(task).map_move(Local::put);
433387
return None;
434388
}
435389
Some(Wake) => {
436-
// this.event_loop.callback(Scheduler::run_sched_once);
437390
this.sleepy = false;
438391
Local::put(this);
439392
return None;
440-
// return Some(this);
441393
}
442394
Some(Shutdown) => {
443395
rtdebug!("shutting down");
444-
// this.event_loop.callback(Scheduler::run_sched_once);
445396
if this.sleepy {
446397
// There may be an outstanding handle on the
447398
// sleeper list. Pop them all to make sure that's
@@ -463,7 +414,6 @@ impl Scheduler {
463414

464415
Local::put(this);
465416
return None;
466-
// return Some(this);
467417
}
468418
None => {
469419
return Some(this);

src/libstd/rt/uv/uvio.rs

Lines changed: 72 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,15 @@ impl EventLoop for UvEventLoop {
116116
}
117117
}
118118

119+
fn pausible_idle_callback(&mut self) -> ~PausibleIdleCallback {
120+
let idle_watcher = IdleWatcher::new(self.uvio.uv_loop());
121+
return ~UvPausibleIdleCallback {
122+
watcher: idle_watcher,
123+
idle_flag: false,
124+
closed: false
125+
};
126+
}
127+
119128
fn callback_ms(&mut self, ms: u64, f: ~fn()) {
120129
let mut timer = TimerWatcher::new(self.uvio.uv_loop());
121130
do timer.start(ms, 0) |timer, status| {
@@ -134,6 +143,44 @@ impl EventLoop for UvEventLoop {
134143
}
135144
}
136145

146+
pub struct UvPausibleIdleCallback {
147+
watcher: IdleWatcher,
148+
idle_flag: bool,
149+
closed: bool
150+
}
151+
152+
impl UvPausibleIdleCallback {
153+
#[inline]
154+
pub fn start(&mut self, f: ~fn()) {
155+
do self.watcher.start |_idle_watcher, _status| {
156+
f();
157+
};
158+
self.idle_flag = true;
159+
}
160+
#[inline]
161+
pub fn pause(&mut self) {
162+
if self.idle_flag == true {
163+
self.watcher.stop();
164+
self.idle_flag = false;
165+
}
166+
}
167+
#[inline]
168+
pub fn resume(&mut self) {
169+
if self.idle_flag == false {
170+
self.watcher.restart();
171+
self.idle_flag = true;
172+
}
173+
}
174+
#[inline]
175+
pub fn close(&mut self) {
176+
self.pause();
177+
if !self.closed {
178+
self.closed = true;
179+
self.watcher.close(||());
180+
}
181+
}
182+
}
183+
137184
#[test]
138185
fn test_callback_run_once() {
139186
do run_in_bare_thread {
@@ -163,24 +210,31 @@ impl UvRemoteCallback {
163210
let async = do AsyncWatcher::new(loop_) |watcher, status| {
164211
assert!(status.is_none());
165212

166-
// The synchronization logic here is subtle. To review, the uv async handle
167-
// type promises that, after it is triggered the remote callback is definitely
168-
// called at least once. UvRemoteCallback needs to maintain those semantics
169-
// while also shutting down cleanly from the dtor. In our case that means that,
170-
// when the UvRemoteCallback dtor calls `async.send()`, here `f` is always called
171-
// later.
172-
173-
// In the dtor both the exit flag is set and the async callback fired under a lock.
174-
// Here, before calling `f`, we take the lock and check the flag. Because we are
175-
// checking the flag before calling `f`, and the flag is set under the same lock
176-
// as the send, then if the flag is set then we're guaranteed to call `f` after
177-
// the final send.
178-
179-
// If the check was done after `f()` then there would be a period between that call
180-
// and the check where the dtor could be called in the other thread, missing the
181-
// final callback while still destroying the handle.
182-
183-
let should_exit = unsafe { exit_flag_clone.with_imm(|&should_exit| should_exit) };
213+
// The synchronization logic here is subtle. To review,
214+
// the uv async handle type promises that, after it is
215+
// triggered the remote callback is definitely called at
216+
// least once. UvRemoteCallback needs to maintain those
217+
// semantics while also shutting down cleanly from the
218+
// dtor. In our case that means that, when the
219+
// UvRemoteCallback dtor calls `async.send()`, here `f` is
220+
// always called later.
221+
222+
// In the dtor both the exit flag is set and the async
223+
// callback fired under a lock. Here, before calling `f`,
224+
// we take the lock and check the flag. Because we are
225+
// checking the flag before calling `f`, and the flag is
226+
// set under the same lock as the send, then if the flag
227+
// is set then we're guaranteed to call `f` after the
228+
// final send.
229+
230+
// If the check was done after `f()` then there would be a
231+
// period between that call and the check where the dtor
232+
// could be called in the other thread, missing the final
233+
// callback while still destroying the handle.
234+
235+
let should_exit = unsafe {
236+
exit_flag_clone.with_imm(|&should_exit| should_exit)
237+
};
184238

185239
f();
186240

0 commit comments

Comments
 (0)