pub struct InstancePerThread<T>where
T: Object,{ /* private fields */ }
Expand description
A wrapper that manages linked instances of T
, ensuring that only one
instance of T
is created per thread.
This is similar to the linked::thread_local_rc!
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 InstancePerThread
and provide it the initial instance of a linked
object T
. Any instance of T
accessed through the same InstancePerThread
or a clone of it
will be linked to the same family.
graph TD subgraph Thread1[Thread 1] Task1a[Local task] -->|"::acquire()"| Local1a[Ref] Task1b[Local task] -->|"::acquire()"| Local1b[Ref] Local1a --> SharedOwnership((Shared
ownership)) Local1b --> SharedOwnership SharedOwnership --> Instance1[Linked object instance] end subgraph Thread2[Thread 2] Task2a[Local task] -->|"::acquire()"| Local2a[Ref] Task2b[Local task] -->|"::acquire()"| Local2b[Ref] Local2a --> SharedOwnership2((Shared
ownership)) Local2b --> 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
Ref<T>
by calling .acquire()
. Then you can
access the T
within by simply dereferencing via the Deref<Target = T>
trait.
Ref<T>
is a thread-isolated type, meaning you cannot move it to a different
thread nor access it from a different thread. To access linked instances on other threads,
you must transfer the InstancePerThread<T>
instance across threads and obtain a new Ref<T>
on the destination thread.
§Resource management
A thread-specific instance of T
is dropped when the last Ref
on that thread
is dropped, similar to how Rc<T>
would behave. If a new Ref
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 acquire temporary Ref
objects 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 Ref
instances around for reuse when possible.
§Ref
storage
Ref
is a thread-isolated type, which means you cannot store it in places that require types
to be thread-mobile. For example, in web framework request handlers the compiler might not
permit you to let a Ref
live across an await
, depending on the web framework, the async
task runtime used and its specific configuration.
Consider using InstancePerThreadSync<T>
if you need a thread-mobile variant of Ref
.
Implementations§
Source§impl<T> InstancePerThread<T>where
T: Object,
impl<T> InstancePerThread<T>where
T: Object,
Sourcepub fn new(inner: T) -> Self
pub fn new(inner: T) -> Self
Creates a new InstancePerThread
with an existing instance of T
.
Any further access of T
instances via the InstancePerThread
(or its clones) will return
instances of T
from the same family.
Sourcepub fn acquire(&self) -> Ref<T>
pub fn acquire(&self) -> Ref<T>
Returns a Ref<T>
that can be used to access the current thread’s instance of T
.
Creating multiple concurrent Ref<T>
instances from the same InstancePerThread<T>
on the same thread is allowed. Every Ref<T>
instance will reference the same
instance of T
per thread.
There are no constraints on the lifetime of the returned Ref<T>
but it is a
thread-isolated type and cannot be moved across threads or accessed from a different thread,
which may impose some limits.
§Example
use linked::InstancePerThread;
let linked_thing = InstancePerThread::new(Thing::new());
let thing = linked_thing.acquire();
thing.increment();
assert_eq!(thing.local_value(), 1);
§Efficiency
Reuse the returned Ref<T>
when possible. Every call to this function has
some overhead, especially if there are no other Ref<T>
instances from the
same family active on the current thread.
§Instance lifecycle
A thread-specific instance of T
is dropped when the last Ref
on that
thread is dropped. If a new Ref
is later obtained, it is initialized
with a new linked instance of T
linked to the same family as the
originating InstancePerThread<T>
.
use linked::InstancePerThread;
let linked_thing = InstancePerThread::new(Thing::new());
let thing = linked_thing.acquire();
thing.increment();
assert_eq!(thing.local_value(), 1);
drop(thing);
// Dropping the only acquired instance above will have reset the thread-local state.
let thing = linked_thing.acquire();
assert_eq!(thing.local_value(), 0);
To minimize the effort spent on re-creating the thread-local state, ensure that you reuse
the Ref<T>
instances as much as possible.
§Thread safety
The returned value is single-threaded and cannot be moved or used across threads. To extend
the linked object family across threads, transfer InstancePerThread<T>
instances across threads.
You can obtain additional InstancePerThread<T>
instances by cloning the original. Every clone
is equivalent.