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}