pub struct PerThread<T>where
T: Object,{ /* private fields */ }
Expand description
A wrapper that manages instances of linked objects of type T
, ensuring that only one
instance of T
is created per thread.
This is a conceptual equivalent of the linked::instance_per_thread!
macro, with the main
difference being that this type operates entirely at runtime using dynamic storage and does
not require a static variable to be defined.
§Usage
Create an instance of PerThread
and provide it an initial instance of a linked object T
.
This initial instance will be used to create additional instances on demand. Any instance
of T
retrieved through the same PerThread
or a clone will be linked to the same family
of T
instances.
graph TD subgraph Thread1[Thread 1] Task1a[Local task] -->|deref| ThreadLocal1a[ThreadLocal] Task1b[Local task] -->|deref| ThreadLocal1b[ThreadLocal] ThreadLocal1a --> SharedOwnership((Shared
ownership)) ThreadLocal1b --> SharedOwnership SharedOwnership --> Instance1[Linked object instance] end subgraph Thread2[Thread 2] Task2a[Local task] -->|deref| ThreadLocal2a[ThreadLocal] Task2b[Local task] -->|deref| ThreadLocal2b[ThreadLocal] ThreadLocal2a --> SharedOwnership2((Shared
ownership)) ThreadLocal2b --> SharedOwnership2 SharedOwnership2 --> Instance2[Linked object instance] end Instance1 --> SharedState[Family state] Instance2 --> SharedState
To access the current thread’s instance of T
, you must first obtain a
ThreadLocal<T>
which works in a manner similar to Rc<T>
, allowing you to
reference the value within. You can obtain a ThreadLocal<T>
by calling
PerThread::local()
.
Once you have a ThreadLocal<T>
, you can access the T
within by simply
dereferencing via the Deref<Target = T>
trait.
§Long-lived thread-specific instances
Note that the ThreadLocal
type is !Send
, which means you cannot store it in places that
need to be thread-mobile. For example, in web framework request handlers the compiler might
not permit you to let a ThreadLocal
live across an await
, depending on the web framework,
the async task runtime used and its specific configuration.
§Resource management
A thread-specific instance of T
is dropped when the last ThreadLocal
on that thread is
dropped. If a new ThreadLocal
is later obtained, it is initialized with a new instance
of the linked object.
It is important to emphasize that this means if you only create temporary ThreadLocal
instances then you will get a new instance of T
every time. The performance impact of
this depends on how T
works internally but you are recommended to keep ThreadLocal
instances around for reuse when possible.
§Advanced scenarios
Use of PerThread
does not close the door on other ways to use linked objects.
For example, you always have the possibility of manually taking the T
and creating
additional clones of it to break out the one-per-thread limitation. The PerThread
type
only controls what happens through the PerThread
type.
Implementations§
Source§impl<T> PerThread<T>where
T: Object,
impl<T> PerThread<T>where
T: Object,
Sourcepub fn new(inner: T) -> Self
pub fn new(inner: T) -> Self
Creates a new PerThread
with an existing instance of T
. Any further access to the T
via the PerThread
(or its clones) will return instances of T
from the same family.
Sourcepub fn local(&self) -> ThreadLocal<T>
pub fn local(&self) -> ThreadLocal<T>
Returns a ThreadLocal<T>
that can be used to efficiently access the current
thread’s T
instance.
§Example
let per_thread_thing = linked::PerThread::new(Thing::new());
let local_thing = per_thread_thing.local();
local_thing.increment();
assert_eq!(local_thing.local_value(), 1);
§Efficiency
Reuse the returned instance as much as possible. Every call to this function has some
overhead, especially if there are no other ThreadLocal<T>
instances from the same family
active on the current thread.
§Instance lifecycle
A thread-specific instance of T
is dropped when the last ThreadLocal
on that thread is
dropped. If a new ThreadLocal
is later obtained, it is initialized with a new instance
of the linked object.
let per_thread_thing = linked::PerThread::new(Thing::new());
let local_thing = per_thread_thing.local();
local_thing.increment();
assert_eq!(local_thing.local_value(), 1);
drop(local_thing);
// Dropping the only thread-local instance above will have reset the thread-local state.
let local_thing = per_thread_thing.local();
assert_eq!(local_thing.local_value(), 0);
To minimize the effort spent on re-creating the thread-local state, ensure that you reuse
the ThreadLocal<T>
instances as much as possible.
§Thread safety
The returned value is single-threaded and cannot be moved or used across threads. For
transfer across threads, you need to preserve and share/send a PerThread<T>
instance.