Skip to content

Commit 33abc9a

Browse files
committed
embedded-io-async: clarify cancel safety of Read and Write (#719)
The previous documentation used permissive language ("encouraged", "implementations should document") that left callers uncertain about what happens to data in flight when a future is dropped mid-transfer. Replace the vague encouragement with normative documentation that: - States explicitly the method is NOT cancel-safe by default - Describes the concrete failure mode: bytes already received from hardware FIFO/DMA may be silently discarded with no error returned - Uses SHOULD/MUST language consistent with the rest of the trait docs - Mentions the CancelSafeRead marker trait path for future opt-in - Gives callers actionable guidance (run future to completion) This follows the pattern established by Tokio's cancel-safety documentation and PR #469 ("io: expand docs"), closing the gap that issue #719 identified. Fixes #719 Signed-off-by: aki1770-del <aki1770@gmail.com>
1 parent e6b10a6 commit 33abc9a

1 file changed

Lines changed: 27 additions & 12 deletions

File tree

embedded-io-async/src/lib.rs

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -46,14 +46,24 @@ pub trait Read: ErrorType {
4646
/// If `buf.len() == 0`, `read` returns without waiting, with either `Ok(0)` or an error.
4747
/// The `Ok(0)` doesn't indicate EOF, unlike when called with a non-empty buffer.
4848
///
49-
/// Implementations are encouraged to make this function side-effect-free on cancel (AKA "cancel-safe"), i.e.
50-
/// guarantee that if you cancel (drop) a `read()` future that hasn't completed yet, the stream's
51-
/// state hasn't changed (no bytes have been read).
49+
/// # Cancel Safety
5250
///
53-
/// This is not a requirement to allow implementations that read into the user's buffer straight from
54-
/// the hardware with e.g. DMA.
51+
/// **This method is NOT guaranteed to be cancel-safe.**
5552
///
56-
/// Implementations should document whether they're actually side-effect-free on cancel or not.
53+
/// If a `read()` future is dropped before it completes, bytes that have already been transferred
54+
/// from the hardware FIFO or DMA buffer may be silently discarded — no error is returned, and
55+
/// no indication is given to the caller that data was lost. The next call to `read()` will return
56+
/// bytes received *after* the cancellation point, not the discarded bytes.
57+
///
58+
/// Implementations that are able to guarantee cancel safety (i.e. that dropping a pending future
59+
/// leaves the stream state unchanged — no bytes consumed) SHOULD document this explicitly and
60+
/// MAY implement the `CancelSafeRead` marker trait when it becomes available.
61+
///
62+
/// Implementations that cannot guarantee cancel safety (e.g. those that use DMA to write
63+
/// directly into the caller's buffer) MUST document this limitation.
64+
///
65+
/// Callers that require cancel-safe reads SHOULD use a pattern that ensures the future runs to
66+
/// completion (e.g. a dedicated read task) rather than relying on cancel safety.
5767
async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error>;
5868

5969
/// Read the exact number of bytes required to fill `buf`.
@@ -116,14 +126,19 @@ pub trait Write: ErrorType {
116126
/// If `buf.len() == 0`, `write` returns without waiting, with either `Ok(0)` or an error.
117127
/// The `Ok(0)` doesn't indicate an error.
118128
///
119-
/// Implementations are encouraged to make this function side-effect-free on cancel (AKA "cancel-safe"), i.e.
120-
/// guarantee that if you cancel (drop) a `write()` future that hasn't completed yet, the stream's
121-
/// state hasn't changed (no bytes have been written).
129+
/// # Cancel Safety
130+
///
131+
/// **This method is NOT guaranteed to be cancel-safe.**
132+
///
133+
/// If a `write()` future is dropped before it completes, bytes may have already been submitted
134+
/// to the hardware without the caller's knowledge — no error is returned. The number of bytes
135+
/// actually written is unknown.
122136
///
123-
/// This is not a requirement to allow implementations that write from the user's buffer straight to
124-
/// the hardware with e.g. DMA.
137+
/// Implementations that are able to guarantee cancel safety (i.e. that dropping a pending future
138+
/// leaves the stream state unchanged — no bytes written) SHOULD document this explicitly.
125139
///
126-
/// Implementations should document whether they're actually side-effect-free on cancel or not.
140+
/// Implementations that cannot guarantee cancel safety (e.g. those that use DMA directly from
141+
/// the caller's buffer) MUST document this limitation.
127142
async fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error>;
128143

129144
/// Flush this output stream, ensuring that all intermediately buffered contents reach their destination.

0 commit comments

Comments
 (0)