Skip to content

Commit b967651

Browse files
committed
core: Add rt::context for figuring out what runtime services are available
Conflicts: src/libcore/rt/sched/mod.rs
1 parent c44d7a6 commit b967651

File tree

5 files changed

+116
-1
lines changed

5 files changed

+116
-1
lines changed

src/libcore/rt/mod.rs

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,3 +88,75 @@ pub fn start(main: *u8, _argc: int, _argv: **c_char, _crate_map: *u8) -> int {
8888
fn rust_call_nullary_fn(f: *u8);
8989
}
9090
}
91+
92+
/// Possible contexts in which Rust code may be executing.
93+
/// Different runtime services are available depending on context.
94+
#[deriving(Eq)]
95+
pub enum RuntimeContext {
96+
// Only default services, e.g. exchange heap
97+
GlobalContext,
98+
// The scheduler may be accessed
99+
SchedulerContext,
100+
// Full task services, e.g. local heap, unwinding
101+
TaskContext,
102+
// Running in an old-style task
103+
OldTaskContext
104+
}
105+
106+
pub fn context() -> RuntimeContext {
107+
108+
use task::rt::rust_task;
109+
use self::sched::Scheduler;
110+
111+
// XXX: Hitting TLS twice to check if the scheduler exists
112+
// then to check for the task is not good for perf
113+
if unsafe { rust_try_get_task().is_not_null() } {
114+
return OldTaskContext;
115+
} else {
116+
if Scheduler::have_local() {
117+
let context = ::cell::empty_cell();
118+
do Scheduler::borrow_local |sched| {
119+
if sched.in_task_context() {
120+
context.put_back(TaskContext);
121+
} else {
122+
context.put_back(SchedulerContext);
123+
}
124+
}
125+
return context.take();
126+
} else {
127+
return GlobalContext;
128+
}
129+
}
130+
131+
pub extern {
132+
#[rust_stack]
133+
fn rust_try_get_task() -> *rust_task;
134+
}
135+
}
136+
137+
#[test]
138+
fn test_context() {
139+
use unstable::run_in_bare_thread;
140+
use self::sched::{Scheduler, Task};
141+
use self::uvio::UvEventLoop;
142+
use cell::Cell;
143+
144+
assert!(context() == OldTaskContext);
145+
do run_in_bare_thread {
146+
assert!(context() == GlobalContext);
147+
let mut sched = ~UvEventLoop::new_scheduler();
148+
let task = ~do Task::new(&mut sched.stack_pool) {
149+
assert!(context() == TaskContext);
150+
let sched = Scheduler::take_local();
151+
do sched.deschedule_running_task_and_then() |task| {
152+
assert!(context() == SchedulerContext);
153+
let task = Cell(task);
154+
do Scheduler::borrow_local |sched| {
155+
sched.task_queue.push_back(task.take());
156+
}
157+
}
158+
};
159+
sched.task_queue.push_back(task);
160+
sched.run();
161+
}
162+
}

src/libcore/rt/sched/local.rs

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
//! Access to the thread-local Scheduler
1212
13+
use prelude::*;
1314
use ptr::mut_null;
1415
use libc::c_void;
1516
use cast::transmute;
@@ -39,6 +40,16 @@ pub fn take() -> ~Scheduler {
3940
}
4041
}
4142

43+
/// Check whether there is a thread-local Scheduler attached to the running thread
44+
pub fn exists() -> bool {
45+
unsafe {
46+
match maybe_tls_key() {
47+
Some(key) => tls::get(key).is_not_null(),
48+
None => false
49+
}
50+
}
51+
}
52+
4253
/// Borrow a mutable reference to the thread-local Scheduler
4354
/// # Safety Note
4455
/// Because this leaves the Scheduler in thread-local storage it is possible
@@ -60,10 +71,31 @@ pub unsafe fn borrow() -> &mut Scheduler {
6071
}
6172

6273
fn tls_key() -> tls::Key {
74+
maybe_tls_key().get()
75+
}
76+
77+
fn maybe_tls_key() -> Option<tls::Key> {
6378
unsafe {
6479
let key: *mut c_void = rust_get_sched_tls_key();
6580
let key: &mut tls::Key = transmute(key);
66-
return *key;
81+
let key = *key;
82+
// Check that the key has been initialized.
83+
84+
// NB: This is a little racy because, while the key is
85+
// initalized under a mutex and it's assumed to be initalized
86+
// in the Scheduler ctor by any thread that needs to use it,
87+
// we are not accessing the key under a mutex. Threads that
88+
// are not using the new Scheduler but still *want to check*
89+
// whether they are running under a new Scheduler may see a 0
90+
// value here that is in the process of being initialized in
91+
// another thread. I think this is fine since the only action
92+
// they could take if it was initialized would be to check the
93+
// thread-local value and see that it's not set.
94+
if key != 0 {
95+
return Some(key);
96+
} else {
97+
return None;
98+
}
6799
}
68100
}
69101

src/libcore/rt/sched/mod.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,11 @@ pub impl Scheduler {
133133
local::take()
134134
}
135135

136+
/// Just check whether there is a local scheduler
137+
fn have_local() -> bool {
138+
local::exists()
139+
}
140+
136141
// * Scheduler-context operations
137142

138143
fn resume_task_from_queue(~self) -> bool {

src/rt/rust_builtin.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -539,6 +539,11 @@ rust_get_task() {
539539
return rust_get_current_task();
540540
}
541541

542+
extern "C" rust_task *
543+
rust_try_get_task() {
544+
return rust_try_get_current_task();
545+
}
546+
542547
extern "C" CDECL stk_seg *
543548
rust_get_stack_segment() {
544549
return rust_get_current_task()->stk;

src/rt/rustrt.def.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ rust_env_pairs
4747
rust_task_yield
4848
rust_task_is_unwinding
4949
rust_get_task
50+
rust_try_get_task
5051
rust_get_stack_segment
5152
rust_log_str
5253
start_task

0 commit comments

Comments
 (0)