Struct PerThread

Source
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,

Source

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.

Source

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.

Trait Implementations§

Source§

impl<T> Clone for PerThread<T>
where T: Object,

Source§

fn clone(&self) -> Self

Returns a copy of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl<T> Debug for PerThread<T>
where T: Object + Debug,

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

§

impl<T> Freeze for PerThread<T>

§

impl<T> !RefUnwindSafe for PerThread<T>

§

impl<T> Send for PerThread<T>

§

impl<T> Sync for PerThread<T>

§

impl<T> Unpin for PerThread<T>

§

impl<T> !UnwindSafe for PerThread<T>

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.