Skip to content

Commit 42c0f88

Browse files
committed
core::rt: Add unwinding to newsched tasks
1 parent 5fbb094 commit 42c0f88

File tree

9 files changed

+190
-20
lines changed

9 files changed

+190
-20
lines changed

src/libcore/rt/io/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,7 @@ pub mod mem;
153153
pub mod stdio;
154154

155155
/// Implementations for Option
156+
#[cfg(not(stage0))] // Requires condition! fixes
156157
mod option;
157158

158159
/// Basic stream compression. XXX: Belongs with other flate code

src/libcore/rt/local_services.rs

Lines changed: 74 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@
1919
//! (freestanding rust with local services?).
2020
2121
use prelude::*;
22-
use libc::c_void;
22+
use libc::{c_void, uintptr_t};
23+
use cast::transmute;
2324
use super::sched::{Task, local_sched};
2425
use super::local_heap::LocalHeap;
2526

@@ -35,7 +36,10 @@ pub struct LocalServices {
3536
pub struct GarbageCollector;
3637
pub struct LocalStorage(*c_void, Option<~fn(*c_void)>);
3738
pub struct Logger;
38-
pub struct Unwinder;
39+
40+
pub struct Unwinder {
41+
unwinding: bool,
42+
}
3943

4044
impl LocalServices {
4145
pub fn new() -> LocalServices {
@@ -44,17 +48,28 @@ impl LocalServices {
4448
gc: GarbageCollector,
4549
storage: LocalStorage(ptr::null(), None),
4650
logger: Logger,
47-
unwinder: Unwinder,
51+
unwinder: Unwinder { unwinding: false },
4852
destroyed: false
4953
}
5054
}
5155

56+
pub fn run(&mut self, f: &fn()) {
57+
// This is just an assertion that `run` was called unsafely
58+
// and this instance of LocalServices is still accessible.
59+
do borrow_local_services |sched| {
60+
assert!(ptr::ref_eq(sched, self));
61+
}
62+
63+
self.unwinder.try(f);
64+
self.destroy();
65+
}
66+
5267
/// Must be called manually before finalization to clean up
5368
/// thread-local resources. Some of the routines here expect
5469
/// LocalServices to be available recursively so this must be
5570
/// called unsafely, without removing LocalServices from
5671
/// thread-local-storage.
57-
pub fn destroy(&mut self) {
72+
fn destroy(&mut self) {
5873
// This is just an assertion that `destroy` was called unsafely
5974
// and this instance of LocalServices is still accessible.
6075
do borrow_local_services |sched| {
@@ -72,6 +87,51 @@ impl Drop for LocalServices {
7287
fn finalize(&self) { assert!(self.destroyed) }
7388
}
7489

90+
// Just a sanity check to make sure we are catching a Rust-thrown exception
91+
static UNWIND_TOKEN: uintptr_t = 839147;
92+
93+
impl Unwinder {
94+
pub fn try(&mut self, f: &fn()) {
95+
use sys::Closure;
96+
97+
unsafe {
98+
let closure: Closure = transmute(f);
99+
let code = transmute(closure.code);
100+
let env = transmute(closure.env);
101+
102+
let token = rust_try(try_fn, code, env);
103+
assert!(token == 0 || token == UNWIND_TOKEN);
104+
}
105+
106+
extern fn try_fn(code: *c_void, env: *c_void) {
107+
unsafe {
108+
let closure: Closure = Closure {
109+
code: transmute(code),
110+
env: transmute(env),
111+
};
112+
let closure: &fn() = transmute(closure);
113+
closure();
114+
}
115+
}
116+
117+
extern {
118+
#[rust_stack]
119+
fn rust_try(f: *u8, code: *c_void, data: *c_void) -> uintptr_t;
120+
}
121+
}
122+
123+
pub fn begin_unwind(&mut self) -> ! {
124+
self.unwinding = true;
125+
unsafe {
126+
rust_begin_unwind(UNWIND_TOKEN);
127+
return transmute(());
128+
}
129+
extern {
130+
fn rust_begin_unwind(token: uintptr_t);
131+
}
132+
}
133+
}
134+
75135
/// Borrow a pointer to the installed local services.
76136
/// Fails (likely aborting the process) if local services are not available.
77137
pub fn borrow_local_services(f: &fn(&mut LocalServices)) {
@@ -125,4 +185,14 @@ mod test {
125185
}
126186
}
127187
}
188+
189+
#[test]
190+
fn unwind() {
191+
do run_in_newsched_task() {
192+
let result = spawn_try(||());
193+
assert!(result.is_ok());
194+
let result = spawn_try(|| fail!());
195+
assert!(result.is_err());
196+
}
197+
}
128198
}

src/libcore/rt/sched/mod.rs

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -353,15 +353,10 @@ pub impl Task {
353353
unsafe {
354354
let sched = local_sched::unsafe_borrow();
355355
sched.run_cleanup_job();
356-
}
357-
358-
start();
359356

360-
unsafe {
361-
// Destroy the local heap, TLS, etc.
362357
let sched = local_sched::unsafe_borrow();
363358
let task = sched.current_task.get_mut_ref();
364-
task.local_services.destroy();
359+
task.local_services.run(start);
365360
}
366361

367362
let sched = local_sched::take();

src/libcore/rt/test.rs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11+
use result::{Result, Ok, Err};
1112
use super::io::net::ip::{IpAddr, Ipv4};
1213

1314
/// Creates a new scheduler in a new thread and runs a task in it,
@@ -47,6 +48,46 @@ pub fn spawn_immediately(f: ~fn()) {
4748
}
4849
}
4950

51+
/// Spawn a task and wait for it to finish, returning whether it completed successfully or failed
52+
pub fn spawn_try(f: ~fn()) -> Result<(), ()> {
53+
use cell::Cell;
54+
use super::sched::*;
55+
use task;
56+
use unstable::finally::Finally;
57+
58+
// Our status variables will be filled in from the scheduler context
59+
let mut failed = false;
60+
let failed_ptr: *mut bool = &mut failed;
61+
62+
// Switch to the scheduler
63+
let f = Cell(Cell(f));
64+
let mut sched = local_sched::take();
65+
do sched.deschedule_running_task_and_then() |old_task| {
66+
let old_task = Cell(old_task);
67+
let f = f.take();
68+
let mut sched = local_sched::take();
69+
let new_task = ~do Task::new(&mut sched.stack_pool) {
70+
do (|| {
71+
(f.take())()
72+
}).finally {
73+
// Check for failure then resume the parent task
74+
unsafe { *failed_ptr = task::failing(); }
75+
let sched = local_sched::take();
76+
do sched.switch_running_tasks_and_then(old_task.take()) |new_task| {
77+
let new_task = Cell(new_task);
78+
do local_sched::borrow |sched| {
79+
sched.task_queue.push_front(new_task.take());
80+
}
81+
}
82+
}
83+
};
84+
85+
sched.resume_task_immediately(new_task);
86+
}
87+
88+
if !failed { Ok(()) } else { Err(()) }
89+
}
90+
5091
/// Get a port number, starting at 9600, for use in tests
5192
pub fn next_test_port() -> u16 {
5293
unsafe {

src/libcore/sys.rs

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -134,12 +134,27 @@ pub fn log_str<T>(t: &T) -> ~str {
134134

135135
/** Initiate task failure */
136136
pub fn begin_unwind(msg: ~str, file: ~str, line: uint) -> ! {
137-
do str::as_buf(msg) |msg_buf, _msg_len| {
138-
do str::as_buf(file) |file_buf, _file_len| {
137+
138+
use rt::{context, OldTaskContext};
139+
use rt::local_services::unsafe_borrow_local_services;
140+
141+
match context() {
142+
OldTaskContext => {
143+
do str::as_buf(msg) |msg_buf, _msg_len| {
144+
do str::as_buf(file) |file_buf, _file_len| {
145+
unsafe {
146+
let msg_buf = cast::transmute(msg_buf);
147+
let file_buf = cast::transmute(file_buf);
148+
begin_unwind_(msg_buf, file_buf, line as libc::size_t)
149+
}
150+
}
151+
}
152+
}
153+
_ => {
154+
gc::cleanup_stack_for_failure();
139155
unsafe {
140-
let msg_buf = cast::transmute(msg_buf);
141-
let file_buf = cast::transmute(file_buf);
142-
begin_unwind_(msg_buf, file_buf, line as libc::size_t)
156+
let local_services = unsafe_borrow_local_services();
157+
local_services.unwinder.begin_unwind();
143158
}
144159
}
145160
}

src/libcore/task/mod.rs

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -558,8 +558,22 @@ pub fn yield() {
558558
pub fn failing() -> bool {
559559
//! True if the running task has failed
560560
561-
unsafe {
562-
rt::rust_task_is_unwinding(rt::rust_get_task())
561+
use rt::{context, OldTaskContext};
562+
use rt::local_services::borrow_local_services;
563+
564+
match context() {
565+
OldTaskContext => {
566+
unsafe {
567+
rt::rust_task_is_unwinding(rt::rust_get_task())
568+
}
569+
}
570+
_ => {
571+
let mut unwinding = false;
572+
do borrow_local_services |local| {
573+
unwinding = local.unwinder.unwinding;
574+
}
575+
return unwinding;
576+
}
563577
}
564578
}
565579

src/rt/rust_builtin.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -886,6 +886,28 @@ rust_boxed_region_free(boxed_region *region, rust_opaque_box *box) {
886886
region->free(box);
887887
}
888888

889+
typedef void *(rust_try_fn)(void*, void*);
890+
891+
extern "C" CDECL uintptr_t
892+
rust_try(rust_try_fn f, void *fptr, void *env) {
893+
try {
894+
f(fptr, env);
895+
} catch (uintptr_t token) {
896+
assert(token != 0);
897+
return token;
898+
}
899+
return 0;
900+
}
901+
902+
extern "C" CDECL void
903+
rust_begin_unwind(uintptr_t token) {
904+
#ifndef __WIN32__
905+
throw token;
906+
#else
907+
abort("failing on win32");
908+
#endif
909+
}
910+
889911
//
890912
// Local Variables:
891913
// mode: C++

src/rt/rust_upcall.cpp

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -272,7 +272,13 @@ upcall_rust_personality(int version,
272272
s_rust_personality_args args = {(_Unwind_Reason_Code)0,
273273
version, actions, exception_class,
274274
ue_header, context};
275-
rust_task *task = rust_get_current_task();
275+
rust_task *task = rust_try_get_current_task();
276+
277+
if (task == NULL) {
278+
// Assuming we're running with the new scheduler
279+
upcall_s_rust_personality(&args);
280+
return args.retval;
281+
}
276282

277283
// The personality function is run on the stack of the
278284
// last function that threw or landed, which is going
@@ -309,8 +315,12 @@ upcall_del_stack() {
309315
// needs to acquire the value of the stack pointer
310316
extern "C" CDECL void
311317
upcall_reset_stack_limit() {
312-
rust_task *task = rust_get_current_task();
313-
task->reset_stack_limit();
318+
rust_task *task = rust_try_get_current_task();
319+
if (task != NULL) {
320+
task->reset_stack_limit();
321+
} else {
322+
// We must be in a newsched task
323+
}
314324
}
315325

316326
//

src/rt/rustrt.def.in

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,3 +228,5 @@ rust_new_boxed_region
228228
rust_delete_boxed_region
229229
rust_boxed_region_malloc
230230
rust_boxed_region_free
231+
rust_try
232+
rust_begin_unwind

0 commit comments

Comments
 (0)