use std::collections::HashMap;
use std::sync::atomic::{AtomicI32, Ordering::*};
use std::sync::Mutex;
#[cfg(not(windows))]
use std::os::unix::io::RawFd;
#[cfg(windows)]
type RawFd = i32;
use super::g::G;
struct GRaw(usize);
unsafe impl Send for GRaw {}
static REG: Mutex<Option<HashMap<RawFd, (GRaw, u32)>>> = Mutex::new(None);
fn with_reg<F, R>(f: F) -> R
where
F: FnOnce(&mut HashMap<RawFd, (GRaw, u32)>) -> R,
{
let mut guard = REG.lock().unwrap();
f(guard.get_or_insert_with(HashMap::new))
}
pub(crate) const POLL_READ: u32 = 1;
pub(crate) const POLL_WRITE: u32 = 2;
static POLL_FD: AtomicI32 = AtomicI32::new(-1);
pub(crate) fn netpoll_init() {
#[cfg(not(windows))]
{
if POLL_FD.load(Relaxed) >= 0 {
return;
}
let fd = create_poll_fd();
assert!(fd >= 0, "netpoll_init: could not create poll fd");
if POLL_FD
.compare_exchange(-1, fd, AcqRel, Relaxed)
.is_err()
{
unsafe { libc::close(fd) };
}
}
}
pub(crate) unsafe fn netpoll_arm(fd: RawFd, mode: u32, gp: *mut G) {
let pfd = POLL_FD.load(Acquire);
if pfd < 0 {
return;
}
with_reg(|reg| {
reg.insert(fd, (GRaw(gp as usize), mode));
});
unsafe { poll_add(pfd, fd, mode) };
}
pub(crate) fn netpoll_unarm(fd: RawFd) {
let pfd = POLL_FD.load(Acquire);
with_reg(|reg| { reg.remove(&fd); });
if pfd >= 0 {
unsafe { poll_del(pfd, fd) };
}
}
pub(crate) unsafe fn netpoll_wait(timeout_ms: i32) -> Vec<*mut G> {
let pfd = POLL_FD.load(Acquire);
if pfd < 0 {
return Vec::new();
}
let ready_fds = unsafe { poll_wait(pfd, timeout_ms) };
let mut goroutines = Vec::with_capacity(ready_fds.len());
with_reg(|reg| {
for fd in &ready_fds {
if let Some((g_raw, _mode)) = reg.remove(fd) {
goroutines.push(g_raw.0 as *mut G);
unsafe { poll_del(pfd, *fd) };
}
}
});
goroutines
}
#[cfg(target_os = "linux")]
fn create_poll_fd() -> RawFd {
unsafe { libc::epoll_create1(libc::EPOLL_CLOEXEC) }
}
#[cfg(target_os = "linux")]
unsafe fn poll_add(epfd: RawFd, fd: RawFd, mode: u32) {
let mut ev = libc::epoll_event {
events: {
let mut e = libc::EPOLLONESHOT as u32;
if mode & POLL_READ != 0 { e |= libc::EPOLLIN as u32; }
if mode & POLL_WRITE != 0 { e |= libc::EPOLLOUT as u32; }
e
},
u64: fd as u64,
};
unsafe {
let ret = libc::epoll_ctl(epfd, libc::EPOLL_CTL_ADD, fd, &mut ev);
if ret < 0 {
libc::epoll_ctl(epfd, libc::EPOLL_CTL_MOD, fd, &mut ev);
}
}
}
#[cfg(target_os = "linux")]
unsafe fn poll_del(epfd: RawFd, fd: RawFd) {
unsafe {
libc::epoll_ctl(epfd, libc::EPOLL_CTL_DEL, fd, std::ptr::null_mut());
}
}
#[cfg(target_os = "linux")]
unsafe fn poll_wait(epfd: RawFd, timeout_ms: i32) -> Vec<RawFd> {
const MAX_EVENTS: usize = 128;
let mut events = [libc::epoll_event { events: 0, u64: 0 }; MAX_EVENTS];
let n = unsafe {
libc::epoll_wait(epfd, events.as_mut_ptr(), MAX_EVENTS as i32, timeout_ms)
};
if n <= 0 {
return Vec::new();
}
(0..n as usize).map(|i| events[i].u64 as RawFd).collect()
}
#[cfg(target_os = "macos")]
fn create_poll_fd() -> RawFd {
unsafe { libc::kqueue() }
}
#[cfg(target_os = "macos")]
unsafe fn poll_add(kq: RawFd, fd: RawFd, mode: u32) {
let mut changes: [libc::kevent; 2] = unsafe { std::mem::zeroed() };
let mut n = 0usize;
if mode & POLL_READ != 0 {
changes[n] = libc::kevent {
ident: fd as libc::uintptr_t,
filter: libc::EVFILT_READ,
flags: libc::EV_ADD | libc::EV_ONESHOT,
fflags: 0,
data: 0,
udata: std::ptr::null_mut(),
};
n += 1;
}
if mode & POLL_WRITE != 0 {
changes[n] = libc::kevent {
ident: fd as libc::uintptr_t,
filter: libc::EVFILT_WRITE,
flags: libc::EV_ADD | libc::EV_ONESHOT,
fflags: 0,
data: 0,
udata: std::ptr::null_mut(),
};
n += 1;
}
if n > 0 {
unsafe {
libc::kevent(
kq,
changes.as_ptr(),
n as libc::c_int,
std::ptr::null_mut(),
0,
std::ptr::null(),
);
}
}
}
#[cfg(target_os = "macos")]
unsafe fn poll_del(kq: RawFd, fd: RawFd) {
let changes = [
libc::kevent {
ident: fd as libc::uintptr_t,
filter: libc::EVFILT_READ,
flags: libc::EV_DELETE,
fflags: 0,
data: 0,
udata: std::ptr::null_mut(),
},
libc::kevent {
ident: fd as libc::uintptr_t,
filter: libc::EVFILT_WRITE,
flags: libc::EV_DELETE,
fflags: 0,
data: 0,
udata: std::ptr::null_mut(),
},
];
unsafe {
libc::kevent(
kq,
changes.as_ptr(),
2,
std::ptr::null_mut(),
0,
std::ptr::null(),
);
}
}
#[cfg(target_os = "macos")]
unsafe fn poll_wait(kq: RawFd, timeout_ms: i32) -> Vec<RawFd> {
const MAX_EVENTS: usize = 128;
let mut events: [libc::kevent; MAX_EVENTS] = unsafe { std::mem::zeroed() };
let ts;
let ts_ptr;
if timeout_ms < 0 {
ts_ptr = std::ptr::null();
} else {
ts = libc::timespec {
tv_sec: (timeout_ms / 1000) as libc::time_t,
tv_nsec: ((timeout_ms % 1000) * 1_000_000) as libc::c_long,
};
ts_ptr = &ts as *const libc::timespec;
}
let n = unsafe {
libc::kevent(
kq,
std::ptr::null(),
0,
events.as_mut_ptr(),
MAX_EVENTS as libc::c_int,
ts_ptr,
)
};
if n <= 0 {
return Vec::new();
}
(0..n as usize)
.map(|i| events[i].ident as RawFd)
.collect()
}
#[cfg(windows)]
fn create_poll_fd() -> RawFd { -1 }
#[cfg(windows)]
unsafe fn poll_add(_pfd: RawFd, _fd: RawFd, _mode: u32) {}
#[cfg(windows)]
unsafe fn poll_del(_pfd: RawFd, _fd: RawFd) {}
#[cfg(windows)]
unsafe fn poll_wait(_pfd: RawFd, _timeout_ms: i32) -> Vec<RawFd> { Vec::new() }