summaryrefslogtreecommitdiffstats
path: root/cpukit/posix/src/nanosleep.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--cpukit/posix/src/nanosleep.c255
1 files changed, 84 insertions, 171 deletions
diff --git a/cpukit/posix/src/nanosleep.c b/cpukit/posix/src/nanosleep.c
index e8a7fe14c1..f65c91bc17 100644
--- a/cpukit/posix/src/nanosleep.c
+++ b/cpukit/posix/src/nanosleep.c
@@ -21,100 +21,18 @@
#endif
#include <time.h>
-#include <errno.h>
-#include <rtems/seterr.h>
#include <rtems/score/threadimpl.h>
#include <rtems/score/threadqimpl.h>
#include <rtems/score/timespec.h>
+#include <rtems/score/todimpl.h>
#include <rtems/score/watchdogimpl.h>
+#include <rtems/posix/posixapi.h>
+#include <rtems/seterr.h>
static Thread_queue_Control _Nanosleep_Pseudo_queue =
THREAD_QUEUE_INITIALIZER( "Nanosleep" );
-static inline int nanosleep_helper(
- clockid_t clock_id,
- uint64_t ticks,
- struct timespec *timeout,
- struct timespec *rmtp,
- Watchdog_Discipline discipline
-)
-{
- Thread_queue_Context queue_context;
- struct timespec stop;
- int err;
-
- err = 0;
-
- _Thread_queue_Context_initialize( &queue_context );
- _Thread_queue_Context_set_thread_state(
- &queue_context,
- STATES_WAITING_FOR_TIME | STATES_INTERRUPTIBLE_BY_SIGNAL
- );
- _Thread_queue_Context_set_enqueue_do_nothing_extra( &queue_context );
-
- if ( discipline == WATCHDOG_ABSOLUTE ) {
- _Thread_queue_Context_set_absolute_timeout( &queue_context, ticks );
- } else {
- _Thread_queue_Context_set_relative_timeout( &queue_context, ticks );
- }
-
- /*
- * Block for the desired amount of time
- */
- _Thread_queue_Acquire( &_Nanosleep_Pseudo_queue, &queue_context );
- _Thread_queue_Enqueue(
- &_Nanosleep_Pseudo_queue.Queue,
- &_Thread_queue_Operations_FIFO,
- _Thread_Executing,
- &queue_context
- );
-
- clock_gettime( clock_id, &stop );
- /*
- * If the user wants the time remaining, do the conversion.
- */
- if ( _Timespec_Less_than( &stop, timeout ) ) {
- /*
- * If there is time remaining, then we were interrupted by a signal.
- */
- err = EINTR;
- if ( rmtp != NULL ) {
- _Timespec_Subtract( &stop, timeout, rmtp );
- }
- } else if ( rmtp != NULL ) {
- /* no time remaining */
- _Timespec_Set_to_zero( rmtp );
- }
-
- return err;
-}
-
-/*
- * A nanosleep for zero time is implemented as a yield.
- * This behavior is also beyond the POSIX specification but is
- * consistent with the RTEMS API and yields desirable behavior.
- */
-static inline int nanosleep_yield( struct timespec *rmtp )
-{
- /*
- * It is critical to obtain the executing thread after thread dispatching is
- * disabled on SMP configurations.
- */
- Thread_Control *executing;
- Per_CPU_Control *cpu_self;
-
- executing = _Thread_Get_executing();
- cpu_self = _Thread_Dispatch_disable();
- _Thread_Yield( executing );
- _Thread_Dispatch_direct( cpu_self );
- if ( rmtp ) {
- rmtp->tv_sec = 0;
- rmtp->tv_nsec = 0;
- }
- return 0;
-}
-
/*
* 14.2.5 High Resolution Sleep, P1003.1b-1993, p. 269
*/
@@ -123,43 +41,15 @@ int nanosleep(
struct timespec *rmtp
)
{
- int err;
- struct timespec now;
- uint64_t ticks;
- Watchdog_Interval relative_interval;
-
- /*
- * Return EINVAL if the delay interval is negative.
- *
- * NOTE: This behavior is beyond the POSIX specification.
- * FSU and GNU/Linux pthreads shares this behavior.
- */
- if ( !_Timespec_Is_valid( rqtp ) ) {
- rtems_set_errno_and_return_minus_one( EINVAL );
- }
+ int eno;
- relative_interval = _Timespec_To_ticks( rqtp );
- if ( relative_interval == 0 )
- return nanosleep_yield( rmtp );
-
- /* CLOCK_REALTIME can be adjusted during the timeout,
- * so convert to an absolute timeout value and put the
- * thread on the WATCHDOG_ABSOLUTE threadq. */
- err = clock_gettime( CLOCK_REALTIME, &now );
- if ( err != 0 )
- return -1;
- _Timespec_Add_to( &now, rqtp );
- ticks = _Watchdog_Realtime_from_timespec( &now );
- err = nanosleep_helper( CLOCK_REALTIME,
- ticks,
- &now,
- rmtp,
- WATCHDOG_ABSOLUTE
- );
- if ( err != 0 ) {
- rtems_set_errno_and_return_minus_one( err );
+ eno = clock_nanosleep( CLOCK_REALTIME, 0, rqtp, rmtp );
+
+ if ( eno != 0 ) {
+ rtems_set_errno_and_return_minus_one( eno );
}
- return 0;
+
+ return eno;
}
/*
@@ -172,59 +62,82 @@ int clock_nanosleep(
struct timespec *rmtp
)
{
- int err = 0;
- struct timespec timeout;
- uint64_t ticks;
- Watchdog_Interval relative_interval;
- TOD_Absolute_timeout_conversion_results status;
-
- if ( !_Timespec_Is_valid( rqtp ) )
- return EINVAL;
-
- /* get relative ticks of the requested timeout */
- if ( flags & TIMER_ABSTIME ) {
- /* See if absolute time already passed */
- status = _TOD_Absolute_timeout_to_ticks(rqtp, clock_id, &relative_interval);
- if ( status == TOD_ABSOLUTE_TIMEOUT_INVALID )
- return EINVAL;
- if ( status == TOD_ABSOLUTE_TIMEOUT_IS_IN_PAST )
- return 0;
- if ( status == TOD_ABSOLUTE_TIMEOUT_IS_NOW )
- return nanosleep_yield( NULL );
- rmtp = NULL; /* Do not touch rmtp when using absolute time */
- timeout.tv_sec = 0;
- timeout.tv_nsec = 0;
+ Thread_queue_Context queue_context;
+ struct timespec spare_end;
+ const struct timespec *end;
+ Thread_Control *executing;
+ int eno;
+
+ if ( clock_id != CLOCK_REALTIME && clock_id != CLOCK_MONOTONIC ) {
+ return ENOTSUP;
+ }
+
+ _Thread_queue_Context_initialize( &queue_context );
+ _Thread_queue_Context_set_thread_state(
+ &queue_context,
+ STATES_WAITING_FOR_TIME | STATES_INTERRUPTIBLE_BY_SIGNAL
+ );
+
+ if ( ( flags & TIMER_ABSTIME ) != 0 ) {
+ end = rqtp;
+
+ if ( clock_id == CLOCK_REALTIME ) {
+ _Thread_queue_Context_set_enqueue_timeout_realtime_timespec(
+ &queue_context,
+ end
+ );
+ } else {
+ _Thread_queue_Context_set_enqueue_timeout_monotonic_timespec(
+ &queue_context,
+ end
+ );
+ }
} else {
- /* prepare to convert relative ticks to absolute timeout */
- err = clock_gettime( clock_id, &timeout );
- if ( err != 0 )
+ if ( !_Watchdog_Is_valid_interval_timespec( rqtp ) ) {
return EINVAL;
- relative_interval = _Timespec_To_ticks( rqtp );
- }
- if ( relative_interval == 0 )
- return nanosleep_yield( rmtp );
-
- _Timespec_Add_to( &timeout, rqtp );
- if ( clock_id == CLOCK_REALTIME ) {
- ticks = _Watchdog_Realtime_from_timespec( &timeout );
- err = nanosleep_helper(
- clock_id,
- ticks,
- &timeout,
- rmtp,
- WATCHDOG_ABSOLUTE
- );
- } else if ( clock_id == CLOCK_MONOTONIC ) {
- /* use the WATCHDOG_RELATIVE to ignore changes in wall time */
- err = nanosleep_helper(
- clock_id,
- relative_interval,
- &timeout,
- rmtp,
- WATCHDOG_RELATIVE
+ }
+
+ _TOD_Get_zero_based_uptime_as_timespec( &spare_end );
+
+ /* In case this overflows, then the enqueue callout will reject it */
+ _Timespec_Add_to( &spare_end, rqtp );
+
+ end = &spare_end;
+ _Thread_queue_Context_set_enqueue_timeout_monotonic_timespec(
+ &queue_context,
+ end
);
- } else {
- err = ENOTSUP;
}
- return err;
+
+ _Thread_queue_Acquire( &_Nanosleep_Pseudo_queue, &queue_context );
+ executing = _Thread_Executing;
+ _Thread_queue_Enqueue(
+ &_Nanosleep_Pseudo_queue.Queue,
+ &_Thread_queue_Operations_FIFO,
+ executing,
+ &queue_context
+ );
+ eno = _POSIX_Get_error_after_wait( executing );
+
+ if ( eno == ETIMEDOUT ) {
+ eno = 0;
+ }
+
+ if ( rmtp != NULL && ( flags & TIMER_ABSTIME ) == 0 ) {
+ if ( eno == EINTR ) {
+ struct timespec actual_end;
+
+ _TOD_Get_zero_based_uptime_as_timespec( &actual_end );
+
+ if ( _Timespec_Less_than( &actual_end, end ) ) {
+ _Timespec_Subtract( &actual_end, end, rmtp );
+ } else {
+ _Timespec_Set_to_zero( rmtp );
+ }
+ } else {
+ _Timespec_Set_to_zero( rmtp );
+ }
+ }
+
+ return eno;
}