polling/src/lib.rs

1126 lines
34 KiB
Rust

//! Portable interface to epoll, kqueue, event ports, and IOCP.
//!
//! Supported platforms:
//! - [epoll](https://en.wikipedia.org/wiki/Epoll): Linux, Android, RedoxOS
//! - [kqueue](https://en.wikipedia.org/wiki/Kqueue): macOS, iOS, tvOS, watchOS, FreeBSD, NetBSD, OpenBSD,
//! DragonFly BSD
//! - [event ports](https://illumos.org/man/port_create): illumos, Solaris
//! - [poll](https://en.wikipedia.org/wiki/Poll_(Unix)): VxWorks, Fuchsia, HermitOS, other Unix systems
//! - [IOCP](https://learn.microsoft.com/en-us/windows/win32/fileio/i-o-completion-ports): Windows, Wine (version 7.13+)
//!
//! By default, polling is done in oneshot mode, which means interest in I/O events needs to
//! be re-enabled after an event is delivered if we're interested in the next event of the same
//! kind. However, level and edge triggered modes are also available for certain operating
//! systems. See the documentation of the [`PollMode`] type for more information.
//!
//! Only one thread can be waiting for I/O events at a time.
//!
//! # Examples
//!
//! ```no_run
//! use polling::{Event, Events, Poller};
//! use std::net::TcpListener;
//!
//! // Create a TCP listener.
//! let socket = TcpListener::bind("127.0.0.1:8000")?;
//! socket.set_nonblocking(true)?;
//! let key = 7; // Arbitrary key identifying the socket.
//!
//! // Create a poller and register interest in readability on the socket.
//! let poller = Poller::new()?;
//! unsafe {
//! poller.add(&socket, Event::readable(key))?;
//! }
//!
//! // The event loop.
//! let mut events = Events::new();
//! loop {
//! // Wait for at least one I/O event.
//! events.clear();
//! poller.wait(&mut events, None)?;
//!
//! for ev in events.iter() {
//! if ev.key == key {
//! // Perform a non-blocking accept operation.
//! socket.accept()?;
//! // Set interest in the next readability event.
//! poller.modify(&socket, Event::readable(key))?;
//! }
//! }
//! }
//!
//! poller.delete(&socket)?;
//! # std::io::Result::Ok(())
//! ```
#![warn(missing_docs, missing_debug_implementations, rust_2018_idioms)]
#![allow(clippy::useless_conversion, clippy::unnecessary_cast, unused_unsafe)]
#![cfg_attr(docsrs, feature(doc_cfg))]
#![doc(
html_favicon_url = "https://raw.githubusercontent.com/smol-rs/smol/master/assets/images/logo_fullsize_transparent.png"
)]
#![doc(
html_logo_url = "https://raw.githubusercontent.com/smol-rs/smol/master/assets/images/logo_fullsize_transparent.png"
)]
use std::cell::Cell;
use std::fmt;
use std::io;
use std::marker::PhantomData;
use std::num::NonZeroUsize;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Mutex;
use std::time::{Duration, Instant};
use cfg_if::cfg_if;
cfg_if! {
// Note: This cfg is intended to make it easy for polling developers to test
// the backend that uses poll, and is not a public API.
if #[cfg(polling_test_poll_backend)] {
mod poll;
use poll as sys;
} else if #[cfg(any(
target_os = "linux",
target_os = "android",
target_os = "redox"
))] {
mod epoll;
use epoll as sys;
} else if #[cfg(any(
target_os = "illumos",
target_os = "solaris",
))] {
mod port;
use port as sys;
} else if #[cfg(any(
target_os = "macos",
target_os = "ios",
target_os = "tvos",
target_os = "watchos",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd",
target_os = "dragonfly",
))] {
mod kqueue;
use kqueue as sys;
} else if #[cfg(any(
target_os = "vxworks",
target_os = "hermit",
target_os = "fuchsia",
target_os = "horizon",
unix,
))] {
mod poll;
use poll as sys;
} else if #[cfg(target_os = "windows")] {
mod iocp;
use iocp as sys;
} else {
compile_error!("polling does not support this target OS");
}
}
pub mod os;
/// Key associated with notifications.
const NOTIFY_KEY: usize = usize::MAX;
/// Indicates that a file descriptor or socket can read or write without blocking.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Event {
/// Key identifying the file descriptor or socket.
pub key: usize,
/// Can it do a read operation without blocking?
pub readable: bool,
/// Can it do a write operation without blocking?
pub writable: bool,
/// System-specific event data.
extra: sys::EventExtra,
}
/// The mode in which the poller waits for I/O events.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[non_exhaustive]
pub enum PollMode {
/// Poll in oneshot mode.
///
/// In this mode, the poller will only deliver one event per file descriptor or socket.
/// Once an event has been delivered, interest in the event needs to be re-enabled
/// by calling `Poller::modify` or `Poller::add`.
///
/// This is the default mode.
Oneshot,
/// Poll in level-triggered mode.
///
/// Once an event has been delivered, polling will continue to deliver that event
/// until interest in the event is disabled by calling `Poller::modify` or `Poller::delete`.
///
/// Not all operating system support this mode. Trying to register a file descriptor with
/// this mode in an unsupported operating system will raise an error. You can check if
/// the operating system supports this mode by calling `Poller::supports_level`.
Level,
/// Poll in edge-triggered mode.
///
/// Once an event has been delivered, polling will not deliver that event again unless
/// a new event occurs.
///
/// Not all operating system support this mode. Trying to register a file descriptor with
/// this mode in an unsupported operating system will raise an error. You can check if
/// the operating system supports this mode by calling `Poller::supports_edge`.
Edge,
/// Poll in both edge-triggered and oneshot mode.
///
/// This mode is similar to the `Oneshot` mode, but it will only deliver one event per new
/// event.
///
/// Not all operating system support this mode. Trying to register a file descriptor with
/// this mode in an unsupported operating system will raise an error. You can check if
/// the operating system supports this mode by calling `Poller::supports_edge`.
EdgeOneshot,
}
impl Event {
/// Create a new event.
pub const fn new(key: usize, readable: bool, writable: bool) -> Event {
Event {
key,
readable,
writable,
extra: sys::EventExtra::empty(),
}
}
/// All kinds of events (readable and writable).
///
/// Equivalent to: `Event::new(key, true, true)`
#[inline]
pub const fn all(key: usize) -> Event {
Event::new(key, true, true)
}
/// Only the readable event.
///
/// Equivalent to: `Event::new(key, true, false)`
#[inline]
pub const fn readable(key: usize) -> Event {
Event::new(key, true, false)
}
/// Only the writable event.
///
/// Equivalent to: `Event::new(key, false, true)`
#[inline]
pub const fn writable(key: usize) -> Event {
Event::new(key, false, true)
}
/// No events.
///
/// Equivalent to: `Event::new(key, false, false)`
#[inline]
pub const fn none(key: usize) -> Event {
Event::new(key, false, false)
}
/// Add interruption events to this interest.
///
/// This usually indicates that the file descriptor or socket has been closed. It corresponds
/// to the `EPOLLHUP` and `POLLHUP` events.
///
/// Interruption events are only supported on the following platforms:
///
/// - `epoll`
/// - `poll`
/// - IOCP
/// - Event Ports
///
/// On other platforms, this function is a no-op.
#[inline]
pub fn set_interrupt(&mut self, active: bool) {
self.extra.set_hup(active);
}
/// Add interruption events to this interest.
///
/// This usually indicates that the file descriptor or socket has been closed. It corresponds
/// to the `EPOLLHUP` and `POLLHUP` events.
///
/// Interruption events are only supported on the following platforms:
///
/// - `epoll`
/// - `poll`
/// - IOCP
/// - Event Ports
///
/// On other platforms, this function is a no-op.
#[inline]
pub fn with_interrupt(mut self) -> Self {
self.set_interrupt(true);
self
}
/// Add priority events to this interest.
///
/// This indicates that there is urgent data to read. It corresponds to the `EPOLLPRI` and
/// `POLLPRI` events.
///
/// Priority events are only supported on the following platforms:
///
/// - `epoll`
/// - `poll`
/// - IOCP
/// - Event Ports
///
/// On other platforms, this function is a no-op.
#[inline]
pub fn set_priority(&mut self, active: bool) {
self.extra.set_pri(active);
}
/// Add priority events to this interest.
///
/// This indicates that there is urgent data to read. It corresponds to the `EPOLLPRI` and
/// `POLLPRI` events.
///
/// Priority events are only supported on the following platforms:
///
/// - `epoll`
/// - `poll`
/// - IOCP
/// - Event Ports
///
/// On other platforms, this function is a no-op.
#[inline]
pub fn with_priority(mut self) -> Self {
self.set_priority(true);
self
}
/// Tell if this event is the result of an interrupt notification.
///
/// This usually indicates that the file descriptor or socket has been closed. It corresponds
/// to the `EPOLLHUP` and `POLLHUP` events.
///
/// Interruption events are only supported on the following platforms:
///
/// - `epoll`
/// - `poll`
/// - IOCP
/// - Event Ports
///
/// On other platforms, this always returns `false`.
#[inline]
pub fn is_interrupt(&self) -> bool {
self.extra.is_hup()
}
/// Tell if this event is the result of a priority notification.
///
/// This indicates that there is urgent data to read. It corresponds to the `EPOLLPRI` and
/// `POLLPRI` events.
///
/// Priority events are only supported on the following platforms:
///
/// - `epoll`
/// - `poll`
/// - IOCP
/// - Event Ports
///
/// On other platforms, this always returns `false`.
#[inline]
pub fn is_priority(&self) -> bool {
self.extra.is_pri()
}
/// Tells if this event is the result of a connection failure.
///
/// This function checks if a TCP connection has failed. It corresponds to the `EPOLLERR` or `EPOLLHUP` event in Linux
/// and `CONNECT_FAILED` event in Windows IOCP.
///
/// # Examples
///
/// ```
/// use std::{io, net};
/// // Assuming polling and socket2 are included as dependencies in Cargo.toml
/// use polling::Event;
/// use socket2::Type;
///
/// fn main() -> io::Result<()> {
/// let socket = socket2::Socket::new(socket2::Domain::IPV4, Type::STREAM, None)?;
/// let poller = polling::Poller::new()?;
/// unsafe {
/// poller.add(&socket, Event::new(0, true, true))?;
/// }
/// let addr = net::SocketAddr::new(net::Ipv4Addr::LOCALHOST.into(), 8080);
/// socket.set_nonblocking(true)?;
/// let _ = socket.connect(&addr.into());
///
/// let mut events = polling::Events::new();
///
/// events.clear();
/// poller.wait(&mut events, None)?;
///
/// let event = events.iter().next();
///
/// let event = match event {
/// Some(event) => event,
/// None => {
/// println!("no event");
/// return Ok(());
/// },
/// };
///
/// println!("event: {:?}", event);
/// if event
/// .is_connect_failed()
/// .unwrap_or_default()
/// {
/// println!("connect failed");
/// }
///
/// Ok(())
/// }
/// ```
///
/// # Returns
///
/// Returns `Some(true)` if the connection has failed, `Some(false)` if the connection has not failed,
/// or `None` if the platform does not support detecting this condition.
#[inline]
#[deprecated(
since = "3.4.0",
note = "use `is_err` in combination of is_hup instead, see documentation for `is_err`"
)]
pub fn is_connect_failed(&self) -> Option<bool> {
self.extra.is_connect_failed()
}
/// Tells if this event is the result of a connection failure.
///
/// This function checks if an error exist, particularly useful in detecting if TCP connection failed. It corresponds to the `EPOLLERR` event in Linux
/// and `CONNECT_FAILED` event in Windows IOCP.
///
/// ## Caveats
///
/// In `epoll`, a TCP connection failure is indicated by `EPOLLERR` + `EPOLLHUP`, though just `EPOLLERR` is enough to indicate a connection failure.
/// EPOLLHUP may happen when we haven't event called `connect` on the socket, but it is still a valid event to check for.
///
/// Returns `Some(true)` if the connection has failed, `Some(false)` if there is an error,
/// or `None` if the platform does not support detecting this condition.
#[inline]
pub fn is_err(&self) -> Option<bool> {
self.extra.is_err()
}
/// Remove any extra information from this event.
#[inline]
pub fn clear_extra(&mut self) {
self.extra = sys::EventExtra::empty();
}
/// Get a version of this event with no extra information.
///
/// This is useful for comparing events with `==`.
#[inline]
pub fn with_no_extra(mut self) -> Self {
self.clear_extra();
self
}
}
/// Waits for I/O events.
pub struct Poller {
poller: sys::Poller,
lock: Mutex<()>,
notified: AtomicBool,
}
impl Poller {
/// Creates a new poller.
///
/// # Examples
///
/// ```
/// use polling::Poller;
///
/// let poller = Poller::new()?;
/// # std::io::Result::Ok(())
/// ```
pub fn new() -> io::Result<Poller> {
Ok(Poller {
poller: sys::Poller::new()?,
lock: Mutex::new(()),
notified: AtomicBool::new(false),
})
}
/// Tell whether or not this `Poller` supports level-triggered polling.
pub fn supports_level(&self) -> bool {
self.poller.supports_level()
}
/// Tell whether or not this `Poller` supports edge-triggered polling.
pub fn supports_edge(&self) -> bool {
self.poller.supports_edge()
}
/// Adds a file descriptor or socket to the poller.
///
/// A file descriptor or socket is considered readable or writable when a read or write
/// operation on it would not block. This doesn't mean the read or write operation will
/// succeed, it only means the operation will return immediately.
///
/// If interest is set in both readability and writability, the two kinds of events might be
/// delivered either separately or together.
///
/// For example, interest in `Event { key: 7, readable: true, writable: true }` might result in
/// a single [`Event`] of the same form, or in two separate [`Event`]s:
/// - `Event { key: 7, readable: true, writable: false }`
/// - `Event { key: 7, readable: false, writable: true }`
///
/// Note that interest in I/O events needs to be re-enabled using
/// [`modify()`][`Poller::modify()`] again after an event is delivered if we're interested in
/// the next event of the same kind.
///
/// It is possible to register interest in the same file descriptor or socket using multiple
/// separate [`Poller`] instances. When the event is delivered, one or more [`Poller`]s are
/// notified with that event. The exact number of [`Poller`]s notified depends on the
/// underlying platform. When registering multiple sources into one event, the user should
/// be careful to accommodate for events lost to other pollers.
///
/// One may also register one source into other, non-`polling` event loops, like GLib's
/// context. While the plumbing will vary from platform to platform, in general the [`Poller`]
/// will act as if the source was registered with another [`Poller`], with the same caveats
/// as above.
///
/// # Safety
///
/// The source must be [`delete()`]d from this `Poller` before it is dropped.
///
/// [`delete()`]: Poller::delete
///
/// # Errors
///
/// This method returns an error in the following situations:
///
/// * If `key` equals `usize::MAX` because that key is reserved for internal use.
/// * If an error is returned by the syscall.
///
/// # Examples
///
/// Set interest in all events:
///
/// ```no_run
/// use polling::{Event, Poller};
///
/// let source = std::net::TcpListener::bind("127.0.0.1:0")?;
/// source.set_nonblocking(true)?;
/// let key = 7;
///
/// let poller = Poller::new()?;
/// unsafe {
/// poller.add(&source, Event::all(key))?;
/// }
/// poller.delete(&source)?;
/// # std::io::Result::Ok(())
/// ```
pub unsafe fn add(&self, source: impl AsRawSource, interest: Event) -> io::Result<()> {
self.add_with_mode(source, interest, PollMode::Oneshot)
}
/// Adds a file descriptor or socket to the poller in the specified mode.
///
/// This is identical to the `add()` function, but allows specifying the
/// polling mode to use for this socket.
///
/// # Safety
///
/// The source must be [`delete()`]d from this `Poller` before it is dropped.
///
/// [`delete()`]: Poller::delete
///
/// # Errors
///
/// If the operating system does not support the specified mode, this function
/// will return an error.
pub unsafe fn add_with_mode(
&self,
source: impl AsRawSource,
interest: Event,
mode: PollMode,
) -> io::Result<()> {
if interest.key == NOTIFY_KEY {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"the key is not allowed to be `usize::MAX`",
));
}
self.poller.add(source.raw(), interest, mode)
}
/// Modifies the interest in a file descriptor or socket.
///
/// This method has the same behavior as [`add()`][`Poller::add()`] except it modifies the
/// interest of a previously added file descriptor or socket.
///
/// To use this method with a file descriptor or socket, you must first add it using
/// [`add()`][`Poller::add()`].
///
/// Note that interest in I/O events needs to be re-enabled using
/// [`modify()`][`Poller::modify()`] again after an event is delivered if we're interested in
/// the next event of the same kind.
///
/// # Errors
///
/// This method returns an error in the following situations:
///
/// * If `key` equals `usize::MAX` because that key is reserved for internal use.
/// * If an error is returned by the syscall.
///
/// # Examples
///
/// To enable interest in all events:
///
/// ```no_run
/// # use polling::{Event, Poller};
/// # let source = std::net::TcpListener::bind("127.0.0.1:0")?;
/// # let key = 7;
/// # let poller = Poller::new()?;
/// # unsafe { poller.add(&source, Event::none(key))?; }
/// poller.modify(&source, Event::all(key))?;
/// # std::io::Result::Ok(())
/// ```
///
/// To enable interest in readable events and disable interest in writable events:
///
/// ```no_run
/// # use polling::{Event, Poller};
/// # let source = std::net::TcpListener::bind("127.0.0.1:0")?;
/// # let key = 7;
/// # let poller = Poller::new()?;
/// # unsafe { poller.add(&source, Event::none(key))?; }
/// poller.modify(&source, Event::readable(key))?;
/// # poller.delete(&source)?;
/// # std::io::Result::Ok(())
/// ```
///
/// To disable interest in readable events and enable interest in writable events:
///
/// ```no_run
/// # use polling::{Event, Poller};
/// # let poller = Poller::new()?;
/// # let key = 7;
/// # let source = std::net::TcpListener::bind("127.0.0.1:0")?;
/// # unsafe { poller.add(&source, Event::none(key))? };
/// poller.modify(&source, Event::writable(key))?;
/// # poller.delete(&source)?;
/// # std::io::Result::Ok(())
/// ```
///
/// To disable interest in all events:
///
/// ```no_run
/// # use polling::{Event, Poller};
/// # let source = std::net::TcpListener::bind("127.0.0.1:0")?;
/// # let key = 7;
/// # let poller = Poller::new()?;
/// # unsafe { poller.add(&source, Event::none(key))?; }
/// poller.modify(&source, Event::none(key))?;
/// # poller.delete(&source)?;
/// # std::io::Result::Ok(())
/// ```
pub fn modify(&self, source: impl AsSource, interest: Event) -> io::Result<()> {
self.modify_with_mode(source, interest, PollMode::Oneshot)
}
/// Modifies interest in a file descriptor or socket to the poller, but with the specified
/// mode.
///
/// This is identical to the `modify()` function, but allows specifying the polling mode
/// to use for this socket.
///
/// # Performance Notes
///
/// This function can be used to change a source from one polling mode to another. However,
/// on some platforms, this switch can cause delays in the delivery of events.
///
/// # Errors
///
/// If the operating system does not support the specified mode, this function will return
/// an error.
pub fn modify_with_mode(
&self,
source: impl AsSource,
interest: Event,
mode: PollMode,
) -> io::Result<()> {
if interest.key == NOTIFY_KEY {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"the key is not allowed to be `usize::MAX`",
));
}
self.poller.modify(source.source(), interest, mode)
}
/// Removes a file descriptor or socket from the poller.
///
/// Unlike [`add()`][`Poller::add()`], this method only removes the file descriptor or
/// socket from the poller without putting it back into blocking mode.
///
/// # Examples
///
/// ```
/// use polling::{Event, Poller};
/// use std::net::TcpListener;
///
/// let socket = TcpListener::bind("127.0.0.1:0")?;
/// socket.set_nonblocking(true)?;
/// let key = 7;
///
/// let poller = Poller::new()?;
/// unsafe { poller.add(&socket, Event::all(key))?; }
/// poller.delete(&socket)?;
/// # std::io::Result::Ok(())
/// ```
pub fn delete(&self, source: impl AsSource) -> io::Result<()> {
self.poller.delete(source.source())
}
/// Waits for at least one I/O event and returns the number of new events.
///
/// New events will be appended to `events`. If necessary, make sure to clear the
/// [`Events`][Events::clear()] before calling [`wait()`][`Poller::wait()`]!
///
/// This method will return with no new events if a notification is delivered by the
/// [`notify()`] method, or the timeout is reached. Sometimes it may even return with no events
/// spuriously.
///
/// Only one thread can wait on I/O. If another thread is already in [`wait()`], concurrent
/// calls to this method will return immediately with no new events.
///
/// If the operating system is ready to deliver a large number of events at once, this method
/// may decide to deliver them in smaller batches.
///
/// [`notify()`]: `Poller::notify()`
/// [`wait()`]: `Poller::wait()`
///
/// # Examples
///
/// ```
/// use polling::{Event, Events, Poller};
/// use std::net::TcpListener;
/// use std::time::Duration;
///
/// let socket = TcpListener::bind("127.0.0.1:0")?;
/// socket.set_nonblocking(true)?;
/// let key = 7;
///
/// let poller = Poller::new()?;
/// unsafe {
/// poller.add(&socket, Event::all(key))?;
/// }
///
/// let mut events = Events::new();
/// let n = poller.wait(&mut events, Some(Duration::from_secs(1)))?;
/// poller.delete(&socket)?;
/// # std::io::Result::Ok(())
/// ```
pub fn wait(&self, events: &mut Events, timeout: Option<Duration>) -> io::Result<usize> {
let span = tracing::trace_span!("Poller::wait", ?timeout);
let _enter = span.enter();
if let Ok(_lock) = self.lock.try_lock() {
let deadline = timeout.and_then(|timeout| Instant::now().checked_add(timeout));
loop {
// Figure out how long to wait for.
let timeout =
deadline.map(|deadline| deadline.saturating_duration_since(Instant::now()));
// Wait for I/O events.
if let Err(e) = self.poller.wait(&mut events.events, timeout) {
// If the wait was interrupted by a signal, clear events and try again.
if e.kind() == io::ErrorKind::Interrupted {
events.clear();
continue;
} else {
return Err(e);
}
}
// Clear the notification, if any.
self.notified.swap(false, Ordering::SeqCst);
// Indicate number of events.
return Ok(events.len());
}
} else {
tracing::trace!("wait: skipping because another thread is already waiting on I/O");
Ok(0)
}
}
/// Wakes up the current or the following invocation of [`wait()`].
///
/// If no thread is calling [`wait()`] right now, this method will cause the following call
/// to wake up immediately.
///
/// [`wait()`]: `Poller::wait()`
///
/// # Examples
///
/// ```
/// use polling::{Events, Poller};
///
/// let poller = Poller::new()?;
///
/// // Notify the poller.
/// poller.notify()?;
///
/// let mut events = Events::new();
/// poller.wait(&mut events, None)?; // wakes up immediately
/// assert!(events.is_empty());
/// # std::io::Result::Ok(())
/// ```
pub fn notify(&self) -> io::Result<()> {
let span = tracing::trace_span!("Poller::notify");
let _enter = span.enter();
if self
.notified
.compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst)
.is_ok()
{
self.poller.notify()?;
}
Ok(())
}
}
/// A container for I/O events.
pub struct Events {
events: sys::Events,
/// This is intended to be used from &mut, thread locally, so we should make it !Sync
/// for consistency with the rest of the API.
_not_sync: PhantomData<Cell<()>>,
}
impl Default for Events {
#[inline]
fn default() -> Self {
Self::new()
}
}
impl Events {
/// Create a new container for events, using the default capacity.
///
/// The default capacity is 1024.
///
/// # Examples
///
/// ```
/// use polling::Events;
///
/// let events = Events::new();
/// ```
#[inline]
pub fn new() -> Self {
// ESP-IDF has a low amount of RAM, so we use a smaller default capacity.
#[cfg(target_os = "espidf")]
const DEFAULT_CAPACITY: usize = 32;
#[cfg(not(target_os = "espidf"))]
const DEFAULT_CAPACITY: usize = 1024;
Self::with_capacity(NonZeroUsize::new(DEFAULT_CAPACITY).unwrap())
}
/// Create a new container with the provided capacity.
///
/// # Examples
///
/// ```
/// use polling::Events;
/// use std::num::NonZeroUsize;
///
/// let capacity = NonZeroUsize::new(1024).unwrap();
/// let events = Events::with_capacity(capacity);
/// ```
#[inline]
pub fn with_capacity(capacity: NonZeroUsize) -> Self {
Self {
events: sys::Events::with_capacity(capacity.get()),
_not_sync: PhantomData,
}
}
/// Create a new iterator over I/O events.
///
/// This returns all of the events in the container, excluding the notification event.
///
/// # Examples
///
/// ```
/// use polling::{Event, Events, Poller};
/// use std::time::Duration;
///
/// # fn main() -> std::io::Result<()> {
/// let poller = Poller::new()?;
/// let mut events = Events::new();
///
/// poller.wait(&mut events, Some(Duration::from_secs(0)))?;
/// assert!(events.iter().next().is_none());
/// # Ok(()) }
/// ```
#[inline]
pub fn iter(&self) -> impl Iterator<Item = Event> + '_ {
self.events.iter().filter(|ev| ev.key != NOTIFY_KEY)
}
/// Delete all of the events in the container.
///
/// # Examples
///
/// ```no_run
/// use polling::{Event, Events, Poller};
///
/// # fn main() -> std::io::Result<()> {
/// let poller = Poller::new()?;
/// let mut events = Events::new();
///
/// /* register some sources */
///
/// poller.wait(&mut events, None)?;
///
/// events.clear();
/// # Ok(()) }
/// ```
#[inline]
pub fn clear(&mut self) {
self.events.clear();
}
/// Returns the number of events in the container.
///
/// # Examples
///
/// ```
/// use polling::Events;
///
/// let events = Events::new();
/// assert_eq!(events.len(), 0);
/// ```
#[inline]
pub fn len(&self) -> usize {
self.iter().count()
}
/// Returns `true` if the container contains no events.
///
/// # Examples
///
/// ```
/// use polling::Events;
///
/// let events = Events::new();
/// assert!(events.is_empty());
/// ```
#[inline]
pub fn is_empty(&self) -> bool {
self.len() == 0
}
/// Get the total capacity of the list.
///
/// # Examples
///
/// ```
/// use polling::Events;
/// use std::num::NonZeroUsize;
///
/// let cap = NonZeroUsize::new(10).unwrap();
/// let events = Events::with_capacity(std::num::NonZeroUsize::new(10).unwrap());
/// assert_eq!(events.capacity(), cap);
/// ```
#[inline]
pub fn capacity(&self) -> NonZeroUsize {
NonZeroUsize::new(self.events.capacity()).unwrap()
}
}
impl fmt::Debug for Events {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("Events { .. }")
}
}
#[cfg(all(
any(
target_os = "linux",
target_os = "android",
target_os = "illumos",
target_os = "solaris",
target_os = "macos",
target_os = "ios",
target_os = "tvos",
target_os = "watchos",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd",
target_os = "dragonfly",
),
not(polling_test_poll_backend),
))]
#[cfg_attr(
docsrs,
doc(cfg(any(
target_os = "linux",
target_os = "android",
target_os = "illumos",
target_os = "solaris",
target_os = "macos",
target_os = "ios",
target_os = "tvos",
target_os = "watchos",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd",
target_os = "dragonfly",
)))
)]
mod raw_fd_impl {
use crate::Poller;
use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, RawFd};
impl AsRawFd for Poller {
fn as_raw_fd(&self) -> RawFd {
self.poller.as_raw_fd()
}
}
impl AsFd for Poller {
fn as_fd(&self) -> BorrowedFd<'_> {
self.poller.as_fd()
}
}
}
#[cfg(windows)]
#[cfg_attr(docsrs, doc(cfg(windows)))]
mod raw_handle_impl {
use crate::Poller;
use std::os::windows::io::{AsHandle, AsRawHandle, BorrowedHandle, RawHandle};
impl AsRawHandle for Poller {
fn as_raw_handle(&self) -> RawHandle {
self.poller.as_raw_handle()
}
}
impl AsHandle for Poller {
fn as_handle(&self) -> BorrowedHandle<'_> {
self.poller.as_handle()
}
}
}
impl fmt::Debug for Poller {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.poller.fmt(f)
}
}
cfg_if! {
if #[cfg(any(unix, target_os = "hermit"))] {
#[cfg(unix)]
use std::os::unix::io::{AsRawFd, RawFd, AsFd, BorrowedFd};
#[cfg(target_os = "hermit")]
use std::os::hermit::io::{AsRawFd, RawFd, AsFd, BorrowedFd};
/// A resource with a raw file descriptor.
pub trait AsRawSource {
/// Returns the raw file descriptor.
fn raw(&self) -> RawFd;
}
impl<T: AsRawFd> AsRawSource for &T {
fn raw(&self) -> RawFd {
self.as_raw_fd()
}
}
impl AsRawSource for RawFd {
fn raw(&self) -> RawFd {
*self
}
}
/// A resource with a borrowed file descriptor.
pub trait AsSource: AsFd {
/// Returns the borrowed file descriptor.
fn source(&self) -> BorrowedFd<'_> {
self.as_fd()
}
}
impl<T: AsFd> AsSource for T {}
} else if #[cfg(windows)] {
use std::os::windows::io::{AsRawSocket, RawSocket, AsSocket, BorrowedSocket};
/// A resource with a raw socket.
pub trait AsRawSource {
/// Returns the raw socket.
fn raw(&self) -> RawSocket;
}
impl<T: AsRawSocket> AsRawSource for &T {
fn raw(&self) -> RawSocket {
self.as_raw_socket()
}
}
impl AsRawSource for RawSocket {
fn raw(&self) -> RawSocket {
*self
}
}
/// A resource with a borrowed socket.
pub trait AsSource: AsSocket {
/// Returns the borrowed socket.
fn source(&self) -> BorrowedSocket<'_> {
self.as_socket()
}
}
impl<T: AsSocket> AsSource for T {}
}
}
#[allow(unused)]
fn unsupported_error(err: impl Into<String>) -> io::Error {
io::Error::new(io::ErrorKind::Unsupported, err.into())
}
fn _assert_send_and_sync() {
fn assert_send<T: Send>() {}
fn assert_sync<T: Sync>() {}
assert_send::<Poller>();
assert_sync::<Poller>();
assert_send::<Event>();
assert_sync::<Event>();
assert_send::<Events>();
// Events can be !Sync
}