Skip to content

Commit eddd817

Browse files
committed
core::rt: Add another context switching operation to the scheduler
`switch_running_tasks_and_then` does a context switch to another task then immediatly runs a closure.
1 parent d261bb3 commit eddd817

File tree

1 file changed

+89
-28
lines changed

1 file changed

+89
-28
lines changed

src/libcore/rt/sched/mod.rs

Lines changed: 89 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ enum CleanupJob {
6565

6666
pub impl Scheduler {
6767

68+
fn in_task_context(&self) -> bool { self.current_task.is_some() }
69+
6870
fn new(event_loop: ~EventLoopObject) -> Scheduler {
6971

7072
// Lazily initialize the global state, currently the scheduler TLS key
@@ -131,6 +133,59 @@ pub impl Scheduler {
131133
}
132134
}
133135

136+
// * Task-context operations
137+
138+
/// Called by a running task to end execution, after which it will
139+
/// be recycled by the scheduler for reuse in a new task.
140+
fn terminate_current_task(~self) {
141+
let mut self = self;
142+
assert!(self.in_task_context());
143+
144+
rtdebug!("ending running task");
145+
146+
let dead_task = self.current_task.swap_unwrap();
147+
self.enqueue_cleanup_job(RecycleTask(dead_task));
148+
149+
local_sched::put(self);
150+
151+
let sched = unsafe { local_sched::unsafe_borrow() };
152+
let (sched_context, last_task_context, _) = sched.get_contexts();
153+
let last_task_context = last_task_context.unwrap();
154+
Context::swap(last_task_context, sched_context);
155+
156+
// Control never reaches here
157+
}
158+
159+
/// Switch directly to another task, without going through the scheduler.
160+
/// You would want to think hard about doing this, e.g. if there are
161+
/// pending I/O events it would be a bad idea.
162+
fn resume_task_from_running_task_direct(~self, next_task: ~Task) {
163+
let mut self = self;
164+
assert!(self.in_task_context());
165+
166+
rtdebug!("switching tasks");
167+
168+
let old_running_task = self.current_task.swap_unwrap();
169+
self.enqueue_cleanup_job(RescheduleTask(old_running_task));
170+
self.current_task = Some(next_task);
171+
172+
local_sched::put(self);
173+
174+
unsafe {
175+
let sched = local_sched::unsafe_borrow();
176+
let (_, last_task_context, next_task_context) = sched.get_contexts();
177+
let last_task_context = last_task_context.unwrap();
178+
let next_task_context = next_task_context.unwrap();
179+
Context::swap(last_task_context, next_task_context);
180+
181+
// We could be executing in a different thread now
182+
let sched = local_sched::unsafe_borrow();
183+
sched.run_cleanup_job();
184+
}
185+
}
186+
187+
// Core scheduling ops
188+
134189
fn resume_task_immediately(~self, task: ~Task) {
135190
let mut self = self;
136191
assert!(!self.in_task_context());
@@ -161,30 +216,6 @@ pub impl Scheduler {
161216
}
162217
}
163218

164-
165-
// * Task-context operations
166-
167-
/// Called by a running task to end execution, after which it will
168-
/// be recycled by the scheduler for reuse in a new task.
169-
fn terminate_current_task(~self) {
170-
let mut self = self;
171-
assert!(self.in_task_context());
172-
173-
rtdebug!("ending running task");
174-
175-
let dead_task = self.current_task.swap_unwrap();
176-
self.enqueue_cleanup_job(RecycleTask(dead_task));
177-
178-
local_sched::put(self);
179-
180-
let sched = unsafe { local_sched::unsafe_borrow() };
181-
let (sched_context, last_task_context, _) = sched.get_contexts();
182-
let last_task_context = last_task_context.unwrap();
183-
Context::swap(last_task_context, sched_context);
184-
185-
// Control never reaches here
186-
}
187-
188219
/// Block a running task, context switch to the scheduler, then pass the
189220
/// blocked task to a closure.
190221
///
@@ -219,14 +250,16 @@ pub impl Scheduler {
219250
/// Switch directly to another task, without going through the scheduler.
220251
/// You would want to think hard about doing this, e.g. if there are
221252
/// pending I/O events it would be a bad idea.
222-
fn resume_task_from_running_task_direct(~self, next_task: ~Task) {
253+
fn switch_running_tasks_and_then(~self, next_task: ~Task, f: &fn(~Task)) {
223254
let mut self = self;
224255
assert!(self.in_task_context());
225256

226257
rtdebug!("switching tasks");
227258

228259
let old_running_task = self.current_task.swap_unwrap();
229-
self.enqueue_cleanup_job(RescheduleTask(old_running_task));
260+
let f_fake_region = unsafe { transmute::<&fn(~Task), &fn(~Task)>(f) };
261+
let f_opaque = ClosureConverter::from_fn(f_fake_region);
262+
self.enqueue_cleanup_job(GiveTask(old_running_task, f_opaque));
230263
self.current_task = Some(next_task);
231264

232265
local_sched::put(self);
@@ -244,9 +277,9 @@ pub impl Scheduler {
244277
}
245278
}
246279

247-
// * Other stuff
248280

249-
fn in_task_context(&self) -> bool { self.current_task.is_some() }
281+
282+
// * Other stuff
250283

251284
fn enqueue_cleanup_job(&mut self, job: CleanupJob) {
252285
assert!(self.cleanup_job.is_none());
@@ -422,6 +455,34 @@ fn test_swap_tasks() {
422455
}
423456
}
424457

458+
#[test]
459+
fn test_swap_tasks_then() {
460+
do run_in_bare_thread {
461+
let mut count = 0;
462+
let count_ptr: *mut int = &mut count;
463+
464+
let mut sched = ~UvEventLoop::new_scheduler();
465+
let task1 = ~do Task::new(&mut sched.stack_pool) {
466+
unsafe { *count_ptr = *count_ptr + 1; }
467+
let mut sched = local_sched::take();
468+
let task2 = ~do Task::new(&mut sched.stack_pool) {
469+
unsafe { *count_ptr = *count_ptr + 1; }
470+
};
471+
// Context switch directly to the new task
472+
do sched.switch_running_tasks_and_then(task2) |task1| {
473+
let task1 = Cell(task1);
474+
do local_sched::borrow |sched| {
475+
sched.task_queue.push_front(task1.take());
476+
}
477+
}
478+
unsafe { *count_ptr = *count_ptr + 1; }
479+
};
480+
sched.task_queue.push_back(task1);
481+
sched.run();
482+
assert!(count == 3);
483+
}
484+
}
485+
425486
#[bench] #[test] #[ignore(reason = "long test")]
426487
fn test_run_a_lot_of_tasks_queued() {
427488
do run_in_bare_thread {

0 commit comments

Comments
 (0)