#![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;
use std::usize;
use cfg_if::cfg_if;
cfg_if! {
if #[cfg(polling_test_poll_backend)] {
mod poll;
use poll as sys;
} else if #[cfg(any(target_os = "linux", target_os = "android"))] {
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 = "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;
const NOTIFY_KEY: usize = std::usize::MAX;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Event {
pub key: usize,
pub readable: bool,
pub writable: bool,
extra: sys::EventExtra,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[non_exhaustive]
pub enum PollMode {
Oneshot,
Level,
Edge,
EdgeOneshot,
}
impl Event {
pub const fn new(key: usize, readable: bool, writable: bool) -> Event {
Event {
key,
readable,
writable,
extra: sys::EventExtra::empty(),
}
}
#[inline]
pub const fn all(key: usize) -> Event {
Event::new(key, true, true)
}
#[inline]
pub const fn readable(key: usize) -> Event {
Event::new(key, true, false)
}
#[inline]
pub const fn writable(key: usize) -> Event {
Event::new(key, false, true)
}
#[inline]
pub const fn none(key: usize) -> Event {
Event::new(key, false, false)
}
#[inline]
pub fn set_interrupt(&mut self, active: bool) {
self.extra.set_hup(active);
}
#[inline]
pub fn with_interrupt(mut self) -> Self {
self.set_interrupt(true);
self
}
#[inline]
pub fn set_priority(&mut self, active: bool) {
self.extra.set_pri(active);
}
#[inline]
pub fn with_priority(mut self) -> Self {
self.set_priority(true);
self
}
#[inline]
pub fn is_interrupt(&self) -> bool {
self.extra.is_hup()
}
#[inline]
pub fn is_priority(&self) -> bool {
self.extra.is_pri()
}
#[inline]
pub fn clear_extra(&mut self) {
self.extra = sys::EventExtra::empty();
}
#[inline]
pub fn with_no_extra(mut self) -> Self {
self.clear_extra();
self
}
}
pub struct Poller {
poller: sys::Poller,
lock: Mutex<()>,
notified: AtomicBool,
}
impl Poller {
pub fn new() -> io::Result<Poller> {
Ok(Poller {
poller: sys::Poller::new()?,
lock: Mutex::new(()),
notified: AtomicBool::new(false),
})
}
pub fn supports_level(&self) -> bool {
self.poller.supports_level()
}
pub fn supports_edge(&self) -> bool {
self.poller.supports_edge()
}
pub unsafe fn add(&self, source: impl AsRawSource, interest: Event) -> io::Result<()> {
self.add_with_mode(source, interest, PollMode::Oneshot)
}
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)
}
pub fn modify(&self, source: impl AsSource, interest: Event) -> io::Result<()> {
self.modify_with_mode(source, interest, PollMode::Oneshot)
}
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)
}
pub fn delete(&self, source: impl AsSource) -> io::Result<()> {
self.poller.delete(source.source())
}
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() {
self.poller.wait(&mut events.events, timeout)?;
self.notified.swap(false, Ordering::SeqCst);
Ok(events.len())
} else {
tracing::trace!("wait: skipping because another thread is already waiting on I/O");
Ok(0)
}
}
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(())
}
}
pub struct Events {
events: sys::Events,
_not_sync: PhantomData<Cell<()>>,
}
impl Default for Events {
#[inline]
fn default() -> Self {
Self::new()
}
}
impl Events {
#[inline]
pub fn new() -> Self {
#[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())
}
#[inline]
pub fn with_capacity(capacity: NonZeroUsize) -> Self {
Self {
events: sys::Events::with_capacity(capacity.get()),
_not_sync: PhantomData,
}
}
#[inline]
pub fn iter(&self) -> impl Iterator<Item = Event> + '_ {
self.events.iter().filter(|ev| ev.key != NOTIFY_KEY)
}
#[inline]
pub fn clear(&mut self) {
self.events.clear();
}
#[inline]
pub fn len(&self) -> usize {
self.iter().count()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.len() == 0
}
#[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(unix)] {
use std::os::unix::io::{AsRawFd, RawFd, AsFd, BorrowedFd};
pub trait AsRawSource {
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
}
}
pub trait AsSource: AsFd {
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};
pub trait AsRawSource {
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
}
}
pub trait AsSource: AsSocket {
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>();
}