mod pipe;
mod stream;
mod tcp;
mod tty;
mod waker;
#[cfg(all(not(miri), test))]
mod tests;
use std::cell::Cell;
use std::cell::RefCell;
use std::collections::BTreeSet;
use std::collections::HashMap;
use std::collections::VecDeque;
use std::ffi::CStr;
use std::ffi::c_int;
use std::ffi::c_void;
use std::sync::Arc;
use std::task::Context;
use std::task::Waker;
use std::time::Duration;
use std::time::Instant;
pub use pipe::*;
pub use stream::*;
pub use tcp::*;
pub use tty::*;
#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum uv_handle_type {
UV_UNKNOWN_HANDLE = 0,
UV_TIMER = 1,
UV_IDLE = 2,
UV_PREPARE = 3,
UV_CHECK = 4,
UV_NAMED_PIPE = 7,
UV_TCP = 12,
UV_TTY = 13,
UV_UDP = 15,
UV_FILE = 17,
}
const UV_HANDLE_ACTIVE: u32 = 1 << 0;
const UV_HANDLE_REF: u32 = 1 << 1;
const UV_HANDLE_CLOSING: u32 = 1 << 2;
macro_rules! uv_errno {
($name:ident, $unix:expr, $win:expr) => {
#[cfg(unix)]
pub const $name: i32 = -($unix);
#[cfg(windows)]
pub const $name: i32 = $win;
};
}
uv_errno!(UV_EAGAIN, libc::EAGAIN, -4088);
uv_errno!(UV_EBADF, libc::EBADF, -4083);
uv_errno!(UV_EADDRINUSE, libc::EADDRINUSE, -4091);
uv_errno!(UV_ECONNREFUSED, libc::ECONNREFUSED, -4078);
uv_errno!(UV_EINVAL, libc::EINVAL, -4071);
uv_errno!(UV_ENOTCONN, libc::ENOTCONN, -4053);
uv_errno!(UV_ECANCELED, libc::ECANCELED, -4081);
uv_errno!(UV_EPIPE, libc::EPIPE, -4047);
uv_errno!(UV_EBUSY, libc::EBUSY, -4082);
uv_errno!(UV_ENOBUFS, libc::ENOBUFS, -4060);
uv_errno!(UV_ENOTSUP, libc::ENOTSUP, -4049);
uv_errno!(UV_EALREADY, libc::EALREADY, -4084);
uv_errno!(UV_ENOENT, libc::ENOENT, -4058);
uv_errno!(UV_ENOTSOCK, libc::ENOTSOCK, -4050);
uv_errno!(UV_ECONNRESET, libc::ECONNRESET, -4077);
uv_errno!(UV_ECONNABORTED, libc::ECONNABORTED, -4079);
uv_errno!(UV_ETIMEDOUT, libc::ETIMEDOUT, -4039);
uv_errno!(UV_EACCES, libc::EACCES, -4092);
pub const UV_EOF: i32 = -4095;
pub fn uv_error_message(err: c_int) -> Option<&'static CStr> {
let message = match err {
x if x == UV_EAGAIN => c"resource temporarily unavailable",
x if x == UV_EBADF => c"bad file descriptor",
x if x == UV_EADDRINUSE => c"address already in use",
x if x == UV_ECONNREFUSED => c"connection refused",
x if x == UV_EINVAL => c"invalid argument",
x if x == UV_ENOTCONN => c"socket is not connected",
x if x == UV_ECANCELED => c"operation canceled",
x if x == UV_EPIPE => c"broken pipe",
x if x == UV_EBUSY => c"resource busy or locked",
x if x == UV_ENOBUFS => c"no buffer space available",
x if x == UV_ENOTSUP => c"operation not supported on socket",
x if x == UV_EALREADY => c"connection already in progress",
x if x == UV_ENOENT => c"no such file or directory",
x if x == UV_ENOTSOCK => c"socket operation on non-socket",
x if x == UV_ECONNRESET => c"connection reset by peer",
x if x == UV_ECONNABORTED => c"software caused connection abort",
x if x == UV_ETIMEDOUT => c"connection timed out",
x if x == UV_EACCES => c"permission denied",
x if x == UV_EOF => c"end of file",
_ => return None,
};
Some(message)
}
pub(crate) fn io_error_to_uv(err: &std::io::Error) -> c_int {
use std::io::ErrorKind;
#[cfg(windows)]
if let Some(code) = err.raw_os_error() {
match code {
231 => return UV_EBUSY, 536 => return UV_EAGAIN, 230 => return UV_EPIPE, 233 => return UV_EPIPE, _ => {}
}
}
match err.kind() {
ErrorKind::AddrInUse => UV_EADDRINUSE,
ErrorKind::AddrNotAvailable => UV_EINVAL,
ErrorKind::ConnectionRefused => UV_ECONNREFUSED,
ErrorKind::ConnectionReset => UV_ECONNRESET,
ErrorKind::ConnectionAborted => UV_ECONNABORTED,
ErrorKind::NotConnected => UV_ENOTCONN,
ErrorKind::NotFound => UV_ENOENT,
ErrorKind::BrokenPipe => UV_EPIPE,
ErrorKind::InvalidInput => UV_EINVAL,
ErrorKind::WouldBlock => UV_EAGAIN,
ErrorKind::TimedOut => UV_ETIMEDOUT,
ErrorKind::PermissionDenied => UV_EACCES,
_ => {
#[cfg(unix)]
if let Some(code) = err.raw_os_error() {
return -code;
}
#[cfg(windows)]
if let Some(code) = err.raw_os_error() {
return match code {
10054 => UV_ECONNRESET, 10053 => UV_ECONNABORTED, 10061 => UV_ECONNREFUSED, 10048 => UV_EADDRINUSE, 10060 => UV_ETIMEDOUT, 10057 => UV_ENOTCONN, 10038 => UV_ENOTSOCK, 10035 => UV_EAGAIN, _ => UV_EINVAL,
};
}
UV_EINVAL
}
}
}
#[repr(C)]
pub struct uv_loop_t {
internal: *mut c_void,
pub data: *mut c_void,
stop_flag: Cell<bool>,
}
impl Drop for uv_loop_t {
fn drop(&mut self) {
if !self.internal.is_null() {
unsafe {
drop(Box::from_raw(self.internal as *mut UvLoopInner));
}
self.internal = std::ptr::null_mut();
}
}
}
#[repr(C)]
pub struct uv_handle_t {
pub r#type: uv_handle_type,
pub loop_: *mut uv_loop_t,
pub data: *mut c_void,
pub flags: u32,
}
#[repr(C)]
pub struct uv_timer_t {
pub r#type: uv_handle_type,
pub loop_: *mut uv_loop_t,
pub data: *mut c_void,
pub flags: u32,
internal_id: u64,
internal_deadline: u64,
cb: Option<unsafe extern "C" fn(*mut uv_timer_t)>,
timeout: u64,
repeat: u64,
}
#[repr(C)]
pub struct uv_idle_t {
pub r#type: uv_handle_type,
pub loop_: *mut uv_loop_t,
pub data: *mut c_void,
pub flags: u32,
cb: Option<unsafe extern "C" fn(*mut uv_idle_t)>,
}
#[repr(C)]
pub struct uv_prepare_t {
pub r#type: uv_handle_type,
pub loop_: *mut uv_loop_t,
pub data: *mut c_void,
pub flags: u32,
cb: Option<unsafe extern "C" fn(*mut uv_prepare_t)>,
}
#[repr(C)]
pub struct uv_check_t {
pub r#type: uv_handle_type,
pub loop_: *mut uv_loop_t,
pub data: *mut c_void,
pub flags: u32,
cb: Option<unsafe extern "C" fn(*mut uv_check_t)>,
}
pub type uv_timer_cb = unsafe extern "C" fn(*mut uv_timer_t);
pub type uv_idle_cb = unsafe extern "C" fn(*mut uv_idle_t);
pub type uv_prepare_cb = unsafe extern "C" fn(*mut uv_prepare_t);
pub type uv_check_cb = unsafe extern "C" fn(*mut uv_check_t);
pub type uv_close_cb = unsafe extern "C" fn(*mut uv_handle_t);
pub type UvHandle = uv_handle_t;
pub type UvLoop = uv_loop_t;
pub type UvTcp = uv_tcp_t;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
struct TimerKey {
deadline_ms: u64,
id: u64,
}
pub(crate) struct UvLoopInner {
timers: RefCell<BTreeSet<TimerKey>>,
next_timer_id: Cell<u64>,
timer_handles: RefCell<HashMap<u64, *mut uv_timer_t>>,
idle_handles: RefCell<Vec<*mut uv_idle_t>>,
prepare_handles: RefCell<Vec<*mut uv_prepare_t>>,
check_handles: RefCell<Vec<*mut uv_check_t>>,
tcp_handles: RefCell<Vec<*mut uv_tcp_t>>,
pipe_handles: RefCell<Vec<*mut uv_pipe_t>>,
tty_handles: RefCell<Vec<*mut uv_tty_t>>,
waker: RefCell<Option<Waker>>,
closing_handles: RefCell<VecDeque<(*mut uv_handle_t, Option<uv_close_cb>)>>,
time_origin: Instant,
cached_time_ms: Cell<u64>,
pub(crate) shared: std::sync::Arc<waker::LoopShared>,
}
impl UvLoopInner {
fn new() -> Self {
let origin = Instant::now();
Self {
timers: RefCell::new(BTreeSet::new()),
next_timer_id: Cell::new(1),
timer_handles: RefCell::new(HashMap::with_capacity(16)),
idle_handles: RefCell::new(Vec::with_capacity(8)),
prepare_handles: RefCell::new(Vec::with_capacity(8)),
check_handles: RefCell::new(Vec::with_capacity(8)),
tcp_handles: RefCell::new(Vec::with_capacity(8)),
pipe_handles: RefCell::new(Vec::with_capacity(4)),
tty_handles: RefCell::new(Vec::with_capacity(4)),
waker: RefCell::new(None),
closing_handles: RefCell::new(VecDeque::with_capacity(16)),
time_origin: origin,
cached_time_ms: Cell::new(0),
shared: waker::LoopShared::new(),
}
}
pub(crate) fn set_waker(&self, waker: &Waker) {
let mut slot = self.waker.borrow_mut();
match slot.as_ref() {
Some(existing) if existing.will_wake(waker) => {}
_ => *slot = Some(waker.clone()),
}
self.shared.loop_waker.register(waker);
}
#[cfg(windows)]
pub(crate) fn wake(&self) {
if let Some(waker) = self.waker.borrow().as_ref() {
waker.wake_by_ref();
}
}
#[inline]
fn alloc_timer_id(&self) -> u64 {
let id = self.next_timer_id.get();
self.next_timer_id.set(id + 1);
id
}
#[inline]
fn now_ms(&self) -> u64 {
self.cached_time_ms.get()
}
#[inline]
pub(crate) fn update_time(&self) {
let ms = Instant::now().duration_since(self.time_origin).as_millis() as u64;
self.cached_time_ms.set(ms);
}
pub(crate) fn next_timeout(&self) -> Option<(u64, Duration)> {
let deadline = self.timers.borrow().iter().next()?.deadline_ms;
Some((
deadline,
Duration::from_millis(deadline.saturating_sub(self.now_ms())),
))
}
pub(crate) fn has_alive_handles(&self) -> bool {
for (_, handle_ptr) in self.timer_handles.borrow().iter() {
let handle = unsafe { &**handle_ptr };
if handle.flags & UV_HANDLE_ACTIVE != 0
&& handle.flags & UV_HANDLE_REF != 0
{
return true;
}
}
for handle_ptr in self.idle_handles.borrow().iter() {
let handle = unsafe { &**handle_ptr };
if handle.flags & UV_HANDLE_ACTIVE != 0
&& handle.flags & UV_HANDLE_REF != 0
{
return true;
}
}
for handle_ptr in self.prepare_handles.borrow().iter() {
let handle = unsafe { &**handle_ptr };
if handle.flags & UV_HANDLE_ACTIVE != 0
&& handle.flags & UV_HANDLE_REF != 0
{
return true;
}
}
for handle_ptr in self.check_handles.borrow().iter() {
let handle = unsafe { &**handle_ptr };
if handle.flags & UV_HANDLE_ACTIVE != 0
&& handle.flags & UV_HANDLE_REF != 0
{
return true;
}
}
for handle_ptr in self.tcp_handles.borrow().iter() {
let handle = unsafe { &**handle_ptr };
if handle.flags & UV_HANDLE_ACTIVE != 0
&& handle.flags & UV_HANDLE_REF != 0
{
return true;
}
}
for handle_ptr in self.tty_handles.borrow().iter() {
let handle = unsafe { &**handle_ptr };
if handle.flags & UV_HANDLE_ACTIVE != 0
&& handle.flags & UV_HANDLE_REF != 0
{
return true;
}
}
for handle_ptr in self.pipe_handles.borrow().iter() {
let handle = unsafe { &**handle_ptr };
if handle.flags & UV_HANDLE_ACTIVE != 0
&& handle.flags & UV_HANDLE_REF != 0
{
return true;
}
}
if !self.closing_handles.borrow().is_empty() {
return true;
}
false
}
pub(crate) unsafe fn run_timers(&self) {
let now = self.now_ms();
let mut expired = Vec::new();
{
let timers = self.timers.borrow();
for key in timers.iter() {
if key.deadline_ms > now {
break;
}
expired.push(*key);
}
}
for key in expired {
self.timers.borrow_mut().remove(&key);
let handle_ptr = match self.timer_handles.borrow().get(&key.id).copied() {
Some(h) => h,
None => continue,
};
let handle = unsafe { &mut *handle_ptr };
if handle.flags & UV_HANDLE_ACTIVE == 0 {
self.timer_handles.borrow_mut().remove(&key.id);
continue;
}
let cb = handle.cb;
let repeat = handle.repeat;
if repeat > 0 {
let new_deadline = now + repeat;
let new_key = TimerKey {
deadline_ms: new_deadline,
id: key.id,
};
handle.internal_deadline = new_deadline;
self.timers.borrow_mut().insert(new_key);
} else {
handle.flags &= !UV_HANDLE_ACTIVE;
self.timer_handles.borrow_mut().remove(&key.id);
}
if let Some(cb) = cb {
unsafe { cb(handle_ptr) };
}
}
}
pub(crate) unsafe fn run_idle(&self) {
let mut i = 0;
loop {
let handle_ptr = {
let handles = self.idle_handles.borrow();
if i >= handles.len() {
break;
}
handles[i]
};
i += 1;
let handle = unsafe { &*handle_ptr };
if handle.flags & UV_HANDLE_ACTIVE != 0
&& let Some(cb) = handle.cb
{
unsafe { cb(handle_ptr) };
}
}
}
pub(crate) unsafe fn run_prepare(&self) {
let mut i = 0;
loop {
let handle_ptr = {
let handles = self.prepare_handles.borrow();
if i >= handles.len() {
break;
}
handles[i]
};
i += 1;
let handle = unsafe { &*handle_ptr };
if handle.flags & UV_HANDLE_ACTIVE != 0
&& let Some(cb) = handle.cb
{
unsafe { cb(handle_ptr) };
}
}
}
pub(crate) unsafe fn run_check(&self) {
let mut i = 0;
loop {
let handle_ptr = {
let handles = self.check_handles.borrow();
if i >= handles.len() {
break;
}
handles[i]
};
i += 1;
let handle = unsafe { &*handle_ptr };
if handle.flags & UV_HANDLE_ACTIVE != 0
&& let Some(cb) = handle.cb
{
unsafe { cb(handle_ptr) };
}
}
}
pub(crate) unsafe fn run_close(&self) {
let mut closing = self.closing_handles.borrow_mut();
let snapshot: Vec<_> = closing.drain(..).collect();
drop(closing);
for (handle_ptr, cb) in snapshot {
if let Some(cb) = cb {
unsafe { cb(handle_ptr) };
}
}
}
pub(crate) unsafe fn run_io(&self) -> bool {
{
let mut any_work = false;
let mut tcp_ready: VecDeque<Arc<waker::TcpHandleWaker>> =
std::mem::take(&mut *self.shared.ready_tcp.lock().unwrap());
while let Some(handle_waker) = tcp_ready.pop_front() {
handle_waker.reset_queued();
let ptr = handle_waker.live_ptr();
if ptr == 0 {
continue;
}
let tcp_ptr = ptr as *mut uv_tcp_t;
if unsafe { (*tcp_ptr).flags } & UV_HANDLE_ACTIVE == 0 {
continue;
}
let waker = std::task::Waker::from(handle_waker);
let mut cx = Context::from_waker(&waker);
any_work |= unsafe { tcp::poll_tcp_handle(tcp_ptr, &mut cx) };
}
let mut pipe_ready: VecDeque<Arc<waker::PipeHandleWaker>> =
std::mem::take(&mut *self.shared.ready_pipe.lock().unwrap());
while let Some(handle_waker) = pipe_ready.pop_front() {
handle_waker.reset_queued();
let ptr = handle_waker.live_ptr();
if ptr == 0 {
continue;
}
let pipe_ptr = ptr as *mut uv_pipe_t;
if unsafe { (*pipe_ptr).flags } & UV_HANDLE_ACTIVE == 0 {
continue;
}
let waker = std::task::Waker::from(handle_waker);
let mut cx = Context::from_waker(&waker);
any_work |= unsafe { pipe::poll_pipe_handle(pipe_ptr, &mut cx) };
}
let mut tty_ready: VecDeque<Arc<waker::TtyHandleWaker>> =
std::mem::take(&mut *self.shared.ready_tty.lock().unwrap());
while let Some(handle_waker) = tty_ready.pop_front() {
handle_waker.reset_queued();
let ptr = handle_waker.live_ptr();
if ptr == 0 {
continue;
}
let tty_ptr = ptr as *mut uv_tty_t;
if unsafe { (*tty_ptr).flags } & UV_HANDLE_ACTIVE == 0 {
continue;
}
let waker = std::task::Waker::from(handle_waker);
let mut cx = Context::from_waker(&waker);
any_work |= unsafe { tty::poll_tty_handle(tty_ptr, &mut cx) };
}
any_work
}
}
unsafe fn stop_timer(&self, handle: *mut uv_timer_t) {
let handle_ref = unsafe { &mut *handle };
let id = handle_ref.internal_id;
if id != 0 {
let key = TimerKey {
deadline_ms: handle_ref.internal_deadline,
id,
};
self.timers.borrow_mut().remove(&key);
self.timer_handles.borrow_mut().remove(&id);
}
handle_ref.flags &= !UV_HANDLE_ACTIVE;
}
fn stop_idle(&self, handle: *mut uv_idle_t) {
self
.idle_handles
.borrow_mut()
.retain(|&h| !std::ptr::eq(h, handle));
unsafe {
(*handle).flags &= !UV_HANDLE_ACTIVE;
}
}
fn stop_prepare(&self, handle: *mut uv_prepare_t) {
self
.prepare_handles
.borrow_mut()
.retain(|&h| !std::ptr::eq(h, handle));
unsafe {
(*handle).flags &= !UV_HANDLE_ACTIVE;
}
}
fn stop_check(&self, handle: *mut uv_check_t) {
self
.check_handles
.borrow_mut()
.retain(|&h| !std::ptr::eq(h, handle));
unsafe {
(*handle).flags &= !UV_HANDLE_ACTIVE;
}
}
fn stop_tty(&self, handle: *mut uv_tty_t) {
self
.tty_handles
.borrow_mut()
.retain(|&h| !std::ptr::eq(h, handle));
unsafe {
let tty = &mut *handle;
if let Some(w) = tty.internal_waker.take() {
w.detach();
}
#[cfg(unix)]
{
tty::restore_termios_on_close(tty.internal_fd);
}
tty.internal_reading = false;
tty.internal_alloc_cb = None;
tty.internal_read_cb = None;
while let Some(pw) = tty.internal_write_queue.pop_front() {
if let Some(cb) = pw.cb {
cb(pw.req, UV_ECANCELED);
}
}
if let Some(pending) = tty.internal_shutdown.take()
&& let Some(cb) = pending.cb
{
cb(pending.req, UV_ECANCELED);
}
#[cfg(unix)]
{
#[cfg(target_os = "macos")]
if let Some(tty::TtyReactor::SelectFallback(ref mut s)) =
tty.internal_reactor
{
tty::shutdown_select_fallback(s);
}
tty.internal_reactor = None;
if tty.internal_fd > 2 {
libc::close(tty.internal_fd);
tty.internal_fd = -1;
}
}
#[cfg(windows)]
{
tty::close_tty_read(handle);
if !tty.internal_handle.is_null() {
if tty.internal_handle_owned {
tty::win_console::CloseHandle(tty.internal_handle);
} else if tty.internal_fd >= 0 {
tty::win_console::_close(tty.internal_fd);
}
tty.internal_handle = std::ptr::null_mut();
tty.internal_fd = -1;
}
}
tty.flags &= !UV_HANDLE_ACTIVE;
}
}
fn stop_tcp(&self, handle: *mut uv_tcp_t) {
self
.tcp_handles
.borrow_mut()
.retain(|&h| !std::ptr::eq(h, handle));
unsafe {
let tcp = &mut *handle;
if let Some(w) = tcp.internal_waker.take() {
w.detach();
}
tcp.internal_reading = false;
tcp.internal_alloc_cb = None;
tcp.internal_read_cb = None;
tcp.internal_connection_cb = None;
if let Some(pending) = tcp.internal_connect.take()
&& let Some(cb) = pending.cb
{
cb(pending.req, UV_ECANCELED);
}
while let Some(pw) = tcp.internal_write_queue.pop_front() {
if let Some(cb) = pw.cb {
cb(pw.req, UV_ECANCELED);
}
}
drop(tcp.internal_stream.take());
tcp.internal_fd = None;
tcp.internal_socket = None;
tcp.internal_delayed_error = 0;
tcp.internal_listener = None;
tcp.internal_backlog.clear();
if let Some(pending) = tcp.internal_shutdown.take()
&& let Some(cb) = pending.cb
{
cb(pending.req, UV_ECANCELED);
}
tcp.flags &= !UV_HANDLE_ACTIVE;
}
}
fn stop_pipe(&self, handle: *mut uv_pipe_t) {
self
.pipe_handles
.borrow_mut()
.retain(|&h| !std::ptr::eq(h, handle));
unsafe {
if let Some(w) = (*handle).internal_waker.take() {
w.detach();
}
while let Some(pw) = (*handle).internal_write_queue.pop_front() {
if let Some(cb) = pw.cb {
cb(pw.req, UV_ECANCELED);
}
}
pipe::close_pipe(handle);
(*handle).flags &= !UV_HANDLE_ACTIVE;
}
}
}
#[inline]
unsafe fn get_inner(loop_: *mut uv_loop_t) -> &'static UvLoopInner {
unsafe { &*((*loop_).internal as *const UvLoopInner) }
}
pub fn uv_guess_handle(fd: c_int) -> uv_handle_type {
if fd < 0 {
return uv_handle_type::UV_UNKNOWN_HANDLE;
}
#[cfg(unix)]
{
if unsafe { libc::isatty(fd) } != 0 {
return uv_handle_type::UV_TTY;
}
let mut s: libc::stat = unsafe { std::mem::zeroed() };
if unsafe { libc::fstat(fd, &mut s) } != 0 {
return uv_handle_type::UV_UNKNOWN_HANDLE;
}
let ft = s.st_mode & libc::S_IFMT;
if ft == libc::S_IFREG || ft == libc::S_IFCHR {
return uv_handle_type::UV_FILE;
}
if ft == libc::S_IFIFO {
return uv_handle_type::UV_NAMED_PIPE;
}
if ft != libc::S_IFSOCK {
return uv_handle_type::UV_UNKNOWN_HANDLE;
}
let mut ss: libc::sockaddr_storage = unsafe { std::mem::zeroed() };
let mut len: libc::socklen_t =
std::mem::size_of::<libc::sockaddr_storage>() as libc::socklen_t;
if unsafe {
libc::getsockname(fd, &mut ss as *mut _ as *mut libc::sockaddr, &mut len)
} != 0
{
return uv_handle_type::UV_UNKNOWN_HANDLE;
}
let mut sock_type: c_int = 0;
let mut type_len: libc::socklen_t =
std::mem::size_of::<c_int>() as libc::socklen_t;
if unsafe {
libc::getsockopt(
fd,
libc::SOL_SOCKET,
libc::SO_TYPE,
&mut sock_type as *mut _ as *mut c_void,
&mut type_len,
)
} != 0
{
return uv_handle_type::UV_UNKNOWN_HANDLE;
}
if sock_type == libc::SOCK_DGRAM
&& (ss.ss_family == libc::AF_INET as libc::sa_family_t
|| ss.ss_family == libc::AF_INET6 as libc::sa_family_t)
{
return uv_handle_type::UV_UDP;
}
if sock_type == libc::SOCK_STREAM {
if ss.ss_family == libc::AF_INET as libc::sa_family_t
|| ss.ss_family == libc::AF_INET6 as libc::sa_family_t
{
return uv_handle_type::UV_TCP;
}
if ss.ss_family == libc::AF_UNIX as libc::sa_family_t {
return uv_handle_type::UV_NAMED_PIPE;
}
}
uv_handle_type::UV_UNKNOWN_HANDLE
}
#[cfg(windows)]
{
let handle = unsafe { tty::win_console::safe_get_osfhandle(fd) };
if handle == -1 {
return uv_handle_type::UV_UNKNOWN_HANDLE;
}
let h = handle as *mut c_void;
match unsafe { tty::win_console::GetFileType(h) } {
tty::win_console::FILE_TYPE_CHAR => {
let mut mode: u32 = 0;
if unsafe { tty::win_console::GetConsoleMode(h, &mut mode) } != 0 {
uv_handle_type::UV_TTY
} else {
uv_handle_type::UV_FILE
}
}
tty::win_console::FILE_TYPE_PIPE => uv_handle_type::UV_NAMED_PIPE,
tty::win_console::FILE_TYPE_DISK => uv_handle_type::UV_FILE,
_ => uv_handle_type::UV_UNKNOWN_HANDLE,
}
}
}
pub unsafe fn uv_loop_get_inner_ptr(
loop_: *const uv_loop_t,
) -> *const std::ffi::c_void {
unsafe { (*loop_).internal as *const std::ffi::c_void }
}
#[cfg_attr(feature = "uv_compat_export", unsafe(no_mangle))]
pub unsafe extern "C" fn uv_loop_init(loop_: *mut uv_loop_t) -> c_int {
let inner = Box::new(UvLoopInner::new());
unsafe {
(*loop_).internal = Box::into_raw(inner) as *mut c_void;
(*loop_).data = std::ptr::null_mut();
(*loop_).stop_flag = Cell::new(false);
}
0
}
#[cfg_attr(feature = "uv_compat_export", unsafe(no_mangle))]
pub unsafe extern "C" fn uv_loop_close(loop_: *mut uv_loop_t) -> c_int {
unsafe {
let internal = (*loop_).internal;
if !internal.is_null() {
let inner = &*(internal as *const UvLoopInner);
if inner.has_alive_handles() {
return UV_EBUSY;
}
drop(Box::from_raw(internal as *mut UvLoopInner));
(*loop_).internal = std::ptr::null_mut();
}
}
0
}
#[cfg_attr(feature = "uv_compat_export", unsafe(no_mangle))]
pub unsafe extern "C" fn uv_now(loop_: *mut uv_loop_t) -> u64 {
let inner = unsafe { get_inner(loop_) };
inner.now_ms()
}
#[cfg_attr(feature = "uv_compat_export", unsafe(no_mangle))]
pub unsafe extern "C" fn uv_update_time(loop_: *mut uv_loop_t) {
let inner = unsafe { get_inner(loop_) };
inner.update_time();
}
#[cfg_attr(feature = "uv_compat_export", unsafe(no_mangle))]
pub unsafe extern "C" fn uv_timer_init(
loop_: *mut uv_loop_t,
handle: *mut uv_timer_t,
) -> c_int {
unsafe {
(*handle).r#type = uv_handle_type::UV_TIMER;
(*handle).loop_ = loop_;
(*handle).data = std::ptr::null_mut();
(*handle).flags = UV_HANDLE_REF;
(*handle).internal_id = 0;
(*handle).internal_deadline = 0;
(*handle).cb = None;
(*handle).timeout = 0;
(*handle).repeat = 0;
}
0
}
#[cfg_attr(feature = "uv_compat_export", unsafe(no_mangle))]
pub unsafe extern "C" fn uv_timer_start(
handle: *mut uv_timer_t,
cb: uv_timer_cb,
timeout: u64,
repeat: u64,
) -> c_int {
unsafe {
if (*handle).flags & UV_HANDLE_CLOSING != 0 {
return UV_EINVAL;
}
let loop_ = (*handle).loop_;
let inner = get_inner(loop_);
if (*handle).flags & UV_HANDLE_ACTIVE != 0 {
inner.stop_timer(handle);
}
let id = inner.alloc_timer_id();
let now = inner.now_ms();
let deadline = now.saturating_add(timeout);
(*handle).cb = Some(cb);
(*handle).timeout = timeout;
(*handle).repeat = repeat;
(*handle).internal_id = id;
(*handle).internal_deadline = deadline;
(*handle).flags |= UV_HANDLE_ACTIVE;
let key = TimerKey {
deadline_ms: deadline,
id,
};
inner.timers.borrow_mut().insert(key);
inner.timer_handles.borrow_mut().insert(id, handle);
}
0
}
#[cfg_attr(feature = "uv_compat_export", unsafe(no_mangle))]
pub unsafe extern "C" fn uv_timer_stop(handle: *mut uv_timer_t) -> c_int {
unsafe {
let loop_ = (*handle).loop_;
if loop_.is_null() || (*loop_).internal.is_null() {
(*handle).flags &= !UV_HANDLE_ACTIVE;
return 0;
}
let inner = get_inner(loop_);
inner.stop_timer(handle);
}
0
}
#[cfg_attr(feature = "uv_compat_export", unsafe(no_mangle))]
pub unsafe extern "C" fn uv_timer_again(handle: *mut uv_timer_t) -> c_int {
unsafe {
if (*handle).cb.is_none() {
return UV_EINVAL;
}
let repeat = (*handle).repeat;
if repeat == 0 {
return 0;
}
let loop_ = (*handle).loop_;
let inner = get_inner(loop_);
inner.stop_timer(handle);
let id = inner.alloc_timer_id();
let now = inner.now_ms();
let deadline = now.saturating_add(repeat);
(*handle).internal_id = id;
(*handle).internal_deadline = deadline;
(*handle).flags |= UV_HANDLE_ACTIVE;
let key = TimerKey {
deadline_ms: deadline,
id,
};
inner.timers.borrow_mut().insert(key);
inner.timer_handles.borrow_mut().insert(id, handle);
}
0
}
#[cfg_attr(feature = "uv_compat_export", unsafe(no_mangle))]
pub unsafe extern "C" fn uv_timer_get_repeat(handle: *const uv_timer_t) -> u64 {
unsafe { (*handle).repeat }
}
#[cfg_attr(feature = "uv_compat_export", unsafe(no_mangle))]
pub unsafe extern "C" fn uv_timer_set_repeat(
handle: *mut uv_timer_t,
repeat: u64,
) {
unsafe {
(*handle).repeat = repeat;
}
}
#[cfg_attr(feature = "uv_compat_export", unsafe(no_mangle))]
pub unsafe extern "C" fn uv_idle_init(
loop_: *mut uv_loop_t,
handle: *mut uv_idle_t,
) -> c_int {
unsafe {
(*handle).r#type = uv_handle_type::UV_IDLE;
(*handle).loop_ = loop_;
(*handle).data = std::ptr::null_mut();
(*handle).flags = UV_HANDLE_REF;
(*handle).cb = None;
}
0
}
#[cfg_attr(feature = "uv_compat_export", unsafe(no_mangle))]
pub unsafe extern "C" fn uv_idle_start(
handle: *mut uv_idle_t,
cb: uv_idle_cb,
) -> c_int {
unsafe {
if (*handle).flags & UV_HANDLE_ACTIVE != 0 {
return 0;
}
(*handle).cb = Some(cb);
(*handle).flags |= UV_HANDLE_ACTIVE;
let loop_ = (*handle).loop_;
let inner = get_inner(loop_);
inner.idle_handles.borrow_mut().push(handle);
}
0
}
#[cfg_attr(feature = "uv_compat_export", unsafe(no_mangle))]
pub unsafe extern "C" fn uv_idle_stop(handle: *mut uv_idle_t) -> c_int {
unsafe {
if (*handle).flags & UV_HANDLE_ACTIVE == 0 {
return 0;
}
let loop_ = (*handle).loop_;
let inner = get_inner(loop_);
inner.stop_idle(handle);
(*handle).cb = None;
}
0
}
#[cfg_attr(feature = "uv_compat_export", unsafe(no_mangle))]
pub unsafe extern "C" fn uv_prepare_init(
loop_: *mut uv_loop_t,
handle: *mut uv_prepare_t,
) -> c_int {
unsafe {
(*handle).r#type = uv_handle_type::UV_PREPARE;
(*handle).loop_ = loop_;
(*handle).data = std::ptr::null_mut();
(*handle).flags = UV_HANDLE_REF;
(*handle).cb = None;
}
0
}
#[cfg_attr(feature = "uv_compat_export", unsafe(no_mangle))]
pub unsafe extern "C" fn uv_prepare_start(
handle: *mut uv_prepare_t,
cb: uv_prepare_cb,
) -> c_int {
unsafe {
if (*handle).flags & UV_HANDLE_ACTIVE != 0 {
return 0;
}
(*handle).cb = Some(cb);
(*handle).flags |= UV_HANDLE_ACTIVE;
let loop_ = (*handle).loop_;
let inner = get_inner(loop_);
inner.prepare_handles.borrow_mut().push(handle);
}
0
}
#[cfg_attr(feature = "uv_compat_export", unsafe(no_mangle))]
pub unsafe extern "C" fn uv_prepare_stop(handle: *mut uv_prepare_t) -> c_int {
unsafe {
if (*handle).flags & UV_HANDLE_ACTIVE == 0 {
return 0;
}
let loop_ = (*handle).loop_;
let inner = get_inner(loop_);
inner.stop_prepare(handle);
(*handle).cb = None;
}
0
}
#[cfg_attr(feature = "uv_compat_export", unsafe(no_mangle))]
pub unsafe extern "C" fn uv_check_init(
loop_: *mut uv_loop_t,
handle: *mut uv_check_t,
) -> c_int {
unsafe {
(*handle).r#type = uv_handle_type::UV_CHECK;
(*handle).loop_ = loop_;
(*handle).data = std::ptr::null_mut();
(*handle).flags = UV_HANDLE_REF;
(*handle).cb = None;
}
0
}
#[cfg_attr(feature = "uv_compat_export", unsafe(no_mangle))]
pub unsafe extern "C" fn uv_check_start(
handle: *mut uv_check_t,
cb: uv_check_cb,
) -> c_int {
unsafe {
if (*handle).flags & UV_HANDLE_ACTIVE != 0 {
return 0;
}
(*handle).cb = Some(cb);
(*handle).flags |= UV_HANDLE_ACTIVE;
let loop_ = (*handle).loop_;
let inner = get_inner(loop_);
inner.check_handles.borrow_mut().push(handle);
}
0
}
#[cfg_attr(feature = "uv_compat_export", unsafe(no_mangle))]
pub unsafe extern "C" fn uv_check_stop(handle: *mut uv_check_t) -> c_int {
unsafe {
if (*handle).flags & UV_HANDLE_ACTIVE == 0 {
return 0;
}
let loop_ = (*handle).loop_;
let inner = get_inner(loop_);
inner.stop_check(handle);
(*handle).cb = None;
}
0
}
pub(crate) struct ImmediateCheckHandle {
check_handle: *mut uv_check_t,
idle_handle: *mut uv_idle_t,
}
unsafe extern "C" fn immediate_check_noop_cb(_: *mut uv_check_t) {}
unsafe extern "C" fn immediate_idle_noop_cb(_: *mut uv_idle_t) {}
impl ImmediateCheckHandle {
pub unsafe fn new(loop_ptr: *mut uv_loop_t) -> Self {
let check_handle = Box::into_raw(Box::new(unsafe {
std::mem::MaybeUninit::<uv_check_t>::zeroed().assume_init()
}));
unsafe {
uv_check_init(loop_ptr, check_handle);
uv_unref(check_handle as *mut uv_handle_t);
uv_check_start(check_handle, immediate_check_noop_cb);
}
let idle_handle = Box::into_raw(Box::new(unsafe {
std::mem::MaybeUninit::<uv_idle_t>::zeroed().assume_init()
}));
unsafe {
uv_idle_init(loop_ptr, idle_handle);
}
Self {
check_handle,
idle_handle,
}
}
pub fn make_ref(&self) {
unsafe {
uv_idle_start(self.idle_handle, immediate_idle_noop_cb);
}
}
pub fn make_unref(&self) {
unsafe {
uv_idle_stop(self.idle_handle);
}
}
pub unsafe fn close(self) {
unsafe {
uv_check_stop(self.check_handle);
drop(Box::from_raw(self.check_handle));
uv_idle_stop(self.idle_handle);
drop(Box::from_raw(self.idle_handle));
}
}
}
#[cfg_attr(feature = "uv_compat_export", unsafe(no_mangle))]
pub unsafe extern "C" fn uv_close(
handle: *mut uv_handle_t,
close_cb: Option<uv_close_cb>,
) {
unsafe {
(*handle).flags |= UV_HANDLE_CLOSING;
(*handle).flags &= !(UV_HANDLE_ACTIVE | UV_HANDLE_REF);
let loop_ = (*handle).loop_;
let inner = get_inner(loop_);
match (*handle).r#type {
uv_handle_type::UV_TIMER => {
inner.stop_timer(handle as *mut uv_timer_t);
}
uv_handle_type::UV_IDLE => {
inner.stop_idle(handle as *mut uv_idle_t);
}
uv_handle_type::UV_PREPARE => {
inner.stop_prepare(handle as *mut uv_prepare_t);
}
uv_handle_type::UV_CHECK => {
inner.stop_check(handle as *mut uv_check_t);
}
uv_handle_type::UV_TCP => {
inner.stop_tcp(handle as *mut uv_tcp_t);
}
uv_handle_type::UV_TTY => {
inner.stop_tty(handle as *mut uv_tty_t);
}
uv_handle_type::UV_NAMED_PIPE => {
inner.stop_pipe(handle as *mut uv_pipe_t);
}
_ => {}
}
inner
.closing_handles
.borrow_mut()
.push_back((handle, close_cb));
}
}
#[cfg_attr(feature = "uv_compat_export", unsafe(no_mangle))]
pub unsafe extern "C" fn uv_ref(handle: *mut uv_handle_t) {
unsafe {
(*handle).flags |= UV_HANDLE_REF;
}
}
#[cfg_attr(feature = "uv_compat_export", unsafe(no_mangle))]
pub unsafe extern "C" fn uv_unref(handle: *mut uv_handle_t) {
unsafe {
(*handle).flags &= !UV_HANDLE_REF;
}
}
#[cfg_attr(feature = "uv_compat_export", unsafe(no_mangle))]
pub unsafe extern "C" fn uv_has_ref(handle: *const uv_handle_t) -> c_int {
unsafe {
if (*handle).flags & UV_HANDLE_REF != 0 {
1
} else {
0
}
}
}
#[cfg_attr(feature = "uv_compat_export", unsafe(no_mangle))]
pub unsafe extern "C" fn uv_is_active(handle: *const uv_handle_t) -> c_int {
unsafe {
if (*handle).flags & UV_HANDLE_ACTIVE != 0 {
1
} else {
0
}
}
}
#[cfg_attr(feature = "uv_compat_export", unsafe(no_mangle))]
pub unsafe extern "C" fn uv_is_closing(handle: *const uv_handle_t) -> c_int {
unsafe {
if (*handle).flags & UV_HANDLE_CLOSING != 0 {
1
} else {
0
}
}
}
pub struct AsyncId(i64);
impl Default for AsyncId {
fn default() -> Self {
Self(1)
}
}
impl AsyncId {
#[allow(clippy::should_implement_trait, reason = "this is more clear")]
pub fn next(&mut self) -> i64 {
self.0 += 1;
self.0
}
}