-
-
Notifications
You must be signed in to change notification settings - Fork 179
Add support for CLS variables with user-defined types #1024
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Signed-off-by: Klimenty Tsoutsman <[email protected]>
Signed-off-by: Klimenty Tsoutsman <[email protected]>
Signed-off-by: Klimenty Tsoutsman <[email protected]>
Signed-off-by: Klimenty Tsoutsman <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very nice! love the API and design decisions. I left a few comments, mostly questions and nitpicky shit.
Signed-off-by: Klimenty Tsoutsman <[email protected]>
Signed-off-by: Klimenty Tsoutsman <[email protected]>
Signed-off-by: Klimenty Tsoutsman <[email protected]>
Signed-off-by: Klimenty Tsoutsman <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks great, thanks as always! Tested and working for me.
* The `cpu_local` macro now generates four functions for user-defined types: ```rust pub fn replace(&self, value: #ty) -> #ty; pub fn replace_guarded<G>(&self, mut value: #ty, guard: &G) -> #ty where G: CpuAtomicGuard pub fn set(&self, value: #ty); pub fn set_guarded<G>(&self, mut value: #ty, guard: &G) where G: CpuAtomicGuard ``` * The new `CpuAtomicGuard` trait is sealed, but implemented for `preemption::PreemptionGuard` and `irq_safety::HeldInterrupts`. Passing a reference to one of those guard types into either `set_guarded()` or `replace_guarded()` can efficiently ensure that either preemption or interrupts are disabled, meaning that the CPU-local variable can be safely accessed in an atomic manner across multiple assembly instructions. * However, this doesn't cover all use cases, so we also add two optional arguments to the `cpu_local` macro: 1. `cls_dep`: a boolean that defaults to true, but can be set to false to prevent the macro from generating functions that depend on `cls`. This is only used by the `preemption` crate to avoid a circular dependency. 2. `stores_guard`: accepts a type denoting that the CLS variable stores an `Option<impl cls::CpuAtomicGuard>`, such as the task switch preemption guard. This argument modifies the signatures of `replace` and `set` to accept the guard type by value, because the guard type obviates the need to pass in a reference to another redundant guard (normally used to ensure atomicity). Signed-off-by: Klimenty Tsoutsman <[email protected]> 8209f16
* The `cpu_local` macro now generates four functions for user-defined types: ```rust pub fn replace(&self, value: #ty) -> #ty; pub fn replace_guarded<G>(&self, mut value: #ty, guard: &G) -> #ty where G: CpuAtomicGuard pub fn set(&self, value: #ty); pub fn set_guarded<G>(&self, mut value: #ty, guard: &G) where G: CpuAtomicGuard ``` * The new `CpuAtomicGuard` trait is sealed, but implemented for `preemption::PreemptionGuard` and `irq_safety::HeldInterrupts`. Passing a reference to one of those guard types into either `set_guarded()` or `replace_guarded()` can efficiently ensure that either preemption or interrupts are disabled, meaning that the CPU-local variable can be safely accessed in an atomic manner across multiple assembly instructions. * However, this doesn't cover all use cases, so we also add two optional arguments to the `cpu_local` macro: 1. `cls_dep`: a boolean that defaults to true, but can be set to false to prevent the macro from generating functions that depend on `cls`. This is only used by the `preemption` crate to avoid a circular dependency. 2. `stores_guard`: accepts a type denoting that the CLS variable stores an `Option<impl cls::CpuAtomicGuard>`, such as the task switch preemption guard. This argument modifies the signatures of `replace` and `set` to accept the guard type by value, because the guard type obviates the need to pass in a reference to another redundant guard (normally used to ensure atomicity). Signed-off-by: Klimenty Tsoutsman <[email protected]> 8209f16
* The `cpu_local` macro now generates four functions for user-defined types: ```rust pub fn replace(&self, value: #ty) -> #ty; pub fn replace_guarded<G>(&self, mut value: #ty, guard: &G) -> #ty where G: CpuAtomicGuard pub fn set(&self, value: #ty); pub fn set_guarded<G>(&self, mut value: #ty, guard: &G) where G: CpuAtomicGuard ``` * The new `CpuAtomicGuard` trait is sealed, but implemented for `preemption::PreemptionGuard` and `irq_safety::HeldInterrupts`. Passing a reference to one of those guard types into either `set_guarded()` or `replace_guarded()` can efficiently ensure that either preemption or interrupts are disabled, meaning that the CPU-local variable can be safely accessed in an atomic manner across multiple assembly instructions. * However, this doesn't cover all use cases, so we also add two optional arguments to the `cpu_local` macro: 1. `cls_dep`: a boolean that defaults to true, but can be set to false to prevent the macro from generating functions that depend on `cls`. This is only used by the `preemption` crate to avoid a circular dependency. 2. `stores_guard`: accepts a type denoting that the CLS variable stores an `Option<impl cls::CpuAtomicGuard>`, such as the task switch preemption guard. This argument modifies the signatures of `replace` and `set` to accept the guard type by value, because the guard type obviates the need to pass in a reference to another redundant guard (normally used to ensure atomicity). Signed-off-by: Klimenty Tsoutsman <[email protected]>
The
cpu_local
macro now generates four functions for user-defined types:cls::Guard
is implemented forpreemption::PreemptionGuard
andirq_safety::HeldInterrupts
. Passing a reference ensures that one or the other is disabled and so it is safe to access the CLS variable across multiple assembly instructions.However, this doesn't work for all use cases so the PR also adds two optional args to the
cpu_local
macro:cls_dep
: Prevents generating functions that depend oncls
. Used by thepreemption
crate to avoid a circular dependency.stores_guard
: Used for CLS variables that store anOption<impl cls::Guard>
(i.e.TASK_SWITCH_PREEMPTION_GUARD
). Modifies the signatures ofreplace
andset
:Pain points
cpu_local
will generate a different API for integers, custom-types, andOption<impl cls::Guard>
which can cause some confusion. In practice this shouldn't be a major problem becauseOption<impl cls::Guard>
is a niche workaround used for a specific CLS variable, andcargo doc
works as you'd expect, but still... Currently, the macro isn't actually necessary as we could recreate all of the functionality usingCpuLocalU*
/CpuLocalCell
types which would dramatically improve the unpredictable API. But (I'm pretty sure) the macro is necessary for when we implement dynamic CLS sections so there's no point removing it now.