about summary refs log tree commit diff
diff options
context:
space:
mode:
authorBrian Anderson <banderson@mozilla.com>2013-01-12 23:27:46 -0800
committerBrian Anderson <banderson@mozilla.com>2013-01-17 19:24:50 -0800
commitdb1abbec4ca9f18a224441c483cf23bb4f8361fd (patch)
tree2adeac14bbca31e2bf09708ea1f36b6a39e65953
parentac435af73a0009daf22164ee2f081a7c98ca844c (diff)
core: Add private global data interface. #3915
-rw-r--r--src/libcore/private.rs8
-rw-r--r--src/libcore/private/global.rs257
-rw-r--r--src/rt/rust_builtin.cpp6
-rw-r--r--src/rt/rust_kernel.cpp4
-rw-r--r--src/rt/rust_kernel.h1
-rw-r--r--src/rt/rustrt.def.in1
6 files changed, 275 insertions, 2 deletions
diff --git a/src/libcore/private.rs b/src/libcore/private.rs
index 48489dab488..ef25cb52e8b 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<T: Owned>(rc: &SharedMutableState<T>)
     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
diff --git a/src/libcore/private/global.rs b/src/libcore/private/global.rs
new file mode 100644
index 00000000000..8c1500353ae
--- /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<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
+        }
+    }
+}
diff --git a/src/rt/rust_builtin.cpp b/src/rt/rust_builtin.cpp
index a37aea13e40..221afb89b23 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 3042b006a92..9c6ba9dcda3 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 b1548e92cdb..99b230f7872 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 719505079e6..8c26832f349 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