Skip to content

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

Merged
merged 8 commits into from
Aug 4, 2023

Conversation

tsoutsman
Copy link
Member

@tsoutsman tsoutsman commented Aug 3, 2023

The cpu_local macro now generates four functions for user-defined types:

pub fn replace(&self, value: #ty) -> #ty;

pub fn replace_guarded<G>(&self, mut value: #ty, guard: &G) -> #ty
where
    G: ::cls::Guard;

pub fn set(&self, value: #ty);

pub fn set_guarded<G>(&self, mut value: #ty, guard: &G)
where
    G: ::cls::Guard;

cls::Guard is implemented for preemption::PreemptionGuard and irq_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 on cls. Used by the preemption crate to avoid a circular dependency.
  • stores_guard: Used for CLS variables that store an Option<impl cls::Guard> (i.e. TASK_SWITCH_PREEMPTION_GUARD). Modifies the signatures of replace and set:
    pub fn replace(&self, guard: #guard_type) -> #ty;
    pub fn set(&self, mut guard: #guard_type);
    As the additional guard reference isn't needed to ensure preemption/interrupts are disabled.

Pain points

  • cpu_local will generate a different API for integers, custom-types, and Option<impl cls::Guard> which can cause some confusion. In practice this shouldn't be a major problem because Option<impl cls::Guard> is a niche workaround used for a specific CLS variable, and cargo doc works as you'd expect, but still... Currently, the macro isn't actually necessary as we could recreate all of the functionality using CpuLocalU*/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.

Signed-off-by: Klimenty Tsoutsman <[email protected]>
Signed-off-by: Klimenty Tsoutsman <[email protected]>
Signed-off-by: Klimenty Tsoutsman <[email protected]>
@tsoutsman tsoutsman marked this pull request as draft August 3, 2023 12:14
Signed-off-by: Klimenty Tsoutsman <[email protected]>
@tsoutsman tsoutsman requested a review from kevinaboos August 3, 2023 12:58
@tsoutsman tsoutsman marked this pull request as ready for review August 3, 2023 12:59
Copy link
Member

@kevinaboos kevinaboos left a 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]>
@tsoutsman tsoutsman requested a review from kevinaboos August 4, 2023 00:13
Copy link
Member

@kevinaboos kevinaboos left a 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.

@kevinaboos kevinaboos merged commit 8209f16 into theseus-os:theseus_main Aug 4, 2023
github-actions bot pushed a commit that referenced this pull request Aug 4, 2023
* 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
github-actions bot pushed a commit to tsoutsman/Theseus that referenced this pull request Aug 4, 2023
* 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
tsoutsman added a commit to tsoutsman/Theseus that referenced this pull request Sep 6, 2023
* 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]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants