This crate provides a ThreadLocal
type as an alternative to
std::thread_local!
that allows per-object thread-local storage, while
providing a similar API. It always uses the thread-local storage primitives
provided by the OS.
On Unix systems, pthread-based thread-local storage is used.
On Windows, fiber-local storage is used. This acts like thread-local
storage when fibers are unused, but also provides per-fiber values
after fibers are created with e.g. winapi::um::winbase::CreateFiber
.
The thread_local
crate is an example
of another crate that provides per-object thread-local storage, with a
different API, and different features, but with more performance overhead than
this one.
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();