Skip to content

Commit db1abbe

Browse files
committed
core: Add private global data interface. #3915
1 parent ac435af commit db1abbe

File tree

6 files changed

+275
-2
lines changed

6 files changed

+275
-2
lines changed

src/libcore/private.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ use uint;
3030

3131
#[path = "private/at_exit.rs"]
3232
pub mod at_exit;
33+
#[path = "private/global.rs"]
34+
pub mod global;
3335

3436
extern mod rustrt {
3537
#[legacy_exports];
@@ -522,6 +524,12 @@ pub unsafe fn clone_shared_mutable_state<T: Owned>(rc: &SharedMutableState<T>)
522524
ArcDestruct((*rc).data)
523525
}
524526

527+
impl<T: Owned> SharedMutableState<T>: Clone {
528+
fn clone(&self) -> SharedMutableState<T> unsafe {
529+
clone_shared_mutable_state(self)
530+
}
531+
}
532+
525533
/****************************************************************************/
526534

527535
#[allow(non_camel_case_types)] // runtime type

src/libcore/private/global.rs

Lines changed: 257 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,257 @@
1+
/*!
2+
Global data
3+
4+
An interface for creating and retrieving values with global
5+
(per-runtime) scope.
6+
7+
Global values are stored in a map and protected by a single global
8+
mutex. Operations are provided for accessing and cloning the value
9+
under the mutex.
10+
11+
Because all globals go through a single mutex, they should be used
12+
sparingly. The interface is intended to be used with clonable,
13+
atomically reference counted synchronization types, like ARCs, in
14+
which case the value should be cached locally whenever possible to
15+
avoid hitting the mutex.
16+
*/
17+
18+
use cast::{transmute, reinterpret_cast};
19+
use clone::Clone;
20+
use kinds::Owned;
21+
use libc::{c_void, uintptr_t};
22+
use option::{Option, Some, None};
23+
use ops::Drop;
24+
use pipes;
25+
use private::{Exclusive, exclusive};
26+
use private::{SharedMutableState, shared_mutable_state};
27+
use private::{get_shared_immutable_state};
28+
use private::at_exit::at_exit;
29+
use send_map::linear::LinearMap;
30+
use sys::Closure;
31+
use task::spawn;
32+
use uint;
33+
34+
pub type GlobalDataKey<T: Owned> = &fn(v: T);
35+
36+
pub unsafe fn global_data_clone_create<T: Owned Clone>(
37+
key: GlobalDataKey<T>, create: &fn() -> ~T) -> T {
38+
/*!
39+
* Clone a global value or, if it has not been created,
40+
* first construct the value then return a clone.
41+
*
42+
* # Safety note
43+
*
44+
* Both the clone operation and the constructor are
45+
* called while the global lock is held. Recursive
46+
* use of the global interface in either of these
47+
* operations will result in deadlock.
48+
*/
49+
global_data_clone_create_(key_ptr(key), create)
50+
}
51+
52+
unsafe fn global_data_clone_create_<T: Owned Clone>(
53+
key: uint, create: &fn() -> ~T) -> T {
54+
55+
let mut clone_value: Option<T> = None;
56+
do global_data_modify_(key) |value: Option<~T>| {
57+
match value {
58+
None => {
59+
let value = create();
60+
clone_value = Some(value.clone());
61+
Some(value)
62+
}
63+
Some(value) => {
64+
clone_value = Some(value.clone());
65+
Some(value)
66+
}
67+
}
68+
}
69+
return clone_value.unwrap();
70+
}
71+
72+
unsafe fn global_data_modify<T: Owned>(
73+
key: GlobalDataKey<T>, op: &fn(Option<~T>) -> Option<~T>) {
74+
75+
global_data_modify_(key_ptr(key), op)
76+
}
77+
78+
unsafe fn global_data_modify_<T: Owned>(
79+
key: uint, op: &fn(Option<~T>) -> Option<~T>) {
80+
81+
let mut old_dtor = None;
82+
do get_global_state().with |gs| unsafe {
83+
let (maybe_new_value, maybe_dtor) = match gs.map.pop(&key) {
84+
Some((ptr, dtor)) => {
85+
let value: ~T = transmute(ptr);
86+
(op(Some(value)), Some(dtor))
87+
}
88+
None => {
89+
(op(None), None)
90+
}
91+
};
92+
match maybe_new_value {
93+
Some(value) => {
94+
let data: *c_void = transmute(value);
95+
let dtor: ~fn() = match maybe_dtor {
96+
Some(dtor) => dtor,
97+
None => {
98+
let dtor: ~fn() = || unsafe {
99+
let _destroy_value: ~T = transmute(data);
100+
};
101+
dtor
102+
}
103+
};
104+
let value = (data, dtor);
105+
gs.map.insert(key, value);
106+
}
107+
None => {
108+
match maybe_dtor {
109+
Some(dtor) => old_dtor = Some(dtor),
110+
None => ()
111+
}
112+
}
113+
}
114+
}
115+
}
116+
117+
// GlobalState is a map from keys to unique pointers and a
118+
// destructor. Keys are pointers derived from the type of the
119+
// global value. There is a single GlobalState instance per runtime.
120+
struct GlobalState {
121+
map: LinearMap<uint, (*c_void, ~fn())>
122+
}
123+
124+
impl GlobalState: Drop {
125+
fn finalize(&self) {
126+
for self.map.each_value |v| {
127+
match v {
128+
&(_, ref dtor) => (*dtor)()
129+
}
130+
}
131+
}
132+
}
133+
134+
fn get_global_state() -> Exclusive<GlobalState> unsafe {
135+
136+
const POISON: int = -1;
137+
138+
// XXX: Doing atomic_cxchg to initialize the global state
139+
// lazily, which wouldn't be necessary with a runtime written
140+
// in Rust
141+
let global_ptr = rust_get_global_data_ptr();
142+
143+
if *global_ptr == 0 {
144+
// Global state doesn't exist yet, probably
145+
146+
// The global state object
147+
let state = GlobalState {
148+
map: LinearMap()
149+
};
150+
151+
// It's under a reference-counted mutex
152+
let state = ~exclusive(state);
153+
154+
// Convert it to an integer
155+
let state_ptr: &Exclusive<GlobalState> = state;
156+
let state_i: int = transmute(state_ptr);
157+
158+
// Swap our structure into the global pointer
159+
let prev_i = atomic_cxchg(&mut *global_ptr, 0, state_i);
160+
161+
// Sanity check that we're not trying to reinitialize after shutdown
162+
assert prev_i != POISON;
163+
164+
if prev_i == 0 {
165+
// Successfully installed the global pointer
166+
167+
// Take a handle to return
168+
let clone = state.clone();
169+
170+
// Install a runtime exit function to destroy the global object
171+
do at_exit || unsafe {
172+
// Poison the global pointer
173+
let prev_i = atomic_cxchg(&mut *global_ptr, state_i, POISON);
174+
assert prev_i == state_i;
175+
176+
// Capture the global state object in the at_exit closure
177+
// so that it is destroyed at the right time
178+
let _capture_global_state = &state;
179+
};
180+
return clone;
181+
} else {
182+
// Somebody else initialized the globals first
183+
let state: &Exclusive<GlobalState> = transmute(prev_i);
184+
return state.clone();
185+
}
186+
} else {
187+
let state: &Exclusive<GlobalState> = transmute(*global_ptr);
188+
return state.clone();
189+
}
190+
}
191+
192+
fn key_ptr<T: Owned>(key: GlobalDataKey<T>) -> uint unsafe {
193+
let closure: Closure = reinterpret_cast(&key);
194+
return transmute(closure.code);
195+
}
196+
197+
extern {
198+
fn rust_get_global_data_ptr() -> *mut int;
199+
}
200+
201+
#[abi = "rust-intrinsic"]
202+
extern {
203+
fn atomic_cxchg(dst: &mut int, old: int, src: int) -> int;
204+
}
205+
206+
#[test]
207+
fn test_clone_rc() unsafe {
208+
type MyType = SharedMutableState<int>;
209+
210+
fn key(_v: SharedMutableState<int>) { }
211+
212+
for uint::range(0, 100) |_| {
213+
do spawn unsafe {
214+
let val = do global_data_clone_create(key) {
215+
~shared_mutable_state(10)
216+
};
217+
218+
assert get_shared_immutable_state(&val) == &10;
219+
}
220+
}
221+
}
222+
223+
#[test]
224+
fn test_modify() unsafe {
225+
type MyType = SharedMutableState<int>;
226+
227+
fn key(_v: SharedMutableState<int>) { }
228+
229+
do global_data_modify(key) |v| unsafe {
230+
match v {
231+
None => {
232+
Some(~shared_mutable_state(10))
233+
}
234+
_ => fail
235+
}
236+
}
237+
238+
do global_data_modify(key) |v| {
239+
match v {
240+
Some(sms) => {
241+
let v = get_shared_immutable_state(sms);
242+
assert *v == 10;
243+
None
244+
},
245+
_ => fail
246+
}
247+
}
248+
249+
do global_data_modify(key) |v| unsafe {
250+
match v {
251+
None => {
252+
Some(~shared_mutable_state(10))
253+
}
254+
_ => fail
255+
}
256+
}
257+
}

