From db1abbec4ca9f18a224441c483cf23bb4f8361fd Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Sat, 12 Jan 2013 23:27:46 -0800 Subject: [PATCH] core: Add private global data interface. #3915 --- src/libcore/private.rs | 8 ++ src/libcore/private/global.rs | 257 ++++++++++++++++++++++++++++++++++ src/rt/rust_builtin.cpp | 6 + src/rt/rust_kernel.cpp | 4 +- src/rt/rust_kernel.h | 1 + src/rt/rustrt.def.in | 1 + 6 files changed, 275 insertions(+), 2 deletions(-) create mode 100644 src/libcore/private/global.rs diff --git a/src/libcore/private.rs b/src/libcore/private.rs index 48489dab4880..ef25cb52e8ba 100644 --- a/src/libcore/private.rs +++ b/src/libcore/private.rs @@ -30,6 +30,8 @@ use uint; #[path = "private/at_exit.rs"] pub mod at_exit; +#[path = "private/global.rs"] +pub mod global; extern mod rustrt { #[legacy_exports]; @@ -522,6 +524,12 @@ pub unsafe fn clone_shared_mutable_state(rc: &SharedMutableState) ArcDestruct((*rc).data) } +impl SharedMutableState: Clone { + fn clone(&self) -> SharedMutableState unsafe { + clone_shared_mutable_state(self) + } +} + /****************************************************************************/ #[allow(non_camel_case_types)] // runtime type diff --git a/src/libcore/private/global.rs b/src/libcore/private/global.rs new file mode 100644 index 000000000000..8c1500353ae6 --- /dev/null +++ b/src/libcore/private/global.rs @@ -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 = &fn(v: T); + +pub unsafe fn global_data_clone_create( + key: GlobalDataKey, 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_( + key: uint, create: &fn() -> ~T) -> T { + + let mut clone_value: Option = 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( + key: GlobalDataKey, op: &fn(Option<~T>) -> Option<~T>) { + + global_data_modify_(key_ptr(key), op) +} + +unsafe fn global_data_modify_( + 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 +} + +impl GlobalState: Drop { + fn finalize(&self) { + for self.map.each_value |v| { + match v { + &(_, ref dtor) => (*dtor)() + } + } + } +} + +fn get_global_state() -> Exclusive 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 = 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 = transmute(prev_i); + return state.clone(); + } + } else { + let state: &Exclusive = transmute(*global_ptr); + return state.clone(); + } +} + +fn key_ptr(key: GlobalDataKey) -> 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; + + fn key(_v: SharedMutableState) { } + + 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; + + fn key(_v: SharedMutableState) { } + + 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 + } + } +} diff --git a/src/rt/rust_builtin.cpp b/src/rt/rust_builtin.cpp index a37aea13e406..221afb89b237 100644 --- a/src/rt/rust_builtin.cpp +++ b/src/rt/rust_builtin.cpp @@ -1032,6 +1032,12 @@ rust_register_exit_function(spawn_fn runner, fn_env_pair *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: // mode: C++ diff --git a/src/rt/rust_kernel.cpp b/src/rt/rust_kernel.cpp index 3042b006a92c..9c6ba9dcda3e 100644 --- a/src/rt/rust_kernel.cpp +++ b/src/rt/rust_kernel.cpp @@ -38,8 +38,8 @@ rust_kernel::rust_kernel(rust_env *env) : global_env_chan(0), at_exit_runner(NULL), at_exit_started(false), - env(env) - + env(env), + global_data(0) { // Create the single threaded scheduler that will run on the platform's // main thread diff --git a/src/rt/rust_kernel.h b/src/rt/rust_kernel.h index b1548e92cdb3..99b230f7872b 100644 --- a/src/rt/rust_kernel.h +++ b/src/rt/rust_kernel.h @@ -144,6 +144,7 @@ class rust_kernel { public: struct rust_env *env; + uintptr_t global_data; rust_kernel(rust_env *env); diff --git a/src/rt/rustrt.def.in b/src/rt/rustrt.def.in index 719505079e65..8c26832f3498 100644 --- a/src/rt/rustrt.def.in +++ b/src/rt/rustrt.def.in @@ -211,3 +211,4 @@ linenoiseHistoryLoad rust_raw_thread_start rust_raw_thread_join_delete rust_register_exit_function +rust_get_global_data_ptr \ No newline at end of file