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}