commonware_runtime/iouring/mod.rs
1//! Task execution and I/O on Linux io_uring.
2//!
3//! [`Runner`] polls ordinary tasks and drives their I/O on a pool of
4//! [`Config::with_worker_threads`] workers, two by default, each with its own
5//! io_uring ring. The first worker runs on the calling thread and also polls
6//! the root.
7//!
8//! A task woken while idle is first queued on the pool worker that woke it,
9//! and one woken during its poll is first requeued on its poller. Tasks
10//! spawned or woken from outside the pool, and tasks spawned on a worker with
11//! other work queued when the pool has more than one worker, go to the global
12//! queue that any worker takes from.
13//!
14//! Each worker has a bounded run queue. A push into a full run queue sends its
15//! newest half and the new task to the global queue, or only the new task
16//! while another worker is stealing from it. Before parking, a worker with
17//! nothing to run can steal half of another worker's queued tasks, rounded
18//! down, so a task queued alone behind a long poll waits for it.
19//!
20//! Dedicated and blocking tasks each receive a supervised thread and io_uring
21//! ring. Their ordinary descendants execute on the pool. Task factories run
22//! synchronously on the thread that calls [`crate::Spawner::spawn`].
23//!
24//! Sockets, blobs, and pending I/O and sleep futures can move between workers.
25//! Registrations stay on their original worker without keeping it alive. If it
26//! closes, pending I/O futures return errors and pending sleeps panic when polled.
27//! [`crate::Blob::start_sync`] handles can be awaited on another thread, even after
28//! the original runner shuts down.
29//!
30//! # Ownership
31//!
32//! Workers own request, timer, and result progress. Each ordinary task has at
33//! most one runnable, which a queue or poller owns, and at most one poll of
34//! that task runs at a time. A queued task can move through overflow or
35//! stealing, and each run queue synchronizes its owner with the workers that
36//! steal from it. Each open serializes its syncs and durable writes across
37//! workers until their results are recorded. Forwarded results, mailboxes, the
38//! global queue, the task set, task handles, supervision, and metrics are also
39//! synchronized across threads.
40//!
41//! # Storage
42//!
43//! Failures while synchronizing blob contents, including during
44//! [SYNC](crate::WriteOptions::SYNC) writes, are retained across opens of the
45//! blob, even after every handle is dropped. Creation failures are retained
46//! when the header is complete. Removing or recreating the blob clears its
47//! retained error. A new runtime instance starts without the error record and
48//! still requires normal storage recovery.
49//!
50//! # Requirements and Progress
51//!
52//! Linux 6.1 or newer is required for single-issuer rings with deferred task
53//! work. Task polls must return so their worker can service I/O and deadlines.
54//! I/O and sleeps stay registered on the worker that first polled them, so a
55//! poll that blocks one worker also delays their results for tasks that have
56//! moved elsewhere.
57//!
58//! Each worker limits in-flight I/O according to [`RingConfig::size`]. A receive
59//! can occupy the last slot while a send needed to satisfy it waits to be
60//! submitted. Use deadlines or cancellation to break such dependencies.
61//!
62//! # Shutdown
63//!
64//! On shutdown, the runner rejects new tasks and I/O, aborts supervised tasks,
65//! and drains registered I/O before returning. The first poll registers an I/O
66//! request with its worker. Registered storage writes and syncs run to completion
67//! even if their futures are dropped while still queued. For pending reads and
68//! network operations, dropping the future or closing the worker requests cancellation.
69//!
70//! Shutdown waits for all workers to finish runtime cleanup and failure publication,
71//! with no timeout. No pool worker closes its mailbox or io_uring ring until
72//! every pool task's future has been dropped, though dedicated and blocking
73//! tasks may still run and find a pool worker closed. Native thread-local
74//! destructors may run after the runner returns.
75//!
76//! # Examples
77//!
78//! ```no_run
79//! use commonware_runtime::{Runner as _, Spawner, Supervisor, iouring};
80//!
81//! iouring::Runner::default().start(|context| async move {
82//! let child = context.child("worker").spawn(|_| async { 42 });
83//! assert_eq!(child.await.unwrap(), 42);
84//! });
85//! ```
86
87mod driver;
88mod idle;
89mod mailbox;
90pub(crate) mod operation;
91mod pool;
92mod queue;
93mod registration;
94pub(crate) mod request;
95mod runtime;
96mod slab;
97mod sleep;
98pub(crate) mod sockaddr;
99mod spinner;
100mod task;
101mod tasks;
102mod timeout;
103mod waiter;
104mod waker;
105
106pub use runtime::{Config, Context, RingConfig, Runner};
107pub use spinner::Config as SpinnerConfig;