Skip to content

Commit 134bb0f

Browse files
committed
core::rt: Change the signature of context switching methods to avoid infinite recursion
1 parent f343e61 commit 134bb0f

File tree

6 files changed

+66
-100
lines changed

6 files changed

+66
-100
lines changed

src/libcore/rt/comm.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ impl<T> PortOne<T> {
159159

160160
// Switch to the scheduler to put the ~Task into the Packet state.
161161
let sched = Local::take::<Scheduler>();
162-
do sched.deschedule_running_task_and_then |task| {
162+
do sched.deschedule_running_task_and_then |sched, task| {
163163
unsafe {
164164
// Atomically swap the task pointer into the Packet state, issuing
165165
// an acquire barrier to prevent reordering of the subsequent read
@@ -178,12 +178,10 @@ impl<T> PortOne<T> {
178178
// triggering infinite recursion on the scheduler's stack.
179179
let task: ~Coroutine = cast::transmute(task_as_state);
180180
let task = Cell(task);
181-
let mut sched = Local::take::<Scheduler>();
182181
do sched.event_loop.callback {
183182
let sched = Local::take::<Scheduler>();
184183
sched.resume_task_immediately(task.take());
185184
}
186-
Local::put(sched);
187185
}
188186
_ => util::unreachable()
189187
}

src/libcore/rt/mod.rs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -238,12 +238,9 @@ fn test_context() {
238238
let task = ~do Coroutine::new(&mut sched.stack_pool) {
239239
assert_eq!(context(), TaskContext);
240240
let sched = Local::take::<Scheduler>();
241-
do sched.deschedule_running_task_and_then() |task| {
241+
do sched.deschedule_running_task_and_then() |sched, task| {
242242
assert_eq!(context(), SchedulerContext);
243-
let task = Cell(task);
244-
do Local::borrow::<Scheduler> |sched| {
245-
sched.enqueue_task(task.take());
246-
}
243+
sched.enqueue_task(task);
247244
}
248245
};
249246
sched.enqueue_task(task);

src/libcore/rt/sched.rs

Lines changed: 30 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -280,11 +280,9 @@ pub impl Scheduler {
280280

281281
rtdebug!("ending running task");
282282

283-
do self.deschedule_running_task_and_then |dead_task| {
283+
do self.deschedule_running_task_and_then |sched, dead_task| {
284284
let dead_task = Cell(dead_task);
285-
do Local::borrow::<Scheduler> |sched| {
286-
dead_task.take().recycle(&mut sched.stack_pool);
287-
}
285+
dead_task.take().recycle(&mut sched.stack_pool);
288286
}
289287

290288
abort!("control reached end of task");
@@ -293,22 +291,18 @@ pub impl Scheduler {
293291
fn schedule_new_task(~self, task: ~Coroutine) {
294292
assert!(self.in_task_context());
295293

296-
do self.switch_running_tasks_and_then(task) |last_task| {
294+
do self.switch_running_tasks_and_then(task) |sched, last_task| {
297295
let last_task = Cell(last_task);
298-
do Local::borrow::<Scheduler> |sched| {
299-
sched.enqueue_task(last_task.take());
300-
}
296+
sched.enqueue_task(last_task.take());
301297
}
302298
}
303299

304300
fn schedule_task(~self, task: ~Coroutine) {
305301
assert!(self.in_task_context());
306302

307-
do self.switch_running_tasks_and_then(task) |last_task| {
303+
do self.switch_running_tasks_and_then(task) |sched, last_task| {
308304
let last_task = Cell(last_task);
309-
do Local::borrow::<Scheduler> |sched| {
310-
sched.enqueue_task(last_task.take());
311-
}
305+
sched.enqueue_task(last_task.take());
312306
}
313307
}
314308

@@ -352,15 +346,20 @@ pub impl Scheduler {
352346
/// The closure here is a *stack* closure that lives in the
353347
/// running task. It gets transmuted to the scheduler's lifetime
354348
/// and called while the task is blocked.
355-
fn deschedule_running_task_and_then(~self, f: &fn(~Coroutine)) {
349+
///
350+
/// This passes a Scheduler pointer to the fn after the context switch
351+
/// in order to prevent that fn from performing further scheduling operations.
352+
/// Doing further scheduling could easily result in infinite recursion.
353+
fn deschedule_running_task_and_then(~self, f: &fn(&mut Scheduler, ~Coroutine)) {
356354
let mut this = self;
357355
assert!(this.in_task_context());
358356

359357
rtdebug!("blocking task");
360358

361359
unsafe {
362360
let blocked_task = this.current_task.swap_unwrap();
363-
let f_fake_region = transmute::<&fn(~Coroutine), &fn(~Coroutine)>(f);
361+
let f_fake_region = transmute::<&fn(&mut Scheduler, ~Coroutine),
362+
&fn(&mut Scheduler, ~Coroutine)>(f);
364363
let f_opaque = ClosureConverter::from_fn(f_fake_region);
365364
this.enqueue_cleanup_job(GiveTask(blocked_task, f_opaque));
366365
}
@@ -382,14 +381,18 @@ pub impl Scheduler {
382381
/// Switch directly to another task, without going through the scheduler.
383382
/// You would want to think hard about doing this, e.g. if there are
384383
/// pending I/O events it would be a bad idea.
385-
fn switch_running_tasks_and_then(~self, next_task: ~Coroutine, f: &fn(~Coroutine)) {
384+
fn switch_running_tasks_and_then(~self, next_task: ~Coroutine,
385+
f: &fn(&mut Scheduler, ~Coroutine)) {
386386
let mut this = self;
387387
assert!(this.in_task_context());
388388

389389
rtdebug!("switching tasks");
390390

391391
let old_running_task = this.current_task.swap_unwrap();
392-
let f_fake_region = unsafe { transmute::<&fn(~Coroutine), &fn(~Coroutine)>(f) };
392+
let f_fake_region = unsafe {
393+
transmute::<&fn(&mut Scheduler, ~Coroutine),
394+
&fn(&mut Scheduler, ~Coroutine)>(f)
395+
};
393396
let f_opaque = ClosureConverter::from_fn(f_fake_region);
394397
this.enqueue_cleanup_job(GiveTask(old_running_task, f_opaque));
395398
this.current_task = Some(next_task);
@@ -426,7 +429,7 @@ pub impl Scheduler {
426429
let cleanup_job = self.cleanup_job.swap_unwrap();
427430
match cleanup_job {
428431
DoNothing => { }
429-
GiveTask(task, f) => (f.to_fn())(task)
432+
GiveTask(task, f) => (f.to_fn())(self, task)
430433
}
431434
}
432435

@@ -535,12 +538,12 @@ pub impl Coroutine {
535538
// complaining
536539
type UnsafeTaskReceiver = sys::Closure;
537540
trait ClosureConverter {
538-
fn from_fn(&fn(~Coroutine)) -> Self;
539-
fn to_fn(self) -> &fn(~Coroutine);
541+
fn from_fn(&fn(&mut Scheduler, ~Coroutine)) -> Self;
542+
fn to_fn(self) -> &fn(&mut Scheduler, ~Coroutine);
540543
}
541544
impl ClosureConverter for UnsafeTaskReceiver {
542-
fn from_fn(f: &fn(~Coroutine)) -> UnsafeTaskReceiver { unsafe { transmute(f) } }
543-
fn to_fn(self) -> &fn(~Coroutine) { unsafe { transmute(self) } }
545+
fn from_fn(f: &fn(&mut Scheduler, ~Coroutine)) -> UnsafeTaskReceiver { unsafe { transmute(f) } }
546+
fn to_fn(self) -> &fn(&mut Scheduler, ~Coroutine) { unsafe { transmute(self) } }
544547
}
545548

546549
#[cfg(test)]
@@ -604,11 +607,9 @@ mod test {
604607
unsafe { *count_ptr = *count_ptr + 1; }
605608
};
606609
// Context switch directly to the new task
607-
do sched.switch_running_tasks_and_then(task2) |task1| {
610+
do sched.switch_running_tasks_and_then(task2) |sched, task1| {
608611
let task1 = Cell(task1);
609-
do Local::borrow::<Scheduler> |sched| {
610-
sched.enqueue_task(task1.take());
611-
}
612+
sched.enqueue_task(task1.take());
612613
}
613614
unsafe { *count_ptr = *count_ptr + 1; }
614615
};
@@ -658,12 +659,10 @@ mod test {
658659
let task = ~do Coroutine::new(&mut sched.stack_pool) {
659660
let sched = Local::take::<Scheduler>();
660661
assert!(sched.in_task_context());
661-
do sched.deschedule_running_task_and_then() |task| {
662+
do sched.deschedule_running_task_and_then() |sched, task| {
662663
let task = Cell(task);
663-
do Local::borrow::<Scheduler> |sched| {
664-
assert!(!sched.in_task_context());
665-
sched.enqueue_task(task.take());
666-
}
664+
assert!(!sched.in_task_context());
665+
sched.enqueue_task(task.take());
667666
}
668667
};
669668
sched.enqueue_task(task);
@@ -680,16 +679,14 @@ mod test {
680679
do run_in_newsched_task {
681680
do spawn {
682681
let sched = Local::take::<Scheduler>();
683-
do sched.deschedule_running_task_and_then |task| {
684-
let mut sched = Local::take::<Scheduler>();
682+
do sched.deschedule_running_task_and_then |sched, task| {
685683
let task = Cell(task);
686684
do sched.event_loop.callback_ms(10) {
687685
rtdebug!("in callback");
688686
let mut sched = Local::take::<Scheduler>();
689687
sched.enqueue_task(task.take());
690688
Local::put(sched);
691689
}
692-
Local::put(sched);
693690
}
694691
}
695692
}

src/libcore/rt/test.rs

Lines changed: 9 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -122,11 +122,7 @@ pub fn spawntask(f: ~fn()) {
122122
let task = ~Coroutine::with_task(&mut sched.stack_pool,
123123
~Task::without_unwinding(),
124124
f);
125-
do sched.switch_running_tasks_and_then(task) |task| {
126-
let task = Cell(task);
127-
let sched = Local::take::<Scheduler>();
128-
sched.schedule_new_task(task.take());
129-
}
125+
sched.schedule_new_task(task);
130126
}
131127

132128
/// Create a new task and run it right now. Aborts on failure
@@ -137,11 +133,8 @@ pub fn spawntask_immediately(f: ~fn()) {
137133
let task = ~Coroutine::with_task(&mut sched.stack_pool,
138134
~Task::without_unwinding(),
139135
f);
140-
do sched.switch_running_tasks_and_then(task) |task| {
141-
let task = Cell(task);
142-
do Local::borrow::<Scheduler> |sched| {
143-
sched.enqueue_task(task.take());
144-
}
136+
do sched.switch_running_tasks_and_then(task) |sched, task| {
137+
sched.enqueue_task(task);
145138
}
146139
}
147140

@@ -172,11 +165,8 @@ pub fn spawntask_random(f: ~fn()) {
172165
f);
173166

174167
if run_now {
175-
do sched.switch_running_tasks_and_then(task) |task| {
176-
let task = Cell(task);
177-
do Local::borrow::<Scheduler> |sched| {
178-
sched.enqueue_task(task.take());
179-
}
168+
do sched.switch_running_tasks_and_then(task) |sched, task| {
169+
sched.enqueue_task(task);
180170
}
181171
} else {
182172
sched.enqueue_task(task);
@@ -199,27 +189,23 @@ pub fn spawntask_try(f: ~fn()) -> Result<(), ()> {
199189
// Switch to the scheduler
200190
let f = Cell(Cell(f));
201191
let sched = Local::take::<Scheduler>();
202-
do sched.deschedule_running_task_and_then() |old_task| {
192+
do sched.deschedule_running_task_and_then() |sched, old_task| {
203193
let old_task = Cell(old_task);
204194
let f = f.take();
205-
let mut sched = Local::take::<Scheduler>();
206195
let new_task = ~do Coroutine::new(&mut sched.stack_pool) {
207196
do (|| {
208197
(f.take())()
209198
}).finally {
210199
// Check for failure then resume the parent task
211200
unsafe { *failed_ptr = task::failing(); }
212201
let sched = Local::take::<Scheduler>();
213-
do sched.switch_running_tasks_and_then(old_task.take()) |new_task| {
214-
let new_task = Cell(new_task);
215-
do Local::borrow::<Scheduler> |sched| {
216-
sched.enqueue_task(new_task.take());
217-
}
202+
do sched.switch_running_tasks_and_then(old_task.take()) |sched, new_task| {
203+
sched.enqueue_task(new_task);
218204
}
219205
}
220206
};
221207

222-
sched.resume_task_immediately(new_task);
208+
sched.enqueue_task(new_task);
223209
}
224210

225211
if !failed { Ok(()) } else { Err(()) }

src/libcore/rt/tube.rs

Lines changed: 14 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ impl<T> Tube<T> {
7272
assert!(self.p.refcount() > 1); // There better be somebody to wake us up
7373
assert!((*state).blocked_task.is_none());
7474
let sched = Local::take::<Scheduler>();
75-
do sched.deschedule_running_task_and_then |task| {
75+
do sched.deschedule_running_task_and_then |_, task| {
7676
(*state).blocked_task = Some(task);
7777
}
7878
rtdebug!("waking after tube recv");
@@ -107,11 +107,10 @@ mod test {
107107
let tube_clone = tube.clone();
108108
let tube_clone_cell = Cell(tube_clone);
109109
let sched = Local::take::<Scheduler>();
110-
do sched.deschedule_running_task_and_then |task| {
110+
do sched.deschedule_running_task_and_then |sched, task| {
111111
let mut tube_clone = tube_clone_cell.take();
112112
tube_clone.send(1);
113-
let sched = Local::take::<Scheduler>();
114-
sched.resume_task_immediately(task);
113+
sched.enqueue_task(task);
115114
}
116115

117116
assert!(tube.recv() == 1);
@@ -123,21 +122,17 @@ mod test {
123122
do run_in_newsched_task {
124123
let mut tube: Tube<int> = Tube::new();
125124
let tube_clone = tube.clone();
126-
let tube_clone = Cell(Cell(Cell(tube_clone)));
125+
let tube_clone = Cell(tube_clone);
127126
let sched = Local::take::<Scheduler>();
128-
do sched.deschedule_running_task_and_then |task| {
129-
let tube_clone = tube_clone.take();
130-
do Local::borrow::<Scheduler> |sched| {
131-
let tube_clone = tube_clone.take();
132-
do sched.event_loop.callback {
133-
let mut tube_clone = tube_clone.take();
134-
// The task should be blocked on this now and
135-
// sending will wake it up.
136-
tube_clone.send(1);
137-
}
127+
do sched.deschedule_running_task_and_then |sched, task| {
128+
let tube_clone = Cell(tube_clone.take());
129+
do sched.event_loop.callback {
130+
let mut tube_clone = tube_clone.take();
131+
// The task should be blocked on this now and
132+
// sending will wake it up.
133+
tube_clone.send(1);
138134
}
139-
let sched = Local::take::<Scheduler>();
140-
sched.resume_task_immediately(task);
135+
sched.enqueue_task(task);
141136
}
142137

143138
assert!(tube.recv() == 1);
@@ -153,7 +148,7 @@ mod test {
153148
let tube_clone = tube.clone();
154149
let tube_clone = Cell(tube_clone);
155150
let sched = Local::take::<Scheduler>();
156-
do sched.deschedule_running_task_and_then |task| {
151+
do sched.deschedule_running_task_and_then |sched, task| {
157152
callback_send(tube_clone.take(), 0);
158153

159154
fn callback_send(tube: Tube<int>, i: int) {
@@ -172,8 +167,7 @@ mod test {
172167
}
173168
}
174169

175-
let sched = Local::take::<Scheduler>();
176-
sched.resume_task_immediately(task);
170+
sched.enqueue_task(task);
177171
}
178172

179173
for int::range(0, MAX) |i| {

0 commit comments

Comments
 (0)