pub struct ThreadLocal<T> { /* private fields */ }
Expand description
A thread-local storage handle.
In many ways, this struct works similarly to std::thread::LocalKey
, but
always relies on OS primitives (see module-level documentation).
The with
method yields a reference to the contained value which cannot
be sent across threads or escape the given closure.
§Initialization and Destruction
Initialization is dynamically performed on the first call to with
within a thread, and values that implement Drop
get destructed when a
thread exits. Some caveats apply, which are explained below.
A ThreadLocal
’s initializer cannot recursively depend on itself, and
using a ThreadLocal
in this way will cause the initializer to infinitely
recurse on the first call to with
.
§Examples
This is the same as the example in the std::thread::LocalKey
documentation,
but adjusted to use ThreadLocal
instead. To use it in a static
context, a
lazy initializer, such as once_cell::sync::Lazy
or lazy_static!
is
required.
use std::cell::RefCell;
use std::thread;
use once_cell::sync::Lazy;
use os_thread_local::ThreadLocal;
static FOO: Lazy<ThreadLocal<RefCell<u32>>> =
Lazy::new(|| ThreadLocal::new(|| RefCell::new(1)));
FOO.with(|f| {
assert_eq!(*f.borrow(), 1);
*f.borrow_mut() = 2;
});
// each thread starts out with the initial value of 1
let t = thread::spawn(move || {
FOO.with(|f| {
assert_eq!(*f.borrow(), 1);
*f.borrow_mut() = 3;
});
});
// wait for the thread to complete and bail out on panic
t.join().unwrap();
// we retain our original value of 2 despite the child thread
FOO.with(|f| {
assert_eq!(*f.borrow(), 2);
});
A variation of the same with scoped threads and per-object thread-local storage:
use std::cell::RefCell;
use crossbeam_utils::thread::scope;
use os_thread_local::ThreadLocal;
struct Foo {
data: u32,
tls: ThreadLocal<RefCell<u32>>,
}
let foo = Foo {
data: 0,
tls: ThreadLocal::new(|| RefCell::new(1)),
};
foo.tls.with(|f| {
assert_eq!(*f.borrow(), 1);
*f.borrow_mut() = 2;
});
scope(|s| {
// each thread starts out with the initial value of 1
let foo2 = &foo;
let t = s.spawn(move |_| {
foo2.tls.with(|f| {
assert_eq!(*f.borrow(), 1);
*f.borrow_mut() = 3;
});
});
// wait for the thread to complete and bail out on panic
t.join().unwrap();
// we retain our original value of 2 despite the child thread
foo.tls.with(|f| {
assert_eq!(*f.borrow(), 2);
});
}).unwrap();
§Platform-specific behavior and caveats
Note that a “best effort” is made to ensure that destructors for types stored in thread-local storage are run, but it is not guaranteed that destructors will be run for all types in thread-local storage.
- Destructors may not run on the main thread when it exits.
- Destructors will not run if the corresponding
ThreadLocal
is dropped in a child thread (this can happen e.g. if the object or binding holding it is moved into a child thread ; or when theThreadLocal
is created in a child thread). - Destructors may not run if a
ThreadLocal
is initialized during theDrop
impl of a type held by anotherThreadLocal
. - The order in which destructors may run when using multiple
ThreadLocal
is not guaranteed.
On Windows, ThreadLocal
provides per-thread storage as long as fibers
are unused. When fibers are used, it provides per-fiber storage, which
is similar but more fine-grained.
Implementations§
Source§impl<T> ThreadLocal<T>
impl<T> ThreadLocal<T>
Sourcepub fn new(f: fn() -> T) -> Self
pub fn new(f: fn() -> T) -> Self
Creates a new thread-local storage handle.
The provided function is used to initialize the value on the first use in each thread.
use os_thread_local::ThreadLocal;
let tls = ThreadLocal::new(|| 42);
Sourcepub fn with<R, F: FnOnce(&T) -> R>(&self, f: F) -> R
pub fn with<R, F: FnOnce(&T) -> R>(&self, f: F) -> R
Acquires a reference to the value in this thread-local storage.
This will lazily initialize the value if this thread has not accessed it yet.
use os_thread_local::ThreadLocal;
use std::cell::Cell;
let tls = ThreadLocal::new(|| Cell::new(42));
tls.with(|v| v.set(21));
§Panics
This function will panic!()
if the handle currently has its destructor
running, and it may panic if the destructor has previously been run for
this thread.
This function can also panic!()
if the storage is uninitialized and there
is not enough available memory to allocate a new thread local storage for
the current thread, or if the OS primitives fail.
Sourcepub fn try_with<R, F: FnOnce(&T) -> R>(&self, f: F) -> Result<R, AccessError>
pub fn try_with<R, F: FnOnce(&T) -> R>(&self, f: F) -> Result<R, AccessError>
Acquires a reference to the value in this thread-local storage.
This will lazily initialize the value if this thread has not accessed it
yet. If the storage has been destroyed, this function will return an
AccessError
.
use os_thread_local::ThreadLocal;
use std::cell::Cell;
let tls = ThreadLocal::new(|| Cell::new(42));
tls.try_with(|v| v.set(21)).expect("storage destroyed");
§Panics
This function will panic!()
if the storage is uninitialized and the
initializer given to ThreadLocal::new
panics.
This function can also panic!()
if the storage is uninitialized and there
is not enough available memory to allocate a new thread local storage for
the current thread, or if the OS primitives fail.