Safe DMA transfer API: ownership-based framebuffer locking#31
Merged
Conversation
…aces The previous `start_dma_transfer(&self, framebuffer: &FrameBuf<...>)` released the borrow immediately on return, leaving the caller free to write to the buffer while DMA was still reading it — a data race the compiler could not catch. The new design follows the embedded-hal ecosystem pattern (stm32f4xx-hal, nrf-hal, Embedonomicon): `start_dma_transfer` takes the framebuffer by value and returns a `DmaTransfer` token. The buffer is locked inside the token until `DmaTransfer::wait` returns it, making concurrent access a compile error. Changes: - New `DmaTransfer` trait with `is_done(&self)` and `wait(self) -> Buffer` - New `TransferError<FB>` carries the buffer back on failure so it is never lost - `DisplayBackend::start_dma_transfer` now takes `FrameBuf` by value and returns `Result<Self::Transfer, TransferError<FB>>` - `wait_for_dma`, `is_dma_ready`, `present`, `present_region` removed from the trait — waiting/polling moves to `DmaTransfer`, presenting stays in `SwapChain` - `SimulatorBackend` gains `CompletedTransfer<FB>` — an immediately-done token - `SwapChain` restructured with a `FrontState<FB, Xfer>` enum: the front buffer is either `Idle(FrameBuf)` or `InFlight(Transfer)`, preventing any access path to the buffer while DMA is active - `TripleSwapChain` follows the same pattern - All call sites (`dma_rendering_demo.rs`, `present()` etc.) are unchanged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #30.
Summary
DisplayBackend::start_dma_transfernow takesFrameBufby value and returns aDmaTransfertokenDmaTransfer::wait(self)returns it — the compiler makes concurrent access impossibleSwapChainis restructured with aFrontState<FB, Xfer>enum (Idle/InFlight) so the front buffer is always either owned by the swap chain or owned by the transfer token, never bothSimulatorBackendgains aCompletedTransfer<FB>type (immediately done, zero hardware cost)TripleSwapChainfollows the same patternpresent,try_present,get_back_buffer,dma_rendering_demo.rs) are unchangedMigration guide for
DisplayBackendimplementorsBefore:
```rust
fn start_dma_transfer(&mut self, framebuffer: &FrameBuf<Rgb565, FB>) -> Result<(), DisplayError>;
fn wait_for_dma(&mut self);
fn is_dma_ready(&self) -> bool;
```
After:
```rust
type Transfer: DmaTransfer<Buffer = FrameBuf<Rgb565, FB>>;
fn start_dma_transfer(
&mut self,
framebuffer: FrameBuf<Rgb565, FB>, // by value, not by reference
) -> Result<Self::Transfer, TransferError>; // return token, not ()
```
Implement
DmaTransferfor your transfer type:```rust
impl DmaTransfer for MyTransfer {
type Buffer = FrameBuf<Rgb565, MyFB>;
fn is_done(&self) -> bool { /* poll DMA done flag */ }
fn wait(self) -> FrameBuf<Rgb565, MyFB> {
while !self.is_done() {}
self.framebuffer
}
}
```
Implement
Dropon your transfer type to cancel DMA if the token is dropped beforewaitis called.Test plan
cargo testpasses (46 tests without physics, 50 with--features physics)cargo test --features triple-bufferingpassescargo check --example dma_rendering_demo --features stdpasses with no changes to the exampleMade with Cursor