1use super::threadpool::Io;
7use super::threadpool::IoInitiator;
8use futures::FutureExt;
9use io_uring::opcode;
10use io_uring::squeue;
11use io_uring::types::TimeoutFlags;
12use io_uring::types::Timespec;
13use pal_async::fd::FdReadyDriver;
14use pal_async::fd::PollFdReady;
15use pal_async::interest::InterestSlot;
16use pal_async::interest::PollEvents;
17use pal_async::interest::SLOT_COUNT;
18use pal_async::timer::Instant;
19use pal_async::timer::PollTimer;
20use pal_async::timer::TimerDriver;
21use pal_async::wait::MAXIMUM_WAIT_READ_SIZE;
22use pal_async::wait::PollWait;
23use pal_async::wait::WaitDriver;
24use std::fmt::Debug;
25use std::io;
26use std::os::unix::prelude::*;
27use std::sync::OnceLock;
28use std::task::Context;
29use std::task::Poll;
30use std::task::Waker;
31
32pub trait Initiate: 'static + Send + Sync + Unpin {
35 fn initiator(&self) -> &IoInitiator;
40}
41
42impl Initiate for IoInitiator {
43 fn initiator(&self) -> &IoInitiator {
44 self
45 }
46}
47
48#[derive(Debug)]
50pub struct FdReady<T: Initiate> {
51 fd: RawFd,
52 initiator: T,
53 interests: [Interest; SLOT_COUNT],
54}
55
56impl<T: Initiate> FdReady<T> {
57 pub fn new(initiator: T, fd: RawFd) -> Self {
59 FdReady {
60 fd,
61 initiator,
62 interests: Default::default(),
63 }
64 }
65}
66
67impl FdReadyDriver for IoInitiator {
68 type FdReady = FdReady<Self>;
69
70 fn new_fd_ready(&self, fd: RawFd) -> io::Result<Self::FdReady> {
71 Ok(FdReady::new(self.clone(), fd))
72 }
73}
74
75#[derive(Debug, Default)]
76struct Interest {
77 io: Option<Io<()>>,
78 cancelled: bool,
79 events: PollEvents,
80 revents: PollEvents,
81}
82
83impl<T: Initiate> PollFdReady for FdReady<T> {
84 fn poll_fd_ready(
85 &mut self,
86 cx: &mut Context<'_>,
87 slot: InterestSlot,
88 events: PollEvents,
89 ) -> Poll<PollEvents> {
90 let interest = &mut self.interests[slot as usize];
91 loop {
92 if !(interest.revents & events).is_empty() {
93 break Poll::Ready(interest.revents & events);
94 } else if let Some(io) = &mut interest.io {
95 if interest.events & events != events && !interest.cancelled {
101 io.cancel_poll();
102 interest.cancelled = true;
103 }
104 let result = std::task::ready!(io.poll_unpin(cx));
105 interest.io = None;
106 match result {
107 Ok(poll_revents) => {
108 interest.revents |= PollEvents::from_poll_events(poll_revents as i16);
109 }
110 Err(err) if err.raw_os_error() == Some(libc::ECANCELED) => {}
111 Err(err) => panic!("poll failed: {}", err),
112 }
113 } else {
114 interest.events = events;
115 let sqe = opcode::PollAdd::new(
116 io_uring::types::Fd(self.fd),
117 events.to_poll_events() as u32,
118 )
119 .build();
120 let io = unsafe { Io::new(self.initiator.initiator().clone(), sqe, ()) };
123 interest.io = Some(io);
124 interest.cancelled = false;
125 }
126 }
127 }
128
129 fn clear_fd_ready(&mut self, slot: InterestSlot) {
130 let interest = &mut self.interests[slot as usize];
131 interest.revents = PollEvents::EMPTY;
132 }
133}
134
135#[derive(Debug)]
137pub struct FdWait<T: Initiate> {
138 inner: FdWaitInner<T>,
139}
140
141#[derive(Debug)]
142enum FdWaitInner<T: Initiate> {
143 ViaPoll(pal_async::unix::FdWait<FdReady<T>>),
144 ViaRead(FdWaitViaRead<T>),
145}
146
147impl WaitDriver for IoInitiator {
148 type Wait = FdWait<Self>;
149
150 fn new_wait(&self, fd: RawFd, read_size: usize) -> io::Result<Self::Wait> {
151 Ok(FdWait::new(self.clone(), fd, read_size))
152 }
153}
154
155impl<T: Initiate> FdWait<T> {
156 pub fn new(initiator: T, fd: RawFd, read_size: usize) -> Self {
158 static SUPPORTS_NONBLOCK_READ: OnceLock<bool> = OnceLock::new();
159 const LINKAT: u8 = 39;
163 let supports_nonblock_read =
164 *SUPPORTS_NONBLOCK_READ.get_or_init(|| initiator.initiator().probe(LINKAT));
165
166 let inner = if supports_nonblock_read {
167 assert!(read_size <= MAXIMUM_WAIT_READ_SIZE);
168 FdWaitInner::ViaRead(FdWaitViaRead {
169 fd,
170 read_size,
171 initiator,
172 state: FdWaitViaReadState::Idle(Box::new(0)),
173 })
174 } else {
175 FdWaitInner::ViaPoll(pal_async::unix::FdWait::new(
176 fd,
177 FdReady::new(initiator, fd),
178 read_size,
179 ))
180 };
181 FdWait { inner }
182 }
183}
184
185impl<T: Initiate> PollWait for FdWait<T> {
186 fn poll_wait(&mut self, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
187 match &mut self.inner {
188 FdWaitInner::ViaPoll(wait) => wait.poll_wait(cx),
189 FdWaitInner::ViaRead(wait) => wait.poll_wait(cx),
190 }
191 }
192
193 fn poll_cancel_wait(&mut self, cx: &mut Context<'_>) -> Poll<bool> {
194 match &mut self.inner {
195 FdWaitInner::ViaPoll(wait) => wait.poll_cancel_wait(cx),
196 FdWaitInner::ViaRead(wait) => wait.poll_cancel_wait(cx),
197 }
198 }
199}
200
201#[derive(Debug)]
202struct FdWaitViaRead<T: Initiate> {
203 fd: RawFd,
204 read_size: usize,
205 initiator: T,
206 state: FdWaitViaReadState,
207}
208
209#[derive(Debug)]
210enum FdWaitViaReadState {
211 Idle(Box<u64>),
212 ReadPending { io: Io<Box<u64>>, cancelling: bool },
213 Invalid,
214}
215
216impl<T: Initiate> PollWait for FdWaitViaRead<T> {
217 fn poll_wait(&mut self, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
218 loop {
219 match std::mem::replace(&mut self.state, FdWaitViaReadState::Invalid) {
220 FdWaitViaReadState::Idle(mut buf) => {
221 assert!(self.read_size <= 8);
222 let sqe = opcode::Read::new(
223 io_uring::types::Fd(self.fd),
224 std::ptr::from_mut(&mut *buf).cast(),
225 self.read_size as u32,
226 )
227 .build();
228 let io = unsafe { Io::new(self.initiator.initiator().clone(), sqe, buf) };
231 self.state = FdWaitViaReadState::ReadPending {
232 io,
233 cancelling: false,
234 };
235 }
236 FdWaitViaReadState::ReadPending { mut io, cancelling } => match io.poll_unpin(cx) {
237 Poll::Ready(r) => {
238 self.state = FdWaitViaReadState::Idle(io.into_mem());
239 match r {
240 Ok(_) => break Poll::Ready(Ok(())),
241 Err(err) if err.raw_os_error() == Some(libc::ECANCELED) => {}
242 Err(err) => return Poll::Ready(Err(err)),
243 }
244 }
245 Poll::Pending => {
246 self.state = FdWaitViaReadState::ReadPending { io, cancelling };
247 return Poll::Pending;
248 }
249 },
250 FdWaitViaReadState::Invalid => unreachable!(),
251 }
252 }
253 }
254
255 fn poll_cancel_wait(&mut self, cx: &mut Context<'_>) -> Poll<bool> {
256 loop {
257 match std::mem::replace(&mut self.state, FdWaitViaReadState::Invalid) {
258 FdWaitViaReadState::Idle(buf) => {
259 self.state = FdWaitViaReadState::Idle(buf);
260 break Poll::Ready(false);
261 }
262 FdWaitViaReadState::ReadPending { mut io, cancelling } => {
263 if cancelling {
264 match io.poll_unpin(cx) {
265 Poll::Ready(r) => {
266 self.state = FdWaitViaReadState::Idle(io.into_mem());
267 break Poll::Ready(r.is_ok());
274 }
275 Poll::Pending => {
276 self.state = FdWaitViaReadState::ReadPending { io, cancelling };
277 break Poll::Pending;
278 }
279 }
280 } else {
281 io.cancel();
282 self.state = FdWaitViaReadState::ReadPending {
283 io,
284 cancelling: true,
285 };
286 }
287 }
288 FdWaitViaReadState::Invalid => unreachable!(),
289 }
290 }
291 }
292}
293
294impl<T: Initiate> Drop for FdWaitViaRead<T> {
295 fn drop(&mut self) {
296 let _ = self.poll_cancel_wait(&mut Context::from_waker(Waker::noop()));
297 }
298}
299
300#[derive(Debug)]
302pub struct Timer<T: Initiate> {
303 initiator: T,
304 target_deadline: Instant,
305 state: Option<TimerState>,
306}
307
308impl<T: Initiate> Timer<T> {
309 pub fn new(initiator: T) -> Self {
311 Timer {
312 initiator,
313 target_deadline: Instant::from_nanos(0),
314 state: None,
315 }
316 }
317}
318
319#[derive(Debug)]
320struct TimerState {
321 io: Io<Box<Timespec>>,
322 cancelled: bool,
323}
324
325impl TimerDriver for IoInitiator {
326 type Timer = Timer<Self>;
327
328 fn new_timer(&self) -> Self::Timer {
329 Timer::new(self.clone())
330 }
331}
332
333impl pal_async::io_uring::IoUringSubmit for IoInitiator {
334 fn probe(&self, opcode: u8) -> bool {
335 self.probe(opcode)
336 }
337
338 async unsafe fn submit(&self, sqe: squeue::Entry) -> io::Result<i32> {
339 unsafe { self.issue_io((), |_| sqe).await.0 }
342 }
343}
344
345impl pal_async::io_uring::IoUringDriver for IoInitiator {
346 type Submitter = Self;
347
348 fn io_uring_submitter(&self) -> Option<&Self> {
349 Some(self)
350 }
351}
352
353impl<T: Initiate> PollTimer for Timer<T> {
354 fn poll_timer(&mut self, cx: &mut Context<'_>, deadline: Option<Instant>) -> Poll<Instant> {
355 if let Some(deadline) = deadline {
356 self.set_deadline(deadline);
357 }
358 loop {
359 let now = Instant::now();
360 if self.target_deadline <= now {
361 break Poll::Ready(now);
362 } else if let Some(state) = &mut self.state {
363 let _ = std::task::ready!(state.io.poll_unpin(cx));
364 self.state = None;
365 } else {
366 let absolute_timeout = self.target_deadline - Instant::from_nanos(0);
369 let timespec = Box::new(
370 Timespec::new()
371 .sec(absolute_timeout.as_secs())
372 .nsec(absolute_timeout.subsec_nanos()),
373 );
374 let sqe = {
375 opcode::Timeout::new(&*timespec)
376 .flags(TimeoutFlags::ABS)
377 .build()
378 };
379 let io = unsafe { Io::new(self.initiator.initiator().clone(), sqe, timespec) };
382 let state = TimerState {
383 io,
384 cancelled: false,
385 };
386 self.state = Some(state);
387 }
388 }
389 }
390
391 fn set_deadline(&mut self, deadline: Instant) {
392 if let Some(state) = &mut self.state {
393 if self.target_deadline > deadline && !state.cancelled {
399 state.io.cancel_timeout();
400 state.cancelled = true;
401 }
402 }
403 self.target_deadline = deadline;
404 }
405}
406
407#[cfg(test)]
408pub(crate) mod tests {
409 use crate::IoInitiator;
410 use crate::IoUringPool;
411 use futures::executor::block_on;
412 use once_cell::sync::OnceCell;
413 use pal_async::executor_tests;
414 use pal_async::task::Spawn;
415 use std::future::Future;
416 use std::io;
417 use std::thread::JoinHandle;
418
419 pub struct SingleThreadPool {
420 _thread: JoinHandle<()>,
421 initiator: IoInitiator,
422 }
423
424 impl SingleThreadPool {
425 pub fn new() -> io::Result<Self> {
426 let pool = IoUringPool::new("test", 16)?;
427 let initiator = pool.client().initiator().clone();
428 let thread = std::thread::spawn(move || pool.run());
429 Ok(Self {
430 _thread: thread,
431 initiator,
432 })
433 }
434
435 pub fn initiator(&self) -> &IoInitiator {
436 &self.initiator
437 }
438 }
439
440 fn test_pool() -> io::Result<&'static SingleThreadPool> {
441 static POOL: OnceCell<SingleThreadPool> = OnceCell::new();
443 POOL.get_or_try_init(SingleThreadPool::new)
444 }
445
446 macro_rules! get_pool_or_skip {
447 () => {
448 match test_pool() {
449 Ok(pool) => pool,
450 Err(err) if err.raw_os_error() == Some(libc::ENOSYS) => {
451 println!("Test case skipped (no IO-Uring support)");
452 return;
453 }
454 Err(err) => panic!("{}", err),
455 }
456 };
457 }
458
459 fn run_until<F>(pool: &SingleThreadPool, fut: F) -> F::Output
460 where
461 F: 'static + Future + Send,
462 F::Output: Send,
463 {
464 block_on(pool.initiator().spawn("test", fut))
465 }
466
467 #[test]
468 fn waker_works() {
469 run_until(get_pool_or_skip!(), executor_tests::waker_tests());
470 }
471
472 #[test]
473 fn spawn_works() {
474 let pool = get_pool_or_skip!();
475 executor_tests::spawn_tests(|| (pool.initiator(), || ()))
476 }
477
478 #[test]
479 fn sleep_works() {
480 let pool = get_pool_or_skip!();
481 run_until(pool, executor_tests::sleep_tests(pool.initiator().clone()))
482 }
483
484 #[test]
485 fn wait_works() {
486 let pool = get_pool_or_skip!();
487 run_until(pool, executor_tests::wait_tests(pool.initiator().clone()))
488 }
489
490 #[test]
491 fn socket_works() {
492 let pool = get_pool_or_skip!();
493 run_until(pool, executor_tests::socket_tests(pool.initiator().clone()))
494 }
495
496 #[test]
497 fn uring_works() {
498 let pool = get_pool_or_skip!();
499 run_until(
500 pool,
501 executor_tests::io_uring_tests::uring_tests(pool.initiator().clone()),
502 )
503 }
504}