Skip to main content

kernel/utilities/
single_thread_value.rs

1// Licensed under the Apache License, Version 2.0 or the MIT License.
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3// Copyright Tock Contributors 2025.
4
5//! A container for objects accessible to a single thread.
6
7use core::cell::UnsafeCell;
8use core::mem::MaybeUninit;
9use core::sync::atomic::AtomicUsize;
10use core::sync::atomic::Ordering;
11
12use crate::platform::chip::ThreadIdProvider;
13
14/// Stages of binding a [`SingleThreadValue`] to a given thread.
15///
16/// The [`SingleThreadValue`] starts out not being bound to, and thus not being
17/// usable by any thread. A `SingleThreadValue` instance is instead bound to
18/// particular thread using either the `SingleThreadValue::bind_to_thread`, or
19/// the [`SingleThreadValue::bind_to_thread_unsafe`] methods. These methods can
20/// be used exactly once, after which the instance is permanently bound to the
21/// given thread that called these methods.
22///
23/// For these and other methods to know whether a [`SingleThreadValue`] has been
24/// bound to a thread already, and thus whether its `thread_id_and_fn` field is
25/// initialized and holds a stable value, it contains an `AtomicUsize` type to
26/// indicate its current "binding stage". Over its lifetime, this stage value is
27/// strictly increasing, transitioning through the [`BoundToThreadStage`] enum
28/// variants as per their documentation.
29#[repr(usize)]
30enum BoundToThreadStage {
31    /// This stage means that the [`SingleThreadValue`] has not been bound to a
32    /// particular thread yet, and its `thread_id_and_fn` field is not
33    /// initialized.
34    ///
35    /// For the `bind_to_thread` and `bind_to_thread_unsafe` methods, this value
36    /// further means that the instance can be bound to the currently running
37    /// thread.
38    Unbound = 0,
39
40    /// To bind a [`SingleThreadValue`] to the currently running thread, the
41    /// `bind_to_thread` function atomically transitions it from the `Unbound`
42    /// stage to the `Binding` stage, through a compare-exchange operation.
43    ///
44    /// When this stage is active, the `thread_id_and_fn` field is not
45    /// initialized, similar to `Unbound`. However, it prevents other,
46    /// concurrent calls to `bind_to_thread` from attempting to bind the value
47    /// to another thread concurrently.
48    ///
49    /// Targets without support for compare-exchange atomic operations on
50    /// `usize` types can utilize `bind_to_thread_unsafe` instead: this method
51    /// requires callers to externally guarantee that there are no concurrent
52    /// calls to either `bind_to_thread` OR `bind_to_thread_unsafe`. Thus, it
53    /// can skip this intermediate state: it can directly transition from
54    /// `Unbound` into `Bound`.
55    #[allow(dead_code)]
56    Binding = 1,
57
58    /// This value indicates that the [`SingleThreadValue`] is bound to a
59    /// particular thread. The `thread_id_and_fn` value is initialized and
60    /// sealed: it must not be modified beyond this point, and no mutable
61    /// references to it may exist. The `Bound` stage is final and cannot be
62    /// transitioned out of.
63    Bound = 2,
64}
65
66/// A container for objects accessible to a single thread.
67///
68/// This type wraps a value of type `T: ?Sync`, accessible to only a single
69/// thread. Only that thread can obtain references to the contained value, and
70/// that thread may obtain multiple _shared_ (`&`) references to the value
71/// concurrently.
72///
73/// This container is [`Sync`], regardless of whether `T` is `Sync`. This is
74/// similar to the standard library's `LocalKey`, and thus appropriate for
75/// static allocations of values that are not themselves [`Sync`]. However,
76/// unlike `LocalKey`, it only holds a value for a single thread, determined at
77/// runtime based on the first call to `SingleThreadValue::bind_to_thread`.
78///
79/// # Example
80///
81/// ```
82/// use core::cell::Cell;
83/// use kernel::utilities::single_thread_value::SingleThreadValue;
84///
85/// // Binding to a thread requires a "ThreadIdProvider", used to query the
86/// // thread ID of the currently running thread at runtime:
87/// enum DummyThreadIdProvider {}
88/// unsafe impl kernel::platform::chip::ThreadIdProvider for DummyThreadIdProvider {
89///     fn running_thread_id() -> usize {
90///         // Return the current thread id. We return a constant for
91///         // demonstration purposes, but doing so in practice is unsound:
92///         42
93///     }
94/// }
95///
96/// static FOO: SingleThreadValue<Cell<usize>> = SingleThreadValue::new();
97///
98/// fn main() {
99///     // Atomically move the value supplied in the constructor into the
100///     // `SingleThreadValue`, and bind it to the currently running thread:
101///     FOO.bind_to_thread::<DummyThreadIdProvider>(Cell::new(123));
102///
103///     // Attempt to access the value. Returns `Some(&T)` if running from the
104///     // thread that the `SingleThreadValue` is bound to:
105///     let foo_ref = FOO.get().unwrap();
106///     foo_ref.set(foo_ref.get() + 1);
107/// }
108/// ```
109///
110/// When creating a new [`SingleThreadValue`] instance, it is uninitialized
111/// until the `bind_to_thread` or
112/// [`bind_to_thread_unsafe`](SingleThreadValue::bind_to_thread_unsafe) methods
113/// are called. Until it is initialized, all attempts to access the shared value
114/// using [`get`](SingleThreadValue::get) will return `None`.
115///
116/// # Single-thread Synchronization
117///
118/// It is possible for the same thread to get multiple, shared, references to
119/// the wrapped value concurrently. Users can use interior mutability (e.g.
120/// [`Cell`](core::cell::Cell), [`MapCell`](tock_cells::map_cell::MapCell), or
121/// [`TakeCell`](tock_cells::take_cell::TakeCell)) to allow obtaining exclusive
122/// mutable access.
123///
124/// # Guaranteeing Single-Thread Access
125///
126/// [`SingleThreadValue`] is safe because it guarantees that the contained value
127/// is only ever made accessible to the same thread from which it originated. To
128/// do this, [`SingleThreadValue`] inspects the currently running thread on
129/// every access to the wrapped value. If the active thread is different than
130/// the original thread to which the [`SingleThreadValue`] is bound, then the
131/// caller will not be able to access the value.
132///
133/// This requires that the system provide a correct implementation of
134/// [`ThreadIdProvider`] to identify the currently executing thread. Internally,
135/// [`SingleThreadValue`] uses the [`ThreadIdProvider::running_thread_id`]
136/// function to identify the current thread and compares it against the thread
137/// ID that the contained value is bound to.
138//
139// # Implementation Trade-Offs
140//
141// Correctly implementing a type which can be safely shared within a single
142// thread requires balancing the following trade-offs:
143//
144// 1. Automated enforcement (either by the compiler or at runtime) vs.
145//    programmer-checked correctness.
146// 2. Runtime overhead vs. compile-time checks.
147// 3. Simplicity for users vs. easy-to-make mistakes.
148//
149// Ideally, the guarantees this type provides would be verified at compile time
150// and be simple for users to use. However, as of July 2025, we don't know how
151// to realistically meet those goals.
152//
153// This approach first chooses to have automated enforcement rather than have
154// correctness (and avoiding unsoundness) based only on carefully written code
155// and scrutiny during code reviews. This is generally consistent with the Tock
156// ethos with using Rust and APIs to enforce correctness, even if the check
157// occurs at runtime.
158//
159// To implement the automated check, this type uses runtime checks. We don't
160// know a realistic way to move those checks to compile time.
161//
162// One downside to these decisions is the need to bind the value to a thread
163// _after_ the type is created. While this is conceptually similar to providing
164// a callback client after an object is constructed, which is widely used in
165// Tock, forgetting this operation will lead to the runtime check always failing
166// and will break the desired functionality of using the wrapped value.
167// However, passing in the type later is needed because the thread identifier is
168// likely not available where users want to construct this type.
169//
170// Ultimately, these trade-offs, runtime checks and an interface where missing a
171// call leads to failures, were deemed acceptable and worth the upside
172// (guaranteed soundness). In part, this is because this type should be used and
173// accessed sparingly within Tock. The complexity of creating this type stems
174// from its general risky-ness: enabling sharing static global variables. While
175// necessary for certain use cases within Tock, this should not be used lightly.
176pub struct SingleThreadValue<T> {
177    /// The contained value, made accessible to a single thread only.
178    ///
179    /// To avoid imposing a constraint of `T: Send`, this value is not populated
180    /// when the [`SingleThreadValue`] is constructed. It is instead initialized
181    /// by moving a value originating from the same thread that the
182    /// `SingleThreadValue` is ultimately bound to, within the
183    /// `bind_to_thread()` function.
184    value: UnsafeCell<MaybeUninit<T>>,
185
186    /// Shared atomic state to indicate whether this type is already bound to a
187    /// particular thread, or in the process of being bound to a particular
188    /// thread. Assumes values of [`BoundToThreadStage`]. Consider that type's
189    /// documentation for how the `bound_to_thread` value is used.
190    bound_to_thread: AtomicUsize,
191
192    /// Context used to determine which thread ID this type is bound to, and how
193    /// to determine the currently running thread ID.
194    ///
195    /// This value must only be used in accordance with the rules around
196    /// [`BoundToThreadStage`]. Consider that type's documentation for more
197    /// information. Whether this value is initialized, and when it is safe to
198    /// read and write depend on the value of `bound_to_thread`.
199    thread_id_and_fn: UnsafeCell<MaybeUninit<(fn() -> usize, usize)>>,
200}
201
202/// Mark that [`SingleThreadValue`] is [`Sync`] to enable multiple accesses.
203///
204/// SAFETY: This is safe because [`SingleThreadValue`] enforces that the shared value
205/// is only ever accessed from the same thread it originated from.
206unsafe impl<T> Sync for SingleThreadValue<T> {}
207
208impl<T> SingleThreadValue<T> {
209    /// Create a [`SingleThreadValue`].
210    ///
211    /// Note that the `SingleThreadValue` will initially be uninitialized. It
212    /// must first be bound to a particular thread, which will initialize with a
213    /// value originating from that thread, using the
214    /// `bind_to_thread` or
215    /// [`bind_to_thread_unsafe`](SingleThreadValue::bind_to_thread_unsafe)
216    /// methods.
217    pub const fn new() -> Self {
218        Self {
219            value: UnsafeCell::new(MaybeUninit::uninit()),
220            bound_to_thread: AtomicUsize::new(BoundToThreadStage::Unbound as usize),
221            thread_id_and_fn: UnsafeCell::new(MaybeUninit::uninit()),
222        }
223    }
224
225    /// Initialize this [`SingleThreadValue`] and bind it the currently running
226    /// thread.
227    ///
228    /// If this [`SingleThreadValue`] is not already bound to a thread, or if it
229    /// is not currently in the process of binding to a thread, then this binds
230    /// the [`SingleThreadValue`] to the current thread.
231    ///
232    /// It further records the [`ThreadIdProvider::running_thread_id`] function
233    /// reference, and uses this function to determine the currently running
234    /// thread for any future queries.
235    ///
236    /// Returns `Ok(())` if this invocation successfully bound the value to the
237    /// current thread. Otherwise, if the value was already bound to this same
238    /// or another thread, or is concurrently being bound to a thread, it
239    /// returns `Err(T)`, returning the supplied value.
240    ///
241    /// This method requires the target to support atomic operations
242    /// (namely, `compare_exchange`) on `usize`-sized values, and thus
243    /// relies on the `cfg(target_has_atomic = "ptr")` conditional.
244    #[cfg(target_has_atomic = "ptr")]
245    pub fn bind_to_thread<P: ThreadIdProvider>(&self, value: T) -> Result<(), T> {
246        // For the check whether we're already bound to a thread, `Relaxed`
247        // ordering is fine: we don't actually care about the value in
248        // `thread_id_and_fn`, and don't need previous writes to it to be
249        // visible to this thread.
250        //
251        // Perform a compare-exchange on the `bound_to_thread` value. If this
252        // operation is successful, the [`SingleThreadValue`] was in the
253        // `Unbound` stage, but is now in the `Binding` stage. This "reserves"
254        // to be initialized: other concurrent accesses observing a `Binding`
255        // stage must not assume that `thread_id_and_fn` is initialized, but
256        // also will not attempt to being initialization themselves:
257        if self
258            .bound_to_thread
259            .compare_exchange(
260                // Expected current value:
261                BoundToThreadStage::Unbound as usize,
262                // New value:
263                BoundToThreadStage::Binding as usize,
264                // Success memory ordering:
265                Ordering::Relaxed,
266                // Failure memory ordering:
267                Ordering::Relaxed,
268            )
269            .is_err()
270        {
271            return Err(value);
272        }
273
274        // Great, we have reserved the value for initialization!
275        //
276        // Write the current thread ID, and the function symbol used to query
277        // the currently running thread ID.
278        let ptr_thread_id_and_fn = self.thread_id_and_fn.get();
279
280        // SAFETY: We must ensure that there are no (mutable) aliases or concurrent
281        // reads or writes of the thread_id_and_fn value.
282        //
283        // This value is accessed in three functions: `bind_to_thread_unsafe`,
284        // `bind_to_thread`, and `bound_to_current_thread`:
285        //
286        // - `bind_to_thread_unsafe`: Callers of that function guarantee that
287        //   there are no concurrent invocations of it together with our
288        //   current function, `bind_to_thread`. Thus, no concurrent call to
289        //   that other function can hold an alias, or perform a read or write
290        //   of this value.
291        //
292        // - `bind_to_thread`: Before obtaining a reference to the
293        //   `thread_id_and_fn` value or reading/writing it, this function
294        //   performs a compare-exchange operation on the `bound_to_thread`
295        //   value, ensuring that it is currently `Unbound`, and atomtically
296        //   transitioning it towards `Binding`.
297        //
298        //   Our own compare-exchange operation on this value was successful. As
299        //   `Binding` cannot transition back to `Unbound`, there can only be a
300        //   single `bind_to_thread` call to successfully perform this
301        //   compare-exchange operation per[`SingleThreadValue`].
302        //
303        //   If another concurrent call had performed this compare-exchange
304        //   successfully, our own operation would have failed. Given that we
305        //   successfully performed this operation, all other concurrent calls
306        //   to this function must fail this operation instead, and thus will
307        //   not take this if-branch, and therefore will not attempt to access
308        //   the `thread_id_and_fn` value.
309        //
310        // - `bound_to_current_thread`: This function is allowed to run
311        //   concurrently with `bind_to_thread`. However, it only accesses the
312        //   `thread_id_and_fn` value when the `bound_to_thread` atomic
313        //   contains `Bound`.
314        //
315        //   The compare and swap has transitioned this value from `Unbound` to
316        //   `Binding`, and no concurrent call can further *modify*
317        //   `bound_to_thread` other than our own function instance. This
318        //   currently running function will only transition the
319        //   `bound_to_thread` value from `Binding` to `Bound` once it has
320        //   initialized `thread_id_and_fn` and all references to it cease to
321        //   exist. Thus, no concurrent calls to `bound_to_current_thread` will
322        //   read the `thread_id_and_fn` before that point.
323        //
324        // Hence this operation is sound.
325        unsafe {
326            *ptr_thread_id_and_fn =
327                MaybeUninit::new((P::running_thread_id, P::running_thread_id()));
328        }
329
330        // Initialize the contained value, constructed on the same (currently
331        // running) thread that we are binding the `SingleThreadValue` to. This
332        // avoids a requirement of `T: Send`.
333        //
334        // SAFETY: We are the only thread with access to `self.value` going forward, and
335        // this is the first time that this value is being accessed (as we're
336        // initializing it). Therefore, we can safely dereference a mutable
337        // (unique) pointer to this value:
338        unsafe {
339            *self.value.get() = MaybeUninit::new(value);
340        }
341
342        // When initializing the `SingleThreadValue`, we must use `Release`
343        // ordering on the `bound_to_thread` store. This ensures that any
344        // subsequent atomic load of this value with at least `Acquire`
345        // ordering constraints will observe the previous write to
346        // `thread_id_and_fn`).
347        self.bound_to_thread
348            .store(BoundToThreadStage::Bound as usize, Ordering::Release);
349
350        // We have successfully bound this `SingleThreadValue` to the
351        // currently running thread:
352        Ok(())
353    }
354
355    /// Initialize this [`SingleThreadValue`] and bind it to the currently
356    /// running thread.
357    ///
358    /// If this [`SingleThreadValue`] is not already bound to a thread, or if it
359    /// is not currently in the process of binding to a thread, then this binds
360    /// the [`SingleThreadValue`] to the current thread.
361    ///
362    /// It further records the [`ThreadIdProvider::running_thread_id`] function
363    /// reference, and uses this function to determine the currently running
364    /// thread for any future queries.
365    ///
366    /// Returns `Ok(())` if this invocation successfully bound the value to the
367    /// current thread. Otherwise, if the value was already bound to this same
368    /// or another thread, it returns `Err(T)`, returning the supplied value.
369    ///
370    /// This method is `unsafe`, and does not require the target to support
371    /// atomic operations (namely, `compare_exchange`) on `usize`-sized values.
372    ///
373    /// # Safety
374    ///
375    /// Callers of this function must ensure that this function is never called
376    /// concurrently with other calls to
377    /// `bind_to_thread` or
378    /// [`bind_to_thread_unsafe`](SingleThreadValue::bind_to_thread_unsafe) on
379    /// the same [`SingleThreadValue`] instance.
380    pub unsafe fn bind_to_thread_unsafe<P: ThreadIdProvider>(&self, value: T) -> Result<(), T> {
381        // For the check whether we're already bound to a thread, `Relaxed`
382        // ordering is fine: we don't actually care about the value in
383        // `thread_id_and_fn`, and don't need previous writes to it to be
384        // visible to this thread.
385        if self.bound_to_thread.load(Ordering::Relaxed) != BoundToThreadStage::Unbound as usize {
386            // This value is already bound to a thread
387            return Err(value);
388        }
389
390        // Write the current thread ID, and the function symbol used to
391        // query the currently running thread ID.
392        let ptr_thread_id_and_fn = self.thread_id_and_fn.get();
393
394        // SAFETY: We must ensure that there are no (mutable) aliases or concurrent
395        // reads or writes of the `thread_id_and_fn` value.
396        //
397        // This value is accessed in three functions: `bind_to_thread`,
398        // `bind_to_thread_unsafe`, and `bound_to_current_thread`:
399        //
400        // - `bind_to_thread` & `bind_to_thread_unsafe`: Callers of this current
401        //   function guarantee that there are no concurrent invocations of
402        //   either `bind_to_thread` or `bind_to_thread_unsafe` on the same
403        //   `SingleThreadValue` object. Thus, no concurrent call to either of
404        //   these functions can hold an alias, or perform a read or write of
405        //   the `thread_id_and_fn` value.
406        //
407        // - `bound_to_current_thread`: This function is allowed to run
408        //   concurrently with `bind_to_thread`. However, it only accesses this
409        //   value when the `bound_to_thread` atomic contains `Bound`.
410        //   `bound_to_current_thread` never writes to `bound_to_thread`.
411        //
412        //   This current function has, before taking this if-branch, checked
413        //   that `bound_to_thread` is currently `Unbound`, and will continue
414        //   to be `Unbound` for the duration of the write to
415        //   `thread_id_and_fn`, as there are no concurrent calls to
416        //   `bind_to_thread` or `bind_to_thread_unsafe`. It only transitions
417        //   `bound_to_thread` to `Bound` after completing the initialization
418        //   of `thread_id_and_fn` and all references to that value cease to
419        //   exist.
420        //
421        // Thus, this operation is sound.
422        unsafe {
423            *ptr_thread_id_and_fn =
424                MaybeUninit::new((P::running_thread_id, P::running_thread_id()));
425        }
426
427        // Initialize the contained value, constructed on the same (currently
428        // running) thread that we are binding the `SingleThreadValue` to. This
429        // avoids a requirement of `T: Send`.
430        //
431        // SAFETY: We are the only thread with access to `self.value` going forward, and
432        // this is the first time that this value is being accessed (as we're
433        // initializing it). Therefore, we can safely dereference a mutable
434        // (unique) pointer to this value:
435        unsafe {
436            *self.value.get() = MaybeUninit::new(value);
437        }
438
439        // When initializing the `SingleThreadValue`, we must use `Release`
440        // ordering on the `bound_to_thread` store. This ensures that any
441        // subsequent atomic load of this value with at least `Acquire`
442        // ordering constraints will observe the previous write to
443        // `thread_id_and_fn`).
444        self.bound_to_thread
445            .store(BoundToThreadStage::Bound as usize, Ordering::Release);
446
447        // We have successfully bound this `SingleThreadValue` to the
448        // currently running thread:
449        Ok(())
450    }
451
452    /// Check whether this `SingleThreadValue` instance is bound to the
453    /// currently running thread ID.
454    ///
455    /// Returns `true` if the value is bound to the thread that is currently
456    /// running (as determined by the implementation of `ThreadIdProvider` used
457    /// in `bind_to_thread` or
458    /// [`bind_to_thread_unsafe`](SingleThreadValue::bind_to_thread_unsafe)).
459    /// Returns `false` when a different thread is executing, when it is not
460    /// bound to any thread yet, or currently in the process of being bound to a
461    /// thread.
462    pub fn bound_to_current_thread(&self) -> bool {
463        // This function loads the `thread_id_and_fn` field, written by either
464        // `bind_to_thread` or `bind_to_thread_unsafe` before setting
465        // `bound_to_thread` to `Bound` with `Release` ordering constraints. To
466        // make those writes visible, we need to load the `bound_to_thread`
467        // value with at least `Acquire` ordering constraints:
468        if self.bound_to_thread.load(Ordering::Acquire) != BoundToThreadStage::Bound as usize {
469            // The `SingleThreadValue` value is not yet bound to any thread:
470            return false;
471        }
472
473        // The `SingleThreadValue` value is bound to _a_ thread, check whether
474        // it is the currently running thread:
475        let ptr_thread_id_and_fn: *mut MaybeUninit<(fn() -> usize, usize)> =
476            self.thread_id_and_fn.get();
477
478        let maybe_thread_id_and_fn: *const MaybeUninit<(fn() -> usize, usize)> =
479            ptr_thread_id_and_fn.cast_const();
480        // SAFETY: We must ensure that there are no (mutable) aliases or concurrent
481        // reads or writes of the thread_id_and_fn value.
482        //
483        // This value is accessed in three functions: `bind_to_thread`,
484        // `bind_to_thread_unsafe`, and `bound_to_current_thread`:
485        //
486        // - `bind_to_thread` / `bind_to_thread_unsafe`: Before creating a
487        //   reference to, reading from, or writing to the `bound_to_thread`
488        //   value, both functions check that `bound_to_thread` is not already
489        //   set to `Bound`. However, when reaching if-branch in this current
490        //   function, we have observed that `bound_to_thread` is set to
491        //   `Bound`. The state of `Bound` cannot be transitioned out of. Thus,
492        //   `bound_to_thread` will never be written again.
493        //
494        //   `bound_to_thread` is only ever set to `Bound`, *after*
495        //   `thread_id_and_fn` has been initialized, and all references to
496        //   `thread_id_and_fn` cease to exist.
497        //
498        //   When reaching this point, neither `bind_to_thread` nor
499        //   `bind_to_thread_unsafe` will ever read from, write to, or create a
500        //   reference to `thread_id_and_fn` again.
501        //
502        // - `bound_to_current_thread`: This function is allowed to run
503        //   concurrently with other calls to `bound_to_current_thread`.
504        //
505        //   However, this function only creates shared / immutable references
506        //   to this value that are never written. Multiple shared references
507        //   to this value are allowed to exist, so long as they are not
508        //   aliased by another exclusive / mutable reference, and the
509        //   underlying memory is not modified otherwise.
510        //
511        //   The only functions writing to `thread_id_and_fn` are
512        //   `bind_to_thread` and `bind_to_thread_unsafe`. As stated above,
513        //   when reaching this point, neither functions will ever write to
514        //   `thread_id_and_fn` again, and all of their internal references
515        //   have ceased to exist.
516        //
517        // Thus, this operation is sound.
518        let maybe_thread_id_and_fn: &MaybeUninit<(fn() -> usize, usize)> =
519            unsafe { &*maybe_thread_id_and_fn };
520
521        // SAFETY: Both `bind_to_thread` and `bind_to_thread_unsafe` are guaranteed to
522        // have initialized `thread_id_and_fn` *before* setting
523        // `bound_to_thread` to `Bound` with `Release` ordering constraints.
524        //
525        // In this if-branch, `bound_to_thread` has been observed to be `Bound`,
526        // a state that cannot be transitioned out of. We have loaded this
527        // value with `Acquire` ordering constraints, making all values written
528        // by other threads before a write to `bound_to_thread` with `Release`
529        // constraints visible to this thread.
530        //
531        // Thus, we can safely rely on `thread_id_and_fn` to be initialized:
532        let (running_thread_id_fn, bound_thread_id) =
533            unsafe { maybe_thread_id_and_fn.assume_init() };
534
535        // Finally, check if the thread this `SingleThreadValue` is bound to
536        // is the running thread ID:
537        bound_thread_id == running_thread_id_fn()
538    }
539
540    /// Obtain a reference to the contained value.
541    ///
542    /// This function checks whether the [`SingleThreadValue`] is bound to the
543    /// currently running thread and, if so, returns a shared / immutable
544    /// reference to its contained value.
545    pub fn get(&self) -> Option<&T> {
546        if self.bound_to_current_thread() {
547            // SAFETY: When `self.bound_to_current_thread()` returns true, we know that
548            // `self.value` is initialized, and that the value belongs to and is
549            // accessible to the currently running thread. We can safely
550            // construct a reference to it.
551            Some(unsafe { (&*self.value.get()).assume_init_ref() })
552        } else {
553            None
554        }
555    }
556}