Skip to content

Conversation

bradjc
Copy link
Contributor

@bradjc bradjc commented Aug 20, 2025

Pull Request Overview

This is an example of using SingleThreadValue for DeferredCall. This is implemented on top of #4551.

Testing Strategy

I tried the nrf with and without calling init, and verified it does not work if init isn't called.

TODO or Help Wanted

All boards need to be updated to call deferred call state init.

Documentation Updated

  • Updated the relevant files in /docs, or no updates are required.

Formatting

  • Ran make prepush.

bradjc and others added 3 commits August 7, 2025 17:57
Introduces a a container for objects accessible to a single thread.

This type wraps a value of type `T: ?Sync`, accessible to only a
single thread. Only that thread can obtain references to the contained
value, and that thread may obtain multiple _shared_ (`&`) references
to the value concurrently.

This container is [`Sync`], regardless of whether `T` is `Sync`. This
is similar to the standard library's `LocalKey`, and thus appropriate
for static allocations of values that are not themselves
[`Sync`]. However, unlike `LocalKey`, it only holds a value for a
single thread, determined at runtime based on the first call to
[`SingleThreadValue::bind_to_thread`].

```
use core::cell::Cell;
use kernel::utilities::single_thread_value::SingleThreadValue;

// Binding to a thread requires a "ThreadIdProvider", used to query the
// thread ID of the currently running thread at runtime:
enum DummyThreadIdProvider {}
unsafe impl kernel::platform::chip::ThreadIdProvider for DummyThreadIdProvider {
    fn running_thread_id() -> usize {
        // Return the current thread id. We return a constant for
        // demonstration purposes, but doing so in practice is unsound:
        42
    }
}

static FOO: SingleThreadValue<Cell<usize>> = SingleThreadValue::new(Cell::new(123));

fn main() {
    // Bind the value contained in the `SingleThreadValue` to the currently
    // running thread:
    FOO.bind_to_thread::<DummyThreadIdProvider>();

    // Attempt to access the value. Returns `Some(&T)` if running from the
    // thread that the `SingleThreadValue` is bound to:
    let foo_ref = FOO.get().unwrap();
    foo_ref.set(foo_ref.get() + 1);
}
```

After creating the [`SingleThreadValue`] and before trying to access
the wrapped value, the [`SingleThreadValue`] must have its
[`bind_to_thread`](SingleThreadValue::bind_to_thread) method
called. Failing to bind it to a thread will prevent any access to the
wrapped value.

It is possible for the same thread to get multiple, shared,
references. As a result, users must use interior mutability (e.g.
[`Cell`](core::cell::Cell),
[`MapCell`](tock_cells::map_cell::MapCell), or
[`TakeCell`](tock_cells::take_cell::TakeCell)) to allow obtaining
exclusive mutable access.

[`SingleThreadValue`] is safe because it guarantees that the value is
only ever accessed from a single thread. To do this,
[`SingleThreadValue`] inspects the currently running thread on every
access to the wrapped value. If the active thread is different than
the original thread then the caller will not be able to access the
value.

This requires that the system provides a correct implementation of
[`ThreadIdProvider`] to identify the currently executing
thread. Internally, [`SingleThreadValue`] uses the
[`ThreadIdProvider::running_thread_id`] function to identify the
current thread and compares it against the thread ID that the
contained value is bound to.

Co-authored-by: Leon Schuermann <[email protected]>
This removes the static muts and uses STV instead.
@github-actions github-actions bot added kernel nrf Change pertains to the nRF5x family of MCUs. risc-v RISC-V architecture labels Aug 20, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
kernel nrf Change pertains to the nRF5x family of MCUs. risc-v RISC-V architecture
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants