diff --git a/libc/Makefile b/libc/Makefile index e8d18de7..ae7b8272 100644 --- a/libc/Makefile +++ b/libc/Makefile @@ -354,8 +354,10 @@ tfork.o \ time/clock_getres.o \ time/clock_gettime.o \ time/clock_gettimeres.o \ +time/clock_nanosleep.o \ time/clock_settime.o \ time/clock_settimeres.o \ +time/nanosleep.o \ time/time.o \ time/timer_create.o \ time/timer_delete.o \ diff --git a/libc/sleep.cpp b/libc/sleep.cpp index ae8f50b7..2034dd57 100644 --- a/libc/sleep.cpp +++ b/libc/sleep.cpp @@ -1,6 +1,6 @@ /******************************************************************************* - Copyright(C) Jonas 'Sortie' Termansen 2011, 2012. + Copyright(C) Jonas 'Sortie' Termansen 2013. This file is part of the Sortix C Library. @@ -22,13 +22,15 @@ *******************************************************************************/ -#include +#include +#include #include -DEFN_SYSCALL1(int, SysSleep, SYSCALL_SLEEP, long); - extern "C" unsigned sleep(unsigned secs) { - SysSleep(secs); + struct timespec delay = timespec_make(secs, 0); + struct timespec left; + if ( nanosleep(&delay, &left) < 0 ) + return left.tv_sec + (left.tv_nsec ? 1 : 0); return 0; } diff --git a/libc/time/clock_nanosleep.cpp b/libc/time/clock_nanosleep.cpp new file mode 100644 index 00000000..dedbbc93 --- /dev/null +++ b/libc/time/clock_nanosleep.cpp @@ -0,0 +1,37 @@ +/******************************************************************************* + + Copyright(C) Jonas 'Sortie' Termansen 2013. + + This file is part of the Sortix C Library. + + The Sortix C Library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or (at your + option) any later version. + + The Sortix C Library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with the Sortix C Library. If not, see . + + time/clock_nanosleep.cpp + Sleep for a duration on a clock. + +*******************************************************************************/ + +#include + +#include + +DEFN_SYSCALL4(int, sys_clock_nanosleep, SYSCALL_CLOCK_NANOSLEEP, clockid_t, + int, const struct timespec*, struct timespec*); + +extern "C" +int clock_nanosleep(clockid_t clockid, int flags, + const struct timespec* duration, struct timespec* remainder) +{ + return sys_clock_nanosleep(clockid, flags, duration, remainder); +} diff --git a/libc/time/nanosleep.cpp b/libc/time/nanosleep.cpp new file mode 100644 index 00000000..c38fbe1a --- /dev/null +++ b/libc/time/nanosleep.cpp @@ -0,0 +1,33 @@ +/******************************************************************************* + + Copyright(C) Jonas 'Sortie' Termansen 2013. + + This file is part of the Sortix C Library. + + The Sortix C Library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or (at your + option) any later version. + + The Sortix C Library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with the Sortix C Library. If not, see . + + time/nanosleep.cpp + Sleep for a duration. + +*******************************************************************************/ + +#include + +extern "C" int nanosleep(const struct timespec* delay, struct timespec* left) +{ + // NOTE: POSIX specifies that we should nanosleep on CLOCK_REALTIME, but it + // makes more sense to sleep on the monotonic system clock. Futhermore + // Linux does this as well and users probably won't notice. + return clock_nanosleep(CLOCK_MONOTONIC, 0, delay, left); +} diff --git a/libc/usleep.cpp b/libc/usleep.cpp index a1efefad..0aa253b6 100644 --- a/libc/usleep.cpp +++ b/libc/usleep.cpp @@ -1,6 +1,6 @@ /******************************************************************************* - Copyright(C) Jonas 'Sortie' Termansen 2011, 2012. + Copyright(C) Jonas 'Sortie' Termansen 2013. This file is part of the Sortix C Library. @@ -18,17 +18,16 @@ along with the Sortix C Library. If not, see . usleep.cpp - Blocks the current thread for for at least N micro seconds. + Blocks the current thread for for at least N microseconds. *******************************************************************************/ -#include +#include +#include #include -DEFN_SYSCALL1(int, SysUSleep, SYSCALL_USLEEP, long); - extern "C" int usleep(useconds_t usecs) { - SysUSleep(usecs); - return 0; + struct timespec delay = timespec_canonalize(timespec_make(0, usecs * 1000)); + return nanosleep(&delay, NULL); } diff --git a/sortix/clock.cpp b/sortix/clock.cpp index 7c7ac4a7..b596be5d 100644 --- a/sortix/clock.cpp +++ b/sortix/clock.cpp @@ -32,6 +32,8 @@ #include #include +#include "signal.h" + namespace Sortix { Clock::Clock() @@ -39,6 +41,7 @@ Clock::Clock() delay_timer = NULL; absolute_timer = NULL; current_time = timespec_nul(); + current_advancement = timespec_nul(); resolution = timespec_nul(); clock_mutex = KTHREAD_MUTEX_INITIALIZER; clock_callable_from_interrupt = false; @@ -233,11 +236,66 @@ void Clock::Cancel(Timer* timer) UnlockClock(); } +// TODO: We need some method for threads to sleep for real but still be +// interrupted by signals. +struct timespec Clock::SleepDelay(struct timespec duration) +{ + struct timespec start_advancement; + struct timespec elapsed = timespec_nul(); + bool start_advancement_set = false; + + while ( timespec_lt(elapsed, duration) ) + { + if ( start_advancement_set ) + { + if ( Signal::IsPending() ) + return duration; + + kthread_yield(); + } + + LockClock(); + + if ( !start_advancement_set ) + start_advancement = current_advancement, + start_advancement_set = true; + + elapsed = timespec_sub(current_advancement, start_advancement); + + UnlockClock(); + } + + return timespec_nul(); +} + +// TODO: We need some method for threads to sleep for real but still be +// interrupted by signals. +struct timespec Clock::SleepUntil(struct timespec expiration) +{ + while ( true ) + { + LockClock(); + struct timespec now = current_time; + UnlockClock(); + + if ( timespec_le(now, expiration) ) + break; + + if ( Signal::IsPending() ) + return timespec_sub(expiration, now); + + kthread_yield(); + } + + return timespec_nul(); +} + void Clock::Advance(struct timespec duration) { LockClock(); current_time = timespec_add(current_time, duration); + current_advancement = timespec_add(current_advancement, duration); TriggerDelay(duration); TriggerAbsolute(); diff --git a/sortix/include/sortix/kernel/clock.h b/sortix/include/sortix/kernel/clock.h index f9f33170..e9dae413 100644 --- a/sortix/include/sortix/kernel/clock.h +++ b/sortix/include/sortix/kernel/clock.h @@ -46,6 +46,7 @@ public: Timer* delay_timer; Timer* absolute_timer; struct timespec current_time; + struct timespec current_advancement; struct timespec resolution; kthread_mutex_t clock_mutex; bool clock_callable_from_interrupt; @@ -61,6 +62,8 @@ public: void Cancel(Timer* timer); void LockClock(); void UnlockClock(); + struct timespec SleepDelay(struct timespec duration); + struct timespec SleepUntil(struct timespec expiration); public: // These should only be called if the clock is locked. void RegisterAbsolute(Timer* timer); diff --git a/sortix/include/sortix/syscallnum.h b/sortix/include/sortix/syscallnum.h index d58e4c88..7895d32b 100644 --- a/sortix/include/sortix/syscallnum.h +++ b/sortix/include/sortix/syscallnum.h @@ -126,6 +126,7 @@ #define SYSCALL_ALARMNS 102 #define SYSCALL_CLOCK_GETTIMERES 103 #define SYSCALL_CLOCK_SETTIMERES 104 -#define SYSCALL_MAX_NUM 105 /* index of highest constant + 1 */ +#define SYSCALL_CLOCK_NANOSLEEP 105 +#define SYSCALL_MAX_NUM 106 /* index of highest constant + 1 */ #endif diff --git a/sortix/scheduler.cpp b/sortix/scheduler.cpp index 4165f1ed..5a08ec6b 100644 --- a/sortix/scheduler.cpp +++ b/sortix/scheduler.cpp @@ -278,6 +278,7 @@ static void SleepUntil(struct timespec wakeat) Yield(); } +// TODO: This function has been obsoleted by the clock_nanosleep system call. int sys_sleep(size_t secs) { struct timespec delay = timespec_make(secs, 0); @@ -286,6 +287,7 @@ int sys_sleep(size_t secs) return 0; } +// TODO: This function has been obsoleted by the clock_nanosleep system call. int sys_usleep(size_t usecs) { size_t secs = usecs / 1000000; diff --git a/sortix/user-timer.cpp b/sortix/user-timer.cpp index 6f8d0d34..fcaffc99 100644 --- a/sortix/user-timer.cpp +++ b/sortix/user-timer.cpp @@ -242,6 +242,28 @@ static int sys_clock_settimeres(clockid_t clockid, const struct timespec* time, return 0; } +static int sys_clock_nanosleep(clockid_t clockid, int flags, + const struct timespec* user_duration, + struct timespec* user_remainder) +{ + struct timespec time; + + Clock* clock = Time::GetClock(clockid); + if ( !clock ) + return -1; + + if ( !CopyFromUser(&time, user_duration, sizeof(time)) ) + return -1; + + time = flags & TIMER_ABSTIME ? clock->SleepUntil(time) : + clock->SleepDelay(time); + + if ( user_remainder && !CopyToUser(user_remainder, &time, sizeof(time)) ) + return -1; + + return timespec_eq(time, timespec_nul()) ? 0 : (errno = EINTR, -1); +} + // TODO: Made obsolete by cloc_gettimeres. static int sys_uptime(uintmax_t* usecssinceboot) { @@ -260,6 +282,7 @@ void UserTimer::Init() { Syscall::Register(SYSCALL_CLOCK_GETTIMERES, (void*) sys_clock_gettimeres); Syscall::Register(SYSCALL_CLOCK_SETTIMERES, (void*) sys_clock_settimeres); + Syscall::Register(SYSCALL_CLOCK_NANOSLEEP, (void*) sys_clock_nanosleep); Syscall::Register(SYSCALL_TIMER_CREATE, (void*) sys_timer_create); Syscall::Register(SYSCALL_TIMER_DELETE, (void*) sys_timer_delete); Syscall::Register(SYSCALL_TIMER_GETOVERRUN, (void*) sys_timer_getoverrun);