1use crate::loom::sync::atomic::AtomicUsize;
2use crate::runtime::io::ScheduledIo;
3use crate::util::linked_list::{self, LinkedList};
45use std::io;
6use std::ptr::NonNull;
7use std::sync::atomic::Ordering::{Acquire, Release};
8use std::sync::Arc;
910// Kind of arbitrary, but buffering 16 `ScheduledIo`s doesn't seem like much
11const NOTIFY_AFTER: usize = 16;
1213pub(super) struct RegistrationSet {
14 num_pending_release: AtomicUsize,
15}
1617pub(super) struct Synced {
18// True when the I/O driver shutdown. At this point, no more registrations
19 // should be added to the set.
20is_shutdown: bool,
2122// List of all registrations tracked by the set
23registrations: LinkedList<Arc<ScheduledIo>, ScheduledIo>,
2425// Registrations that are pending drop. When a `Registration` is dropped, it
26 // stores its `ScheduledIo` in this list. The I/O driver is responsible for
27 // dropping it. This ensures the `ScheduledIo` is not freed while it can
28 // still be included in an I/O event.
29pending_release: Vec<Arc<ScheduledIo>>,
30}
3132impl RegistrationSet {
33pub(super) fn new() -> (RegistrationSet, Synced) {
34let set = RegistrationSet {
35 num_pending_release: AtomicUsize::new(0),
36 };
3738let synced = Synced {
39 is_shutdown: false,
40 registrations: LinkedList::new(),
41 pending_release: Vec::with_capacity(NOTIFY_AFTER),
42 };
4344 (set, synced)
45 }
4647pub(super) fn is_shutdown(&self, synced: &Synced) -> bool {
48 synced.is_shutdown
49 }
5051/// Returns `true` if there are registrations that need to be released
52pub(super) fn needs_release(&self) -> bool {
53self.num_pending_release.load(Acquire) != 0
54}
5556pub(super) fn allocate(&self, synced: &mut Synced) -> io::Result<Arc<ScheduledIo>> {
57if synced.is_shutdown {
58return Err(io::Error::new(
59 io::ErrorKind::Other,
60crate::util::error::RUNTIME_SHUTTING_DOWN_ERROR,
61 ));
62 }
6364let ret = Arc::new(ScheduledIo::default());
6566// Push a ref into the list of all resources.
67synced.registrations.push_front(ret.clone());
6869Ok(ret)
70 }
7172// Returns `true` if the caller should unblock the I/O driver to purge
73 // registrations pending release.
74pub(super) fn deregister(&self, synced: &mut Synced, registration: &Arc<ScheduledIo>) -> bool {
75 synced.pending_release.push(registration.clone());
7677let len = synced.pending_release.len();
78self.num_pending_release.store(len, Release);
7980 len == NOTIFY_AFTER
81 }
8283pub(super) fn shutdown(&self, synced: &mut Synced) -> Vec<Arc<ScheduledIo>> {
84if synced.is_shutdown {
85return vec![];
86 }
8788 synced.is_shutdown = true;
89 synced.pending_release.clear();
9091// Building a vec of all outstanding I/O handles could be expensive, but
92 // this is the shutdown operation. In theory, shutdowns should be
93 // "clean" with no outstanding I/O resources. Even if it is slow, we
94 // aren't optimizing for shutdown.
95let mut ret = vec![];
9697while let Some(io) = synced.registrations.pop_back() {
98 ret.push(io);
99 }
100101 ret
102 }
103104pub(super) fn release(&self, synced: &mut Synced) {
105let pending = std::mem::take(&mut synced.pending_release);
106107for io in pending {
108// safety: the registration is part of our list
109unsafe { self.remove(synced, &io) }
110 }
111112self.num_pending_release.store(0, Release);
113 }
114115// This function is marked as unsafe, because the caller must make sure that
116 // `io` is part of the registration set.
117pub(super) unsafe fn remove(&self, synced: &mut Synced, io: &Arc<ScheduledIo>) {
118// SAFETY: Pointers into an Arc are never null.
119let io = unsafe { NonNull::new_unchecked(Arc::as_ptr(io).cast_mut()) };
120121super::EXPOSE_IO.unexpose_provenance(io.as_ptr());
122let _ = synced.registrations.remove(io);
123 }
124}
125126// Safety: `Arc` pins the inner data
127unsafe impl linked_list::Link for Arc<ScheduledIo> {
128type Handle = Arc<ScheduledIo>;
129type Target = ScheduledIo;
130131fn as_raw(handle: &Self::Handle) -> NonNull<ScheduledIo> {
132// safety: Arc::as_ptr never returns null
133unsafe { NonNull::new_unchecked(Arc::as_ptr(handle) as *mut _) }
134 }
135136unsafe fn from_raw(ptr: NonNull<Self::Target>) -> Arc<ScheduledIo> {
137// safety: the linked list currently owns a ref count
138unsafe { Arc::from_raw(ptr.as_ptr() as *const _) }
139 }
140141unsafe fn pointers(
142 target: NonNull<Self::Target>,
143 ) -> NonNull<linked_list::Pointers<ScheduledIo>> {
144 NonNull::new_unchecked(target.as_ref().linked_list_pointers.get())
145 }
146}