core: Add rt::context for figuring out what runtime services are available

Conflicts:
	src/libcore/rt/sched/mod.rs
This commit is contained in:
Brian Anderson 2013-03-27 15:24:50 -07:00
parent c44d7a6486
commit b96765179e
5 changed files with 116 additions and 1 deletions

View File

@ -88,3 +88,75 @@ pub fn start(main: *u8, _argc: int, _argv: **c_char, _crate_map: *u8) -> int {
fn rust_call_nullary_fn(f: *u8);
}
}
/// Possible contexts in which Rust code may be executing.
/// Different runtime services are available depending on context.
#[deriving(Eq)]
pub enum RuntimeContext {
// Only default services, e.g. exchange heap
GlobalContext,
// The scheduler may be accessed
SchedulerContext,
// Full task services, e.g. local heap, unwinding
TaskContext,
// Running in an old-style task
OldTaskContext
}
pub fn context() -> RuntimeContext {
use task::rt::rust_task;
use self::sched::Scheduler;
// XXX: Hitting TLS twice to check if the scheduler exists
// then to check for the task is not good for perf
if unsafe { rust_try_get_task().is_not_null() } {
return OldTaskContext;
} else {
if Scheduler::have_local() {
let context = ::cell::empty_cell();
do Scheduler::borrow_local |sched| {
if sched.in_task_context() {
context.put_back(TaskContext);
} else {
context.put_back(SchedulerContext);
}
}
return context.take();
} else {
return GlobalContext;
}
}
pub extern {
#[rust_stack]
fn rust_try_get_task() -> *rust_task;
}
}
#[test]
fn test_context() {
use unstable::run_in_bare_thread;
use self::sched::{Scheduler, Task};
use self::uvio::UvEventLoop;
use cell::Cell;
assert!(context() == OldTaskContext);
do run_in_bare_thread {
assert!(context() == GlobalContext);
let mut sched = ~UvEventLoop::new_scheduler();
let task = ~do Task::new(&mut sched.stack_pool) {
assert!(context() == TaskContext);
let sched = Scheduler::take_local();
do sched.deschedule_running_task_and_then() |task| {
assert!(context() == SchedulerContext);
let task = Cell(task);
do Scheduler::borrow_local |sched| {
sched.task_queue.push_back(task.take());
}
}
};
sched.task_queue.push_back(task);
sched.run();
}
}

View File

@ -10,6 +10,7 @@
//! Access to the thread-local Scheduler
use prelude::*;
use ptr::mut_null;
use libc::c_void;
use cast::transmute;
@ -39,6 +40,16 @@ pub fn take() -> ~Scheduler {
}
}
/// Check whether there is a thread-local Scheduler attached to the running thread
pub fn exists() -> bool {
unsafe {
match maybe_tls_key() {
Some(key) => tls::get(key).is_not_null(),
None => false
}
}
}
/// Borrow a mutable reference to the thread-local Scheduler
/// # Safety Note
/// Because this leaves the Scheduler in thread-local storage it is possible
@ -60,10 +71,31 @@ pub unsafe fn borrow() -> &mut Scheduler {
}
fn tls_key() -> tls::Key {
maybe_tls_key().get()
}
fn maybe_tls_key() -> Option<tls::Key> {
unsafe {
let key: *mut c_void = rust_get_sched_tls_key();
let key: &mut tls::Key = transmute(key);
return *key;
let key = *key;
// Check that the key has been initialized.
// NB: This is a little racy because, while the key is
// initalized under a mutex and it's assumed to be initalized
// in the Scheduler ctor by any thread that needs to use it,
// we are not accessing the key under a mutex. Threads that
// are not using the new Scheduler but still *want to check*
// whether they are running under a new Scheduler may see a 0
// value here that is in the process of being initialized in
// another thread. I think this is fine since the only action
// they could take if it was initialized would be to check the
// thread-local value and see that it's not set.
if key != 0 {
return Some(key);
} else {
return None;
}
}
}

View File

@ -133,6 +133,11 @@ pub impl Scheduler {
local::take()
}
/// Just check whether there is a local scheduler
fn have_local() -> bool {
local::exists()
}
// * Scheduler-context operations
fn resume_task_from_queue(~self) -> bool {

View File

@ -539,6 +539,11 @@ rust_get_task() {
return rust_get_current_task();
}
extern "C" rust_task *
rust_try_get_task() {
return rust_try_get_current_task();
}
extern "C" CDECL stk_seg *
rust_get_stack_segment() {
return rust_get_current_task()->stk;

View File

@ -47,6 +47,7 @@ rust_env_pairs
rust_task_yield
rust_task_is_unwinding
rust_get_task
rust_try_get_task
rust_get_stack_segment
rust_log_str
start_task