diff --git a/Cargo.lock b/Cargo.lock index 007adf424..cacb8e855 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -62,6 +62,12 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitflags" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "630be753d4e58660abd17930c71b647fe46c27ea6b63cc59e1e3851406972e42" + [[package]] name = "blake2" version = "0.10.6" @@ -190,6 +196,27 @@ version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" +[[package]] +name = "errno" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" +dependencies = [ + "errno-dragonfly", + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + [[package]] name = "fnv" version = "1.0.7" @@ -274,9 +301,15 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.144" +version = "0.2.147" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" + +[[package]] +name = "linux-raw-sys" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1" +checksum = "09fc20d2ca12cb9f044c93e3bd6d32d523e6e2ec3db4f7b2939cd99026ecd3f0" [[package]] name = "llvm-sys" @@ -376,7 +409,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce" dependencies = [ "autocfg", - "bitflags", + "bitflags 1.3.2", "cfg-if", "concurrent-queue", "libc", @@ -445,7 +478,7 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" dependencies = [ - "bitflags", + "bitflags 1.3.2", ] [[package]] @@ -481,6 +514,7 @@ dependencies = [ "libc", "polling", "rand", + "rustix", "socket2", "unicode-segmentation", ] @@ -491,6 +525,19 @@ version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +[[package]] +name = "rustix" +version = "0.38.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbc6396159432b5c8490d4e301d8c705f61860b8b6c863bf79942ce5401968f3" +dependencies = [ + "bitflags 2.3.3", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.48.0", +] + [[package]] name = "scopeguard" version = "1.1.0" diff --git a/Cargo.toml b/Cargo.toml index 9be6f9552..8c7de2757 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,6 @@ [workspace] members = ["ast", "inko", "compiler", "rt"] +resolver = "2" [profile.release] panic = "abort" diff --git a/rt/Cargo.toml b/rt/Cargo.toml index ae0bf8b01..b6be253ad 100644 --- a/rt/Cargo.toml +++ b/rt/Cargo.toml @@ -19,6 +19,7 @@ rand = { version = "^0.8", features = ["default", "small_rng"] } polling = "^2.8" unicode-segmentation = "^1.8" backtrace = "^0.3" +rustix = { version = "^0.38", features = ["fs", "mm", "param", "process", "net", "std", "time"], default-features = false } [dependencies.socket2] version = "^0.5" diff --git a/rt/src/memory_map.rs b/rt/src/memory_map.rs index 0fcf8b5b0..ca810704d 100644 --- a/rt/src/memory_map.rs +++ b/rt/src/memory_map.rs @@ -1,7 +1,6 @@ use crate::page::{multiple_of_page_size, page_size}; -use libc::{ - c_int, mmap, mprotect, munmap, MAP_ANON, MAP_FAILED, MAP_PRIVATE, - PROT_NONE, PROT_READ, PROT_WRITE, +use rustix::mm::{ + mmap_anonymous, mprotect, munmap, MapFlags, MprotectFlags, ProtFlags, }; use std::io::{Error, Result as IoResult}; use std::ptr::null_mut; @@ -12,8 +11,8 @@ pub(crate) struct MemoryMap { pub(crate) len: usize, } -fn mmap_options(_stack: bool) -> c_int { - let base = MAP_PRIVATE | MAP_ANON; +fn mmap_options(_stack: bool) -> MapFlags { + let base = MapFlags::PRIVATE; #[cfg(any( target_os = "linux", @@ -21,7 +20,7 @@ fn mmap_options(_stack: bool) -> c_int { target_os = "openbsd" ))] if _stack { - return base | libc::MAP_STACK; + return base | MapFlags::STACK; } base @@ -32,26 +31,36 @@ impl MemoryMap { let size = multiple_of_page_size(size); let opts = mmap_options(stack); - let ptr = unsafe { - mmap(null_mut(), size, PROT_READ | PROT_WRITE, opts, -1, 0) + let res = unsafe { + mmap_anonymous( + null_mut(), + size, + ProtFlags::READ | ProtFlags::WRITE, + opts, + ) }; - if ptr == MAP_FAILED { - panic!("mmap(2) failed: {}", Error::last_os_error()); + match res { + Ok(ptr) => MemoryMap { ptr: ptr as *mut u8, len: size }, + Err(e) => panic!( + "mmap(2) failed: {}", + Error::from_raw_os_error(e.raw_os_error()) + ), } - - MemoryMap { ptr: ptr as *mut u8, len: size } } pub(crate) fn protect(&mut self, start: usize) -> IoResult<()> { let res = unsafe { - mprotect(self.ptr.add(start) as _, page_size(), PROT_NONE) + mprotect( + self.ptr.add(start) as _, + page_size(), + MprotectFlags::empty(), + ) }; - if res == 0 { - Ok(()) - } else { - Err(Error::last_os_error()) + match res { + Ok(_) => Ok(()), + Err(e) => Err(Error::from_raw_os_error(e.raw_os_error())), } } } @@ -59,7 +68,7 @@ impl MemoryMap { impl Drop for MemoryMap { fn drop(&mut self) { unsafe { - munmap(self.ptr as _, self.len); + let _ = munmap(self.ptr as _, self.len); } } } diff --git a/rt/src/page.rs b/rt/src/page.rs index 624d765da..2cb97a224 100644 --- a/rt/src/page.rs +++ b/rt/src/page.rs @@ -1,16 +1,11 @@ -use libc::{sysconf, _SC_PAGESIZE}; use std::sync::atomic::{AtomicUsize, Ordering}; static PAGE_SIZE: AtomicUsize = AtomicUsize::new(0); -fn page_size_raw() -> usize { - unsafe { sysconf(_SC_PAGESIZE) as usize } -} - pub(crate) fn page_size() -> usize { match PAGE_SIZE.load(Ordering::Relaxed) { 0 => { - let size = page_size_raw(); + let size = rustix::param::page_size(); PAGE_SIZE.store(size, Ordering::Relaxed); size diff --git a/rt/src/runtime.rs b/rt/src/runtime.rs index a81bf4d40..1cc980675 100644 --- a/rt/src/runtime.rs +++ b/rt/src/runtime.rs @@ -27,8 +27,10 @@ use std::io::{stdout, Write as _}; use std::process::exit as rust_exit; use std::thread; -#[cfg(unix)] -fn ignore_sigpipe() { +const SIGPIPE: i32 = 13; +const SIG_IGN: usize = 1; + +extern "C" { // Broken pipe errors default to terminating the entire program, making it // impossible to handle such errors. This is especially problematic for // sockets, as writing to a socket closed on the other end would terminate @@ -37,14 +39,7 @@ fn ignore_sigpipe() { // While Rust handles this for us when compiling an executable, it doesn't // do so when compiling it to a static library and linking it to our // generated code, so we must handle this ourselves. - unsafe { - libc::signal(libc::SIGPIPE, libc::SIG_IGN); - } -} - -#[cfg(not(unix))] -fn ignore_sigpipe() { - // Not needed on these platforms + fn signal(sig: i32, handler: usize) -> usize; } #[no_mangle] @@ -65,7 +60,7 @@ pub unsafe extern "system" fn inko_runtime_start( class: ClassPointer, method: NativeAsyncMethod, ) { - ignore_sigpipe(); + unsafe { signal(SIGPIPE, SIG_IGN) }; (*runtime).start(class, method); flush_stdout(); } diff --git a/rt/src/runtime/time.rs b/rt/src/runtime/time.rs index 7ab520870..aa7711353 100644 --- a/rt/src/runtime/time.rs +++ b/rt/src/runtime/time.rs @@ -1,19 +1,11 @@ use crate::mem::Float; use crate::state::State; +use rustix::time; use std::mem::MaybeUninit; fn utc() -> f64 { - unsafe { - let mut ts = MaybeUninit::uninit(); - - if libc::clock_gettime(libc::CLOCK_REALTIME, ts.as_mut_ptr()) != 0 { - panic!("clock_gettime() failed"); - } - - let ts = ts.assume_init(); - - ts.tv_sec as f64 + (ts.tv_nsec as f64 / 1_000_000_000.0) - } + let ts = time::clock_gettime(time::ClockId::Realtime); + ts.tv_sec as f64 + (ts.tv_nsec as f64 / 1_000_000_000.0) } fn offset() -> i64 { @@ -22,16 +14,7 @@ fn offset() -> i64 { fn tzset(); } - let ts = { - let mut ts = MaybeUninit::uninit(); - - if libc::clock_gettime(libc::CLOCK_REALTIME, ts.as_mut_ptr()) != 0 { - panic!("clock_gettime() failed"); - } - - ts.assume_init() - }; - + let ts = time::clock_gettime(time::ClockId::Realtime); let mut tm = MaybeUninit::uninit(); // localtime_r() doesn't necessarily call tzset() for us. diff --git a/rt/src/scheduler/mod.rs b/rt/src/scheduler/mod.rs index a625f22cd..35653039e 100644 --- a/rt/src/scheduler/mod.rs +++ b/rt/src/scheduler/mod.rs @@ -5,19 +5,14 @@ pub mod timeouts; use std::thread::available_parallelism; #[cfg(target_os = "linux")] -use { - libc::{cpu_set_t, sched_setaffinity, CPU_SET}, - std::mem::{size_of, zeroed}, -}; +use rustix::process::{sched_setaffinity, CpuSet, Pid}; #[cfg(target_os = "linux")] pub(crate) fn pin_thread_to_core(core: usize) { - unsafe { - let mut set: cpu_set_t = zeroed(); + let mut set = CpuSet::new(); + set.set(core); - CPU_SET(core, &mut set); - sched_setaffinity(0, size_of::(), &set); - } + let _ = sched_setaffinity(Pid::from_raw(0), &set); } #[cfg(not(target_os = "linux"))] diff --git a/rt/src/socket.rs b/rt/src/socket.rs index fc0e37c24..9026affdd 100644 --- a/rt/src/socket.rs +++ b/rt/src/socket.rs @@ -4,7 +4,7 @@ use crate::network_poller::Interest; use crate::process::ProcessPointer; use crate::socket::socket_address::SocketAddress; use crate::state::State; -use libc::{EINPROGRESS, EISCONN}; +use rustix::io::Errno; use socket2::{Domain, SockAddr, Socket as RawSocket, Type}; use std::io::{self, Read}; use std::mem::transmute; @@ -197,7 +197,8 @@ impl Socket { Ok(_) => Ok(()), Err(ref e) if e.kind() == io::ErrorKind::WouldBlock - || e.raw_os_error() == Some(EINPROGRESS) => + || e.raw_os_error() + == Some(Errno::INPROGRESS.raw_os_error()) => { if let Ok(Some(err)) = self.inner.take_error() { // When performing a connect(), the error returned may be @@ -208,8 +209,10 @@ impl Socket { Err(io::Error::from(io::ErrorKind::WouldBlock)) } - Err(ref e) if e.raw_os_error() == Some(EISCONN) => { - // We may run into an EISCONN if a previous connect(2) attempt + Err(ref e) + if e.raw_os_error() == Some(Errno::ISCONN.raw_os_error()) => + { + // We may run into an ISCONN if a previous connect(2) attempt // would block. In this case we can just continue. Ok(()) } diff --git a/rt/src/socket/socket_address.rs b/rt/src/socket/socket_address.rs index 037c7881d..13dbeda9d 100644 --- a/rt/src/socket/socket_address.rs +++ b/rt/src/socket/socket_address.rs @@ -2,40 +2,27 @@ use socket2::SockAddr; #[cfg(unix)] use { - libc::sockaddr_un, - std::ffi::OsStr, - std::mem::transmute, - std::os::{raw::c_char, unix::ffi::OsStrExt}, + rustix::net::SocketAddrUnix, std::ffi::OsStr, std::os::unix::ffi::OsStrExt, }; -#[cfg(unix)] -#[cfg_attr(feature = "cargo-clippy", allow(clippy::uninit_assumed_init))] -fn sun_path_offset(addr: &sockaddr_un) -> usize { - let base = addr as *const sockaddr_un as usize; - let path = &addr.sun_path as *const c_char as usize; - - path - base -} - #[cfg(unix)] fn unix_socket_path(sockaddr: &SockAddr) -> String { - let raw_addr = unsafe { &*(sockaddr.as_ptr() as *const sockaddr_un) }; - let len = sockaddr.len() as usize - sun_path_offset(raw_addr); - let path = unsafe { transmute::<&[c_char], &[u8]>(&raw_addr.sun_path) }; - - if len == 0 || (cfg!(not(target_os = "linux")) && raw_addr.sun_path[0] == 0) - { - return String::new(); - } - - let (start, stop) = - if raw_addr.sun_path[0] == 0 { (1, len) } else { (0, len - 1) }; - - // Abstract names might contain NULL bytes and invalid UTF8. Since Inko - // doesn't provide any better types at the moment we'll use a string and - // convert the data to UTF8. A byte array would technically be better, but - // these are mutable and make for an unpleasant runtime API. - OsStr::from_bytes(&path[start..stop]).to_string_lossy().into_owned() + sockaddr + .as_pathname() + .and_then(|p| SocketAddrUnix::new(p).ok()) + .and_then(|addr| { + addr.path().map(|path| { + // Abstract names might contain NULL bytes and invalid UTF8. + // Since Inko doesn't provide any better types at the moment + // we'll use a string and convert the data to UTF8. A byte array + // would technically be better, but these are mutable and make + // for an unpleasant runtime API. + OsStr::from_bytes(path.to_bytes()) + .to_string_lossy() + .into_owned() + }) + }) + .unwrap_or_else(String::new) } #[cfg(not(unix))]