usesuper::iocp::{CompletionPort, CompletionStatus}; use std::collections::VecDeque; use std::ffi::c_void; use std::io; use std::marker::PhantomPinned; use std::os::windows::io::RawSocket; use std::pin::Pin; #[cfg(debug_assertions)] use std::sync::atomic::AtomicUsize; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, Mutex}; use std::time::Duration;
use windows_sys::Win32::Foundation::{
ERROR_INVALID_HANDLE, ERROR_IO_PENDING, HANDLE, STATUS_CANCELLED, WAIT_TIMEOUT,
}; use windows_sys::Win32::System::IO::OVERLAPPED;
// make sure to reset previous error before a new update self.error = None;
iflet SockPollStatus::Pending = self.poll_status { if (self.user_evts & afd::KNOWN_EVENTS & !self.pending_evts) == 0 { /* All the events the user is interested in are already being monitored by *thependingpolloperation.Itmightspuriouslycompletebecauseofan *eventthatwe'renolongerinterestedin;whenthathappenswe'llsubmit
* a new poll operation with the updated event mask. */
} else { /* A poll operation is already pending, but it's not monitoring for all the *eventsthattheuserisinterestedin.Therefore,cancelthepending *polloperation;whenwereceiveit'scompletionpackage,anewpoll
* operation will be submitted with the correct event mask. */ iflet Err(e) = self.cancel() { self.error = e.raw_os_error(); return Err(e);
} return Ok(());
}
} elseiflet SockPollStatus::Cancelled = self.poll_status { /* The poll operation has already been cancelled, we're still waiting for
* it to return. For now, there's nothing that needs to be done. */
} elseiflet SockPollStatus::Idle = self.poll_status { /* No poll operation is pending; start one. */ self.poll_info.exclusive = 0; self.poll_info.number_of_handles = 1; self.poll_info.timeout = i64::MAX; self.poll_info.handles[0].handle = self.base_socket as HANDLE; self.poll_info.handles[0].status = 0; self.poll_info.handles[0].events = self.user_evts | afd::POLL_LOCAL_CLOSE;
// Increase the ref count as the memory will be used by the kernel. let overlapped_ptr = into_overlapped(self_arc.clone());
let result = unsafe { self.afd
.poll(&mutself.poll_info, &mut *self.iosb, overlapped_ptr)
}; iflet Err(e) = result { let code = e.raw_os_error().unwrap(); if code == ERROR_IO_PENDING as i32 { /* Overlapped poll operation in progress; this is expected. */
} else { // Since the operation failed it means the kernel won't be // using the memory any more.
drop(from_overlapped(overlapped_ptr as *mut _)); if code == ERROR_INVALID_HANDLE as i32 { /* Socket closed; it'll be dropped. */ self.mark_delete(); return Ok(());
} else { self.error = e.raw_os_error(); return Err(e);
}
}
}
self.poll_status = SockPollStatus::Pending; self.pending_evts = self.user_evts;
} else {
unreachable!("Invalid poll status during update, {:#?}", self)
}
// This is the function called from the overlapped using as Arc<Mutex<SockState>>. Watch out for reference counting. fn feed_event(&mutself) -> Option<Event> { self.poll_status = SockPollStatus::Idle; self.pending_evts = 0;
letmut afd_events = 0; // We use the status info in IO_STATUS_BLOCK to determine the socket poll status. It is unsafe to use a pointer of IO_STATUS_BLOCK. unsafe { ifself.delete_pending { return None;
} elseifself.iosb.Anonymous.Status == STATUS_CANCELLED { /* The poll request was cancelled by CancelIoEx. */
} elseifself.iosb.Anonymous.Status < 0 { /* The overlapped request itself failed in an unexpected way. */
afd_events = afd::POLL_CONNECT_FAIL;
} elseifself.poll_info.number_of_handles < 1 { /* This poll operation succeeded but didn't report any socket events. */
} elseifself.poll_info.handles[0].events & afd::POLL_LOCAL_CLOSE != 0 { /* The poll operation reported that the socket was closed. */ self.mark_delete(); return None;
} else {
afd_events = self.poll_info.handles[0].events;
}
}
afd_events &= self.user_evts;
if afd_events == 0 { return None;
}
// In mio, we have to simulate Edge-triggered behavior to match API usage. // The strategy here is to intercept all read/write from user that could cause WouldBlock usage, // then reregister the socket to reset the interests. self.user_evts &= !afd_events;
/// True if need to be added on update queue, false otherwise. fn set_event(&mutself, ev: Event) -> bool { /* afd::POLL_CONNECT_FAIL and afd::POLL_ABORT are always reported, even when not requested by the caller. */ let events = ev.flags | afd::POLL_CONNECT_FAIL | afd::POLL_ABORT;
impl Drop for SockState { fn drop(&mutself) { self.mark_delete();
}
}
/// Converts the pointer to a `SockState` into a raw pointer. /// To revert see `from_overlapped`. fn into_overlapped(sock_state: Pin<Arc<Mutex<SockState>>>) -> *mut c_void { let overlapped_ptr: *const Mutex<SockState> = unsafe { Arc::into_raw(Pin::into_inner_unchecked(sock_state)) };
overlapped_ptr as *mut _
}
/// Convert a raw overlapped pointer into a reference to `SockState`. /// Reverts `into_overlapped`. fn from_overlapped(ptr: *mut OVERLAPPED) -> Pin<Arc<Mutex<SockState>>> { let sock_ptr: *const Mutex<SockState> = ptr as *const _; unsafe { Pin::new_unchecked(Arc::from_raw(sock_ptr)) }
}
/// Each Selector has a globally unique(ish) ID associated with it. This ID /// gets tracked by `TcpStream`, `TcpListener`, etc... when they are first /// registered with the `Selector`. If a type that is previously associated with /// a `Selector` attempts to register itself with a different `Selector`, the /// operation will return with an error. This matches windows behavior. #[cfg(debug_assertions)] static NEXT_ID: AtomicUsize = AtomicUsize::new(0);
/// Windows implementation of `sys::Selector` /// /// Edge-triggered event notification is simulated by resetting internal event flag of each socket state `SockState` /// and setting all events back by intercepting all requests that could cause `io::ErrorKind::WouldBlock` happening. /// /// This selector is currently only support socket due to `Afd` driver is winsock2 specific. #[derive(Debug)] pubstruct Selector { #[cfg(debug_assertions)]
id: usize, pub(super) inner: Arc<SelectorInner>,
}
/// # Safety /// /// This requires a mutable reference to self because only a single thread /// can poll IOCP at a time. pubfn select(&mutself, events: &mut Events, timeout: Option<Duration>) -> io::Result<()> { self.inner.select(events, timeout)
}
match result {
Ok(iocp_events) => Ok(unsafe { self.feed_events(events, iocp_events) }),
Err(ref e) if e.raw_os_error() == Some(WAIT_TIMEOUT as i32) => Ok(0),
Err(e) => Err(e),
}
}
unsafefn update_sockets_events(&self) -> io::Result<()> { letmut update_queue = self.update_queue.lock().unwrap(); for sock in update_queue.iter_mut() { letmut sock_internal = sock.lock().unwrap(); if !sock_internal.is_pending_deletion() {
sock_internal.update(sock)?;
}
}
// remove all sock which do not have error, they have afd op pending
update_queue.retain(|sock| sock.lock().unwrap().has_error());
self.afd_group.release_unused_afd();
Ok(())
}
// It returns processed count of iocp_events rather than the events itself. unsafefn feed_events(
&self,
events: &mut Vec<Event>,
iocp_events: &[CompletionStatus],
) -> usize { letmut n = 0; letmut update_queue = self.update_queue.lock().unwrap(); for iocp_event in iocp_events.iter() { if iocp_event.overlapped().is_null() {
events.push(Event::from_completion_status(iocp_event));
n += 1; continue;
} elseif iocp_event.token() % 2 == 1 { // Handle is a named pipe. This could be extended to be any non-AFD event. let callback = (*(iocp_event.overlapped() as *mutsuper::Overlapped)).callback;
let len = events.len();
callback(iocp_event.entry(), Some(events));
n += events.len() - len; continue;
}
let sock_state = from_overlapped(iocp_event.overlapped()); letmut sock_guard = sock_state.lock().unwrap(); iflet Some(e) = sock_guard.feed_event() {
events.push(e);
n += 1;
}
if !sock_guard.is_pending_deletion() {
update_queue.push_back(sock_state.clone());
}
} self.afd_group.release_unused_afd();
n
}
}
cfg_io_source! { use std::mem::size_of; use std::ptr::null_mut;
use windows_sys::Win32::Networking::WinSock::{
WSAGetLastError, WSAIoctl, SIO_BASE_HANDLE, SIO_BSP_HANDLE,
SIO_BSP_HANDLE_POLL, SIO_BSP_HANDLE_SELECT, SOCKET_ERROR,
};
// FIXME: a sock which has_error true should not be re-added to // the update queue because it's already there. self.queue_state(state); unsafe { self.update_sockets_events_if_polling() }
}
/// This function is called by register() and reregister() to start an /// IOCTL_AFD_POLL operation corresponding to the registered events, but /// only if necessary. /// /// Since it is not possible to modify or synchronously cancel an AFD_POLL /// operation, and there can be only one active AFD_POLL operation per /// (socket, completion port) pair at any time, it is expensive to change /// a socket's event registration after it has been submitted to the kernel. /// /// Therefore, if no other threads are polling when interest in a socket /// event is (re)registered, the socket is added to the 'update queue', but /// the actual syscall to start the IOCTL_AFD_POLL operation is deferred /// until just before the GetQueuedCompletionStatusEx() syscall is made. /// /// However, when another thread is already blocked on /// GetQueuedCompletionStatusEx() we tell the kernel about the registered /// socket event(s) immediately. unsafefn update_sockets_events_if_polling(&self) -> io::Result<()> { ifself.is_polling.load(Ordering::Acquire) { self.update_sockets_events()
} else {
Ok(())
}
}
fn get_base_socket(raw_socket: RawSocket) -> io::Result<RawSocket> { let res = try_get_base_socket(raw_socket, SIO_BASE_HANDLE); iflet Ok(base_socket) = res { return Ok(base_socket);
}
// The `SIO_BASE_HANDLE` should not be intercepted by LSPs, therefore // it should not fail as long as `raw_socket` is a valid socket. See // https://docs.microsoft.com/en-us/windows/win32/winsock/winsock-ioctls. // However, at least one known LSP deliberately breaks it, so we try // some alternative IOCTLs, starting with the most appropriate one. for &ioctl in &[
SIO_BSP_HANDLE_SELECT,
SIO_BSP_HANDLE_POLL,
SIO_BSP_HANDLE,
] { iflet Ok(base_socket) = try_get_base_socket(raw_socket, ioctl) { // Since we know now that we're dealing with an LSP (otherwise // SIO_BASE_HANDLE would't have failed), only return any result // when it is different from the original `raw_socket`. if base_socket != raw_socket { return Ok(base_socket);
}
}
}
// If the alternative IOCTLs also failed, return the original error. let os_error = res.unwrap_err(); let err = io::Error::from_raw_os_error(os_error);
Err(err)
}
}
impl Drop for SelectorInner { fn drop(&mutself) { loop { let events_num: usize; letmut statuses: [CompletionStatus; 1024] = [CompletionStatus::zero(); 1024];
let result = self
.cp
.get_many(&mut statuses, Some(std::time::Duration::from_millis(0))); match result {
Ok(iocp_events) => {
events_num = iocp_events.iter().len(); for iocp_event in iocp_events.iter() { if iocp_event.overlapped().is_null() { // Custom event
} elseif iocp_event.token() % 2 == 1 { // Named pipe, dispatch the event so it can release resources let callback = unsafe {
(*(iocp_event.overlapped() as *mutsuper::Overlapped)).callback
};
callback(iocp_event.entry(), None);
} else { // drain sock state to release memory of Arc reference let _sock_state = from_overlapped(iocp_event.overlapped());
}
}
}
Err(_) => { break;
}
}
if events_num == 0 { // continue looping until all completion statuses have been drained break;
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.