core: Add private global data interface. #3915
This commit is contained in:
parent
ac435af73a
commit
db1abbec4c
6 changed files with 275 additions and 2 deletions
|
@ -30,6 +30,8 @@ use uint;
|
||||||
|
|
||||||
#[path = "private/at_exit.rs"]
|
#[path = "private/at_exit.rs"]
|
||||||
pub mod at_exit;
|
pub mod at_exit;
|
||||||
|
#[path = "private/global.rs"]
|
||||||
|
pub mod global;
|
||||||
|
|
||||||
extern mod rustrt {
|
extern mod rustrt {
|
||||||
#[legacy_exports];
|
#[legacy_exports];
|
||||||
|
@ -522,6 +524,12 @@ pub unsafe fn clone_shared_mutable_state<T: Owned>(rc: &SharedMutableState<T>)
|
||||||
ArcDestruct((*rc).data)
|
ArcDestruct((*rc).data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T: Owned> SharedMutableState<T>: Clone {
|
||||||
|
fn clone(&self) -> SharedMutableState<T> unsafe {
|
||||||
|
clone_shared_mutable_state(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/****************************************************************************/
|
/****************************************************************************/
|
||||||
|
|
||||||
#[allow(non_camel_case_types)] // runtime type
|
#[allow(non_camel_case_types)] // runtime type
|
||||||
|
|
257
src/libcore/private/global.rs
Normal file
257
src/libcore/private/global.rs
Normal file
|
@ -0,0 +1,257 @@
|
||||||
|
/*!
|
||||||
|
Global data
|
||||||
|
|
||||||
|
An interface for creating and retrieving values with global
|
||||||
|
(per-runtime) scope.
|
||||||
|
|
||||||
|
Global values are stored in a map and protected by a single global
|
||||||
|
mutex. Operations are provided for accessing and cloning the value
|
||||||
|
under the mutex.
|
||||||
|
|
||||||
|
Because all globals go through a single mutex, they should be used
|
||||||
|
sparingly. The interface is intended to be used with clonable,
|
||||||
|
atomically reference counted synchronization types, like ARCs, in
|
||||||
|
which case the value should be cached locally whenever possible to
|
||||||
|
avoid hitting the mutex.
|
||||||
|
*/
|
||||||
|
|
||||||
|
use cast::{transmute, reinterpret_cast};
|
||||||
|
use clone::Clone;
|
||||||
|
use kinds::Owned;
|
||||||
|
use libc::{c_void, uintptr_t};
|
||||||
|
use option::{Option, Some, None};
|
||||||
|
use ops::Drop;
|
||||||
|
use pipes;
|
||||||
|
use private::{Exclusive, exclusive};
|
||||||
|
use private::{SharedMutableState, shared_mutable_state};
|
||||||
|
use private::{get_shared_immutable_state};
|
||||||
|
use private::at_exit::at_exit;
|
||||||
|
use send_map::linear::LinearMap;
|
||||||
|
use sys::Closure;
|
||||||
|
use task::spawn;
|
||||||
|
use uint;
|
||||||
|
|
||||||
|
pub type GlobalDataKey<T: Owned> = &fn(v: T);
|
||||||
|
|
||||||
|
pub unsafe fn global_data_clone_create<T: Owned Clone>(
|
||||||
|
key: GlobalDataKey<T>, create: &fn() -> ~T) -> T {
|
||||||
|
/*!
|
||||||
|
* Clone a global value or, if it has not been created,
|
||||||
|
* first construct the value then return a clone.
|
||||||
|
*
|
||||||
|
* # Safety note
|
||||||
|
*
|
||||||
|
* Both the clone operation and the constructor are
|
||||||
|
* called while the global lock is held. Recursive
|
||||||
|
* use of the global interface in either of these
|
||||||
|
* operations will result in deadlock.
|
||||||
|
*/
|
||||||
|
global_data_clone_create_(key_ptr(key), create)
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn global_data_clone_create_<T: Owned Clone>(
|
||||||
|
key: uint, create: &fn() -> ~T) -> T {
|
||||||
|
|
||||||
|
let mut clone_value: Option<T> = None;
|
||||||
|
do global_data_modify_(key) |value: Option<~T>| {
|
||||||
|
match value {
|
||||||
|
None => {
|
||||||
|
let value = create();
|
||||||
|
clone_value = Some(value.clone());
|
||||||
|
Some(value)
|
||||||
|
}
|
||||||
|
Some(value) => {
|
||||||
|
clone_value = Some(value.clone());
|
||||||
|
Some(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return clone_value.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn global_data_modify<T: Owned>(
|
||||||
|
key: GlobalDataKey<T>, op: &fn(Option<~T>) -> Option<~T>) {
|
||||||
|
|
||||||
|
global_data_modify_(key_ptr(key), op)
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn global_data_modify_<T: Owned>(
|
||||||
|
key: uint, op: &fn(Option<~T>) -> Option<~T>) {
|
||||||
|
|
||||||
|
let mut old_dtor = None;
|
||||||
|
do get_global_state().with |gs| unsafe {
|
||||||
|
let (maybe_new_value, maybe_dtor) = match gs.map.pop(&key) {
|
||||||
|
Some((ptr, dtor)) => {
|
||||||
|
let value: ~T = transmute(ptr);
|
||||||
|
(op(Some(value)), Some(dtor))
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
(op(None), None)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
match maybe_new_value {
|
||||||
|
Some(value) => {
|
||||||
|
let data: *c_void = transmute(value);
|
||||||
|
let dtor: ~fn() = match maybe_dtor {
|
||||||
|
Some(dtor) => dtor,
|
||||||
|
None => {
|
||||||
|
let dtor: ~fn() = || unsafe {
|
||||||
|
let _destroy_value: ~T = transmute(data);
|
||||||
|
};
|
||||||
|
dtor
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let value = (data, dtor);
|
||||||
|
gs.map.insert(key, value);
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
match maybe_dtor {
|
||||||
|
Some(dtor) => old_dtor = Some(dtor),
|
||||||
|
None => ()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GlobalState is a map from keys to unique pointers and a
|
||||||
|
// destructor. Keys are pointers derived from the type of the
|
||||||
|
// global value. There is a single GlobalState instance per runtime.
|
||||||
|
struct GlobalState {
|
||||||
|
map: LinearMap<uint, (*c_void, ~fn())>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GlobalState: Drop {
|
||||||
|
fn finalize(&self) {
|
||||||
|
for self.map.each_value |v| {
|
||||||
|
match v {
|
||||||
|
&(_, ref dtor) => (*dtor)()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_global_state() -> Exclusive<GlobalState> unsafe {
|
||||||
|
|
||||||
|
const POISON: int = -1;
|
||||||
|
|
||||||
|
// XXX: Doing atomic_cxchg to initialize the global state
|
||||||
|
// lazily, which wouldn't be necessary with a runtime written
|
||||||
|
// in Rust
|
||||||
|
let global_ptr = rust_get_global_data_ptr();
|
||||||
|
|
||||||
|
if *global_ptr == 0 {
|
||||||
|
// Global state doesn't exist yet, probably
|
||||||
|
|
||||||
|
// The global state object
|
||||||
|
let state = GlobalState {
|
||||||
|
map: LinearMap()
|
||||||
|
};
|
||||||
|
|
||||||
|
// It's under a reference-counted mutex
|
||||||
|
let state = ~exclusive(state);
|
||||||
|
|
||||||
|
// Convert it to an integer
|
||||||
|
let state_ptr: &Exclusive<GlobalState> = state;
|
||||||
|
let state_i: int = transmute(state_ptr);
|
||||||
|
|
||||||
|
// Swap our structure into the global pointer
|
||||||
|
let prev_i = atomic_cxchg(&mut *global_ptr, 0, state_i);
|
||||||
|
|
||||||
|
// Sanity check that we're not trying to reinitialize after shutdown
|
||||||
|
assert prev_i != POISON;
|
||||||
|
|
||||||
|
if prev_i == 0 {
|
||||||
|
// Successfully installed the global pointer
|
||||||
|
|
||||||
|
// Take a handle to return
|
||||||
|
let clone = state.clone();
|
||||||
|
|
||||||
|
// Install a runtime exit function to destroy the global object
|
||||||
|
do at_exit || unsafe {
|
||||||
|
// Poison the global pointer
|
||||||
|
let prev_i = atomic_cxchg(&mut *global_ptr, state_i, POISON);
|
||||||
|
assert prev_i == state_i;
|
||||||
|
|
||||||
|
// Capture the global state object in the at_exit closure
|
||||||
|
// so that it is destroyed at the right time
|
||||||
|
let _capture_global_state = &state;
|
||||||
|
};
|
||||||
|
return clone;
|
||||||
|
} else {
|
||||||
|
// Somebody else initialized the globals first
|
||||||
|
let state: &Exclusive<GlobalState> = transmute(prev_i);
|
||||||
|
return state.clone();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let state: &Exclusive<GlobalState> = transmute(*global_ptr);
|
||||||
|
return state.clone();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn key_ptr<T: Owned>(key: GlobalDataKey<T>) -> uint unsafe {
|
||||||
|
let closure: Closure = reinterpret_cast(&key);
|
||||||
|
return transmute(closure.code);
|
||||||
|
}
|
||||||
|
|
||||||
|
extern {
|
||||||
|
fn rust_get_global_data_ptr() -> *mut int;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[abi = "rust-intrinsic"]
|
||||||
|
extern {
|
||||||
|
fn atomic_cxchg(dst: &mut int, old: int, src: int) -> int;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_clone_rc() unsafe {
|
||||||
|
type MyType = SharedMutableState<int>;
|
||||||
|
|
||||||
|
fn key(_v: SharedMutableState<int>) { }
|
||||||
|
|
||||||
|
for uint::range(0, 100) |_| {
|
||||||
|
do spawn unsafe {
|
||||||
|
let val = do global_data_clone_create(key) {
|
||||||
|
~shared_mutable_state(10)
|
||||||
|
};
|
||||||
|
|
||||||
|
assert get_shared_immutable_state(&val) == &10;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_modify() unsafe {
|
||||||
|
type MyType = SharedMutableState<int>;
|
||||||
|
|
||||||
|
fn key(_v: SharedMutableState<int>) { }
|
||||||
|
|
||||||
|
do global_data_modify(key) |v| unsafe {
|
||||||
|
match v {
|
||||||
|
None => {
|
||||||
|
Some(~shared_mutable_state(10))
|
||||||
|
}
|
||||||
|
_ => fail
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
do global_data_modify(key) |v| {
|
||||||
|
match v {
|
||||||
|
Some(sms) => {
|
||||||
|
let v = get_shared_immutable_state(sms);
|
||||||
|
assert *v == 10;
|
||||||
|
None
|
||||||
|
},
|
||||||
|
_ => fail
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
do global_data_modify(key) |v| unsafe {
|
||||||
|
match v {
|
||||||
|
None => {
|
||||||
|
Some(~shared_mutable_state(10))
|
||||||
|
}
|
||||||
|
_ => fail
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1032,6 +1032,12 @@ rust_register_exit_function(spawn_fn runner, fn_env_pair *f) {
|
||||||
task->kernel->register_exit_function(runner, f);
|
task->kernel->register_exit_function(runner, f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern "C" void *
|
||||||
|
rust_get_global_data_ptr() {
|
||||||
|
rust_task *task = rust_get_current_task();
|
||||||
|
return &task->kernel->global_data;
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Local Variables:
|
// Local Variables:
|
||||||
// mode: C++
|
// mode: C++
|
||||||
|
|
|
@ -38,8 +38,8 @@ rust_kernel::rust_kernel(rust_env *env) :
|
||||||
global_env_chan(0),
|
global_env_chan(0),
|
||||||
at_exit_runner(NULL),
|
at_exit_runner(NULL),
|
||||||
at_exit_started(false),
|
at_exit_started(false),
|
||||||
env(env)
|
env(env),
|
||||||
|
global_data(0)
|
||||||
{
|
{
|
||||||
// Create the single threaded scheduler that will run on the platform's
|
// Create the single threaded scheduler that will run on the platform's
|
||||||
// main thread
|
// main thread
|
||||||
|
|
|
@ -144,6 +144,7 @@ class rust_kernel {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
struct rust_env *env;
|
struct rust_env *env;
|
||||||
|
uintptr_t global_data;
|
||||||
|
|
||||||
rust_kernel(rust_env *env);
|
rust_kernel(rust_env *env);
|
||||||
|
|
||||||
|
|
|
@ -211,3 +211,4 @@ linenoiseHistoryLoad
|
||||||
rust_raw_thread_start
|
rust_raw_thread_start
|
||||||
rust_raw_thread_join_delete
|
rust_raw_thread_join_delete
|
||||||
rust_register_exit_function
|
rust_register_exit_function
|
||||||
|
rust_get_global_data_ptr
|
Loading…
Add table
Add a link
Reference in a new issue