source: rtems/cpukit/posix/src/nanosleep.c @ 620b23e

5
Last change on this file since 620b23e was 620b23e, checked in by Sebastian Huber <sebastian.huber@…>, on 11/24/16 at 05:13:11

score: Optimize _Thread_queue_Enqueue()

Move thread state for _Thread_queue_Enqueue() to the thread queue
context. This reduces the parameter count of _Thread_queue_Enqueue()
from five to four (ARM for example has only four function parameter
registers). Since the thread state is used after several function calls
inside _Thread_queue_Enqueue() this parameter was saved on the stack
previously.

  • Property mode set to 100644
File size: 5.8 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  clockid_t             clock_id,
37  uint64_t              ticks,
38  struct timespec      *timeout,
39  struct timespec      *rmtp,
40  Watchdog_Discipline   discipline
41)
42{
43  Thread_queue_Context queue_context;
44  struct timespec      stop;
45  int                  err;
46
47  err = 0;
48
49  _Thread_queue_Context_initialize( &queue_context );
50  _Thread_queue_Context_set_thread_state(
51    &queue_context,
52    STATES_DELAYING | STATES_INTERRUPTIBLE_BY_SIGNAL
53  );
54  _Thread_queue_Context_set_enqueue_callout(
55    &queue_context,
56    _Thread_queue_Enqueue_do_nothing
57  );
58
59  if ( discipline == WATCHDOG_ABSOLUTE ) {
60    _Thread_queue_Context_set_absolute_timeout( &queue_context, ticks );
61  } else {
62    _Thread_queue_Context_set_relative_timeout( &queue_context, ticks );
63  }
64
65  /*
66   *  Block for the desired amount of time
67   */
68  _Thread_queue_Acquire( &_Nanosleep_Pseudo_queue, &queue_context );
69  _Thread_queue_Enqueue(
70    &_Nanosleep_Pseudo_queue.Queue,
71    &_Thread_queue_Operations_FIFO,
72    _Thread_Executing,
73    &queue_context
74  );
75
76  clock_gettime( clock_id, &stop );
77  /*
78   * If the user wants the time remaining, do the conversion.
79   */
80  if ( _Timespec_Less_than( &stop, timeout ) ) {
81    /*
82     *  If there is time remaining, then we were interrupted by a signal.
83     */
84    err = EINTR;
85    if ( rmtp != NULL ) {
86      _Timespec_Subtract( &stop, timeout, rmtp );
87    }
88  } else if ( rmtp != NULL ) {
89    /* no time remaining */
90    _Timespec_Set_to_zero( rmtp );
91  }
92
93  return err;
94}
95
96/*
97 * A nanosleep for zero time is implemented as a yield.
98 * This behavior is also beyond the POSIX specification but is
99 * consistent with the RTEMS API and yields desirable behavior.
100 */
101static inline int nanosleep_yield( struct timespec *rmtp )
102{
103  /*
104   * It is critical to obtain the executing thread after thread dispatching is
105   * disabled on SMP configurations.
106   */
107  Thread_Control  *executing;
108  Per_CPU_Control *cpu_self;
109
110  executing = _Thread_Get_executing();
111  cpu_self = _Thread_Dispatch_disable();
112  _Thread_Yield( executing );
113  _Thread_Dispatch_direct( cpu_self );
114  if ( rmtp ) {
115    rmtp->tv_sec = 0;
116    rmtp->tv_nsec = 0;
117  }
118  return 0;
119}
120
121/*
122 *  14.2.5 High Resolution Sleep, P1003.1b-1993, p. 269
123 */
124int nanosleep(
125  const struct timespec  *rqtp,
126  struct timespec        *rmtp
127)
128{
129  int err;
130  struct timespec now;
131  uint64_t ticks;
132  Watchdog_Interval relative_interval;
133
134  /*
135   *  Return EINVAL if the delay interval is negative.
136   *
137   *  NOTE:  This behavior is beyond the POSIX specification.
138   *         FSU and GNU/Linux pthreads shares this behavior.
139   */
140  if ( !_Timespec_Is_valid( rqtp ) ) {
141    rtems_set_errno_and_return_minus_one( EINVAL );
142  }
143
144  relative_interval = _Timespec_To_ticks( rqtp );
145  if ( relative_interval == 0 )
146    return nanosleep_yield( rmtp );
147
148 /* CLOCK_REALTIME can be adjusted during the timeout,
149  * so convert to an absolute timeout value and put the
150  * thread on the WATCHDOG_ABSOLUTE threadq. */
151  err = clock_gettime( CLOCK_REALTIME, &now );
152  if ( err != 0 )
153    return -1;
154  _Timespec_Add_to( &now, rqtp );
155  ticks = _Watchdog_Ticks_from_timespec( &now );
156  err = nanosleep_helper( CLOCK_REALTIME,
157      ticks,
158      &now,
159      rmtp,
160      WATCHDOG_ABSOLUTE
161  );
162  if ( err != 0 ) {
163    rtems_set_errno_and_return_minus_one( err );
164  }
165  return 0;
166}
167
168/*
169 * High Resolution Sleep with Specifiable Clock, IEEE Std 1003.1, 2001
170 */
171int clock_nanosleep(
172  clockid_t               clock_id,
173  int                     flags,
174  const struct timespec  *rqtp,
175  struct timespec        *rmtp
176)
177{
178  int err = 0;
179  struct timespec timeout;
180  uint64_t ticks;
181  Watchdog_Interval relative_interval;
182  TOD_Absolute_timeout_conversion_results status;
183
184  if ( !_Timespec_Is_valid( rqtp ) )
185    return EINVAL;
186
187  /* get relative ticks of the requested timeout */
188  if ( flags & TIMER_ABSTIME ) {
189    /* See if absolute time already passed */
190    status = _TOD_Absolute_timeout_to_ticks(rqtp, clock_id, &relative_interval);
191    if ( status == TOD_ABSOLUTE_TIMEOUT_INVALID )
192      return EINVAL;
193    if ( status == TOD_ABSOLUTE_TIMEOUT_IS_IN_PAST )
194      return 0;
195    if ( status == TOD_ABSOLUTE_TIMEOUT_IS_NOW )
196      return nanosleep_yield( NULL );
197    rmtp = NULL; /* Do not touch rmtp when using absolute time */
198    timeout.tv_sec = 0;
199    timeout.tv_nsec = 0;
200  } else {
201    /* prepare to convert relative ticks to absolute timeout */
202    err = clock_gettime( clock_id, &timeout );
203    if ( err != 0 )
204      return EINVAL;
205    relative_interval = _Timespec_To_ticks( rqtp );
206  }
207  if ( relative_interval == 0 )
208    return nanosleep_yield( rmtp );
209
210  _Timespec_Add_to( &timeout, rqtp );
211  if ( clock_id == CLOCK_REALTIME ) {
212    ticks = _Watchdog_Ticks_from_timespec( &timeout );
213    err = nanosleep_helper(
214        clock_id,
215        ticks,
216        &timeout,
217        rmtp,
218        WATCHDOG_ABSOLUTE
219    );
220  } else if ( clock_id == CLOCK_MONOTONIC ) {
221    /* use the WATCHDOG_RELATIVE to ignore changes in wall time */
222    err = nanosleep_helper(
223        clock_id,
224        relative_interval,
225        &timeout,
226        rmtp,
227        WATCHDOG_RELATIVE
228    );
229  } else {
230    err = ENOTSUP;
231  }
232  return err;
233}
Note: See TracBrowser for help on using the repository browser.