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>
impl<T> ThreadLock<T>
Sourcepub fn new(value: T) -> Self
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
.
Sourcepub const fn on_thread(value: T, thread: ThreadId) -> Self
pub const fn on_thread(value: T, thread: ThreadId) -> Self
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
.
Sourcepub fn try_into_inner(self) -> Result<T, TryIntoWrongThreadError<Self>>
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.
Sourcepub fn into_inner(self) -> T
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.
Sourcepub fn into_inner_unlocked(self) -> Twhere
T: Send,
pub fn into_inner_unlocked(self) -> Twhere
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>
impl<T: ?Sized> ThreadLock<T>
Sourcepub fn can_current_thread_access(&self) -> bool
pub fn can_current_thread_access(&self) -> bool
Returns whether this value is locked to the current thread.
Sourcepub fn try_get(&self) -> Result<&T, WrongThreadError>
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.
Sourcepub fn try_get_mut(&mut self) -> Result<&mut T, WrongThreadError>
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.
Sourcepub fn get(&self) -> &T
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.
Sourcepub fn get_mut(&mut self) -> &mut T
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.
Sourcepub fn get_unlocked(&self) -> &T
pub fn get_unlocked(&self) -> &T
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.
Sourcepub fn get_unlocked_mut(&mut self) -> &mut Twhere
T: Send,
pub fn get_unlocked_mut(&mut self) -> &mut Twhere
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>
impl<T: Debug> Debug for ThreadLock<T>
Source§impl<T: Default> Default for ThreadLock<T>
impl<T: Default> Default for ThreadLock<T>
Source§fn default() -> Self
fn default() -> Self
Constructs a new ThreadLock
with the default value of T
, locked to the current thread; cf. ThreadLock::new
.
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).
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).