Struct ThreadLock

Source
pub struct ThreadLock<T: ?Sized> { /* private fields */ }
Expand description

A ThreadLock<T> contains a value of type T, but only allows access to it from one thread, checked at runtime; in exchange, every ThreadLock<T> is both Send and Sync

Logically, this can be thought of as similar to a RefCell: a RefCell waives (some of) the restrictions of borrow checker at compile time, but enforces them at runtime; likewise, a ThreadLock waives (some of) the restrictions of Send and Sync as compile time, but enforces them at runtime.

The methods ThreadLock::into_inner_unlocked, ThreadLock::get_unlocked, and ThreadLock::get_unlocked_mut, henceforth reffered to collectively as the *_unlocked methods, allow users to fall back to compile-time checking of Send and Sync. Because of these methods, users cannot assume that other threads have not observed the value in a ThreadLock, unless they are certain that that value is not Send (all of the *_unlocked methods require that the value is Send; ThreadLock::get_unlocked addionally requires that the value is Sync). Users can, however, assume that the value has only been observed in ways that fulfil the contract given by the presence or absence of Send and Sync for that type.

§Examples

let message = 42i32; // i32 is Send and Sync, so all the `*_unlocked` methods are available
let locked_message = Arc::new(ThreadLock::new(message));
let locked_message2 = Arc::clone(&locked_message);
thread::spawn(move || {
  assert_eq!(locked_message2.try_get(), Err(WrongThreadError)); // non-`*_unlocked` methods perform runtime checks, even for Send and Sync types
  assert_eq!(locked_message2.get_unlocked(), &42);
});
assert_eq!(locked_message.try_get(), Ok(&42));

Implementations§

Source§

impl<T> ThreadLock<T>

Source

pub fn new(value: T) -> Self

Constructs a new ThreadLock, locked to the current thread; that is, only the current thread will have access to value.

Because this method guarantees that this value will not be observed on any other threads (except through the *_unlocked methods), T does not need to be Send or Sync.

Source

pub const fn on_thread(value: T, thread: ThreadId) -> Self
where T: Send + Sync,

Constructs a new ThreadLock, locked to the given thread; that is, only the given thread will have access to value.

Because this method can be used to move or share value to another thread, T must be Send and Sync.

Source

pub fn try_into_inner(self) -> Result<T, TryIntoWrongThreadError<Self>>

Deconstructs this ThreadLock and returns the contained value.

§Errors

If self is locked to a different thread; the error object contains self, so that it can be recovered if that necessary.

Source

pub fn into_inner(self) -> T

Deconstructs this ThreadLock and returns the contained value. Equivalent to self.try_into_inner().unwrap().

§Panics

If self is locked to a different thread.

Source

pub fn into_inner_unlocked(self) -> T
where T: Send,

Deconstructs this ThreadLock and returns the contained value.

This methods circumvents the thread lock; that is, it allows any thread access to the underlying value, provided that that thread can prove that it is safe to move (Send) that type to other threads (as that is essentially what will happen if the thread that calls this method is not the one to which this value is locked).

Source§

impl<T: ?Sized> ThreadLock<T>

Source

pub fn thread(&self) -> ThreadId

Returns the id of the thread to which this value is locked.

Source

pub fn can_current_thread_access(&self) -> bool

Returns whether this value is locked to the current thread.

Source

pub fn try_get(&self) -> Result<&T, WrongThreadError>

Returns a shared reference to the underlying data.

§Errors

If the current thread does not have access to the underlying data.

Source

pub fn try_get_mut(&mut self) -> Result<&mut T, WrongThreadError>

Returns a unique (mutable) reference to the underlying data.

§Errors

If the current thread does not have access to the underlying data.

Source

pub fn get(&self) -> &T

Returns a shared reference to the underlying data. Equivalent to self.try_get().unwrap()

§Panics

If the current thread does not have access to the underlying data.

Source

pub fn get_mut(&mut self) -> &mut T

Returns a unique (mutable) reference to the underlying data. Equivalent to self.try_get_mut().unwrap()

§Panics

If the current thread does not have access to the underlying data.

Source

pub fn get_unlocked(&self) -> &T
where T: Send + Sync,

Returns a shared reference to the underlying data.

This methods circumvents the thread lock; that is, it allows any thread access to the underlying value, provided that that thread can prove that it is safe to share (Sync) that type with other threads (as that is essentially what will happen if the thread that calls this method is not the one to which this value is locked).

Note that this method also requires that the value be Send; I’m not sure if this is necessary: if T: Sync + Copy, it can be copied onto other threads, and I’m not sure if that falls under the contract of Sync, so I’m erring on the safe side. If you happen to know whether the Send bound is necessary, I would appreciate it if you would open an issue to let me know.

Source

pub fn get_unlocked_mut(&mut self) -> &mut T
where T: Send,

Returns a unique (mutable) reference to the underlying data.

This methods circumvents the thread lock; that is, it allows any thread access to the underlying value, provided that that thread can prove that it is safe to move (Send) that type to other threads (as that is essentially what will happen if the thread that calls this method is not the one to which this value is locked).

A note on the Send bound: T must be Send because there are ways to move data out of it, e.g. via std::mem::replace; however, it does not need to be Sync because it takes self by mutable reference, which guarantees that the current thread has unique access to this ThreadLock, and therefore to the data contained in it.

Trait Implementations§

Source§

impl<T: Debug> Debug for ThreadLock<T>

Source§

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

This method will only display the underlying data if it is called from the thread to which the data is locked, so it can be called from any thread.

Source§

impl<T: Default> Default for ThreadLock<T>

Source§

fn default() -> Self

Constructs a new ThreadLock with the default value of T, locked to the current thread; cf. ThreadLock::new.

Source§

impl<T: ?Sized> Send for ThreadLock<T>

SAFETY: the value of T can only be accessed on a single thread, regardless of which thread actually owns this lock; thus, it can be safely moved around even if T is not Send, because the T will never be observed on any other thread (except through the *_unlocked methods, which do have appropriate Send and Sync bounds).

Source§

impl<T: ?Sized> Sync for ThreadLock<T>

SAFETY: the value of T can only be accessed on a single thread, regardless of which thread owns this lock; thus, it can be safely shared even if T is not Sync, because the T will never be observed on any other thread (except through the *_unlocked methods, which do have appropriate Send and Sync bounds).

Auto Trait Implementations§

§

impl<T> Freeze for ThreadLock<T>
where T: Freeze + ?Sized,

§

impl<T> RefUnwindSafe for ThreadLock<T>
where T: RefUnwindSafe + ?Sized,

§

impl<T> Unpin for ThreadLock<T>
where T: Unpin + ?Sized,

§

impl<T> UnwindSafe for ThreadLock<T>
where T: UnwindSafe + ?Sized,

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