Skip to content

Commit 1176579

Browse files
Merge #6
6: Add Static versions for the traits r=eldruin a=thalesfragoso This adds and recommends a new stricter variants of the unsafe traits. The new traits also carry an invariant of having a `'static` lifetime, which is usually required in the most common DMA api because of `mem::forget`. This is better than just adding a `+ 'static` to the other traits because it still allows end users to unsafely bail out of the `'static` lifetime if they know what they're doing. CC @Dirbaio Co-authored-by: Thales Fragoso <[email protected]>
2 parents 3498d8d + cb2eec3 commit 1176579

File tree

3 files changed

+176
-1
lines changed

3 files changed

+176
-1
lines changed

.github/workflows/ci.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ jobs:
1010
runs-on: ubuntu-latest
1111
strategy:
1212
matrix:
13-
# All generated code should be running on stable now
1413
rust: [stable]
1514

1615
# The default target we're compiling on and for

.github/workflows/test.yml

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
on:
2+
push:
3+
branches: [ staging, trying, master ]
4+
pull_request:
5+
6+
name: Test Suite
7+
8+
jobs:
9+
ci-linux:
10+
runs-on: ubuntu-latest
11+
strategy:
12+
matrix:
13+
rust: [stable]
14+
15+
include:
16+
# Test MSRV
17+
- rust: 1.37.0
18+
19+
# Test nightly but don't fail
20+
- rust: nightly
21+
experimental: true
22+
23+
steps:
24+
- uses: actions/checkout@v2
25+
- uses: actions-rs/toolchain@v1
26+
with:
27+
profile: minimal
28+
toolchain: ${{ matrix.rust }}
29+
override: true
30+
- uses: actions-rs/cargo@v1
31+
with:
32+
command: test

src/lib.rs

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,19 @@
1919
//! because it would block implementations that can deal with stack allocated buffers, like APIs
2020
//! that use closures to prevent memory corruption.
2121
//!
22+
//! If your API also needs a `'static` bound, prefer the use of [StaticReadBuffer] and
23+
//! [StaticWriteBuffer]. They are a stricter version that requires a `'static` lifetime invariant,
24+
//! while also allowing end users to __unsafely__ bypass it.
25+
//!
26+
//! If you are not sure which version of the traits you should be bounding to in your DMA
27+
//! implementations, prefer the "Static" versions, they are sound for a bigger number of techniques
28+
//! that deal with DMA.
29+
//!
2230
//! The above list is not exhaustive, for a complete set of requirements and guarantees, the
2331
//! documentation of each trait and method should be analyzed.
32+
//!
33+
//! [StaticReadBuffer]: trait.StaticReadBuffer.html
34+
//! [StaticWriteBuffer]: trait.StaticWriteBuffer.html
2435
#![no_std]
2536

