threadcell/lib.rs
1#![doc = include_str!("../README.md")]
2#![warn(clippy::cargo_common_metadata)]
3#![warn(clippy::doc_markdown)]
4#![warn(clippy::missing_panics_doc)]
5#![warn(clippy::must_use_candidate)]
6#![warn(clippy::semicolon_if_nothing_returned)]
7#![warn(missing_docs)]
8#![warn(rustdoc::missing_crate_level_docs)]
9#![cfg_attr(feature = "nightly_thread_id_value", feature(thread_id_value))]
10
11use std::mem::ManuallyDrop;
12use std::ops::{Deref, DerefMut};
13use std::sync::atomic::{AtomicU64, Ordering};
14use std::{cmp, fmt, mem};
15
16/// A cell that can be owned by a single thread or none at all.
17pub struct ThreadCell<T> {
18 data: ManuallyDrop<T>,
19 thread_id: AtomicU64,
20}
21
22// We use the highest bit of a thread id to indicate that we hold a guard
23const GUARD_BIT: u64 = i64::MAX as u64 + 1;
24
25#[allow(clippy::non_send_fields_in_send_ty)]
26unsafe impl<T: Send> Send for ThreadCell<T> {}
27unsafe impl<T: Send> Sync for ThreadCell<T> {}
28
29impl<T> ThreadCell<T> {
30 /// Creates a `ThreadCell` that is not owned by any thread. This is a const fn which
31 /// allows static construction of `ThreadCells`.
32 pub const fn new_disowned(data: T) -> Self {
33 Self {
34 data: ManuallyDrop::new(data),
35 thread_id: AtomicU64::new(0),
36 }
37 }
38
39 /// Creates a `ThreadCell` that is owned by the current thread.
40 pub fn new_owned(data: T) -> Self {
41 Self {
42 data: ManuallyDrop::new(data),
43 thread_id: AtomicU64::new(current_thread_id()),
44 }
45 }
46
47 /// Takes the ownership of a cell.
48 ///
49 /// # Panics
50 ///
51 /// When the cell is already owned by this thread or it is owned by another thread.
52 pub fn acquire(&self) {
53 self.thread_id
54 .compare_exchange(0, current_thread_id(), Ordering::Acquire, Ordering::Relaxed)
55 .expect("Thread can not acquire ThreadCell");
56 }
57
58 /// Tries to take the ownership of a cell. Returns true when the ownership could be
59 /// obtained or the cell was already owned by the current thread and false when the cell
60 /// is owned by another thread.
61 pub fn try_acquire(&self) -> bool {
62 if self.is_acquired() {
63 true
64 } else {
65 self.thread_id
66 .compare_exchange(0, current_thread_id(), Ordering::Acquire, Ordering::Relaxed)
67 .is_ok()
68 }
69 }
70
71 /// Tries to take the ownership of a cell. Returns true when the ownership could be
72 /// obtained and false when the cell is already owned or owned by another thread.
73 /// Note that this fails when the cell is already owned (unlike `try_acquire()`).
74 pub fn try_acquire_once(&self) -> bool {
75 self.thread_id
76 .compare_exchange(0, current_thread_id(), Ordering::Acquire, Ordering::Relaxed)
77 .is_ok()
78 }
79
80 /// Takes the ownership of a cell and returns a reference to its value.
81 ///
82 /// # Panics
83 ///
84 /// When the cell is owned by another thread.
85 pub fn acquire_get(&self) -> &T {
86 if !self.is_owned() {
87 self.acquire();
88 }
89 // Safety: we have it
90 unsafe { self.get_unchecked() }
91 }
92
93 /// Tries to take the ownership of a cell and returns a reference to its value.
94 /// Will return 'None' when the cell is owned by another thread.
95 pub fn try_acquire_get(&self) -> Option<&T> {
96 if self.try_acquire() {
97 // Safety: we have it
98 Some(unsafe { self.get_unchecked() })
99 } else {
100 None
101 }
102 }
103
104 /// Takes the ownership of a cell and returns a mutable reference to its value.
105 ///
106 /// # Panics
107 ///
108 /// When the cell is owned by another thread.
109 pub fn acquire_get_mut(&mut self) -> &mut T {
110 if !self.is_owned() {
111 self.acquire();
112 }
113 // Safety: we have it
114 unsafe { self.get_mut_unchecked() }
115 }
116
117 /// Tries to take the ownership of a cell and returns a mutable reference to its value.
118 /// Will return 'None' when the cell is owned by another thread.
119 pub fn try_acquire_get_mut(&mut self) -> Option<&mut T> {
120 if self.try_acquire() {
121 // Safety: we have it
122 Some(unsafe { self.get_mut_unchecked() })
123 } else {
124 None
125 }
126 }
127
128 /// Acquires a `ThreadCell` returning a `Guard` that releases it when becoming dropped.
129 ///
130 /// # Panics
131 ///
132 /// When the cell is owned by another thread.
133 #[inline]
134 pub fn acquire_guard(&self) -> Guard<T> {
135 self.thread_id
136 .compare_exchange(
137 0,
138 current_thread_id() | GUARD_BIT,
139 Ordering::Acquire,
140 Ordering::Relaxed,
141 )
142 .expect("Thread can not acquire ThreadCell");
143 Guard(self)
144 }
145
146 /// Acquires a `ThreadCell` returning a `Option<Guard>` that releases it when becoming
147 /// dropped. Returns `None` when self is owned by another thread.
148 #[inline]
149 #[mutants::skip]
150 pub fn try_acquire_guard(&self) -> Option<Guard<T>> {
151 if self
152 .thread_id
153 .compare_exchange(
154 0,
155 current_thread_id() | GUARD_BIT,
156 Ordering::Acquire,
157 Ordering::Relaxed,
158 )
159 .is_ok()
160 {
161 Some(Guard(self))
162 } else {
163 None
164 }
165 }
166
167 /// Acquires a `ThreadCell` returning a `GuardMut` that releases it when becoming dropped.
168 ///
169 /// # Panics
170 ///
171 /// When the cell is owned by another thread.
172 #[inline]
173 pub fn acquire_guard_mut(&mut self) -> GuardMut<T> {
174 self.thread_id
175 .compare_exchange(
176 0,
177 current_thread_id() | GUARD_BIT,
178 Ordering::Acquire,
179 Ordering::Relaxed,
180 )
181 .expect("Thread can not acquire ThreadCell");
182 GuardMut(self)
183 }
184
185 /// Acquires a `ThreadCell` returning a `Option<GuardMut>` that releases it when becoming
186 /// dropped. Returns `None` when self is owned by another thread.
187 #[inline]
188 pub fn try_acquire_guard_mut(&mut self) -> Option<GuardMut<T>> {
189 if self
190 .thread_id
191 .compare_exchange(
192 0,
193 current_thread_id() | GUARD_BIT,
194 Ordering::Acquire,
195 Ordering::Relaxed,
196 )
197 .is_ok()
198 {
199 Some(GuardMut(self))
200 } else {
201 None
202 }
203 }
204
205 /// Runs a closure on a `ThreadCell` with acquire/release.
206 ///
207 /// # Panics
208 ///
209 /// When the cell is already owned by the current thread or is owned by another thread.
210 pub fn with<R, F: FnOnce(&T) -> R>(&self, f: F) -> R {
211 f(&*self.acquire_guard())
212 }
213
214 /// Runs a closure on a mutable `ThreadCell` with acquire/release.
215 ///
216 /// # Panics
217 ///
218 /// When the cell is already owned by the current thread or is owned by another thread.
219 pub fn with_mut<R, F: FnOnce(&mut T) -> R>(&mut self, f: F) -> R {
220 f(&mut *self.acquire_guard_mut())
221 }
222
223 /// Tries to run a closure on a `ThreadCell` with acquire/release. Returns `Some(Result)`
224 /// when the cell could be acquired and None when it is owned by another thread.
225 pub fn try_with<R, F: FnOnce(&T) -> R>(&self, f: F) -> Option<R> {
226 Some(f(&*self.try_acquire_guard()?))
227 }
228
229 /// Tries to run a closure on a mutable `ThreadCell` with acquire/release. Returns
230 /// `Some(Result)` when the cell could be acquired and None when it is owned by another
231 /// thread.
232 pub fn try_with_mut<R, F: FnOnce(&mut T) -> R>(&mut self, f: F) -> Option<R> {
233 Some(f(&mut *self.try_acquire_guard_mut()?))
234 }
235
236 /// Takes the ownership of a cell unconditionally. This is a no-op when the cell is
237 /// already owned by the current thread. Returns 'self' thus it can be chained with
238 /// `.release()`.
239 ///
240 /// # Safety
241 ///
242 /// This method does not check if the cell is owned by another thread. The owning thread
243 /// may operate on the content, thus a data race/UB will happen when the accessed value is
244 /// not Sync. The previous owning thread may panic when it expects owning the cell. The
245 /// only safe way to use this method is to recover a cell that is owned by a thread that
246 /// finished without releasing it (e.g after a panic). Attention should be paid to the
247 /// fact that the value protected by the `ThreadCell` might be in a undefined state.
248 ///
249 /// # Panics
250 ///
251 /// The `ThreadCell` has a `Guard` on it. `steal()` can only be used with acquire/release
252 /// semantics.
253 pub unsafe fn steal(&self) -> &Self {
254 if !self.is_acquired() {
255 assert!(
256 self.thread_id.load(Ordering::Acquire) & GUARD_BIT == 0,
257 "Can't steal guarded ThreadCell"
258 );
259 self.thread_id.store(current_thread_id(), Ordering::SeqCst);
260 }
261
262 self
263 }
264
265 /// Sets a `ThreadCell` which is owned by the current thread into the disowned state.
266 ///
267 /// # Panics
268 ///
269 /// The current thread does not own the cell.
270 pub fn release(&self) {
271 self.thread_id
272 .compare_exchange(current_thread_id(), 0, Ordering::Release, Ordering::Relaxed)
273 .expect("Thread has no access to ThreadCell");
274 }
275
276 /// Unsafe as it doesn't check for ownership.
277 #[mutants::skip]
278 unsafe fn release_unchecked(&self) {
279 debug_assert!(self.is_owned());
280 self.thread_id.store(0, Ordering::Release);
281 }
282
283 /// Tries to set a `ThreadCell` which is owned by the current thread into the disowned
284 /// state. Returns *true* on success and *false* when the current thread does not own the
285 /// cell.
286 pub fn try_release(&self) -> bool {
287 self.thread_id
288 .compare_exchange(current_thread_id(), 0, Ordering::Release, Ordering::Relaxed)
289 .is_ok()
290 }
291
292 /// Returns true when the current thread owns this cell.
293 #[inline(always)]
294 pub fn is_owned(&self) -> bool {
295 // This can be Relaxed because when we already own it (with Acquire), no other thread
296 // can change the ownership. When we do not own it this may return Zero or some other
297 // thread id in a racy way, which is ok (to indicate disowned state) either way.
298 self.thread_id.load(Ordering::Relaxed) & !GUARD_BIT == current_thread_id()
299 }
300
301 /// Returns true when this `ThreadCell` is not owned by any thread. As this can change at
302 /// any time by another taking ownership of this `ThreadCell` the result of this function
303 /// may be **inexact and racy**. Use this only when only a hint is required or access to the
304 /// `ThreadCell` is synchronized by some other means.
305 #[inline(always)]
306 pub fn is_disowned(&self) -> bool {
307 self.thread_id.load(Ordering::Acquire) == 0
308 }
309
310 /// Returns true when the current thread owns this cell by acquire.
311 #[inline(always)]
312 pub fn is_acquired(&self) -> bool {
313 // This can be Relaxed because when we already own it (with Acquire), no other thread
314 // can change the ownership. When we do not own it this may return Zero or some other
315 // thread id in a racy way, which is ok (to indicate disowned state) either way.
316 self.thread_id.load(Ordering::Relaxed) == current_thread_id()
317 }
318
319 /// Returns true when the current thread holds a guard on this cell.
320 #[inline(always)]
321 pub fn is_guarded(&self) -> bool {
322 // This can be Relaxed because when we already own it (with Acquire), no other thread
323 // can change the ownership. When we do not own it this may return Zero or some other
324 // thread id in a racy way, which is ok (to indicate disowned state) either way.
325 self.thread_id.load(Ordering::Relaxed) == current_thread_id() | GUARD_BIT
326 }
327
328 #[inline]
329 #[track_caller]
330 fn assert_owned(&self) {
331 assert!(self.is_owned(), "Thread has no access to ThreadCell");
332 }
333
334 /// Consumes a owned cell and returns its content.
335 ///
336 /// # Panics
337 ///
338 /// The current thread does not own the cell.
339 #[inline]
340 pub fn into_inner(mut self) -> T {
341 self.assert_owned();
342 unsafe { ManuallyDrop::take(&mut self.data) }
343 }
344
345 /// Gets an immutable reference to the cells content.
346 ///
347 /// # Panics
348 ///
349 /// The current thread does not own the cell.
350 #[inline]
351 pub fn get(&self) -> &T {
352 self.assert_owned();
353 &self.data
354 }
355
356 /// Gets a mutable reference to the cells content.
357 ///
358 /// # Panics
359 ///
360 /// The current thread does not own the cell.
361 #[inline]
362 pub fn get_mut(&mut self) -> &mut T {
363 self.assert_owned();
364 &mut self.data
365 }
366
367 /// Tries to get an immutable reference to the cells content.
368 /// Returns 'None' when the thread does not own the cell.
369 #[inline]
370 pub fn try_get(&self) -> Option<&T> {
371 if self.is_owned() {
372 Some(&self.data)
373 } else {
374 None
375 }
376 }
377
378 /// Tries to get a mutable reference to the cells content.
379 /// Returns 'None' when the thread does not own the cell.
380 #[inline]
381 pub fn try_get_mut(&mut self) -> Option<&mut T> {
382 if self.is_owned() {
383 Some(&mut self.data)
384 } else {
385 None
386 }
387 }
388
389 /// Gets an immutable reference to the cells content without checking for ownership.
390 ///
391 /// # Safety
392 ///
393 /// This is always safe when the thread owns the cell, for example after a `acquire()`
394 /// call. When the current thread does not own the cell then it is only safe when T is a
395 /// Sync type.
396 // PLANNED: When specialization is available: 'fn is_sync<T>() -> bool' and debug_assert!(is_owned() || is_sync::<T>())
397 #[inline]
398 pub unsafe fn get_unchecked(&self) -> &T {
399 debug_assert!(self.is_owned(), "Thread has no access to ThreadCell");
400 &self.data
401 }
402
403 /// Gets an mutable reference to the cells content without checking for ownership.
404 ///
405 /// # Safety
406 ///
407 /// This is always safe when the thread owns the cell, for example after a `acquire()`
408 /// call. When the current thread does not own the cell then it is only safe when T is a
409 /// Sync type.
410 // PLANNED: When specialization is available: 'fn is_sync<T>() -> bool' and debug_assert!(is_owned() || is_sync::<T>())
411 #[inline]
412 pub unsafe fn get_mut_unchecked(&mut self) -> &mut T {
413 &mut self.data
414 }
415}
416
417/// Destroys a `ThreadCell`. The cell must be either owned by the current thread or disowned.
418///
419/// # Panics
420///
421/// Another thread owns the cell.
422#[mutants::skip]
423impl<T> Drop for ThreadCell<T> {
424 // In debug builds we check first for ownership since dropping cells whose types do not
425 // need dropping would still be a violation.
426 #[cfg(debug_assertions)]
427 fn drop(&mut self) {
428 let owner = self.thread_id.load(Ordering::Acquire) & !GUARD_BIT;
429 if owner == 0 || owner == current_thread_id() {
430 if mem::needs_drop::<T>() {
431 unsafe { ManuallyDrop::drop(&mut self.data) };
432 }
433 } else {
434 panic!("Thread has no access to ThreadCell");
435 }
436 }
437
438 // In release builds we can reverse the check to be slightly more efficient. The side
439 // effect that dropping cells which one are not allowed to but don't need a destructor
440 // either is safe and harmless anyway.
441 #[cfg(not(debug_assertions))]
442 fn drop(&mut self) {
443 if mem::needs_drop::<T>() {
444 let owner = self.thread_id.load(Ordering::Acquire) & !GUARD_BIT;
445 if owner == 0 || owner == current_thread_id() {
446 unsafe { ManuallyDrop::drop(&mut self.data) };
447 } else {
448 panic!("Thread has no access to ThreadCell");
449 }
450 }
451 }
452}
453
454/// Creates a new owned `ThreadCell` from the given value.
455impl<T> From<T> for ThreadCell<T> {
456 #[inline]
457 fn from(t: T) -> ThreadCell<T> {
458 ThreadCell::new_owned(t)
459 }
460}
461
462/// Clones a owned `ThreadCell`.
463///
464/// # Panics
465///
466/// Another thread owns the cell.
467impl<T: Clone> Clone for ThreadCell<T> {
468 #[inline]
469 fn clone(&self) -> ThreadCell<T> {
470 ThreadCell::new_owned(self.get().clone())
471 }
472}
473
474/// Creates a new owned `ThreadCell` with the default constructed target value.
475impl<T: Default> Default for ThreadCell<T> {
476 #[inline]
477 fn default() -> ThreadCell<T> {
478 ThreadCell::new_owned(T::default())
479 }
480}
481
482/// Check two `ThreadCells` for partial equality.
483///
484/// # Panics
485///
486/// Either cell is not owned by the current thread.
487#[mutants::skip]
488impl<T: PartialEq> PartialEq for ThreadCell<T> {
489 #[inline]
490 fn eq(&self, other: &ThreadCell<T>) -> bool {
491 *self.get() == *other.get()
492 }
493}
494
495impl<T: Eq> Eq for ThreadCell<T> {}
496
497/// Comparison functions between `ThreadCells`.
498///
499/// # Panics
500///
501/// Either cell is not owned by the current thread.
502#[mutants::skip]
503impl<T: PartialOrd> PartialOrd for ThreadCell<T> {
504 #[inline]
505 fn partial_cmp(&self, other: &ThreadCell<T>) -> Option<cmp::Ordering> {
506 self.get().partial_cmp(other.get())
507 }
508
509 #[inline]
510 fn lt(&self, other: &ThreadCell<T>) -> bool {
511 *self.get() < *other.get()
512 }
513
514 #[inline]
515 fn le(&self, other: &ThreadCell<T>) -> bool {
516 *self.get() <= *other.get()
517 }
518
519 #[inline]
520 fn gt(&self, other: &ThreadCell<T>) -> bool {
521 *self.get() > *other.get()
522 }
523
524 #[inline]
525 fn ge(&self, other: &ThreadCell<T>) -> bool {
526 *self.get() >= *other.get()
527 }
528}
529
530/// Compare two `ThreadCells`.
531///
532/// # Panics
533///
534/// Either cell is not owned by the current thread.
535#[mutants::skip]
536impl<T: Ord> Ord for ThreadCell<T> {
537 #[inline]
538 fn cmp(&self, other: &ThreadCell<T>) -> cmp::Ordering {
539 self.get().cmp(other.get())
540 }
541}
542
543/// Formatted output of the value inside a `ThreadCell`.
544///
545/// # Panics
546///
547/// The cell is not owned by the current thread.
548#[mutants::skip]
549impl<T: fmt::Display> fmt::Display for ThreadCell<T> {
550 fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
551 fmt::Display::fmt(self.get(), f)
552 }
553}
554
555#[allow(clippy::doc_markdown)]
556#[mutants::skip]
557/// Debug information of a `ThreadCell`.
558/// Prints "\<ThreadCell\>" when the current thread does not own the cell.
559impl<T: fmt::Debug> fmt::Debug for ThreadCell<T> {
560 fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
561 match self.try_get() {
562 Some(data) => f.debug_struct("ThreadCell").field("data", data).finish(),
563 None => f.write_str("<ThreadCell>"),
564 }
565 }
566}
567
568#[cfg(not(feature = "nightly_thread_id_value"))]
569use std::num::NonZeroU64;
570
571/// A unique identifier for every thread.
572#[cfg(not(feature = "nightly_thread_id_value"))]
573struct ThreadId(NonZeroU64);
574
575#[cfg(not(feature = "nightly_thread_id_value"))]
576impl ThreadId {
577 #[inline]
578 #[must_use]
579 #[mutants::skip]
580 fn current() -> ThreadId {
581 thread_local!(static THREAD_ID: NonZeroU64 = {
582 static COUNTER: AtomicU64 = AtomicU64::new(1);
583 {
584 let id = NonZeroU64::new(COUNTER.fetch_add(1, Ordering::Relaxed)).unwrap();
585 assert!(id.get() <= i64::MAX as u64, "more than i64::MAX threads");
586 id
587 }
588 });
589 THREAD_ID.with(|&x| ThreadId(x))
590 }
591
592 #[inline(always)]
593 #[must_use]
594 #[mutants::skip]
595 fn as_u64(&self) -> NonZeroU64 {
596 self.0
597 }
598}
599
600#[test]
601#[cfg(not(feature = "nightly_thread_id_value"))]
602fn threadid() {
603 let main = ThreadId::current().as_u64().get();
604 let child = std::thread::spawn(|| ThreadId::current().as_u64().get())
605 .join()
606 .unwrap();
607
608 // just info, actual values are unspecified
609 println!("{main}, {child}");
610
611 assert_ne!(main, 0);
612 assert_ne!(main, child);
613}
614
615#[cfg(not(feature = "nightly_thread_id_value"))]
616#[mutants::skip]
617#[inline]
618fn current_thread_id() -> u64 {
619 ThreadId::current().as_u64().get()
620}
621
622#[cfg(feature = "nightly_thread_id_value")]
623#[mutants::skip]
624#[inline]
625fn current_thread_id() -> u64 {
626 std::thread::current().id().as_u64().get()
627}
628
629/// Guards that a referenced `ThreadCell` becomes properly released when its guard becomes
630/// dropped. This covers releasing threadcells on panic. Guards do not prevent the explicit
631/// release of a `ThreadCell`. Deref a `Guard` referencing a released `ThreadCell` will panic!
632#[repr(transparent)]
633pub struct Guard<'a, T>(&'a ThreadCell<T>);
634
635/// Releases the referenced `ThreadCell` when it is owned by the current thread.
636impl<T> Drop for Guard<'_, T> {
637 #[mutants::skip]
638 fn drop(&mut self) {
639 unsafe {
640 // SAFETY: a guard is guaranteed to own the cell
641 self.0.release_unchecked();
642 }
643 }
644}
645
646/// One can deref a `Guard` as long the `ThreadCell` is owned by the current thread this
647/// should be the case as long the guarded `ThreadCell` got not explicitly released or stolen.
648///
649/// # Panics
650///
651/// When the underlying `ThreadCell` is not owned by the current thread.
652impl<T> Deref for Guard<'_, T> {
653 type Target = T;
654
655 fn deref(&self) -> &Self::Target {
656 self.0.get()
657 }
658}
659
660/// Mutable Guard that ensures that a referenced `ThreadCell` becomes properly released when
661/// it becomes dropped. Guards do not prevent the explicit release of a `ThreadCell`. Deref a
662/// `GuardMut` referencing a released `ThreadCell` will panic!
663#[repr(transparent)]
664pub struct GuardMut<'a, T>(&'a mut ThreadCell<T>);
665
666/// Releases the referenced `ThreadCell` when it is owned by the current thread.
667impl<T> Drop for GuardMut<'_, T> {
668 fn drop(&mut self) {
669 unsafe {
670 // SAFETY: a guard is guaranteed to own the cell
671 self.0.release_unchecked();
672 }
673 }
674}
675
676/// One can deref a `GuardMut` as long the `ThreadCell` is owned by the current thread this
677/// should be the case as long the guarded `ThreadCell` got not explicitly released or stolen.
678///
679/// # Panics
680///
681/// When the underlying `ThreadCell` is not owned by the current thread.
682impl<T> Deref for GuardMut<'_, T> {
683 type Target = T;
684
685 fn deref(&self) -> &Self::Target {
686 self.0.get()
687 }
688}
689
690impl<T> DerefMut for GuardMut<'_, T> {
691 fn deref_mut(&mut self) -> &mut Self::Target {
692 self.0.get_mut()
693 }
694}