|
| 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 | +} |
0 commit comments