mod pipe;
mod stream;
mod tcp;
mod tty;
#[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::c_int;
use std::ffi::c_void;
use std::task::Context;
use std::task::Waker;
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);
pub const UV_EOF: i32 = -4095;
pub(crate) fn io_error_to_uv(err: &std::io::Error) -> c_int {
use std::io::ErrorKind;
match err.kind() {
ErrorKind::AddrInUse => UV_EADDRINUSE,
ErrorKind::AddrNotAvailable => UV_EINVAL,
ErrorKind::ConnectionRefused => UV_ECONNREFUSED,
ErrorKind::NotConnected => UV_ENOTCONN,
ErrorKind::NotFound => UV_ENOENT,
ErrorKind::BrokenPipe => UV_EPIPE,
ErrorKind::InvalidInput => UV_EINVAL,
ErrorKind::WouldBlock => UV_EAGAIN,
_ => {
#[cfg(unix)]
if let Some(code) = err.raw_os_error() {
return -code;
}
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>,
}
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),
}
}
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()),
}
}
#[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 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 noop = Waker::noop();
let waker_ref = self.waker.borrow();
let waker = waker_ref.as_ref().unwrap_or(noop);
let mut cx = Context::from_waker(waker);
let mut did_any_work = false;
for _pass in 0..16 {
let mut any_work = false;
let mut i = 0;
loop {
let tcp_ptr = {
let handles = self.tcp_handles.borrow();
if i >= handles.len() {
break;
}
handles[i]
};
i += 1;
if unsafe { (*tcp_ptr).flags } & UV_HANDLE_ACTIVE == 0 {
continue;
}
let work = unsafe { tcp::poll_tcp_handle(tcp_ptr, &mut cx) };
any_work |= work;
}
{
let mut pi = 0;
loop {
let pipe_ptr = {
let handles = self.pipe_handles.borrow();
if pi >= handles.len() {
break;
}
handles[pi]
};
pi += 1;
if unsafe { (*pipe_ptr).flags } & UV_HANDLE_ACTIVE == 0 {
continue;
}
let work = unsafe { pipe::poll_pipe_handle(pipe_ptr, &mut cx) };
any_work |= work;
}
}
let mut j = 0;
loop {
let tty_ptr = {
let handles = self.tty_handles.borrow();
if j >= handles.len() {
break;
}
handles[j]
};
j += 1;
if unsafe { (*tty_ptr).flags } & UV_HANDLE_ACTIVE == 0 {
continue;
}
any_work |= unsafe { tty::poll_tty_handle(tty_ptr, &mut cx) };
}
if !any_work {
break;
}
did_any_work = true;
}
did_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;
#[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;
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);
}
}
if let Some(stream) = tcp.internal_stream.take() {
if let Ok(std_stream) = stream.into_std() {
let _ = std_stream.shutdown(std::net::Shutdown::Both);
}
}
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 {
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
}
}