Description
I'm working on a linked list implementation for an embedded kernel. We have the following Thread
structure, which will be a member of a list:
struct Node<T> {
next: Option<NonNull<Node<T>>>,
prev: Option<NonNull<Node<T>>>,
t: T,
}
struct Thread {
stack: [u8; STACK_SIZE],
id: u32,
...
}
The stack
is the thread's stack, which will be used to hold arbitrary Rust values, which might include &mut
references to other values in the stack (i.e., it might be self-referential). (While pinning is obviously a concern, that's not what this issue is about.)
In addition to self-references in the stack, we also need to be able to inspect other fields in Thread
from inside the scheduler.
Here's an example - possibly problematic - sequence of events:
- Some code runs in the thread which writes a
&mut
reference to the stack that also refers to the stack. - The thread yields control, which hands control over to the scheduler
- The scheduler obtains a
&Node<Thread>
reference to the currently-running thread and reads itsid
field
While, in (3), we haven't read data from stack
, I presume that it's still considered UB to have a &Node<Thread>
while other &mut
references exist which point into the stack
, which overlaps with the referent of the &Node<Thread>
?
My first thought was to just wrap the whole thing in UnsafeCell
so that the Node
API doesn't provide &Thread
s, but instead provides &UnsafeCell<Thread>
s. But IIUC that doesn't solve the problem - it's still illegal to have &UnsafeCell<T>
and &mut T
overlap.
My next thought was to just use raw pointers and never synthesize &
references.
Concretely, my questions are the following. All of them are in the context of &mut
references existing which refer to stack
.
- Given a
NonNull<Node<Thread>>
, is it sound to perform pointer arithmetic to obtain aNonNull<u32>
and dereference it? Specifically, is it problematic that the originalNonNull<Node<Thread>>
refers to the wholeThread
, including thestack
? - Is there any way to provide a non-raw-pointer API which provides access to the inner
T
in aNode<T>
which would play nicely with this use case?