source: rtems/cpukit/posix/src/nanosleep.c @ e0f17fc

Last change on this file since e0f17fc was e0f17fc, checked in by Gedare Bloom <gedare@…>, on Jul 15, 2016 at 4:52:05 PM

posix: fix clock_nanosleep and nanosleep clock use

Sleeping with CLOCK_REALTIME should use the WATCHDOG_ABSOLUTE
clock discipline for the threadq so that the timeout interval
may change in case the clock source changes. Similarly,
CLOCK_MONOTONIC uses the WATCHDOG_RELATIVE threadq that will
only wakeup the thread after the requested count of ticks elapse.

updates #2732

  • Property mode set to 100644
File size: 5.1 KB
Line 
1/**
2 * @file
3 *
4 * @brief Suspends Execution of calling thread until Time elapses
5 * @ingroup POSIXAPI
6 */
7
8/*
9 *  COPYRIGHT (c) 1989-2015.
10 *  On-Line Applications Research Corporation (OAR).
11 *
12 *  Copyright (c) 2016. Gedare Bloom.
13 *
14 *  The license and distribution terms for this file may be
15 *  found in the file LICENSE in this distribution or at
16 *  http://www.rtems.org/license/LICENSE.
17 */
18
19#if HAVE_CONFIG_H
20#include "config.h"
21#endif
22
23#include <time.h>
24#include <errno.h>
25
26#include <rtems/seterr.h>
27#include <rtems/score/threadimpl.h>
28#include <rtems/score/threadqimpl.h>
29#include <rtems/score/timespec.h>
30#include <rtems/score/watchdogimpl.h>
31
32static Thread_queue_Control _Nanosleep_Pseudo_queue =
33  THREAD_QUEUE_INITIALIZER( "Nanosleep" );
34
35static inline int nanosleep_helper(
36  uint64_t              ticks,
37  struct timespec      *rmtp,
38  Watchdog_Discipline   discipline
39)
40{
41  /*
42   * It is critical to obtain the executing thread after thread dispatching is
43   * disabled on SMP configurations.
44   */
45  Thread_Control  *executing;
46  Per_CPU_Control *cpu_self;
47
48  Watchdog_Interval  start;
49  Watchdog_Interval  elapsed;
50
51  executing = _Thread_Get_executing();
52
53  /*
54   *  A nanosleep for zero time is implemented as a yield.
55   *  This behavior is also beyond the POSIX specification but is
56   *  consistent with the RTEMS API and yields desirable behavior.
57   */
58  if ( !ticks ) {
59    cpu_self = _Thread_Dispatch_disable();
60      _Thread_Yield( executing );
61    _Thread_Dispatch_enable( cpu_self );
62    if ( rmtp ) {
63       rmtp->tv_sec = 0;
64       rmtp->tv_nsec = 0;
65    }
66    return 0;
67  }
68
69  start = _Watchdog_Ticks_since_boot;
70
71  /*
72   *  Block for the desired amount of time
73   */
74  _Thread_queue_Enqueue(
75    &_Nanosleep_Pseudo_queue,
76    &_Thread_queue_Operations_FIFO,
77    executing,
78    STATES_DELAYING | STATES_INTERRUPTIBLE_BY_SIGNAL,
79    ticks,
80    discipline,
81    1
82  );
83
84  /*
85   * Calculate the time that passed while we were sleeping and how
86   * much remains from what we requested.
87   */
88  elapsed = _Watchdog_Ticks_since_boot - start;
89  if ( elapsed >= ticks )
90    ticks = 0;
91  else
92    ticks -= elapsed;
93
94  /*
95   * If the user wants the time remaining, do the conversion.
96   */
97  if ( rmtp ) {
98    _Timespec_From_ticks( ticks, rmtp );
99  }
100
101  /*
102   *  Only when POSIX is enabled, can a sleep be interrupted.
103   */
104  #if defined(RTEMS_POSIX_API)
105    /*
106     *  If there is time remaining, then we were interrupted by a signal.
107     */
108    if ( ticks )
109      return EINTR;
110  #endif
111
112  return 0;
113}
114/*
115 *  14.2.5 High Resolution Sleep, P1003.1b-1993, p. 269
116 */
117int nanosleep(
118  const struct timespec  *rqtp,
119  struct timespec        *rmtp
120)
121{
122  int err;
123  struct timespec timeout;
124  uint64_t ticks;
125
126  /*
127   *  Return EINVAL if the delay interval is negative.
128   *
129   *  NOTE:  This behavior is beyond the POSIX specification.
130   *         FSU and GNU/Linux pthreads shares this behavior.
131   */
132  if ( !_Timespec_Is_valid( rqtp ) )
133    return EINVAL;
134
135 /* CLOCK_REALTIME can be adjusted during the timeout,
136  * so convert to an absolute timeout value and put the
137  * thread on the WATCHDOG_ABSOLUTE threadq. */
138  err = clock_gettime( CLOCK_REALTIME, &timeout );
139  if ( err != 0 )
140    return -1;
141
142  _Timespec_Add_to( &timeout, rqtp );
143  ticks = _Watchdog_Ticks_from_timespec( &timeout );
144  err = nanosleep_helper(ticks, rmtp, WATCHDOG_ABSOLUTE );
145  if ( err != 0 ) {
146    rtems_set_errno_and_return_minus_one( err );
147  }
148  return 0;
149}
150
151/*
152 * High Resolution Sleep with Specifiable Clock, IEEE Std 1003.1, 2001
153 */
154int clock_nanosleep(
155  clockid_t               clock_id,
156  int                     flags,
157  const struct timespec  *rqtp,
158  struct timespec        *rmtp
159)
160{
161  int err = 0;
162  struct timespec absolute_timeout;
163  uint64_t ticks;
164  Watchdog_Interval relative_ticks;
165  TOD_Absolute_timeout_conversion_results status;
166
167  /*
168   *  Return EINVAL if the delay interval is negative.
169   *
170   *  NOTE:  This behavior is beyond the POSIX specification.
171   *         FSU and GNU/Linux pthreads shares this behavior.
172   */
173  if ( !_Timespec_Is_valid( rqtp ) )
174    return EINVAL;
175
176  if ( flags & TIMER_ABSTIME ) {
177    /* See if absolute time already passed */
178    status = _TOD_Absolute_timeout_to_ticks(rqtp, clock_id, &relative_ticks);
179    if ( status == TOD_ABSOLUTE_TIMEOUT_INVALID )
180      return EINVAL;
181    if ( status == TOD_ABSOLUTE_TIMEOUT_IS_IN_PAST ||
182        status == TOD_ABSOLUTE_TIMEOUT_IS_NOW ) {
183      return 0;
184    }
185    rmtp = NULL; /* Do not touch rmtp when using absolute time */
186  } else {
187    relative_ticks = _Timespec_To_ticks(rqtp);
188  }
189
190  if ( clock_id == CLOCK_REALTIME ) {
191    if ( flags & TIMER_ABSTIME ) {
192      ticks = _Watchdog_Ticks_from_timespec(rqtp);
193    } else {
194      err = clock_gettime( CLOCK_REALTIME, &absolute_timeout );
195      if ( err != 0 ) {
196        return EINVAL;
197      }
198      _Timespec_Add_to( &absolute_timeout, rqtp );
199      ticks = _Watchdog_Ticks_from_timespec( &absolute_timeout );
200    }
201    err = nanosleep_helper( ticks, rmtp, WATCHDOG_ABSOLUTE );
202  } else if ( clock_id == CLOCK_MONOTONIC ) {
203    /* use the WATCHDOG_RELATIVE to ignore changes in wall time */
204    err = nanosleep_helper( relative_ticks, rmtp, WATCHDOG_RELATIVE );
205  } else {
206    err = ENOTSUP;
207  }
208  return err;
209}
Note: See TracBrowser for help on using the repository browser.