Skip to content
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

Add steal() for each peripheral. #723

Merged
merged 6 commits into from
Aug 15, 2023

Conversation

JalonWong
Copy link
Contributor

This is more convenient than the Peripherals::steal()

@JalonWong JalonWong requested a review from a team as a code owner June 1, 2023 07:58
@JalonWong JalonWong changed the title Add steal() for each peripheral. Add steal() for each peripheral. Jun 1, 2023
@JalonWong JalonWong changed the title Add steal() for each peripheral. Add steal() for each peripheral. Jun 1, 2023
@thejpster
Copy link
Contributor

Is it sufficient to jus tcall the existing FOO::ptr() method to get a Foo::RegisterBlock instead?

I'm not sure I'd want to make it easier to steal peripherals, because a stolen peripheral bypasses the ownership system (p.FOO is supposed to be a singleton and indicate exclusive access to the Foo peripheral).

@JalonWong
Copy link
Contributor Author

JalonWong commented Jun 5, 2023

@thejpster
The FOO::ptr() is not sufficient. For instance, I have five UARTs, but only two different uart::RegisterBlock types. This makes it challenging for me to treat each UART differently. Moreover, using UART types also enhances code readability.

Indeed, a stolen peripheral bypasses the ownership system. So I implemented the steal() as an unsafe method. And there are indeed practical needs for it, such as when I want to split a UART into four instances: a sender, a receiver and two interrupt handlers. I found it difficult to achieve this without the steal() method.

I believe that anyone who uses unsafe methods should take responsibility for their choices. And it can be challenging without these unsafe methods when working on low-level MCU development.

@JalonWong JalonWong force-pushed the feature/steal branch 2 times, most recently from 0708f6c to 20a581a Compare June 5, 2023 02:56
@thejpster
Copy link
Contributor

thejpster commented Jun 6, 2023

But you don't have a RegisterBlock, you have a *mut RegisterBlock - the value of the pointer indicates which of the multiple UART peripherals you are currently dealing with. I seem to recall that only thing the singleton objects really do is hide this from you by automatically conjuring up a pointer whenever it is required, and implementing trait Deref so accesses to it are transparent.

If a HAL wishes to unsafely access the peripherals without using the PAC's sense of ownership, it can do that by creating its own types which are Copy or Clone. How it chooses to ensure that a read-modify-write cannot happen concurrently from multiple threads (or interrupts) is then a problem for the HAL author to solve.

But I'm still not sure the PAC needs to make it easier for HAL authors to do this, because it's outside the normal pattern of operation.

@adamgreig
Copy link
Member

We discussed this PR a bit more in today's meeting, I think it's fair to say there is interest in merging it but we're not sure yet, and in particular wonder if the new function would need to set the global flag that Peripherals::steal() sets. Hopefully not, but we'll try and find out.

@JalonWong
Copy link
Contributor Author

We discussed this PR a bit more in today's meeting, I think it's fair to say there is interest in merging it but we're not sure yet, and in particular wonder if the new function would need to set the global flag that Peripherals::steal() sets. Hopefully not, but we'll try and find out.

No need to set the global flag, because before calling the steal() of each peripheral, Peripherals::take() or Peripherals::steal() must be called first.

@reitermarkus
Copy link
Member

No need to set the global flag, because before calling the steal() of each peripheral, Peripherals::take() or Peripherals::steal() must be called first.

Given the peripherals are already stolen/taken, maybe this should be named something like clone_unchecked.

@JalonWong
Copy link
Contributor Author

JalonWong commented Jun 8, 2023

Given the peripherals are already stolen/taken, maybe this should be named something like clone_unchecked.

@reitermarkus
This does make more sense.

But you don't have a RegisterBlock, you have a *mut RegisterBlock - the value of the pointer indicates which of the multiple UART peripherals you are currently dealing with. I seem to recall that only thing the singleton objects really do is hide this from you by automatically conjuring up a pointer whenever it is required, and implementing trait Deref so accesses to it are transparent.

@thejpster
Implementing traits and writing generics for RegisterBlock always feels a bit weird to me.
So currently, I'm doing it like this:

pub trait UartReg {
    fn ptr() -> *mut Self;
    // or
    unsafe fn mut_ref() -> &'static mut Self;
    // or
    unsafe fn steal_mut(&self) -> &'static mut Self;
}

impl UartReg for USART1 { 
    fn ptr() -> *mut Self {
        (1_usize as *mut Self)
    }
    //...
}

impl UartReg for UART4 { //... }
fn foo<U: UartReg>() {
    let u = unsafe { &(*U::ptr()) };
}

They are very useful. However, they are also weird because the pointer points to an invalid address.

So I think adding an unsafe clone method to each peripheral would be a more elegant solution.

@adamgreig
Copy link
Member

Thanks for the PR! We discussed it again in today's meeting and we think it might be best if these new methods were associated functions (i.e., like fn steal() -> Self without requiring &self).

They'd still be unsafe and do the same thing, but you don't need to already have the peripheral before you can make a new copy. With an associated steal() there's also no need for a clone_unchecked(), since you can just steal() directly. We would separately make it so that Peripherals::steal() does not set the global flag, so that doesn't need to be a problem for this PR.

What do you think?

@JalonWong
Copy link
Contributor Author

@adamgreig

Thanks for the PR! We discussed it again in today's meeting and we think it might be best if these new methods were associated functions (i.e., like fn steal() -> Self without requiring &self).

It is sufficient for me.
But this approach makes it easier to steal peripheral instances than my current approach. If you think it's appropriate, I have no objections.

@adamgreig
Copy link
Member

I think this is ready to merge, @rust-embedded/tools any last thoughts?

@thejpster
Copy link
Contributor

Won't clippy complain if an unsafe fn doesn't have a # Safety in the docs?

@burrbull
Copy link
Member

burrbull commented Aug 3, 2023

@adamgreig Maybe you fix docs yourself?

@burrbull
Copy link
Member

burrbull commented Aug 9, 2023

cc @thejpster

@thejpster
Copy link
Contributor

Sorry I'm on vacation at the moment.

@burrbull burrbull added this pull request to the merge queue Aug 15, 2023
Merged via the queue into rust-embedded:master with commit 222be1f Aug 15, 2023
43 checks passed
@JalonWong JalonWong deleted the feature/steal branch August 16, 2023 09:11
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.

5 participants