use crate::error::{Error, Result};
use std::collections::HashMap;
use std::io;
use std::os::raw::c_int;
use std::sync::{Arc, Mutex};
use std::task::Waker;
use std::time::Duration;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Interest {
pub readable: bool,
pub writable: bool,
}
impl Interest {
pub const READABLE: Interest = Interest {
readable: true,
writable: false,
};
pub const WRITABLE: Interest = Interest {
readable: false,
writable: true,
};
pub const READ_WRITE: Interest = Interest {
readable: true,
writable: true,
};
pub fn contains(&self, other: Interest) -> bool {
(other.readable && self.readable) || (other.writable && self.writable)
}
}
#[derive(Debug, Clone, Copy)]
pub struct Event {
pub token: usize,
pub readable: bool,
pub writable: bool,
}
pub struct Reactor {
inner: PlatformReactor,
wakers: Arc<Mutex<HashMap<usize, Waker>>>,
}
impl Reactor {
pub fn new() -> Result<Self> {
Ok(Self {
inner: PlatformReactor::new()?,
wakers: Arc::new(Mutex::new(HashMap::new())),
})
}
pub fn register(&mut self, fd: c_int, token: usize, interest: Interest) -> Result<()> {
self.inner.register(fd, token, interest)
}
pub fn deregister(&mut self, fd: c_int) -> Result<()> {
self.inner.deregister(fd)
}
pub fn wait(&mut self, events: &mut Vec<Event>, timeout: Option<Duration>) -> Result<usize> {
self.inner.wait(events, timeout)
}
pub fn register_waker(&self, token: usize, waker: Waker) {
let mut wakers = self.wakers.lock().unwrap();
wakers.insert(token, waker);
}
pub fn wake_events(&self, events: &[Event]) {
let mut wakers = self.wakers.lock().unwrap();
for event in events {
if let Some(waker) = wakers.remove(&event.token) {
waker.wake();
}
}
}
}
impl Default for Reactor {
fn default() -> Self {
Self::new().expect("Failed to create reactor")
}
}
#[cfg(target_os = "linux")]
mod platform {
use super::*;
use std::os::unix::io::RawFd;
const EPOLL_CTL_ADD: c_int = 1;
const EPOLL_CTL_DEL: c_int = 2;
const EPOLL_CTL_MOD: c_int = 3;
const EPOLLIN: u32 = 0x001;
const EPOLLOUT: u32 = 0x004;
const EPOLLET: u32 = 1 << 31;
#[repr(C)]
union EpollData {
ptr: *mut (),
fd: c_int,
u32_: u32,
u64_: u64,
}
#[repr(C)]
struct EpollEvent {
events: u32,
data: EpollData,
}
extern "C" {
fn epoll_create1(flags: c_int) -> c_int;
fn epoll_ctl(epfd: c_int, op: c_int, fd: c_int, event: *mut EpollEvent) -> c_int;
fn epoll_wait(epfd: c_int, events: *mut EpollEvent, maxevents: c_int, timeout: c_int) -> c_int;
}
pub struct PlatformReactor {
epoll_fd: RawFd,
}
impl PlatformReactor {
pub fn new() -> Result<Self> {
let epoll_fd = unsafe { epoll_create1(0) };
if epoll_fd < 0 {
return Err(Error::Internal {
message: format!("Failed to create epoll: {}", io::Error::last_os_error()),
});
}
Ok(Self { epoll_fd })
}
pub fn register(&mut self, fd: c_int, token: usize, interest: Interest) -> Result<()> {
let mut events = 0u32;
if interest.readable {
events |= EPOLLIN;
}
if interest.writable {
events |= EPOLLOUT;
}
events |= EPOLLET;
let mut event = EpollEvent {
events,
data: EpollData { u64_: token as u64 },
};
let result = unsafe { epoll_ctl(self.epoll_fd, EPOLL_CTL_ADD, fd, &mut event) };
if result < 0 {
return Err(Error::Internal {
message: format!("Failed to register fd: {}", io::Error::last_os_error()),
});
}
Ok(())
}
pub fn deregister(&mut self, fd: c_int) -> Result<()> {
let result = unsafe { epoll_ctl(self.epoll_fd, EPOLL_CTL_DEL, fd, std::ptr::null_mut()) };
if result < 0 {
return Err(Error::Internal {
message: format!("Failed to deregister fd: {}", io::Error::last_os_error()),
});
}
Ok(())
}
pub fn wait(&mut self, events: &mut Vec<Event>, timeout: Option<Duration>) -> Result<usize> {
let timeout_ms = timeout
.map(|d| d.as_millis() as c_int)
.unwrap_or(-1);
let mut raw_events = vec![
EpollEvent {
events: 0,
data: EpollData { u64_: 0 },
};
128
];
let n = unsafe {
epoll_wait(
self.epoll_fd,
raw_events.as_mut_ptr(),
raw_events.len() as c_int,
timeout_ms,
)
};
if n < 0 {
return Err(Error::Internal {
message: format!("epoll_wait failed: {}", io::Error::last_os_error()),
});
}
events.clear();
for i in 0..n as usize {
let raw_event = &raw_events[i];
let token = unsafe { raw_event.data.u64_ } as usize;
events.push(Event {
token,
readable: (raw_event.events & EPOLLIN) != 0,
writable: (raw_event.events & EPOLLOUT) != 0,
});
}
Ok(n as usize)
}
}
impl Drop for PlatformReactor {
fn drop(&mut self) {
unsafe {
libc::close(self.epoll_fd);
}
}
}
}
#[cfg(any(target_os = "macos", target_os = "freebsd", target_os = "openbsd"))]
mod platform {
use super::*;
use std::os::unix::io::RawFd;
const EVFILT_READ: i16 = -1;
const EVFILT_WRITE: i16 = -2;
const EV_ADD: u16 = 0x0001;
const EV_DELETE: u16 = 0x0002;
const EV_CLEAR: u16 = 0x0020;
#[repr(C)]
struct Kevent {
ident: usize,
filter: i16,
flags: u16,
fflags: u32,
data: isize,
udata: *mut (),
}
#[repr(C)]
struct Timespec {
tv_sec: isize,
tv_nsec: isize,
}
extern "C" {
fn kqueue() -> c_int;
fn kevent(
kq: c_int,
changelist: *const Kevent,
nchanges: c_int,
eventlist: *mut Kevent,
nevents: c_int,
timeout: *const Timespec,
) -> c_int;
}
pub struct PlatformReactor {
kqueue_fd: RawFd,
}
impl PlatformReactor {
pub fn new() -> Result<Self> {
let kqueue_fd = unsafe { kqueue() };
if kqueue_fd < 0 {
return Err(Error::Internal {
message: format!("Failed to create kqueue: {}", io::Error::last_os_error()),
});
}
Ok(Self { kqueue_fd })
}
pub fn register(&mut self, fd: c_int, token: usize, interest: Interest) -> Result<()> {
let mut changes = Vec::new();
if interest.readable {
changes.push(Kevent {
ident: fd as usize,
filter: EVFILT_READ,
flags: EV_ADD | EV_CLEAR,
fflags: 0,
data: 0,
udata: token as *mut (),
});
}
if interest.writable {
changes.push(Kevent {
ident: fd as usize,
filter: EVFILT_WRITE,
flags: EV_ADD | EV_CLEAR,
fflags: 0,
data: 0,
udata: token as *mut (),
});
}
let result = unsafe {
kevent(
self.kqueue_fd,
changes.as_ptr(),
changes.len() as c_int,
std::ptr::null_mut(),
0,
std::ptr::null(),
)
};
if result < 0 {
return Err(Error::Internal {
message: format!("Failed to register fd: {}", io::Error::last_os_error()),
});
}
Ok(())
}
pub fn deregister(&mut self, fd: c_int) -> Result<()> {
let changes = vec![
Kevent {
ident: fd as usize,
filter: EVFILT_READ,
flags: EV_DELETE,
fflags: 0,
data: 0,
udata: std::ptr::null_mut(),
},
Kevent {
ident: fd as usize,
filter: EVFILT_WRITE,
flags: EV_DELETE,
fflags: 0,
data: 0,
udata: std::ptr::null_mut(),
},
];
let result = unsafe {
kevent(
self.kqueue_fd,
changes.as_ptr(),
changes.len() as c_int,
std::ptr::null_mut(),
0,
std::ptr::null(),
)
};
if result < 0 {
return Err(Error::Internal {
message: format!("Failed to deregister fd: {}", io::Error::last_os_error()),
});
}
Ok(())
}
pub fn wait(&mut self, events: &mut Vec<Event>, timeout: Option<Duration>) -> Result<usize> {
let timeout_spec = timeout.map(|d| Timespec {
tv_sec: d.as_secs() as isize,
tv_nsec: d.subsec_nanos() as isize,
});
let timeout_ptr = timeout_spec
.as_ref()
.map(|t| t as *const Timespec)
.unwrap_or(std::ptr::null());
let mut raw_events = vec![
Kevent {
ident: 0,
filter: 0,
flags: 0,
fflags: 0,
data: 0,
udata: std::ptr::null_mut(),
};
128
];
let n = unsafe {
kevent(
self.kqueue_fd,
std::ptr::null(),
0,
raw_events.as_mut_ptr(),
raw_events.len() as c_int,
timeout_ptr,
)
};
if n < 0 {
return Err(Error::Internal {
message: format!("kevent failed: {}", io::Error::last_os_error()),
});
}
events.clear();
let mut event_map: HashMap<usize, Event> = HashMap::new();
for i in 0..n as usize {
let raw_event = &raw_events[i];
let token = raw_event.udata as usize;
let event = event_map.entry(token).or_insert(Event {
token,
readable: false,
writable: false,
});
if raw_event.filter == EVFILT_READ {
event.readable = true;
}
if raw_event.filter == EVFILT_WRITE {
event.writable = true;
}
}
events.extend(event_map.into_values());
Ok(events.len())
}
}
impl Drop for PlatformReactor {
fn drop(&mut self) {
unsafe {
libc::close(self.kqueue_fd);
}
}
}
}
#[cfg(target_os = "windows")]
mod platform {
use super::*;
use std::ptr;
use std::mem;
type HANDLE = *mut std::ffi::c_void;
type DWORD = u32;
type ULONG_PTR = usize;
type LPOVERLAPPED = *mut OVERLAPPED;
#[repr(C)]
struct OVERLAPPED {
internal: ULONG_PTR,
internal_high: ULONG_PTR,
offset: DWORD,
offset_high: DWORD,
h_event: HANDLE,
}
#[repr(C)]
struct OVERLAPPED_ENTRY {
lp_completion_key: ULONG_PTR,
lp_overlapped: LPOVERLAPPED,
internal: ULONG_PTR,
dw_number_of_bytes_transferred: DWORD,
}
const INVALID_HANDLE_VALUE: HANDLE = (-1isize) as HANDLE;
#[link(name = "kernel32")]
extern "system" {
fn CreateIoCompletionPort(
file_handle: HANDLE,
existing_completion_port: HANDLE,
completion_key: ULONG_PTR,
number_of_concurrent_threads: DWORD,
) -> HANDLE;
fn GetQueuedCompletionStatusEx(
completion_port: HANDLE,
lp_completion_port_entries: *mut OVERLAPPED_ENTRY,
ul_count: DWORD,
ul_num_entries_removed: *mut DWORD,
dw_milliseconds: DWORD,
f_alertable: i32,
) -> i32;
fn PostQueuedCompletionStatus(
completion_port: HANDLE,
dw_number_of_bytes_transferred: DWORD,
dw_completion_key: ULONG_PTR,
lp_overlapped: LPOVERLAPPED,
) -> i32;
fn CloseHandle(h_object: HANDLE) -> i32;
}
pub struct PlatformReactor {
iocp_handle: HANDLE,
registered_sockets: std::collections::HashMap<c_int, (usize, Interest)>,
}
impl PlatformReactor {
pub fn new() -> Result<Self> {
unsafe {
let iocp = CreateIoCompletionPort(
INVALID_HANDLE_VALUE,
ptr::null_mut(),
0,
0,
);
if iocp.is_null() {
return Err(Error::Internal {
message: "Failed to create IOCP".to_string(),
});
}
Ok(Self {
iocp_handle: iocp,
registered_sockets: std::collections::HashMap::new(),
})
}
}
pub fn register(&mut self, fd: c_int, token: usize, interest: Interest) -> Result<()> {
unsafe {
let handle = fd as HANDLE;
let result = CreateIoCompletionPort(
handle,
self.iocp_handle,
token as ULONG_PTR,
0,
);
if result.is_null() {
return Err(Error::Internal {
message: format!("Failed to register fd {} with IOCP", fd),
});
}
self.registered_sockets.insert(fd, (token, interest));
Ok(())
}
}
pub fn deregister(&mut self, fd: c_int) -> Result<()> {
self.registered_sockets.remove(&fd);
Ok(())
}
pub fn wait(&mut self, events: &mut Vec<Event>, timeout: Option<Duration>) -> Result<usize> {
events.clear();
let timeout_ms = match timeout {
Some(d) => d.as_millis() as DWORD,
None => !0u32, };
unsafe {
const MAX_EVENTS: usize = 64;
let mut entries: [OVERLAPPED_ENTRY; MAX_EVENTS] = mem::zeroed();
let mut num_entries: DWORD = 0;
let result = GetQueuedCompletionStatusEx(
self.iocp_handle,
entries.as_mut_ptr(),
MAX_EVENTS as DWORD,
&mut num_entries,
timeout_ms,
0,
);
if result == 0 {
let error = std::io::Error::last_os_error();
if error.raw_os_error() == Some(258) { return Ok(0);
}
return Err(Error::Internal {
message: format!("GetQueuedCompletionStatusEx failed: {}", error),
});
}
for i in 0..num_entries as usize {
let entry = &entries[i];
let token = entry.lp_completion_key as usize;
let interest = self.registered_sockets
.values()
.find(|(t, _)| *t == token)
.map(|(_, int)| *int)
.unwrap_or(Interest::READABLE);
let mut event = Event {
token,
readable: false,
writable: false,
};
if interest.contains(Interest::READABLE) {
event.readable = true;
}
if interest.contains(Interest::WRITABLE) {
event.writable = true;
}
events.push(event);
}
Ok(num_entries as usize)
}
}
}
impl Drop for PlatformReactor {
fn drop(&mut self) {
unsafe {
if !self.iocp_handle.is_null() {
CloseHandle(self.iocp_handle);
}
}
}
}
}
#[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "freebsd", target_os = "openbsd", target_os = "windows")))]
mod platform {
use super::*;
pub struct PlatformReactor;
impl PlatformReactor {
pub fn new() -> Result<Self> {
Err(Error::Internal {
message: "Reactor not supported on this platform".to_string(),
})
}
pub fn register(&mut self, _fd: c_int, _token: usize, _interest: Interest) -> Result<()> {
Err(Error::Internal {
message: "Reactor not supported on this platform".to_string(),
})
}
pub fn deregister(&mut self, _fd: c_int) -> Result<()> {
Err(Error::Internal {
message: "Reactor not supported on this platform".to_string(),
})
}
pub fn wait(&mut self, _events: &mut Vec<Event>, _timeout: Option<Duration>) -> Result<usize> {
Err(Error::Internal {
message: "Reactor not supported on this platform".to_string(),
})
}
}
}
use platform::PlatformReactor;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_reactor_creation() {
let result = Reactor::new();
assert!(result.is_ok());
}
#[test]
fn test_interest() {
let readable = Interest::READABLE;
assert!(readable.readable);
assert!(!readable.writable);
let writable = Interest::WRITABLE;
assert!(!writable.readable);
assert!(writable.writable);
let both = Interest::READ_WRITE;
assert!(both.readable);
assert!(both.writable);
}
}