OS-backed thread-local storage

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 also provides per-object thread-local storage, with a different API, and different features, but with more performance overhead than this one.

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.

```rust use std::cell::RefCell; use std::thread; use oncecell::sync::Lazy; use osthread_local::ThreadLocal;

static FOO: Lazy>> = Lazy::new(|| ThreadLocal::new(|| RefCell::new(1)));

FOO.with(|f| { asserteq!(*f.borrow(), 1); *f.borrowmut() = 2; });

// each thread starts out with the initial value of 1 let t = thread::spawn(move || { FOO.with(|f| { asserteq!(*f.borrow(), 1); *f.borrowmut() = 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:

```rust use std::cell::RefCell; use crossbeamutils::thread::scope; use osthread_local::ThreadLocal;

struct Foo { data: u32, tls: ThreadLocal>, }

let foo = Foo { data: 0, tls: ThreadLocal::new(|| RefCell::new(1)), };

foo.tls.with(|f| { asserteq!(*f.borrow(), 1); *f.borrowmut() = 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| { asserteq!(*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(); ```