2637
use core::{
@@ -244,3 +255,136 @@ dma_target_array_impls!(
244255
unsafe impl<T: WriteTarget> WriteTarget for MaybeUninit<T> {
245256
type Word = T::Word;
246257
}
258+
259+
/// Trait for buffers that can be given to DMA for reading. This is a more strict version of
260+
/// [ReadBuffer](trait.ReadBuffer.html), if you are not sure about which one to use on your safe
261+
/// API, prefer this one. This trait also allows end users to __unsafely__ bypass the `'static`
262+
/// invariant.
263+
///
264+
/// # Safety
265+
///
266+
/// This has the same invariants as [ReadBuffer](trait.ReadBuffer.html) with the additional
267+
/// requirement that the buffer should have a `'static` lifetime.
268+
pub unsafe trait StaticReadBuffer: ReadBuffer {
269+
type Word;
270+
271+
/// Provide a buffer usable for DMA reads.
272+
///
273+
/// The return value is:
274+
///
275+
/// - pointer to the start of the buffer
276+
/// - buffer size in words
277+
///
278+
/// # Safety
279+
///
280+
/// Once this method has been called, it is unsafe to call any `&mut self`
281+
/// methods on this object as long as the returned value is in use (by DMA).
282+
unsafe fn static_read_buffer(&self) -> (*const <Self as StaticReadBuffer>::Word, usize);
283+
}
284+
285+
/// Trait for buffers that can be given to DMA for writing. This is a more strict version of
286+
/// [WriteBuffer](trait.WriteBuffer.html), if you are not sure about which one to use on your safe
287+
/// API, prefer this one. This trait also allows end users to __unsafely__ bypass the `'static`
288+
/// invariant.
289+
///
290+
/// # Safety
291+
///
292+
/// This has the same invariants as [WriteBuffer](trait.WriteBuffer.html) with the additional
293+
/// requirement that the buffer should have a `'static` lifetime.
294+
pub unsafe trait StaticWriteBuffer: WriteBuffer {
295+
type Word;
296+
297+
/// Provide a buffer usable for DMA writes.
298+
///
299+
/// The return value is:
300+
///
301+
/// - pointer to the start of the buffer
302+
/// - buffer size in words
303+
///
304+
/// # Safety
305+
///
306+
/// Once this method has been called, it is unsafe to call any `&mut self`
307+
/// methods, except for `write_buffer`, on this object as long as the
308+
/// returned value is in use (by DMA).
309+
unsafe fn static_write_buffer(&mut self) -> (*mut <Self as StaticWriteBuffer>::Word, usize);
310+
}
311+
312+
unsafe impl<B: ReadBuffer + 'static> StaticReadBuffer for B {
313+
type Word = <Self as ReadBuffer>::Word;
314+
315+
unsafe fn static_read_buffer(&self) -> (*const <Self as StaticReadBuffer>::Word, usize) {
316+
self.read_buffer()
317+
}
318+
}
319+
320+
unsafe impl<B: WriteBuffer + 'static> StaticWriteBuffer for B {
321+
type Word = <Self as WriteBuffer>::Word;
322+
323+
unsafe fn static_write_buffer(&mut self) -> (*mut <Self as StaticWriteBuffer>::Word, usize) {
324+
self.write_buffer()
325+
}
326+
}
327+
328+
#[cfg(test)]
329+
mod tests {
330+
use super::*;
331+
use core::any::Any;
332+
333+
fn api_read<W, B>(buffer: B) -> (*const W, usize)
334+
where
335+
B: ReadBuffer<Word = W>,
336+
{
337+
unsafe { buffer.read_buffer() }
338+
}
339+
340+
fn static_api_read<W, B>(buffer: B) -> (*const W, usize)
341+
where
342+
B: StaticReadBuffer<Word = W>,
343+
{
344+
unsafe { buffer.static_read_buffer() }
345+
}
346+
347+
fn api_write<W, B>(mut buffer: B) -> (*mut W, usize)
348+
where
349+
B: WriteBuffer<Word = W>,
350+
{
351+
unsafe { buffer.write_buffer() }
352+
}
353+
354+
fn static_api_write<W, B>(mut buffer: B) -> (*mut W, usize)
355+
where
356+
B: StaticWriteBuffer<Word = W>,
357+
{
358+
unsafe { buffer.static_write_buffer() }
359+
}
360+
361+
#[test]
362+
fn read_api() {
363+
const SIZE: usize = 128;
364+
static BUF: [u8; SIZE] = [0u8; SIZE];
365+
let local_buf = [0u8; SIZE];
366+
367+
let (ptr, size_local) = api_read(&local_buf);
368+
assert!(unsafe { (&*ptr as &dyn Any).is::<u8>() });
369+
assert!(size_local == SIZE);
370+
371+
let (ptr, size_static) = static_api_read(&BUF);
372+
assert!(unsafe { (&*ptr as &dyn Any).is::<u8>() });
373+
assert!(size_static == SIZE);
374+
}
375+
376+
#[test]
377+
fn write_api() {
378+
const SIZE: usize = 128;
379+
static mut BUF: [u8; SIZE] = [0u8; SIZE];
380+
let mut local_buf = [0u8; SIZE];
381+
382+
let (ptr, size_local) = api_write(&mut local_buf);
383+
assert!(unsafe { (&*ptr as &dyn Any).is::<u8>() });
384+
assert!(size_local == SIZE);
385+
386+
let (ptr, size_static) = static_api_write(unsafe { &mut BUF });
387+
assert!(unsafe { (&*ptr as &dyn Any).is::<u8>() });
388+
assert!(size_static == SIZE);
389+
}
390+
}

0 commit comments

Comments
 (0)