1use std::convert::TryInto;
47use std::ffi::CStr;
48use std::fs::File;
49use std::future::Future;
50use std::io;
51use std::mem;
52use std::mem::MaybeUninit;
53use std::os::unix::io::FromRawFd;
54use std::os::unix::io::RawFd;
55use std::pin::Pin;
56use std::sync::Arc;
57use std::sync::LazyLock;
58use std::sync::Weak;
59use std::task::Context;
60use std::task::Poll;
61use std::task::Waker;
62use std::thread;
63use std::thread::ThreadId;
64
65use base::trace;
66use base::warn;
67use base::AsRawDescriptor;
68use base::EventType;
69use base::IoBufMut;
70use base::RawDescriptor;
71use io_uring::URingAllowlist;
72use io_uring::URingContext;
73use io_uring::URingOperation;
74use remain::sorted;
75use slab::Slab;
76use sync::Mutex;
77use thiserror::Error as ThisError;
78
79use crate::common_executor::RawExecutor;
80use crate::common_executor::RawTaskHandle;
81use crate::common_executor::Reactor;
82use crate::mem::BackingMemory;
83use crate::waker::WakerToken;
84use crate::waker::WeakWake;
85use crate::AsyncError;
86use crate::AsyncResult;
87use crate::IoSource;
88use crate::MemRegion;
89use crate::TaskHandle;
90
91#[sorted]
92#[derive(Debug, ThisError)]
93pub enum Error {
94 #[error("Error creating the fd waiting context: {0}")]
96 CreatingContext(io_uring::Error),
97 #[error("Failed to discard a block: {0}")]
99 Discard(base::Error),
100 #[error("Failed to copy the FD for the polling context: {0}")]
102 DuplicatingFd(base::Error),
103 #[error("Error enabling the URing context: {0}")]
105 EnablingContext(io_uring::Error),
106 #[error("The executor is gone")]
108 ExecutorGone,
109 #[error("Invalid offset/len for getting an iovec")]
111 InvalidOffset,
112 #[error("Invalid source, FD not registered for use")]
114 InvalidSource,
115 #[error("Error during IO: {0}")]
117 Io(io::Error),
118 #[error("Error registering restrictions to the URing context: {0}")]
120 RegisteringURingRestriction(io_uring::Error),
121 #[error("Error removing from the URing context: {0}")]
123 RemovingWaker(io_uring::Error),
124 #[error("Error adding to the URing context: {0}")]
126 SubmittingOp(io_uring::Error),
127 #[error("URingContext failure: {0}")]
129 URingContextError(io_uring::Error),
130 #[error("URing::enter: {0}")]
132 URingEnter(io_uring::Error),
133}
134pub type Result<T> = std::result::Result<T, Error>;
135
136impl From<Error> for io::Error {
137 fn from(e: Error) -> Self {
138 use Error::*;
139 match e {
140 Discard(e) => e.into(),
141 DuplicatingFd(e) => e.into(),
142 ExecutorGone => io::Error::other(ExecutorGone),
143 InvalidOffset => io::Error::new(io::ErrorKind::InvalidInput, InvalidOffset),
144 InvalidSource => io::Error::new(io::ErrorKind::InvalidData, InvalidSource),
145 Io(e) => e,
146 CreatingContext(e) => e.into(),
147 RemovingWaker(e) => e.into(),
148 SubmittingOp(e) => e.into(),
149 URingContextError(e) => e.into(),
150 URingEnter(e) => e.into(),
151 EnablingContext(e) => e.into(),
152 RegisteringURingRestriction(e) => e.into(),
153 }
154 }
155}
156
157impl From<Error> for AsyncError {
158 fn from(e: Error) -> AsyncError {
159 AsyncError::SysVariants(e.into())
160 }
161}
162
163static IS_URING_STABLE: LazyLock<bool> = LazyLock::new(|| {
164 let mut utsname = MaybeUninit::zeroed();
165
166 let res = unsafe { libc::uname(utsname.as_mut_ptr()) };
169 if res < 0 {
170 return false;
171 }
172
173 let utsname = unsafe { utsname.assume_init() };
176
177 let release = unsafe { CStr::from_ptr(utsname.release.as_ptr()) };
180
181 let mut components = match release.to_str().map(|r| r.split('.').map(str::parse)) {
182 Ok(c) => c,
183 Err(_) => return false,
184 };
185
186 match (components.next(), components.next()) {
188 (Some(Ok(major)), Some(Ok(minor))) if (major, minor) >= (5, 10) => {
189 URingContext::new(8, None).is_ok()
191 }
192 _ => false,
193 }
194});
195
196pub fn is_uring_stable() -> bool {
200 *IS_URING_STABLE
201}
202
203pub(crate) fn check_uring_availability() -> Result<()> {
207 URingContext::new(8, None)
208 .map(drop)
209 .map_err(Error::URingContextError)
210}
211
212pub struct RegisteredSource {
213 tag: usize,
214 ex: Weak<RawExecutor<UringReactor>>,
215}
216
217impl RegisteredSource {
218 pub fn start_read_to_mem(
219 &self,
220 file_offset: Option<u64>,
221 mem: Arc<dyn BackingMemory + Send + Sync>,
222 addrs: impl IntoIterator<Item = MemRegion>,
223 ) -> Result<PendingOperation> {
224 let ex = self.ex.upgrade().ok_or(Error::ExecutorGone)?;
225 let token = ex
226 .reactor
227 .submit_read_to_vectored(self, mem, file_offset, addrs)?;
228
229 Ok(PendingOperation {
230 waker_token: Some(token),
231 ex: self.ex.clone(),
232 submitted: false,
233 })
234 }
235
236 pub fn start_write_from_mem(
237 &self,
238 file_offset: Option<u64>,
239 mem: Arc<dyn BackingMemory + Send + Sync>,
240 addrs: impl IntoIterator<Item = MemRegion>,
241 ) -> Result<PendingOperation> {
242 let ex = self.ex.upgrade().ok_or(Error::ExecutorGone)?;
243 let token = ex
244 .reactor
245 .submit_write_from_vectored(self, mem, file_offset, addrs)?;
246
247 Ok(PendingOperation {
248 waker_token: Some(token),
249 ex: self.ex.clone(),
250 submitted: false,
251 })
252 }
253
254 pub fn start_fallocate(&self, offset: u64, len: u64, mode: u32) -> Result<PendingOperation> {
255 let ex = self.ex.upgrade().ok_or(Error::ExecutorGone)?;
256 let token = ex.reactor.submit_fallocate(self, offset, len, mode)?;
257
258 Ok(PendingOperation {
259 waker_token: Some(token),
260 ex: self.ex.clone(),
261 submitted: false,
262 })
263 }
264
265 pub fn start_fsync(&self) -> Result<PendingOperation> {
266 let ex = self.ex.upgrade().ok_or(Error::ExecutorGone)?;
267 let token = ex.reactor.submit_fsync(self)?;
268
269 Ok(PendingOperation {
270 waker_token: Some(token),
271 ex: self.ex.clone(),
272 submitted: false,
273 })
274 }
275
276 pub fn poll_fd_readable(&self) -> Result<PendingOperation> {
277 let events = EventType::Read;
278
279 let ex = self.ex.upgrade().ok_or(Error::ExecutorGone)?;
280 let token = ex.reactor.submit_poll(self, events)?;
281
282 Ok(PendingOperation {
283 waker_token: Some(token),
284 ex: self.ex.clone(),
285 submitted: false,
286 })
287 }
288}
289
290impl Drop for RegisteredSource {
291 fn drop(&mut self) {
292 if let Some(ex) = self.ex.upgrade() {
293 ex.reactor.deregister_source(self);
294 }
295 }
296}
297
298const NUM_ENTRIES: usize = 256;
300
301struct OpData {
303 _file: Arc<File>,
304 _mem: Option<Arc<dyn BackingMemory + Send + Sync>>,
305 waker: Option<Waker>,
306 canceled: bool,
307}
308
309enum OpStatus {
311 Nop,
312 Pending(OpData),
313 Completed(Option<::std::io::Result<u32>>),
314}
315
316struct Ring {
317 ops: Slab<OpStatus>,
318 registered_sources: Slab<Arc<File>>,
319}
320
321pub struct UringReactor {
323 ctx: URingContext,
326 ring: Mutex<Ring>,
327 thread_id: Mutex<Option<ThreadId>>,
328}
329
330impl UringReactor {
331 fn new() -> Result<UringReactor> {
332 let mut restrictions = URingAllowlist::new();
334 let ops = [
335 URingOperation::Writev,
336 URingOperation::Readv,
337 URingOperation::Write,
338 URingOperation::Read,
339 URingOperation::Nop,
340 URingOperation::Fsync,
341 URingOperation::Fallocate,
342 URingOperation::PollAdd,
343 URingOperation::PollRemove,
344 URingOperation::AsyncCancel,
345 ];
346 for op in ops {
347 restrictions.allow_submit_operation(op);
348 }
349
350 let ctx =
351 URingContext::new(NUM_ENTRIES, Some(&restrictions)).map_err(Error::CreatingContext)?;
352
353 Ok(UringReactor {
354 ctx,
355 ring: Mutex::new(Ring {
356 ops: Slab::with_capacity(NUM_ENTRIES),
357 registered_sources: Slab::with_capacity(NUM_ENTRIES),
358 }),
359 thread_id: Mutex::new(None),
360 })
361 }
362
363 fn runs_tasks_on_current_thread(&self) -> bool {
364 let executor_thread = self.thread_id.lock();
365 executor_thread
366 .map(|id| id == thread::current().id())
367 .unwrap_or(false)
368 }
369
370 fn get_result(&self, token: &WakerToken, cx: &mut Context) -> Option<io::Result<u32>> {
371 let mut ring = self.ring.lock();
372
373 let op = ring
374 .ops
375 .get_mut(token.0)
376 .expect("`get_result` called on unknown operation");
377 match op {
378 OpStatus::Nop => panic!("`get_result` called on nop"),
379 OpStatus::Pending(data) => {
380 if data.canceled {
381 panic!("`get_result` called on canceled operation");
382 }
383 data.waker = Some(cx.waker().clone());
384 None
385 }
386 OpStatus::Completed(res) => {
387 let out = res.take();
388 ring.ops.remove(token.0);
389 Some(out.expect("Missing result in completed operation"))
390 }
391 }
392 }
393
394 fn cancel_operation(&self, token: WakerToken) {
396 let mut ring = self.ring.lock();
397 let submit_cancel = if let Some(op) = ring.ops.get_mut(token.0) {
398 match op {
399 OpStatus::Nop => panic!("`cancel_operation` called on nop"),
400 OpStatus::Pending(data) => {
401 if data.canceled {
402 panic!("uring operation canceled more than once");
403 }
404
405 if let Some(waker) = data.waker.take() {
406 waker.wake();
407 }
408 data.waker = None;
410 data.canceled = true;
411
412 true
416 }
417 OpStatus::Completed(_) => {
418 ring.ops.remove(token.0);
419 false
420 }
421 }
422 } else {
423 false
424 };
425 std::mem::drop(ring);
426 if submit_cancel {
427 let _best_effort = self.submit_cancel_async(token.0);
428 }
429 }
430
431 pub(crate) fn register_source<F: AsRawDescriptor>(
432 &self,
433 raw: &Arc<RawExecutor<UringReactor>>,
434 fd: &F,
435 ) -> Result<RegisteredSource> {
436 let duped_fd = unsafe { File::from_raw_fd(dup_fd(fd.as_raw_descriptor())?) };
440
441 Ok(RegisteredSource {
442 tag: self
443 .ring
444 .lock()
445 .registered_sources
446 .insert(Arc::new(duped_fd)),
447 ex: Arc::downgrade(raw),
448 })
449 }
450
451 fn deregister_source(&self, source: &RegisteredSource) {
452 self.ring.lock().registered_sources.remove(source.tag);
456 }
457
458 fn submit_op<F>(
462 &self,
463 source: &RegisteredSource,
464 mem: Option<Arc<dyn BackingMemory + Send + Sync>>,
465 add_op: F,
466 ) -> Result<WakerToken>
467 where
468 F: FnOnce(RawDescriptor, u64) -> std::result::Result<(), io_uring::Error>,
469 {
470 let mut ring = self.ring.lock();
471 let src = ring
472 .registered_sources
473 .get(source.tag)
474 .ok_or(Error::InvalidSource)?
475 .clone();
476
477 let entry = ring.ops.vacant_entry();
478 let next_op_token = entry.key();
479
480 add_op(src.as_raw_descriptor(), usize_to_u64(next_op_token))
481 .map_err(Error::SubmittingOp)?;
482
483 entry.insert(OpStatus::Pending(OpData {
484 _file: src,
485 _mem: mem,
486 waker: None,
487 canceled: false,
488 }));
489
490 Ok(WakerToken(next_op_token))
491 }
492
493 fn submit_poll(
494 &self,
495 source: &RegisteredSource,
496 events: base::EventType,
497 ) -> Result<WakerToken> {
498 self.submit_op(source, None, |fd, token| {
499 self.ctx.add_poll_fd(fd, events, token)
500 })
501 }
502
503 fn submit_fallocate(
504 &self,
505 source: &RegisteredSource,
506 offset: u64,
507 len: u64,
508 mode: u32,
509 ) -> Result<WakerToken> {
510 self.submit_op(source, None, |fd, token| {
511 self.ctx.add_fallocate(fd, offset, len, mode, token)
512 })
513 }
514
515 fn submit_cancel_async(&self, token: usize) -> Result<WakerToken> {
516 let mut ring = self.ring.lock();
517 let entry = ring.ops.vacant_entry();
518 let next_op_token = entry.key();
519 self.ctx
520 .async_cancel(usize_to_u64(token), usize_to_u64(next_op_token))
521 .map_err(Error::SubmittingOp)?;
522
523 entry.insert(OpStatus::Nop);
524
525 Ok(WakerToken(next_op_token))
526 }
527
528 fn submit_fsync(&self, source: &RegisteredSource) -> Result<WakerToken> {
529 self.submit_op(source, None, |fd, token| self.ctx.add_fsync(fd, token))
530 }
531
532 fn submit_read_to_vectored(
533 &self,
534 source: &RegisteredSource,
535 mem: Arc<dyn BackingMemory + Send + Sync>,
536 offset: Option<u64>,
537 addrs: impl IntoIterator<Item = MemRegion>,
538 ) -> Result<WakerToken> {
539 let mut addrs = addrs.into_iter().peekable();
540 let first = addrs.next();
541
542 if let (Some(region), None) = (first, addrs.peek()) {
545 let (ptr, size) = {
546 let vslice = mem
547 .get_volatile_slice(region)
548 .map_err(|_| Error::InvalidOffset)?;
549 (vslice.as_mut_ptr(), vslice.size())
550 };
551 if let Ok(len) = u32::try_from(size) {
553 return self.submit_op(source, Some(mem), |fd, token| {
554 unsafe { self.ctx.add_read(ptr, len, fd, offset, token) }
559 });
560 }
561 }
562
563 let iovecs = first
564 .into_iter()
565 .chain(addrs)
566 .map(|mem_range| {
567 let vslice = mem
568 .get_volatile_slice(mem_range)
569 .map_err(|_| Error::InvalidOffset)?;
570 Ok(unsafe { IoBufMut::from_raw_parts(vslice.as_mut_ptr(), vslice.size()) })
574 })
575 .collect::<Result<Vec<_>>>()?;
576 let iovecs = Pin::from(iovecs.into_boxed_slice());
577
578 self.submit_op(source, Some(mem), |fd, token| {
579 unsafe { self.ctx.add_readv(iovecs, fd, offset, token) }
584 })
585 }
586
587 fn submit_write_from_vectored(
588 &self,
589 source: &RegisteredSource,
590 mem: Arc<dyn BackingMemory + Send + Sync>,
591 offset: Option<u64>,
592 addrs: impl IntoIterator<Item = MemRegion>,
593 ) -> Result<WakerToken> {
594 let mut addrs = addrs.into_iter().peekable();
595 let first = addrs.next();
596
597 if let (Some(region), None) = (first, addrs.peek()) {
600 let (ptr, size) = {
601 let vslice = mem
602 .get_volatile_slice(region)
603 .map_err(|_| Error::InvalidOffset)?;
604 (vslice.as_ptr(), vslice.size())
605 };
606 if let Ok(len) = u32::try_from(size) {
608 return self.submit_op(source, Some(mem), |fd, token| {
609 unsafe { self.ctx.add_write(ptr, len, fd, offset, token) }
614 });
615 }
616 }
617
618 let iovecs = first
619 .into_iter()
620 .chain(addrs)
621 .map(|mem_range| {
622 let vslice = mem
623 .get_volatile_slice(mem_range)
624 .map_err(|_| Error::InvalidOffset)?;
625 Ok(unsafe { IoBufMut::from_raw_parts(vslice.as_mut_ptr(), vslice.size()) })
629 })
630 .collect::<Result<Vec<_>>>()?;
631 let iovecs = Pin::from(iovecs.into_boxed_slice());
632
633 self.submit_op(source, Some(mem), |fd, token| {
634 unsafe { self.ctx.add_writev(iovecs, fd, offset, token) }
639 })
640 }
641}
642
643impl Reactor for UringReactor {
644 fn new() -> std::io::Result<Self> {
645 Ok(UringReactor::new()?)
646 }
647
648 fn wake(&self) {
649 let mut ring = self.ring.lock();
650 let entry = ring.ops.vacant_entry();
651 let next_op_token = entry.key();
652 if let Err(e) = self.ctx.add_nop(usize_to_u64(next_op_token)) {
653 warn!("Failed to add NOP for waking up executor: {}", e);
654 }
655 entry.insert(OpStatus::Nop);
656 mem::drop(ring);
657
658 match self.ctx.submit() {
659 Ok(()) => {}
660 Err(io_uring::Error::RingEnter(libc::EBUSY)) => {}
663 Err(e) => warn!("Failed to submit NOP for waking up executor: {}", e),
664 }
665 }
666
667 fn on_executor_drop<'a>(&'a self) -> Pin<Box<dyn Future<Output = ()> + 'a>> {
668 #[allow(clippy::needless_collect)]
674 let ops: Vec<_> = self
675 .ring
676 .lock()
677 .ops
678 .iter_mut()
679 .filter_map(|op| match op.1 {
680 OpStatus::Pending(data) if !data.canceled => Some(op.0),
681 _ => None,
682 })
683 .collect();
684 for token in ops {
685 self.cancel_operation(WakerToken(token));
686 }
687
688 *self.thread_id.lock() = None;
692
693 Box::pin(futures::future::poll_fn(|_cx| {
700 if self.ring.lock().ops.is_empty() {
701 Poll::Ready(())
702 } else {
703 Poll::Pending
704 }
705 }))
706 }
707
708 fn on_thread_start(&self) {
709 let current_thread = thread::current().id();
710 let mut thread_id = self.thread_id.lock();
711 assert_eq!(
712 *thread_id.get_or_insert(current_thread),
713 current_thread,
714 "`UringReactor::wait_for_work` cannot be called from more than one thread"
715 );
716 }
717
718 fn wait_for_work(&self, set_processing: impl Fn()) -> std::io::Result<()> {
719 trace!(
720 "Waiting on events, {} pending ops",
721 self.ring.lock().ops.len()
722 );
723 let events = self.ctx.wait().map_err(Error::URingEnter)?;
724
725 set_processing();
728
729 let mut ring = self.ring.lock();
730 for (raw_token, result) in events {
731 let token = raw_token
735 .try_into()
736 .expect("`u64` doesn't fit inside a `usize`");
737
738 let op = ring
739 .ops
740 .get_mut(token)
741 .expect("Received completion token for unexpected operation");
742 match mem::replace(op, OpStatus::Completed(Some(result))) {
743 OpStatus::Nop => mem::drop(ring.ops.remove(token)),
745 OpStatus::Pending(data) => {
746 if data.canceled {
747 ring.ops.remove(token);
750 }
751 if let Some(waker) = data.waker {
752 waker.wake();
753 }
754 }
755 OpStatus::Completed(_) => panic!("uring operation completed more than once"),
756 }
757 }
758
759 Ok(())
760 }
761
762 fn new_source<F: AsRawDescriptor>(
763 &self,
764 ex: &Arc<RawExecutor<Self>>,
765 f: F,
766 ) -> AsyncResult<IoSource<F>> {
767 Ok(IoSource::Uring(super::UringSource::new(f, ex)?))
768 }
769
770 fn wrap_task_handle<R>(task: RawTaskHandle<UringReactor, R>) -> TaskHandle<R> {
771 TaskHandle::Uring(task)
772 }
773}
774
775impl AsRawDescriptor for UringReactor {
776 fn as_raw_descriptor(&self) -> RawDescriptor {
777 self.ctx.as_raw_descriptor()
778 }
779}
780
781impl WeakWake for UringReactor {
782 fn wake_by_ref(weak_self: &Weak<Self>) {
783 if let Some(arc_self) = weak_self.upgrade() {
784 Reactor::wake(&*arc_self);
785 }
786 }
787}
788
789impl Drop for UringReactor {
790 fn drop(&mut self) {
791 assert!(self.ring.lock().ops.is_empty());
793 }
794}
795
796unsafe fn dup_fd(fd: RawFd) -> Result<RawFd> {
800 let ret = libc::fcntl(fd, libc::F_DUPFD_CLOEXEC, 0);
801 if ret < 0 {
802 Err(Error::DuplicatingFd(base::Error::last()))
803 } else {
804 Ok(ret)
805 }
806}
807
808#[inline]
810fn usize_to_u64(val: usize) -> u64 {
811 val.try_into().expect("`usize` doesn't fit inside a `u64`")
812}
813
814pub struct PendingOperation {
815 waker_token: Option<WakerToken>,
816 ex: Weak<RawExecutor<UringReactor>>,
817 submitted: bool,
818}
819
820impl Future for PendingOperation {
821 type Output = Result<u32>;
822
823 fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
824 let token = self
825 .waker_token
826 .as_ref()
827 .expect("PendingOperation polled after returning Poll::Ready");
828 if let Some(ex) = self.ex.upgrade() {
829 if let Some(result) = ex.reactor.get_result(token, cx) {
830 self.waker_token = None;
831 Poll::Ready(result.map_err(Error::Io))
832 } else {
833 if !self.submitted && !ex.reactor.runs_tasks_on_current_thread() {
837 match ex.reactor.ctx.submit() {
838 Ok(()) => self.submitted = true,
839 Err(io_uring::Error::RingEnter(libc::EBUSY)) => {}
843 Err(e) => return Poll::Ready(Err(Error::URingEnter(e))),
844 }
845 }
846 Poll::Pending
847 }
848 } else {
849 Poll::Ready(Err(Error::ExecutorGone))
850 }
851 }
852}
853
854impl Drop for PendingOperation {
855 fn drop(&mut self) {
856 if let Some(waker_token) = self.waker_token.take() {
857 if let Some(ex) = self.ex.upgrade() {
858 ex.reactor.cancel_operation(waker_token);
859 }
860 }
861 }
862}
863
864#[cfg(test)]
865mod tests {
866 use std::future::Future;
867 use std::io::Read;
868 use std::io::Write;
869 use std::mem;
870 use std::pin::Pin;
871 use std::rc::Rc;
872 use std::task::Context;
873 use std::task::Poll;
874
875 use futures::executor::block_on;
876
877 use super::*;
878 use crate::mem::BackingMemory;
879 use crate::mem::MemRegion;
880 use crate::mem::VecIoWrapper;
881 use crate::BlockingPool;
882 use crate::ExecutorTrait;
883
884 struct UringQueueEmpty<'a> {
886 ex: &'a Arc<RawExecutor<UringReactor>>,
887 }
888
889 impl Future for UringQueueEmpty<'_> {
890 type Output = ();
891
892 fn poll(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<Self::Output> {
893 if self.ex.reactor.ring.lock().ops.is_empty() {
894 Poll::Ready(())
895 } else {
896 Poll::Pending
897 }
898 }
899 }
900
901 #[test]
902 fn dont_drop_backing_mem_read() {
903 if !is_uring_stable() {
904 return;
905 }
906
907 let bm =
910 Arc::new(VecIoWrapper::from(vec![0u8; 4096])) as Arc<dyn BackingMemory + Send + Sync>;
911
912 let (rx, mut tx) = base::pipe().unwrap();
914
915 let ex = RawExecutor::<UringReactor>::new().unwrap();
917
918 let registered_source = ex
920 .reactor
921 .register_source(&ex, &rx)
922 .expect("register source failed");
923
924 let pending_op = registered_source
927 .start_read_to_mem(None, Arc::clone(&bm), [MemRegion { offset: 0, len: 8 }])
928 .expect("failed to start read to mem");
929
930 assert_eq!(Arc::strong_count(&bm), 2);
933
934 drop(pending_op);
937 assert_eq!(Arc::strong_count(&bm), 2);
938
939 tx.write_all(&[0u8; 8]).expect("write failed");
943 ex.run_until(UringQueueEmpty { ex: &ex })
944 .expect("Failed to wait for read pipe ready");
945 assert_eq!(Arc::strong_count(&bm), 1);
946 }
947
948 #[test]
949 fn dont_drop_backing_mem_write() {
950 if !is_uring_stable() {
951 return;
952 }
953
954 let bm =
957 Arc::new(VecIoWrapper::from(vec![0u8; 4096])) as Arc<dyn BackingMemory + Send + Sync>;
958
959 let (mut rx, tx) = base::new_pipe_full().expect("Pipe failed");
961
962 let ex = RawExecutor::<UringReactor>::new().unwrap();
964
965 let registered_source = ex
967 .reactor
968 .register_source(&ex, &tx)
969 .expect("register source failed");
970
971 let pending_op = registered_source
974 .start_write_from_mem(None, Arc::clone(&bm), [MemRegion { offset: 0, len: 8 }])
975 .expect("failed to start write to mem");
976
977 assert_eq!(Arc::strong_count(&bm), 2);
980
981 drop(pending_op);
984 assert_eq!(Arc::strong_count(&bm), 2);
985
986 let mut buf = vec![0u8; base::round_up_to_page_size(1)];
990 rx.read_exact(&mut buf).expect("read to empty failed");
991 ex.run_until(UringQueueEmpty { ex: &ex })
992 .expect("Failed to wait for write pipe ready");
993 assert_eq!(Arc::strong_count(&bm), 1);
994 }
995
996 #[test]
997 fn canceled_before_completion() {
998 if !is_uring_stable() {
999 return;
1000 }
1001
1002 async fn cancel_io(op: PendingOperation) {
1003 mem::drop(op);
1004 }
1005
1006 async fn check_result(op: PendingOperation, expected: u32) {
1007 let actual = op.await.expect("operation failed to complete");
1008 assert_eq!(expected, actual);
1009 }
1010
1011 let bm =
1012 Arc::new(VecIoWrapper::from(vec![0u8; 16])) as Arc<dyn BackingMemory + Send + Sync>;
1013
1014 let (rx, tx) = base::pipe().expect("Pipe failed");
1015
1016 let ex = RawExecutor::<UringReactor>::new().unwrap();
1017
1018 let rx_source = ex
1019 .reactor
1020 .register_source(&ex, &rx)
1021 .expect("register source failed");
1022 let tx_source = ex
1023 .reactor
1024 .register_source(&ex, &tx)
1025 .expect("register source failed");
1026
1027 let read_task = rx_source
1028 .start_read_to_mem(None, Arc::clone(&bm), [MemRegion { offset: 0, len: 8 }])
1029 .expect("failed to start read to mem");
1030
1031 ex.spawn_local(cancel_io(read_task)).detach();
1032
1033 let buf =
1035 Arc::new(VecIoWrapper::from(vec![0xc2u8; 16])) as Arc<dyn BackingMemory + Send + Sync>;
1036 let write_task = tx_source
1037 .start_write_from_mem(None, Arc::clone(&buf), [MemRegion { offset: 0, len: 8 }])
1038 .expect("failed to start write from mem");
1039
1040 ex.run_until(check_result(write_task, 8))
1041 .expect("Failed to run executor");
1042 }
1043
1044 #[ignore]
1046 #[test]
1047 fn drop_before_completion() {
1048 if !is_uring_stable() {
1049 return;
1050 }
1051
1052 const VALUE: u64 = 0xef6c_a8df_b842_eb9c;
1053
1054 async fn check_op(op: PendingOperation) {
1055 let err = op.await.expect_err("Op completed successfully");
1056 match err {
1057 Error::ExecutorGone => {}
1058 e => panic!("Unexpected error from op: {e}"),
1059 }
1060 }
1061
1062 let (mut rx, mut tx) = base::pipe().expect("Pipe failed");
1063
1064 let ex = RawExecutor::<UringReactor>::new().unwrap();
1065
1066 let tx_source = ex
1067 .reactor
1068 .register_source(&ex, &tx)
1069 .expect("Failed to register source");
1070 let bm = Arc::new(VecIoWrapper::from(VALUE.to_ne_bytes().to_vec()));
1071 let op = tx_source
1072 .start_write_from_mem(
1073 None,
1074 bm,
1075 [MemRegion {
1076 offset: 0,
1077 len: mem::size_of::<u64>(),
1078 }],
1079 )
1080 .expect("Failed to start write from mem");
1081
1082 ex.spawn_local(check_op(op)).detach();
1083
1084 mem::drop(ex);
1086
1087 let new_val = [0x2e; 8];
1089 tx.write_all(&new_val).unwrap();
1090
1091 let mut buf = 0u64.to_ne_bytes();
1092 rx.read_exact(&mut buf[..])
1093 .expect("Failed to read from pipe");
1094
1095 assert_eq!(buf, new_val);
1096 }
1097
1098 #[test]
1100 fn drop_detached_blocking_pool() {
1101 if !is_uring_stable() {
1102 return;
1103 }
1104
1105 struct Cleanup(BlockingPool);
1106
1107 impl Drop for Cleanup {
1108 fn drop(&mut self) {
1109 self.0
1111 .shutdown(Some(
1112 std::time::Instant::now() + std::time::Duration::from_secs(1),
1113 ))
1114 .unwrap();
1115 }
1116 }
1117
1118 let rc = Rc::new(std::cell::Cell::new(0));
1119 {
1120 let ex = RawExecutor::<UringReactor>::new().unwrap();
1121 let rc_clone = rc.clone();
1122 ex.spawn_local(async move {
1123 rc_clone.set(1);
1124 let pool = Cleanup(BlockingPool::new(1, std::time::Duration::new(60, 0)));
1125 let (send, recv) = std::sync::mpsc::sync_channel::<()>(0);
1126 let blocking_task = pool.0.spawn(move || {
1128 assert_eq!(recv.recv(), Ok(()));
1130 assert_eq!(recv.recv(), Err(std::sync::mpsc::RecvError));
1132 });
1133 send.send(()).unwrap();
1138 blocking_task.await;
1140 rc_clone.set(2);
1141 })
1142 .detach();
1143 ex.run_until(async {}).unwrap();
1144 }
1149 assert_eq!(rc.get(), 1);
1150 Rc::try_unwrap(rc).expect("Rc had too many refs");
1151 }
1152
1153 #[test]
1154 fn drop_on_different_thread() {
1155 if !is_uring_stable() {
1156 return;
1157 }
1158
1159 let ex = RawExecutor::<UringReactor>::new().unwrap();
1160
1161 let ex2 = ex.clone();
1162 let t = thread::spawn(move || ex2.run_until(async {}));
1163
1164 t.join().unwrap().unwrap();
1165
1166 let (_rx, tx) = base::pipe().expect("Pipe failed");
1169 let tx = ex
1170 .reactor
1171 .register_source(&ex, &tx)
1172 .expect("Failed to register source");
1173 let bm = Arc::new(VecIoWrapper::from(0xf2e96u64.to_ne_bytes().to_vec()));
1174 let op = tx
1175 .start_write_from_mem(
1176 None,
1177 bm,
1178 [MemRegion {
1179 offset: 0,
1180 len: mem::size_of::<u64>(),
1181 }],
1182 )
1183 .expect("Failed to start write from mem");
1184
1185 mem::drop(ex);
1186
1187 match block_on(op).expect_err("Pending operation completed after executor was dropped") {
1188 Error::ExecutorGone => {}
1189 e => panic!("Unexpected error after dropping executor: {e}"),
1190 }
1191 }
1192}