proc: sleep interruption handling
This commit is contained in:
parent
efb4909fd5
commit
09a0b01855
@ -8,4 +8,4 @@ mod timer;
|
||||
|
||||
pub use executor::{run_to_completion, spawn, spawn_async_worker};
|
||||
pub use task_queue::init_task_queue;
|
||||
pub use timer::{maybe_timeout, sleep, tick, with_timeout, SleepFuture};
|
||||
pub use timer::{maybe_timeout, sleep, sleep_until, tick, with_timeout, SleepFuture};
|
||||
|
@ -101,7 +101,7 @@ impl Future for SleepFuture {
|
||||
}
|
||||
}
|
||||
|
||||
/// Suspends the task until given duration passes
|
||||
/// Suspends the task until given duration passes.
|
||||
pub fn sleep(duration: Duration) -> SleepFuture {
|
||||
let now = libk_device::monotonic_timestamp().unwrap();
|
||||
let deadline = now + duration;
|
||||
@ -109,6 +109,11 @@ pub fn sleep(duration: Duration) -> SleepFuture {
|
||||
SleepFuture { deadline }
|
||||
}
|
||||
|
||||
/// Suspends the task until the given deadline comes.
|
||||
pub fn sleep_until(deadline: Duration) -> SleepFuture {
|
||||
SleepFuture { deadline }
|
||||
}
|
||||
|
||||
pub fn with_timeout<'a, T: 'a, F: Future<Output = T> + Send + 'a>(
|
||||
fut: F,
|
||||
timeout: Duration,
|
||||
|
@ -646,9 +646,9 @@ impl CurrentThread {
|
||||
self.exit_process(ExitCode::BySignal(signal));
|
||||
}
|
||||
|
||||
// TODO check if really in a syscall, lol
|
||||
let syscall_return = -(u32::from(Error::Interrupted) as isize);
|
||||
frame.set_return_value(syscall_return as u64);
|
||||
// // TODO check if really in a syscall, lol
|
||||
// let syscall_return = -(u32::from(Error::Interrupted) as isize);
|
||||
// frame.set_return_value(syscall_return as u64);
|
||||
|
||||
// Setup signal frame
|
||||
// FIXME use write_foreign_*** for this
|
||||
|
@ -1,4 +1,4 @@
|
||||
use core::{num::NonZeroUsize, sync::atomic::AtomicU32, time::Duration};
|
||||
use core::{mem::MaybeUninit, num::NonZeroUsize, sync::atomic::AtomicU32, time::Duration};
|
||||
|
||||
use abi::{
|
||||
error::Error,
|
||||
@ -18,6 +18,7 @@ use libk::{
|
||||
},
|
||||
vfs::IoContext,
|
||||
};
|
||||
use libk_device::monotonic_timestamp;
|
||||
use libk_mm::{
|
||||
process::VirtualRangeBacking,
|
||||
table::{EntryLevelExt, MapAttributes},
|
||||
@ -191,9 +192,23 @@ pub(crate) fn get_pid() -> ProcessId {
|
||||
process.id
|
||||
}
|
||||
|
||||
pub(crate) fn nanosleep(duration: &Duration) -> Result<(), Error> {
|
||||
block! {
|
||||
runtime::sleep(*duration).await
|
||||
pub(crate) fn nanosleep(
|
||||
duration: &Duration,
|
||||
remaining: &mut MaybeUninit<Duration>,
|
||||
) -> Result<(), Error> {
|
||||
let duration = *duration;
|
||||
let now = monotonic_timestamp().expect("Monotonic timestamp provider broke");
|
||||
let deadline = now + duration;
|
||||
let result = block! { runtime::sleep_until(deadline).await };
|
||||
match result {
|
||||
Ok(()) => Ok(()),
|
||||
Err(Error::Interrupted) => {
|
||||
let now = monotonic_timestamp().expect("Monotonic timestamp provider broke");
|
||||
let rem = deadline - now;
|
||||
remaining.write(rem);
|
||||
Err(Error::Interrupted)
|
||||
}
|
||||
Err(_) => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -86,7 +86,7 @@ syscall wait_thread(tid: u32) -> Result<()>;
|
||||
syscall get_thread_option(option: &mut ThreadOption) -> Result<()>;
|
||||
syscall set_thread_option(option: &ThreadOption) -> Result<()>;
|
||||
|
||||
syscall nanosleep(dur: &Duration) -> Result<()>;
|
||||
syscall nanosleep(duration: &Duration, remaining: &mut MaybeUninit<Duration>) -> Result<()>;
|
||||
|
||||
syscall exit_signal(frame: &SignalEntryData) -> !;
|
||||
syscall set_signal_entry(ip: usize, sp: usize);
|
||||
|
@ -1,5 +1,12 @@
|
||||
//! Yggdrasil OS application runtime
|
||||
#![feature(rustc_private, decl_macro, f128, linkage, naked_functions)]
|
||||
#![feature(
|
||||
rustc_private,
|
||||
decl_macro,
|
||||
f128,
|
||||
linkage,
|
||||
naked_functions,
|
||||
thread_local
|
||||
)]
|
||||
#![no_std]
|
||||
#![deny(missing_docs)]
|
||||
#![allow(nonstandard_style)]
|
||||
|
@ -1,10 +1,41 @@
|
||||
//! Process management data types
|
||||
|
||||
use core::{mem::MaybeUninit, time::Duration};
|
||||
|
||||
use abi::error::Error;
|
||||
pub use abi::process::{
|
||||
auxv, AuxValue, AuxValueIter, ExecveOptions, ExitCode, MutexOperation, ProcessGroupId,
|
||||
ProcessId, ProcessInfoElement, ProgramArgumentInner, Signal, SignalEntryData, SpawnFlags,
|
||||
SpawnOption, SpawnOptions, StringArgIter, ThreadId, ThreadSpawnOptions,
|
||||
};
|
||||
|
||||
use crate::sys;
|
||||
|
||||
pub mod signal;
|
||||
pub mod thread;
|
||||
pub mod thread_local;
|
||||
|
||||
/// Makes the current thread wait until *at least* the amount of time specified in `duration`
|
||||
/// passes.
|
||||
///
|
||||
/// If the underlying sleep syscall is interrupted by a signal, the function will resume the sleep.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Will panic if sleep returns any error besides [crate::Error::Interrupted].
|
||||
pub fn uninterruptible_sleep(mut duration: Duration) {
|
||||
let mut remaining = MaybeUninit::uninit();
|
||||
loop {
|
||||
match unsafe { sys::nanosleep(&duration, &mut remaining) } {
|
||||
Ok(()) => return,
|
||||
Err(Error::Interrupted) => {
|
||||
let remaining = unsafe { remaining.assume_init() };
|
||||
debug_assert!(remaining <= duration);
|
||||
duration = remaining;
|
||||
}
|
||||
Err(err) => {
|
||||
panic!("nanosleep syscall failed: {err:?}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
242
lib/runtime/src/process/thread.rs
Normal file
242
lib/runtime/src/process/thread.rs
Normal file
@ -0,0 +1,242 @@
|
||||
//! Runtime utilities for thread handling
|
||||
|
||||
use core::{
|
||||
cell::UnsafeCell,
|
||||
mem::MaybeUninit,
|
||||
ptr,
|
||||
sync::atomic::{AtomicU32, AtomicUsize, Ordering},
|
||||
};
|
||||
|
||||
use abi::{
|
||||
error::Error,
|
||||
mem::{MappingFlags, MappingSource},
|
||||
process::ThreadSpawnOptions,
|
||||
};
|
||||
use alloc::{boxed::Box, sync::Arc};
|
||||
|
||||
use super::signal;
|
||||
|
||||
/// Describes a runtime thread.
|
||||
///
|
||||
/// `R` generic parameter denotes the thread's return type.
|
||||
pub struct Thread<R> {
|
||||
id: AtomicU32,
|
||||
// TODO mutex
|
||||
result: UnsafeCell<MaybeUninit<R>>,
|
||||
}
|
||||
|
||||
/// Describes a "handle" to some runtime thread. This is what gets returned to the caller
|
||||
/// when a thread is spawned and can be used to, e.g., join the created thread later.
|
||||
pub struct ThreadHandle<R> {
|
||||
stack: Option<OwnedStack>,
|
||||
thread: Arc<Thread<R>>,
|
||||
}
|
||||
|
||||
struct OwnedStack {
|
||||
base: usize,
|
||||
size: usize,
|
||||
}
|
||||
|
||||
/// Describes how a thread's signal stack should be set up.
|
||||
pub enum ThreadSignalStack {
|
||||
/// Thread signal stack will be allocated with the requested size.
|
||||
Allocate(usize),
|
||||
/// Do not set up a signal stack.
|
||||
None,
|
||||
}
|
||||
|
||||
/// Describes how a thread's main stack should be set up.
|
||||
pub enum ThreadStack {
|
||||
/// Thread stack will be allocated with the requested size.
|
||||
Allocate(usize),
|
||||
/// A raw value will be used for the stack pointer (unsafe).
|
||||
Raw(usize),
|
||||
}
|
||||
|
||||
/// Describes the thread's function that will get entered once the thread starts running.
|
||||
///
|
||||
/// `A` parameter denotes the argument type that will be passed to the thread.
|
||||
pub enum ThreadFunction<A, R: Send> {
|
||||
/// A boxed closure will be called.
|
||||
Closure(Box<dyn FnOnce(A) -> R + 'static>),
|
||||
/// A function pointer will be called.
|
||||
Pointer(fn(A) -> R),
|
||||
}
|
||||
|
||||
/// Describes how a thread should be constructed
|
||||
pub struct ThreadCreateInfo<A, R: Send> {
|
||||
/// See [ThreadSignalStack].
|
||||
pub signal_stack: ThreadSignalStack,
|
||||
/// See [ThreadStack].
|
||||
pub stack: ThreadStack,
|
||||
/// See [ThreadFunction].
|
||||
pub entry: ThreadFunction<A, R>,
|
||||
}
|
||||
|
||||
struct ThreadArgument<A, R: Send> {
|
||||
entry: Option<(A, ThreadFunction<A, R>)>,
|
||||
signal_stack: ThreadSignalStack,
|
||||
thread_self: Arc<Thread<R>>,
|
||||
set_thread_self: bool,
|
||||
}
|
||||
|
||||
// TODO maybe put this under a `thread-self` feature and avoid a TLS allocation?
|
||||
#[thread_local]
|
||||
static mut SELF: usize = 0;
|
||||
|
||||
impl<R: Send> Thread<R> {
|
||||
/// Creates a new thread with the requested options and passes it an argument.
|
||||
pub fn spawn<A>(
|
||||
info: ThreadCreateInfo<A, R>,
|
||||
argument: A,
|
||||
runtime: bool,
|
||||
) -> Result<ThreadHandle<R>, Error> {
|
||||
let (stack, sp) = match info.stack {
|
||||
ThreadStack::Allocate(size) => {
|
||||
let (stack, sp) = OwnedStack::allocate(size)?;
|
||||
(Some(stack), sp)
|
||||
}
|
||||
ThreadStack::Raw(_) => todo!(),
|
||||
};
|
||||
|
||||
let thread = Arc::new(Thread::<R> {
|
||||
id: AtomicU32::new(0),
|
||||
result: UnsafeCell::new(MaybeUninit::uninit()),
|
||||
});
|
||||
|
||||
let thread_argument = Box::into_raw(Box::new(ThreadArgument {
|
||||
entry: Some((argument, info.entry)),
|
||||
thread_self: thread.clone(),
|
||||
signal_stack: info.signal_stack,
|
||||
set_thread_self: runtime,
|
||||
}))
|
||||
.addr();
|
||||
|
||||
let spawn_options = ThreadSpawnOptions {
|
||||
entry: Self::thread_entry::<A>,
|
||||
argument: thread_argument,
|
||||
stack_top: sp,
|
||||
};
|
||||
|
||||
let id = unsafe { crate::sys::spawn_thread(&spawn_options) }?;
|
||||
thread.id.store(id, Ordering::Release);
|
||||
|
||||
Ok(ThreadHandle { stack, thread })
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// This function has to be called with the same `Self` type as the thread it was created with.
|
||||
pub unsafe fn current() -> Arc<Self> {
|
||||
let raw: *const Self = ptr::with_exposed_provenance(SELF);
|
||||
if raw.is_null() {
|
||||
panic!("Thread::SELF == NULL, was spawn() called with runtime=false?");
|
||||
}
|
||||
// This will "move" the pointer, so an extra strong count increment is required.
|
||||
Arc::increment_strong_count(raw);
|
||||
Arc::from_raw(raw)
|
||||
}
|
||||
|
||||
extern "C" fn thread_entry<A>(raw: usize) -> ! {
|
||||
crate::debug_trace!("thread_entry {raw:#x}");
|
||||
let raw: *mut ThreadArgument<A, R> = ptr::with_exposed_provenance_mut(raw);
|
||||
// This scope will ensure all the stuff is dropped before thread exit is called.
|
||||
{
|
||||
let mut argument = unsafe { Box::from_raw(raw) };
|
||||
|
||||
// TODO there will be a better way to do this, I promise
|
||||
while argument.thread_self.id.load(Ordering::Acquire) == 0 {
|
||||
core::hint::spin_loop();
|
||||
}
|
||||
crate::debug_trace!(
|
||||
"thread_entry id={}",
|
||||
argument.thread_self.id.load(Ordering::Acquire)
|
||||
);
|
||||
|
||||
// Setup SELF if needed
|
||||
if argument.set_thread_self {
|
||||
unsafe {
|
||||
SELF = Arc::into_raw(argument.thread_self.clone()).addr();
|
||||
debug_assert!(Arc::ptr_eq(&Thread::current(), &argument.thread_self));
|
||||
}
|
||||
crate::debug_trace!("thread_entry set SELF!!!");
|
||||
}
|
||||
|
||||
// Setup signal stack if needed
|
||||
let result = match argument.signal_stack {
|
||||
ThreadSignalStack::None => Ok(()),
|
||||
ThreadSignalStack::Allocate(size) => signal::setup_signal_stack(size),
|
||||
};
|
||||
if let Err(err) = result {
|
||||
panic!("Failed to set up thread's signal stack: {err:?}");
|
||||
}
|
||||
|
||||
// Call the inner function
|
||||
let result = match argument.entry.take() {
|
||||
Some((arg, ThreadFunction::Closure(f))) => f(arg),
|
||||
Some((arg, ThreadFunction::Pointer(f))) => f(arg),
|
||||
None => unreachable!(),
|
||||
};
|
||||
|
||||
let result_ptr = argument.thread_self.result.get();
|
||||
unsafe {
|
||||
(*result_ptr).write(result);
|
||||
}
|
||||
}
|
||||
|
||||
crate::debug_trace!("thread_entry exit_thread()");
|
||||
unsafe { crate::sys::exit_thread() };
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: Send> ThreadHandle<R> {
|
||||
/// Waits for the thread to finish and returns its result.
|
||||
pub fn join(self) -> Result<R, Error> {
|
||||
// TODO prevent threads from attempting to join themselves?
|
||||
unsafe {
|
||||
crate::sys::wait_thread(self.thread.id.load(Ordering::Acquire))?;
|
||||
Ok(self.into_result())
|
||||
}
|
||||
}
|
||||
|
||||
/// Waits for the thread to finish, ignoring interrupts.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Will panic if the kernel returns any error besides [Error::Interrupted].
|
||||
pub fn join_uninterruptible(self) -> R {
|
||||
loop {
|
||||
match unsafe { crate::sys::wait_thread(self.thread.id.load(Ordering::Acquire)) } {
|
||||
Ok(result) => (),
|
||||
Err(Error::Interrupted) => continue,
|
||||
Err(error) => panic!("wait_thread syscall returned error: {error:?}"),
|
||||
}
|
||||
return unsafe { self.into_result() };
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn into_result(self) -> R {
|
||||
(*self.thread.result.get()).assume_init_read()
|
||||
}
|
||||
}
|
||||
|
||||
impl OwnedStack {
|
||||
fn allocate(mut size: usize) -> Result<(Self, usize), Error> {
|
||||
if size < 0x1000 {
|
||||
size = 0x1000;
|
||||
}
|
||||
size = (size + 0xFFF) & !0xFFF;
|
||||
let base = unsafe {
|
||||
crate::sys::map_memory(None, size, MappingFlags::WRITE, &MappingSource::Anonymous)
|
||||
}?;
|
||||
let top = base + size;
|
||||
|
||||
Ok((Self { base, size }, top))
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for OwnedStack {
|
||||
fn drop(&mut self) {
|
||||
unsafe { crate::sys::unmap_memory(self.base, self.size) }.ok();
|
||||
}
|
||||
}
|
30
test.c
30
test.c
@ -1,3 +1,33 @@
|
||||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
|
||||
static void silence_sigint(int signum) {}
|
||||
|
||||
int main(int argc, const char **argv) {
|
||||
signal(silence_sigint, SIGINT);
|
||||
struct timespec duration;
|
||||
struct timespec remaining;
|
||||
|
||||
while (1) {
|
||||
duration.tv_sec = 3;
|
||||
duration.tv_nsec = 0;
|
||||
|
||||
int result = nanosleep(&duration, &remaining);
|
||||
|
||||
if (result != 0) {
|
||||
assert(errno == EINTR);
|
||||
printf(
|
||||
"EINTR: remaining: sec=%lu, nsec=%u\n",
|
||||
(unsigned long) remaining.tv_sec,
|
||||
(unsigned int) remaining.tv_nsec
|
||||
);
|
||||
} else {
|
||||
printf("NO EINTR\n");
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
use core::ffi::{c_char, c_int, c_long, c_void};
|
||||
use core::{ffi::{c_char, c_int, c_long, c_void}, time::Duration};
|
||||
|
||||
use super::sys_types::{suseconds_t, time_t};
|
||||
|
||||
@ -49,3 +49,21 @@ unsafe extern "C" fn setitimer(_timer: c_int, _new: *const itimerval, _old: *mut
|
||||
unsafe extern "C" fn utimes(_path: *const c_char, _times: *const timeval) -> c_int {
|
||||
todo!()
|
||||
}
|
||||
|
||||
impl From<__ygg_timespec_t> for Duration {
|
||||
fn from(value: __ygg_timespec_t) -> Self {
|
||||
Self::new(
|
||||
value.tv_sec.0.try_into().unwrap(),
|
||||
value.tv_nsec.try_into().unwrap(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Duration> for __ygg_timespec_t {
|
||||
fn from(value: Duration) -> Self {
|
||||
Self {
|
||||
tv_sec: time_t(value.as_secs().try_into().unwrap()),
|
||||
tv_nsec: value.subsec_nanos().try_into().unwrap()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
use core::{
|
||||
ffi::{c_char, c_int, c_long},
|
||||
ptr::null_mut,
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use chrono::{DateTime, Datelike, NaiveDate, NaiveDateTime, NaiveTime, TimeZone, Timelike, Utc};
|
||||
@ -116,15 +115,6 @@ impl<T: Datelike + Timelike> From<T> for tm {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<__ygg_timespec_t> for Duration {
|
||||
fn from(value: __ygg_timespec_t) -> Self {
|
||||
Self::new(
|
||||
value.tv_sec.0.try_into().unwrap(),
|
||||
value.tv_nsec.try_into().unwrap(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn get_timezone() -> impl TimeZone {
|
||||
// TODO get it from C
|
||||
Utc
|
||||
|
@ -1,9 +1,9 @@
|
||||
use core::{ffi::c_double, time::Duration};
|
||||
|
||||
use crate::{
|
||||
error::CIntZeroResult,
|
||||
headers::{sys_time::__ygg_timespec_t, sys_types::time_t},
|
||||
process,
|
||||
error::{self, CIntZeroResult, CResult},
|
||||
headers::{errno, sys_time::__ygg_timespec_t, sys_types::time_t},
|
||||
process, util::PointerExt,
|
||||
};
|
||||
|
||||
#[no_mangle]
|
||||
@ -11,16 +11,21 @@ unsafe extern "C" fn nanosleep(
|
||||
rqtp: *const __ygg_timespec_t,
|
||||
rmtp: *mut __ygg_timespec_t,
|
||||
) -> CIntZeroResult {
|
||||
let rqtp = rqtp.as_ref().unwrap();
|
||||
if let Some(_rmtp) = rmtp.as_mut() {
|
||||
todo!();
|
||||
}
|
||||
|
||||
let rqtp = rqtp.ensure();
|
||||
let amount = Duration::from(*rqtp);
|
||||
|
||||
process::sleep(amount).expect("TODO interruptible sleep in nanosleep()");
|
||||
|
||||
CIntZeroResult::SUCCESS
|
||||
match process::sleep(amount) {
|
||||
Ok(()) => {
|
||||
CIntZeroResult::SUCCESS
|
||||
}
|
||||
Err(remaining) => {
|
||||
if let Some(rmtp) = rmtp.as_mut() {
|
||||
*rmtp = remaining.into();
|
||||
}
|
||||
error::errno = errno::EINTR;
|
||||
CIntZeroResult::ERROR
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
|
@ -86,10 +86,12 @@ unsafe extern "C" fn pause() -> CIntZeroResult {
|
||||
unsafe extern "C" fn setegid(gid: gid_t) -> c_int {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn seteuid(uid: uid_t) -> c_int {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn setgid(gid: gid_t) -> c_int {
|
||||
todo!()
|
||||
@ -98,26 +100,32 @@ unsafe extern "C" fn setgid(gid: gid_t) -> c_int {
|
||||
unsafe extern "C" fn setpgid(pid: pid_t, pgid: pid_t) -> c_int {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn setpgrp() -> pid_t {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn setregid(rgid: gid_t, egid: gid_t) -> c_int {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn setreuid(ruid: uid_t, euid: uid_t) -> c_int {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn setsid() -> pid_t {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn setuid(uid: uid_t) -> c_int {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn sleep(seconds: c_uint) -> c_uint {
|
||||
let duration = Duration::from_secs(seconds.try_into().unwrap());
|
||||
@ -125,7 +133,7 @@ unsafe extern "C" fn sleep(seconds: c_uint) -> c_uint {
|
||||
Ok(()) => 0,
|
||||
Err(remaining) => {
|
||||
error::errno = errno::EINTR;
|
||||
remaining.as_secs() as _
|
||||
remaining.as_secs() as c_uint + (remaining.subsec_nanos() != 0) as c_uint
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
use core::{
|
||||
ffi::{c_char, c_int, CStr},
|
||||
time::Duration,
|
||||
ffi::{c_char, c_int, CStr}, mem::MaybeUninit, time::Duration
|
||||
};
|
||||
|
||||
use yggdrasil_rt::{
|
||||
@ -24,8 +23,12 @@ pub fn getpgrp() -> pid_t {
|
||||
}
|
||||
|
||||
pub fn sleep(duration: Duration) -> Result<(), Duration> {
|
||||
// TODO make nanosleep return duration
|
||||
unsafe { syscall::nanosleep(&duration) }.map_err(|_| Duration::from_secs(1))
|
||||
let mut remaining = MaybeUninit::uninit();
|
||||
if unsafe { syscall::nanosleep(&duration, &mut remaining) }.is_ok() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(unsafe { remaining.assume_init() })
|
||||
}
|
||||
}
|
||||
|
||||
pub fn abort() -> ! {
|
||||
|
Loading…
x
Reference in New Issue
Block a user