about summary refs log tree commit diff
diff options
context:
space:
mode:
authorBrian Anderson <banderson@mozilla.com>2013-04-22 17:15:31 -0700
committerBrian Anderson <banderson@mozilla.com>2013-04-22 17:15:31 -0700
commit42c0f88232847e97e6cf3578ef197d1942bba44d (patch)
tree6167cb170f891dad4674e2c186f785d6a0160b5d
parent5fbb0949a53a6ac51c6d9b187ef4c464e52ae536 (diff)
downloadrust-42c0f88232847e97e6cf3578ef197d1942bba44d.tar.gz
rust-42c0f88232847e97e6cf3578ef197d1942bba44d.zip
core::rt: Add unwinding to newsched tasks
-rw-r--r--src/libcore/rt/io/mod.rs1
-rw-r--r--src/libcore/rt/local_services.rs78
-rw-r--r--src/libcore/rt/sched/mod.rs7
-rw-r--r--src/libcore/rt/test.rs41
-rw-r--r--src/libcore/sys.rs25
-rw-r--r--src/libcore/task/mod.rs18
-rw-r--r--src/rt/rust_builtin.cpp22
-rw-r--r--src/rt/rust_upcall.cpp16
-rw-r--r--src/rt/rustrt.def.in2
9 files changed, 190 insertions, 20 deletions
diff --git a/src/libcore/rt/io/mod.rs b/src/libcore/rt/io/mod.rs
index d9d6622277f..131743305bc 100644
--- a/src/libcore/rt/io/mod.rs
+++ b/src/libcore/rt/io/mod.rs
@@ -153,6 +153,7 @@ pub mod mem;
 pub mod stdio;
 
 /// Implementations for Option
+#[cfg(not(stage0))] // Requires condition! fixes
 mod option;
 
 /// Basic stream compression. XXX: Belongs with other flate code
diff --git a/src/libcore/rt/local_services.rs b/src/libcore/rt/local_services.rs
index d29e57a17af..fc75a256428 100644
--- a/src/libcore/rt/local_services.rs
+++ b/src/libcore/rt/local_services.rs
@@ -19,7 +19,8 @@
 //! (freestanding rust with local services?).
 
 use prelude::*;
-use libc::c_void;
+use libc::{c_void, uintptr_t};
+use cast::transmute;
 use super::sched::{Task, local_sched};
 use super::local_heap::LocalHeap;
 
