-
Notifications
You must be signed in to change notification settings - Fork 234
[WIP] Add mutex traits #132
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: Rahix <[email protected]>
Thanks for the pull request, and welcome! The Rust team is excited to review your changes, and you should hear from @therealprof (or someone else) soon. If any changes to this PR are deemed necessary, please add them as extra commits. This ensures that the reviewer can see what has changed since they last reviewed the code. Due to the way GitHub handles out-of-date commits, this should also make it reasonably obvious what issues have or haven't been addressed. Large or tricky changes may require several passes of review and changes. Please see the contribution instructions for more information. |
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 good to me! i guess we could provide an implementation for std::sync::Mutex
in a different crate too?
Yes! I think that would belong into |
brilliant, i saw the docs example but hoped i wouldn't need to use it ^_^ still lgtm, @eldruin and @therealprof do either of you have opinions here? if not i'll kick of the merge tomorrow. |
@ryankurte I haven't been able to check it out yet. I was also under the impression this is not meant to go in yet due to the |
@Rahix You mentioned one of the main usecases would be to allow drivers access to a suitable |
@ryankurte I am looking forward to using this on the pcf857x driver. I will do so soon and post here how it goes. I have been too absorbed into finishing the kxcj9 driver and the ad983x driver I just started. Interestingly, I recently read this paper which talks about a similar approach about using closures in a cell-like type and was thinking if that would ease some of the code in the pcf857x driver but did not think about applying this to a mutex. |
Ah, well I added WIP because I first wanted some discussion before finalizing the design. Mostly whether it makes sense to have both Another question is compatibility with frameworks like RTFM. Unfortunately I don't have much experience with RTFM, so I am unsure if a straightforward implementation is possible ...
Yes, most likely similar to this: let driver = driver_crate::Driver::<CortexMMutex<_>>::new(); The same was already happening in What I expect most people will do is aliasing a mutex in the crate root and then refer to it later, so they can easily swap out implementations: use cortex_m::interrupt::Mutex;
// or
use some_other_device::OtherDeviceMutex as Mutex;
// later ...
let driver = driver_crate::Driver::<crate::Mutex<_>>::new();
Sure! I'll cook something up and post it here ... |
I am unfortunately quite busy with other things right now, so I might still take some time until I can continue working on this. In the meantime, could anyone who is familiar with RTFM give some input whether this API choice is compatible with an RTFM implementation of a mutex type that properly handles priorities? |
Hi, this has slipped under my radar - but we have been discussing the same thing within RTFM and maybe try to get it in somewhere. I can give feedback from the RTFM PoV, and I'll link in @perlindgren here as well. We have been discussing this. |
@korken89, that would be great! I have very limited knowledge of the inner workings of RTFM but from what I have seen so far I am quite worried about whether this is possible at all. Can you give more insight? Did you RTFM guys come up with an alternative design which is compatible if this one fails to be? |
Hi, we are meeting to discuss this tomorrow and I'll give an update here after that! :) |
Hi @Rahix, thanks for starting this discussion! We had quite a discussion on this and I will recite it here, but after this I recommend that we talk about this at today's Embedded WG meeting. APIOn the API side, we discussed for quite a while and came to the conclusion that the following things should be changed:
The And here comes the problem with a The next thing we though about is, why should a lock be fallible? We found one implementation of a spin-lock mutex which used a All these questions point to an infallible It boils down to this philosophy: Placement of the trait (which crate)On the point that this PR is to the The issue here is that this Recommended actions
Thanks again to starting this discussion! |
Hi @korken89, thanks a lot for the feedback! I'll join the WG meeting so we can talk more though I'd like to make a few points in advance as well: API
As in fn lock_mut<R>(&self, f: impl FnOnce(&mut T) -> R) -> Result<R, Self::Error>;
// becomes
fn lock_mut<R>(&mut self, f: impl FnOnce(&mut T) -> R) -> Result<R, Self::Error>; ? I see how this would be necessary for implementing the Making the mutex take I don't know much about RTFM but I took some time to read through the docs. From what I gathered, the On the other hand, my initial use-case for the Mutex trait - I feel like we might be talking about two very different concepts of a mutex. And I fear these two aren't easily compatible. Your proposed Maybe we need to step back here and outline what this proposed
Ok, makes sense. I'd like to mention that this will likely make I also have a point which you have not touched yet: Usefulness of
|
@Rahix I think the original proposal with non- |
@ilya-epifanov This does however open up for unsound mutex implementations. |
With respect to the infallible Mutex, what is it supposed to do if someone already holds the Mutex? Panic? That seems undesirable, given that there may be a better approach to resolve the problem. If I call a function on my, say, SD Card block driver, but it can't lock the SPI bus because there's an outstanding transaction in progress on another chip select, I expect the block driver function call to return with a EBUSY error code, telling me I just need to try again later. If the Mutex panics, how does my SD card driver know it's safe to attempt to lock the SPI bus to initiate its next SPI transaction? |
@thejpster I think you have misunderstood the usage, let me clarify. If we take the example implementation linked above what you describe can never happen. If you want a Mutex which you can lock somewhere, and later unlock (not bounded by the scope of a closure) - this cannot be implemented with this kind of mutex, and it is not something we want to be able to do either. I will also add this as one example in the RFC to clarify. |
@thejpster @korken89 So this Mutex is the kind of a quick lock, which is usually implemented on single-core systems as If you need a lock (be it infallible but blocking or non-blocking), you could use this Mutex to implement it on top of your OS/scheduler. |
Well the implementation can be whatever, panicing, spin-waiting, yielding, ..., the important thing is that locking should not be falliable. In the case of RTFM, the behavior is a guaranteed success, and immediate progression without the need of the disable/enable interrupt dance. Instead RTFM filters interrupts using the BASEPRI (on M3 and above), according to the Stack Resource Policy. While the MutexTrait could potentially provide a So as a consequence, MutexTrait lock should be infalliable, and the coordination of resources managed at higher level in the driver/application design to ensure progression (and successfully meeting implicit and explicit timing constraints). |
@korken89, gotcha. Thanks! |
The RFC has now been accepted, and the trait is available in https://github.com/rust-embedded/mutex-trait Should this PR be closed? |
I've finally gotten around to writing a proposal for an
embedded_hal
mutex trait as discussed in #119.Idea
I have found the most generic way for a mutex implementation is to use a closure passed to the
lock
function. A design like thestd
mutex where thelock
function returns a guard is currently not possible because of the way most hardware-crates implement critical sections. This means, with the current proposal, mutex usage would look like this:Mutability
There are two mutex traits available:
RoMutex
andRwMutex
.RoMutex
is not likeRwLock
where multiple read accesses are allowed simultaneously.RoMutex
only allows a single context to gain exclusive read access.RwMutex
is in line with thestd
mutex type.The reason for having this separation is to allow efficient implementations based on both
std
andbare-metal
mutex types. For compatibility, there are two blanket implementations:DefaultRo
: ImplementsRoMutex
for aRwMutex
.RefCellRw
: ImplementsRwMutex
forRoMutex
es using aRefCell
.Mutex Creation
I think, a lot of driver crates which would benefit from this trait will need to create the mutex somewhere inside their initialization routine. For this to be possible, the trait should have a generic
create
ornew
method. I went withcreate
to prevent name conflicts.Because this is shared between
RoMutex
andRwMutex
, I separated it into its own trait:Mutex
. I also made it return aResult
, in case an implementation might fail.Example Implementations
I've written two example implementations for these traits which I included in the documentation. A rendered version can be found here.
Open Questions
Return value of
create()
In the current design, I made
create()
return a result. However, none of the mutex implementations I looked at would actually be able to fail, which makes me unsure if aResult
is really necessary here ...Usefulness of
RoMutex
I am unsure if
RoMutex
is actually useful. I added it for easy compatibility with thebare-metal
mutex but I don't know of any real uses. Maybe I misunderstood the reasoning for the design inbare-metal
?Docs
I added
cortex-m
as a dev-dependency for a doc-test. Is this acceptable or should I rather use a dummy implementation?cc @eldruin, @therealprof