1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212
extern crate thread_local; use std::fmt; use self::thread_local::ThreadLocal; use storage::Storage; pub struct LocalValue<T: Send + 'static> { tls: ThreadLocal<T>, init_fn: Box<Fn() -> T>, } impl<T: Send + 'static> LocalValue<T> { pub fn new<F: Fn() -> T + 'static>(init_fn: F) -> LocalValue<T> { LocalValue { tls: ThreadLocal::new(), init_fn: Box::new(init_fn), } } pub fn get(&self) -> &T { self.tls.get_or(|| Box::new((self.init_fn)())) } } unsafe impl<T: Send + 'static> Sync for LocalValue<T> {} unsafe impl<T: Send + 'static> Send for LocalValue<T> {} /// A single storage location for global access to thread-local values. /// /// A `LocalStorage` instance allows global access to a `n` per-thread values, /// all of which are initialized in the same manner when the value is first /// retrieved from a thread. // /// The initialization function for values in `LocalStorage` is specified via /// the [set](#method.set) method. The initialization function must be set /// before a value is attempted to be retrieved via the [get](#method.get) /// method. The [try_get](#method.try_get) can be used to determine whether the /// `LocalStorage` has been initialized before attempting to retrieve a value. /// /// For safety reasons, values stored in `Storage` must be `Send + 'static`. /// /// # Comparison with `Storage` /// /// When the use-case allows, there are two primary advantages to using a /// `LocalStorage` instance over a `Storage` instance: /// /// 1. Values stored in `LocalStorage` do not need to be thread-safe. In /// other words, their type does not need to implement `Sync`. /// 2. There is no synchronization overhead when modifying values. /// /// /// Keep in mind that values stored in `LocalStorage` are _not_ the same across /// different threads. Modifications made to the stored value in one thread are /// _not_ visible in another. Furthermore, because Rust reuses thread IDs, a new /// thread is _not_ guaranteed to receive a newly initialized value on its first /// call to `get`. /// /// # Usage /// /// **This type is only available when the `"tls"` feature is enabled.** To /// enable the feature, include the `state` dependency in your `Cargo.toml` as /// follows: /// /// ```toml /// [dependencies] /// state = { version = "0.2", features = ["tls"] } /// ``` /// /// # Example /// /// The following example uses `LocalStorage` to store a per-thread count: /// /// ```rust /// # #![feature(const_fn)] /// # extern crate state; /// # use std::cell::Cell; /// # use std::thread; /// # use state::LocalStorage; /// static COUNT: LocalStorage<Cell<usize>> = LocalStorage::new(); /// /// fn check_count() { /// let count = COUNT.get(); /// count.set(count.get() + 1); /// /// // The count should always be 1 since the state is thread-local. /// assert_eq!(count.get(), 1); /// } /// /// fn main() { /// // setup the initializer for thread-local state /// COUNT.set(|| Cell::new(0)); /// /// // spin up many threads that call `check_count`. /// let mut threads = vec![]; /// for i in 0..10 { /// threads.push(thread::spawn(|| check_count())); /// } /// /// // Wait for all of the thread to finish. /// for thread in threads { /// thread.join().expect("correct count"); /// } /// } /// ``` pub struct LocalStorage<T: Send + 'static> { storage: Storage<LocalValue<T>> } impl<T: Send + 'static> LocalStorage<T> { /// Create a new, uninitialized storage location. /// /// # Example /// /// ```rust /// # #![feature(const_fn)] /// use state::LocalStorage; /// /// static MY_GLOBAL: LocalStorage<String> = LocalStorage::new(); /// ``` pub const fn new() -> LocalStorage<T> { LocalStorage { storage: Storage::new() } } /// Sets the initialization function for this local storage unit to /// `state_init` if it has not already been set before. The function will be /// used to initialize values on the first access from a thread with a new /// thread ID. /// /// If a value has previously been set, `self` is unchanged and `false` is /// returned. Otherwise `true` is returned. /// /// # Example /// /// ```rust /// # #![feature(const_fn)] /// # use state::LocalStorage; /// static MY_GLOBAL: LocalStorage<&'static str> = LocalStorage::new(); /// /// assert_eq!(MY_GLOBAL.set(|| "Hello, world!"), true); /// assert_eq!(MY_GLOBAL.set(|| "Goodbye, world!"), false); /// ``` #[inline] pub fn set<F: Fn() -> T + 'static>(&self, state_init: F) -> bool { self.storage.set(LocalValue::new(state_init)) } /// Attempts to borrow the value in this storage location. If this is the /// first time a thread with the current thread ID has called `get` or /// `try_get` for `self`, the value will be initialized using the /// initialization function. /// /// Returns `Some` if the state has previously been [set](#method.set). /// Otherwise returns `None`. /// /// # Example /// /// ```rust /// # #![feature(const_fn)] /// # use state::LocalStorage; /// static MY_GLOBAL: LocalStorage<&'static str> = LocalStorage::new(); /// /// assert_eq!(MY_GLOBAL.try_get(), None); /// /// MY_GLOBAL.set(|| "Hello, world!"); /// /// assert_eq!(MY_GLOBAL.try_get(), Some(&"Hello, world!")); /// ``` #[inline] pub fn try_get(&self) -> Option<&T> { self.storage.try_get().map(|v| v.get()) } /// If this is the first time a thread with the current thread ID has called /// `get` or `try_get` for `self`, the value will be initialized using the /// initialization function. /// /// # Panics /// /// Panics if an initialization function has not previously been /// [set](#method.set). Use [try_get](#method.try_get) for a non-panicking /// version. /// /// # Example /// /// ```rust /// # #![feature(const_fn)] /// # use state::LocalStorage; /// static MY_GLOBAL: LocalStorage<&'static str> = LocalStorage::new(); /// /// MY_GLOBAL.set(|| "Hello, world!"); /// assert_eq!(*MY_GLOBAL.get(), "Hello, world!"); /// ``` #[inline] pub fn get(&self) -> &T { self.try_get().expect("localstorage::get(): called get() before set()") } } unsafe impl<T: Send + 'static> Send for LocalStorage<T> { } unsafe impl<T: Send + 'static> Sync for LocalStorage<T> { } impl<T: fmt::Debug + Send + Sync + 'static> fmt::Debug for LocalStorage<T> { fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { match self.try_get() { Some(object) => object.fmt(f), None => write!(f, "[uninitialized local storage]") } } }