-
Notifications
You must be signed in to change notification settings - Fork 87
Enforce correct SD state at compilation using a new type #39
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
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.
I like this approach. I think it needs a 'cargo fmt' though (unless it's my phone making a mess of things).
I |
4ec85c9
to
9b2d80e
Compare
I rebased to get rid of the extra commits and removed the "RFC" to reflect that. |
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.
Thanks for your work!
The tests need to be adapted, though.
CS: embedded_hal::digital::v2::OutputPin, | ||
{ | ||
fn drop(&mut self) { | ||
self.deinit() |
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.
At the moment this is fine since deinit
cannot fail.
However, its docs already hint at flushing data if needed during deinit
, which can fail.
The problem is that Drop
cannot return an error so if we would add some data flushing during deinit
, this Drop
implementation would need to panic
on error.
Have you thought about this?
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.
That's a good observation, and I have one example of the same problem in the standard library:
https://doc.rust-lang.org/src/std/io/buffered/bufwriter.rs.html#668
Here, the error is dropped. It's not an amazing solution though, so I have another idea to the same effect: expose the BlockSpi inside a closure instead of in the "guard" pattern. Like this:
let result = sdmmcspi.acquire(|block: BlockSpi| { do_my_stuff() });
In this case, the result from acquire
would contain the result of the final flush, e.g. Result<ClosureRet, FlushFailed>
.
Closures can be nasty to use, so I see this more as an addition to the guard pattern than a replacement.
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.
I have implemented that in 6005533 but it's not actually necessary as long as the SPI device deinit is infallible, so I didn't add that commit to this proposal.
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.
I see. It would need to be documented that the with_block
method flushes, though.
However, the problem would still be there, although mitigated, if deinit
did anything fallible, independently of whether the operations and their flush ran within a closure.
I cannot think of a solution to this, though. We would need rust-lang/rfcs#814
I still think this approach is more ergonomic than the status-quo.
Could you change the documentation of deinit
to say that any flush must be done beforehand and deinit
must always be kept infallible due to this drop?
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.
Updated with more docs. I think now it's a bit split-brain, because I think deinit
should be called manually before drops, but it also should attempt to flush, following the logic that a failed flush attempted always is better then not enforcing any flush at all.
Rebased and fixed tests. |
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.
Ok, looks good to me, thanks!
Any further concerns @thejpster?
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.
LGTM
Oh sorry, one thing - is the README now wrong? Maybe we should compile-check the README... |
Also the Changelog in the README. We can do this separately if you want, as long as we don't leave it too long. |
Pinging @dcz-self - sorry we took so long to get back to you! |
The struct `SdMmcSpi` had two separate methods for initialization and deinitialization. It was up to the user not to mess them up at runtime. A new `BlockSpi` struct takes over `BlockDevice` interface duties, making it impossible to use block procedures while the SD interface is in the wrong state.
Thanks for reminding me. I've updated the readme and the changelog, and rebased. |
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 just made a suggestion to the changelog entry.
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.
Thank you!
The struct
SdMmcSpi
had two separate methods for initialization and deinitialization. It was up to the user not to mess them up at runtime.A new
BlockSpi
struct takes overBlockDevice
interface duties, making it impossible to use block procedures while the SD interface is in the wrong state.I'm not 100% sure which methods should go to which interface, because that doesn't matter a lot for private ones.
I haven't updated the README or examples. I haven't tested how it works with the
Controller
(not interested in the file system right now).The change in the API is breaking, but easy: instead of doing this:
now you do this:
The block device is deinitialized when dropped.