src/rt/rust_builtin.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1032,6 +1032,12 @@ rust_register_exit_function(spawn_fn runner, fn_env_pair *f) {
10321032
task->kernel->register_exit_function(runner, f);
10331033
}
10341034

1035+
extern "C" void *
1036+
rust_get_global_data_ptr() {
1037+
rust_task *task = rust_get_current_task();
1038+
return &task->kernel->global_data;
1039+
}
1040+
10351041
//
10361042
// Local Variables:
10371043
// mode: C++

src/rt/rust_kernel.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,8 @@ rust_kernel::rust_kernel(rust_env *env) :
3838
global_env_chan(0),
3939
at_exit_runner(NULL),
4040
at_exit_started(false),
41-
env(env)
42-
41+
env(env),
42+
global_data(0)
4343
{
4444
// Create the single threaded scheduler that will run on the platform's
4545
// main thread

src/rt/rust_kernel.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@ class rust_kernel {
144144

145145
public:
146146
struct rust_env *env;
147+
uintptr_t global_data;
147148

148149
rust_kernel(rust_env *env);
149150

src/rt/rustrt.def.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,3 +211,4 @@ linenoiseHistoryLoad
211211
rust_raw_thread_start
212212
rust_raw_thread_join_delete
213213
rust_register_exit_function
214+
rust_get_global_data_ptr

0 commit comments

Comments
 (0)