Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 59 additions & 0 deletions lio-test/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,36 @@ macro_rules! test_io_backend {
);
}

#[cfg(unix)]
#[test]
fn pushed_work_is_not_observable_until_flush() {
let mut backend = new_backend();
backend.init(64).unwrap();

let path = unix_socket_path("not-flushed");
let storage = unix_sockaddr_un(&path);

push_op(&mut backend,
90,
Op::Connect {
fd: invalid_fd_resource(),
addr: storage,
},
);

let completed = wait_completions(&mut backend, Some(Duration::ZERO));
assert!(
completed.is_empty(),
"queued work must not become observable before flush()"
);

backend.flush().unwrap();

let completed = wait_completions(&mut backend, Some(Duration::ZERO));
assert_eq!(completed.len(), 1);
assert_exact_result(&completed[0], 90, -(libc::EBADF as isize));
}

#[cfg(unix)]
#[test]
fn flush_can_produce_immediate_completions_without_pending_work() {
Expand Down Expand Up @@ -2585,6 +2615,35 @@ macro_rules! test_io_backend {
);
}

#[test]
fn second_flush_does_not_replay_already_submitted_work() {
let mut backend = new_backend();
backend.init(64).unwrap();

let path = unix_socket_path("flush-idempotent");
let storage = unix_sockaddr_un(&path);

push_op(&mut backend,
63,
Op::Connect {
fd: invalid_fd_resource(),
addr: storage,
},
);
backend.flush().unwrap();
backend.flush().unwrap();

let completed = wait_completions(&mut backend, Some(Duration::ZERO));
assert_eq!(completed.len(), 1);
assert_exact_result(&completed[0], 63, -(libc::EBADF as isize));

let completed = wait_completions(&mut backend, Some(Duration::ZERO));
assert!(
completed.is_empty(),
"a second flush() must not replay already-submitted work"
);
}

#[test]
fn completions_are_not_duplicated_across_waits() {
let mut backend = new_backend();
Expand Down
13 changes: 11 additions & 2 deletions lio/src/backend/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,15 @@ impl OpCompleted {
/// Designed for single-thread ownership (`&mut self`), dyn-compatible for
/// runtime backend selection via `Box<dyn IoBackend>`.
///
/// Contract:
/// - `init()` must be called before `push()`, `flush()`, or `wait()`
/// - `push()` only queues work locally; queued operations are not observable
/// until `flush()` submits them
/// - `flush()` submits all currently queued operations and may also make
/// immediate completions observable on the next `wait()`
/// - `wait()` writes zero or more completions into the caller-provided
/// `completed` vector for that call only
///
/// # Usage
///
/// ```ignore
Expand Down Expand Up @@ -162,8 +171,8 @@ pub trait IoBackend {
/// - `None` = block until at least one completion
/// - `Some(ZERO)` = non-blocking poll
/// - `Some(duration)` = wait up to duration
///
/// Slice valid until next `wait()` or `push()`.
/// - `completed` is caller-owned output storage; implementations may clear
/// and rewrite it on each call
fn wait(
&mut self,
timeout: Option<Duration>,
Expand Down