@@ -35,7 +36,10 @@ pub struct LocalServices {
 pub struct GarbageCollector;
 pub struct LocalStorage(*c_void, Option<~fn(*c_void)>);
 pub struct Logger;
-pub struct Unwinder;
+
+pub struct Unwinder {
+    unwinding: bool,
+}
 
 impl LocalServices {
     pub fn new() -> LocalServices {
@@ -44,17 +48,28 @@ impl LocalServices {
             gc: GarbageCollector,
             storage: LocalStorage(ptr::null(), None),
             logger: Logger,
-            unwinder: Unwinder,
+            unwinder: Unwinder { unwinding: false },
             destroyed: false
         }
     }
 
+    pub fn run(&mut self, f: &fn()) {
+        // This is just an assertion that `run` was called unsafely
+        // and this instance of LocalServices is still accessible.
+        do borrow_local_services |sched| {
+            assert!(ptr::ref_eq(sched, self));
+        }
+
+        self.unwinder.try(f);
+        self.destroy();
+    }
+
     /// Must be called manually before finalization to clean up
     /// thread-local resources. Some of the routines here expect
     /// LocalServices to be available recursively so this must be
     /// called unsafely, without removing LocalServices from
     /// thread-local-storage.
-    pub fn destroy(&mut self) {
+    fn destroy(&mut self) {
         // This is just an assertion that `destroy` was called unsafely
         // and this instance of LocalServices is still accessible.
         do borrow_local_services |sched| {
@@ -72,6 +87,51 @@ impl Drop for LocalServices {
     fn finalize(&self) { assert!(self.destroyed) }
 }
 
+// Just a sanity check to make sure we are catching a Rust-thrown exception
+static UNWIND_TOKEN: uintptr_t = 839147;
+
+impl Unwinder {
+    pub fn try(&mut self, f: &fn()) {
+        use sys::Closure;
+
+        unsafe {
+            let closure: Closure = transmute(f);
+            let code = transmute(closure.code);
+            let env = transmute(closure.env);
+
+            let token = rust_try(try_fn, code, env);
+            assert!(token == 0 || token == UNWIND_TOKEN);
+        }
+
+        extern fn try_fn(code: *c_void, env: *c_void) {
+            unsafe {
+                let closure: Closure = Closure {
+                    code: transmute(code),
+                    env: transmute(env),
+                };
+                let closure: &fn() = transmute(closure);
+                closure();
+            }
+        }
+
+        extern {
+            #[rust_stack]
+            fn rust_try(f: *u8, code: *c_void, data: *c_void) -> uintptr_t;
+        }
+    }
+
+    pub fn begin_unwind(&mut self) -> ! {
+        self.unwinding = true;
+        unsafe {
+            rust_begin_unwind(UNWIND_TOKEN);
+            return transmute(());
+        }
+        extern {
+            fn rust_begin_unwind(token: uintptr_t);
+        }
+    }
+}
+
 /// Borrow a pointer to the installed local services.
 /// Fails (likely aborting the process) if local services are not available.
 pub fn borrow_local_services(f: &fn(&mut LocalServices)) {
@@ -125,4 +185,14 @@ mod test {
             }
         }
     }
+
+    #[test]
+    fn unwind() {
+        do run_in_newsched_task() {
+            let result = spawn_try(||());
+            assert!(result.is_ok());
+            let result = spawn_try(|| fail!());
+            assert!(result.is_err());
+        }
+    }
 }
\ No newline at end of file
diff --git a/src/libcore/rt/sched/mod.rs b/src/libcore/rt/sched/mod.rs
index b7d861b8946..65456c30fee 100644
--- a/src/libcore/rt/sched/mod.rs
+++ b/src/libcore/rt/sched/mod.rs
@@ -353,15 +353,10 @@ pub impl Task {
             unsafe {
                 let sched = local_sched::unsafe_borrow();
                 sched.run_cleanup_job();
-            }
-
-            start();
 
-            unsafe {
-                // Destroy the local heap, TLS, etc.
                 let sched = local_sched::unsafe_borrow();
                 let task = sched.current_task.get_mut_ref();
-                task.local_services.destroy();
+                task.local_services.run(start);
             }
 
             let sched = local_sched::take();
diff --git a/src/libcore/rt/test.rs b/src/libcore/rt/test.rs
index e394a873fea..f3d73c91bd6 100644
--- a/src/libcore/rt/test.rs
+++ b/src/libcore/rt/test.rs
@@ -8,6 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+use result::{Result, Ok, Err};
 use super::io::net::ip::{IpAddr, Ipv4};
 
 /// Creates a new scheduler in a new thread and runs a task in it,
@@ -47,6 +48,46 @@ pub fn spawn_immediately(f: ~fn()) {
     }
 }
 
+/// Spawn a task and wait for it to finish, returning whether it completed successfully or failed
+pub fn spawn_try(f: ~fn()) -> Result<(), ()> {
+    use cell::Cell;
+    use super::sched::*;
+    use task;
+    use unstable::finally::Finally;
+
+    // Our status variables will be filled in from the scheduler context
+    let mut failed = false;
+    let failed_ptr: *mut bool = &mut failed;
+
+    // Switch to the scheduler
+    let f = Cell(Cell(f));
+    let mut sched = local_sched::take();
+    do sched.deschedule_running_task_and_then() |old_task| {
+        let old_task = Cell(old_task);
+        let f = f.take();
+        let mut sched = local_sched::take();
+        let new_task = ~do Task::new(&mut sched.stack_pool) {
+            do (|| {
+                (f.take())()
+            }).finally {
+                // Check for failure then resume the parent task
+                unsafe { *failed_ptr = task::failing(); }
+                let sched = local_sched::take();
+                do sched.switch_running_tasks_and_then(old_task.take()) |new_task| {
+                    let new_task = Cell(new_task);
+                    do local_sched::borrow |sched| {
+                        sched.task_queue.push_front(new_task.take());
+                    }
+                }
+            }
+        };
+
+        sched.resume_task_immediately(new_task);
+    }
+
+    if !failed { Ok(()) } else { Err(()) }
+}
+
 /// Get a port number, starting at 9600, for use in tests
 pub fn next_test_port() -> u16 {
     unsafe {
diff --git a/src/libcore/sys.rs b/src/libcore/sys.rs
index 04f96f5eb22..c50bc03517f 100644
--- a/src/libcore/sys.rs
+++ b/src/libcore/sys.rs
@@ -134,12 +134,27 @@ pub fn log_str<T>(t: &T) -> ~str {
 
 /** Initiate task failure */
 pub fn begin_unwind(msg: ~str, file: ~str, line: uint) -> ! {
-    do str::as_buf(msg) |msg_buf, _msg_len| {
-        do str::as_buf(file) |file_buf, _file_len| {
+
+    use rt::{context, OldTaskContext};
+    use rt::local_services::unsafe_borrow_local_services;
+
+    match context() {
+        OldTaskContext => {
+            do str::as_buf(msg) |msg_buf, _msg_len| {
+                do str::as_buf(file) |file_buf, _file_len| {
+                    unsafe {
+                        let msg_buf = cast::transmute(msg_buf);
+                        let file_buf = cast::transmute(file_buf);
+                        begin_unwind_(msg_buf, file_buf, line as libc::size_t)
+                    }
+                }
+            }
+        }
+        _ => {
+            gc::cleanup_stack_for_failure();
             unsafe {
-                let msg_buf = cast::transmute(msg_buf);
-                let file_buf = cast::transmute(file_buf);
-                begin_unwind_(msg_buf, file_buf, line as libc::size_t)
+                let local_services = unsafe_borrow_local_services();
+                local_services.unwinder.begin_unwind();
             }
         }
     }
diff --git a/src/libcore/task/mod.rs b/src/libcore/task/mod.rs
index a243bfba85c..e1f4805a692 100644
--- a/src/libcore/task/mod.rs
+++ b/src/libcore/task/mod.rs
@@ -558,8 +558,22 @@ pub fn yield() {
 pub fn failing() -> bool {
     //! True if the running task has failed
 
-    unsafe {
-        rt::rust_task_is_unwinding(rt::rust_get_task())
+    use rt::{context, OldTaskContext};
+    use rt::local_services::borrow_local_services;
+
+    match context() {
+        OldTaskContext => {
+            unsafe {
+                rt::rust_task_is_unwinding(rt::rust_get_task())
+            }
+        }
+        _ => {
+            let mut unwinding = false;
+            do borrow_local_services |local| {
+                unwinding = local.unwinder.unwinding;
+            }
+            return unwinding;
+        }
     }
 }
 
diff --git a/src/rt/rust_builtin.cpp b/src/rt/rust_builtin.cpp
index b8749b8f73d..b37644460aa 100644
--- a/src/rt/rust_builtin.cpp
+++ b/src/rt/rust_builtin.cpp
@@ -886,6 +886,28 @@ rust_boxed_region_free(boxed_region *region, rust_opaque_box *box) {
     region->free(box);
 }
 
+typedef void *(rust_try_fn)(void*, void*);
+
+extern "C" CDECL uintptr_t
+rust_try(rust_try_fn f, void *fptr, void *env) {
+    try {
+        f(fptr, env);
+    } catch (uintptr_t token) {
+        assert(token != 0);
+        return token;
+    }
+    return 0;
+}
+
+extern "C" CDECL void
+rust_begin_unwind(uintptr_t token) {
+#ifndef __WIN32__
+    throw token;
+#else
+    abort("failing on win32");
+#endif
+}
+
 //
 // Local Variables:
 // mode: C++
diff --git a/src/rt/rust_upcall.cpp b/src/rt/rust_upcall.cpp
index 9f39e1433fc..34236c36c14 100644
--- a/src/rt/rust_upcall.cpp
+++ b/src/rt/rust_upcall.cpp
@@ -272,7 +272,13 @@ upcall_rust_personality(int version,
     s_rust_personality_args args = {(_Unwind_Reason_Code)0,
                                     version, actions, exception_class,
                                     ue_header, context};
-    rust_task *task = rust_get_current_task();
+    rust_task *task = rust_try_get_current_task();
+
+    if (task == NULL) {
+        // Assuming we're running with the new scheduler
+        upcall_s_rust_personality(&args);
+        return args.retval;
+    }
 
     // The personality function is run on the stack of the
     // last function that threw or landed, which is going
@@ -309,8 +315,12 @@ upcall_del_stack() {
 // needs to acquire the value of the stack pointer
 extern "C" CDECL void
 upcall_reset_stack_limit() {
-    rust_task *task = rust_get_current_task();
-    task->reset_stack_limit();
+    rust_task *task = rust_try_get_current_task();
+    if (task != NULL) {
+        task->reset_stack_limit();
+    } else {
+        // We must be in a newsched task
+    }
 }
 
 //
diff --git a/src/rt/rustrt.def.in b/src/rt/rustrt.def.in
index 9aa80156783..5e9a4b343ee 100644
--- a/src/rt/rustrt.def.in
+++ b/src/rt/rustrt.def.in
@@ -228,3 +228,5 @@ rust_new_boxed_region
 rust_delete_boxed_region
 rust_boxed_region_malloc
 rust_boxed_region_free
+rust_try
+rust_begin_unwind
\ No newline at end of file