Skip to content

Commit 505ef7e

Browse files
committed
std::rt: Tasks contain a JoinLatch
1 parent 90fbe38 commit 505ef7e

File tree

2 files changed

+56
-32
lines changed

2 files changed

+56
-32
lines changed

src/libstd/rt/task.rs

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,20 @@
1616
use prelude::*;
1717
use libc::{c_void, uintptr_t};
1818
use cast::transmute;
19+
use option::{Option, Some, None};
1920
use rt::local::Local;
2021
use super::local_heap::LocalHeap;
2122
use rt::logging::StdErrLogger;
23+
use rt::join_latch::JoinLatch;
2224

2325
pub struct Task {
2426
heap: LocalHeap,
2527
gc: GarbageCollector,
2628
storage: LocalStorage,
2729
logger: StdErrLogger,
2830
unwinder: Unwinder,
31+
join_latch: Option<~JoinLatch>,
32+
on_exit: Option<~fn(bool)>,
2933
destroyed: bool
3034
}
3135

@@ -44,6 +48,8 @@ impl Task {
4448
storage: LocalStorage(ptr::null(), None),
4549
logger: StdErrLogger,
4650
unwinder: Unwinder { unwinding: false },
51+
join_latch: Some(JoinLatch::new_root()),
52+
on_exit: None,
4753
destroyed: false
4854
}
4955
}
@@ -55,6 +61,8 @@ impl Task {
5561
storage: LocalStorage(ptr::null(), None),
5662
logger: StdErrLogger,
5763
unwinder: Unwinder { unwinding: false },
64+
join_latch: Some(self.join_latch.get_mut_ref().new_child()),
65+
on_exit: None,
5866
destroyed: false
5967
}
6068
}
@@ -68,9 +76,22 @@ impl Task {
6876

6977
self.unwinder.try(f);
7078
self.destroy();
79+
80+
// Wait for children. Possibly report the exit status.
81+
let local_success = !self.unwinder.unwinding;
82+
let join_latch = self.join_latch.swap_unwrap();
83+
match self.on_exit {
84+
Some(ref on_exit) => {
85+
let success = join_latch.wait(local_success);
86+
(*on_exit)(success);
87+
}
88+
None => {
89+
join_latch.release(local_success);
90+
}
91+
}
7192
}
7293

73-
/// Must be called manually before finalization to clean up
94+
/// must be called manually before finalization to clean up
7495
/// thread-local resources. Some of the routines here expect
7596
/// Task to be available recursively so this must be
7697
/// called unsafely, without removing Task from
@@ -216,5 +237,15 @@ mod test {
216237
assert!(port.recv() == 10);
217238
}
218239
}
240+
241+
#[test]
242+
fn linked_failure() {
243+
do run_in_newsched_task() {
244+
let res = do spawntask_try {
245+
spawntask_random(|| fail!());
246+
};
247+
assert!(res.is_err());
248+
}
249+
}
219250
}
220251

src/libstd/rt/test.rs

Lines changed: 24 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ use vec::OwnedVector;
1818
use result::{Result, Ok, Err};
1919
use unstable::run_in_bare_thread;
2020
use super::io::net::ip::{IpAddr, Ipv4};
21+
use rt::comm::oneshot;
2122
use rt::task::Task;
2223
use rt::thread::Thread;
2324
use rt::local::Local;
@@ -47,8 +48,11 @@ pub fn run_in_newsched_task(f: ~fn()) {
4748

4849
do run_in_bare_thread {
4950
let mut sched = ~new_test_uv_sched();
51+
let mut new_task = ~Task::new_root();
52+
let on_exit: ~fn(bool) = |exit_status| rtassert!(exit_status);
53+
new_task.on_exit = Some(on_exit);
5054
let task = ~Coroutine::with_task(&mut sched.stack_pool,
51-
~Task::new_root(),
55+
new_task,
5256
f.take());
5357
sched.enqueue_task(task);
5458
sched.run();
@@ -94,16 +98,20 @@ pub fn run_in_mt_newsched_task(f: ~fn()) {
9498

9599
let f_cell = Cell(f_cell.take());
96100
let handles = Cell(handles);
97-
let main_task = ~do Coroutine::new_root(&mut scheds[0].stack_pool) {
98-
f_cell.take()();
101+
let mut new_task = ~Task::new_root();
102+
let on_exit: ~fn(bool) = |exit_status| {
99103

100104
let mut handles = handles.take();
101105
// Tell schedulers to exit
102106
for handles.each_mut |handle| {
103107
handle.send(Shutdown);
104108
}
105-
};
106109

110+
rtassert!(exit_status);
111+
};
112+
new_task.on_exit = Some(on_exit);
113+
let main_task = ~Coroutine::with_task(&mut scheds[0].stack_pool,
114+
new_task, f_cell.take());
107115
scheds[0].enqueue_task(main_task);
108116

109117
let mut threads = ~[];
@@ -213,36 +221,21 @@ pub fn spawntask_random(f: ~fn()) {
213221
pub fn spawntask_try(f: ~fn()) -> Result<(), ()> {
214222
use cell::Cell;
215223
use super::sched::*;
216-
use task;
217-
use unstable::finally::Finally;
218-
219-
// Our status variables will be filled in from the scheduler context
220-
let mut failed = false;
221-
let failed_ptr: *mut bool = &mut failed;
222-
223-
// Switch to the scheduler
224-
let f = Cell(Cell(f));
225-
let sched = Local::take::<Scheduler>();
226-
do sched.deschedule_running_task_and_then() |sched, old_task| {
227-
let old_task = Cell(old_task);
228-
let f = f.take();
229-
let new_task = ~do Coroutine::new_root(&mut sched.stack_pool) {
230-
do (|| {
231-
(f.take())()
232-
}).finally {
233-
// Check for failure then resume the parent task
234-
unsafe { *failed_ptr = task::failing(); }
235-
let sched = Local::take::<Scheduler>();
236-
do sched.switch_running_tasks_and_then(old_task.take()) |sched, new_task| {
237-
sched.enqueue_task(new_task);
238-
}
239-
}
240-
};
241224

242-
sched.enqueue_task(new_task);
225+
let (port, chan) = oneshot();
226+
let chan = Cell(chan);
227+
let mut new_task = ~Task::new_root();
228+
let on_exit: ~fn(bool) = |exit_status| chan.take().send(exit_status);
229+
new_task.on_exit = Some(on_exit);
230+
let mut sched = Local::take::<Scheduler>();
231+
let new_task = ~Coroutine::with_task(&mut sched.stack_pool,
232+
new_task, f);
233+
do sched.switch_running_tasks_and_then(new_task) |sched, old_task| {
234+
sched.enqueue_task(old_task);
243235
}
244236

245-
if !failed { Ok(()) } else { Err(()) }
237+
let exit_status = port.recv();
238+
if exit_status { Ok(()) } else { Err(()) }
246239
}
247240

248241
// Spawn a new task in a new scheduler and return a thread handle.

0 commit comments

Comments
 (0)