thread_local_object/lib.rs
1//! Per-object thread local storage.
2//!
3//! The `ThreadLocal` type which stores a distinct (nullable) value of some type for each thread
4//! that accesses it.
5//!
6//! A thread's values are destroyed when it exits, but the values associated with a `ThreadLocal`
7//! instance are not destroyed when it is dropped. These are in some ways the opposite semantics of
8//! those provided by the `thread_local` crate, where values are cleaned up when a `ThreadLocal`
9//! object is dropped, but not when individual threads exit.
10//!
11//! Because of this, this crate is an appropriate choice for use cases where you have long lived
12//! `ThreadLocal` instances which are widely shared among threads that are created and destroyed
13//! through the runtime of a program, while the `thread_local` crate is an appropriate choice for
14//! short lived values.
15//!
16//! # Examples
17//!
18//! ```rust
19//! use std::sync::Arc;
20//! use std::thread;
21//! use thread_local_object::ThreadLocal;
22//!
23//! let tls = Arc::new(ThreadLocal::new());
24//!
25//! tls.set(1);
26//!
27//! let tls2 = tls.clone();
28//! thread::spawn(move || {
29//! // the other thread doesn't see the 1
30//! assert_eq!(tls2.get_cloned(), None);
31//! tls2.set(2);
32//! }).join().unwrap();
33//!
34//! // we still see our original value
35//! assert_eq!(tls.get_cloned(), Some(1));
36//! ```
37#![warn(missing_docs)]
38#![doc(html_root_url="https://docs.rs/thread-local-object/0.1.0")]
39
40extern crate unsafe_any;
41
42use std::cell::RefCell;
43use std::collections::hash_map::{self, HashMap};
44use std::marker::PhantomData;
45use std::mem;
46use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering};
47use unsafe_any::UnsafeAny;
48
49thread_local! {
50 static VALUES: RefCell<HashMap<usize, Box<UnsafeAny>>> = RefCell::new(HashMap::new());
51}
52
53static NEXT_ID: AtomicUsize = ATOMIC_USIZE_INIT;
54
55// if IDs ever wrap around we'll run into soundness issues with downcasts, so panic if we're out of
56// IDs. On 64 bit platforms this can literally never happen (it'd take 584 years even if you were
57// generating a billion IDs per second), but is more realistic a concern on 32 bit platforms.
58//
59// FIXME use AtomicU64 when it's stable
60fn next_id() -> usize {
61 let mut id = NEXT_ID.load(Ordering::SeqCst);
62 loop {
63 assert!(id != usize::max_value(), "thread local ID overflow");
64 let old = id;
65 id = NEXT_ID.compare_and_swap(old, old + 1, Ordering::SeqCst);
66 if id == old {
67 return id;
68 }
69 }
70}
71
72/// A thread local variable wrapper.
73pub struct ThreadLocal<T: 'static> {
74 id: usize,
75 _p: PhantomData<T>,
76}
77
78impl<T: 'static> ThreadLocal<T> {
79 /// Creates a new `ThreadLocal` with no values for any threads.
80 ///
81 /// # Panics
82 ///
83 /// Panics if more than `usize::max_value()` `ThreadLocal` objects have already been created.
84 /// This can only ever realistically happen on 32 bit platforms.
85 pub fn new() -> ThreadLocal<T> {
86 ThreadLocal {
87 id: next_id(),
88 _p: PhantomData,
89 }
90 }
91
92 /// Sets this thread's value, returning the previous value if present.
93 ///
94 /// # Panics
95 ///
96 /// Panics if called from within the execution of a closure provided to another method on this
97 /// value.
98 pub fn set(&self, value: T) -> Option<T> {
99 self.entry(|e| match e {
100 Entry::Occupied(mut e) => Some(e.insert(value)),
101 Entry::Vacant(e) => {
102 e.insert(value);
103 None
104 }
105 })
106 }
107
108 /// Removes this thread's value, returning it if it existed.
109 ///
110 /// # Panics
111 ///
112 /// Panics if called from within the execution of a closure provided to another method on this
113 /// value.
114 pub fn remove(&self) -> Option<T> {
115 VALUES.with(|v| {
116 v.borrow_mut()
117 .remove(&self.id)
118 .map(|v| unsafe { *v.downcast_unchecked::<T>() })
119 })
120 }
121
122 /// Passes a handle to the current thread's value to a closure for in-place manipulation.
123 ///
124 /// The closure is required for the same soundness reasons it is required for the standard
125 /// library's `thread_local!` values.
126 ///
127 /// # Panics
128 ///
129 /// Panics if called from within the execution of a closure provided to another method on this
130 /// value.
131 pub fn entry<F, R>(&self, f: F) -> R
132 where F: FnOnce(Entry<T>) -> R
133 {
134 VALUES.with(|v| {
135 let mut v = v.borrow_mut();
136 let entry = match v.entry(self.id) {
137 hash_map::Entry::Occupied(e) => Entry::Occupied(OccupiedEntry(e, PhantomData)),
138 hash_map::Entry::Vacant(e) => Entry::Vacant(VacantEntry(e, PhantomData)),
139 };
140 f(entry)
141 })
142 }
143
144 /// Passes a mutable reference to the current thread's value to a closure.
145 ///
146 /// The closure is required for the same soundness reasons it is required for the standard
147 /// library's `thread_local!` values.
148 ///
149 /// # Panics
150 ///
151 /// Panics if called from within the execution of a closure passed to `entry` or `get_mut` on
152 /// this value.
153 pub fn get<F, R>(&self, f: F) -> R
154 where F: FnOnce(Option<&T>) -> R
155 {
156 VALUES.with(|v| {
157 let v = v.borrow();
158 let value = v.get(&self.id)
159 .map(|v| unsafe { v.downcast_ref_unchecked() });
160 f(value)
161 })
162 }
163
164 /// Passes a mutable reference to the current thread's value to a closure.
165 ///
166 /// The closure is required for the same soundness reasons it is required for the standard
167 /// library's `thread_local!` values.
168 ///
169 /// # Panics
170 ///
171 /// Panics if called from within the execution of a closure provided to another method on this
172 /// value.
173 pub fn get_mut<F, R>(&self, f: F) -> R
174 where F: FnOnce(Option<&mut T>) -> R
175 {
176 VALUES.with(|v| {
177 let mut v = v.borrow_mut();
178 let value = v.get_mut(&self.id)
179 .map(|v| unsafe { v.downcast_mut_unchecked() });
180 f(value)
181 })
182 }
183}
184
185impl<T> ThreadLocal<T>
186 where T: 'static + Clone
187{
188 /// Returns a copy of the current thread's value.
189 ///
190 /// # Panics
191 ///
192 /// Panics if called from within the execution of a closure passed to `entry` or `get_mut` on
193 /// this value.
194 pub fn get_cloned(&self) -> Option<T> {
195 VALUES.with(|v| {
196 v.borrow()
197 .get(&self.id)
198 .map(|v| unsafe { v.downcast_ref_unchecked::<T>().clone() })
199 })
200 }
201}
202
203/// A view into a thread's slot in a `ThreadLocal` that may be empty.
204pub enum Entry<'a, T: 'static> {
205 /// An occupied entry.
206 Occupied(OccupiedEntry<'a, T>),
207 /// A vacant entry.
208 Vacant(VacantEntry<'a, T>),
209}
210
211impl<'a, T: 'static> Entry<'a, T> {
212 /// Ensures a value is in the entry by inserting the default if it is empty, and returns a
213 /// mutable reference to the value in the entry.
214 pub fn or_insert(self, default: T) -> &'a mut T {
215 match self {
216 Entry::Occupied(e) => e.into_mut(),
217 Entry::Vacant(e) => e.insert(default),
218 }
219 }
220
221 /// Ensures a value is in the entry by inserting the result of the default function if it is
222 /// empty, and returns a mutable reference to the value in the entry.
223 pub fn or_insert_with<F>(self, default: F) -> &'a mut T
224 where F: FnOnce() -> T
225 {
226 match self {
227 Entry::Occupied(e) => e.into_mut(),
228 Entry::Vacant(e) => e.insert(default()),
229 }
230 }
231}
232
233/// A view into a thread's slot in a `ThreadLocal` which is occupied.
234pub struct OccupiedEntry<'a, T: 'static>(hash_map::OccupiedEntry<'a, usize, Box<UnsafeAny>>,
235 PhantomData<&'a mut T>);
236
237impl<'a, T: 'static> OccupiedEntry<'a, T> {
238 /// Returns a reference to the value in the entry.
239 pub fn get(&self) -> &T {
240 unsafe { self.0.get().downcast_ref_unchecked() }
241 }
242
243 /// Returns a mutable reference to the value in the entry.
244 pub fn get_mut(&mut self) -> &mut T {
245 unsafe { self.0.get_mut().downcast_mut_unchecked() }
246 }
247
248 /// Converts an `OccupiedEntry` into a mutable reference to the value in the entry with a
249 /// lifetime bound of the slot itself.
250 pub fn into_mut(self) -> &'a mut T {
251 unsafe { self.0.into_mut().downcast_mut_unchecked() }
252 }
253
254 /// Sets the value of the entry, and returns the entry's old value.
255 pub fn insert(&mut self, value: T) -> T {
256 mem::replace(self.get_mut(), value)
257 }
258
259 /// Takes the value out of the entry, and returns it.
260 pub fn remove(self) -> T {
261 unsafe { *self.0.remove().downcast_unchecked() }
262 }
263}
264
265/// A view into a thread's slot in a `ThreadLocal` which is unoccupied.
266pub struct VacantEntry<'a, T: 'static>(hash_map::VacantEntry<'a, usize, Box<UnsafeAny>>,
267 PhantomData<&'a mut T>);
268
269impl<'a, T: 'static> VacantEntry<'a, T> {
270 /// Sets the value of the entry, and returns a mutable reference to it.
271 pub fn insert(self, value: T) -> &'a mut T {
272 unsafe { self.0.insert(Box::new(value)).downcast_mut_unchecked() }
273 }
274}