source: rtems/cpukit/posix/src/condwaitsupp.c @ 127c20e

Last change on this file since 127c20e was 127c20e, checked in by Gedare Bloom <gedare@…>, on Jun 23, 2016 at 8:10:39 PM

posix: refactor cond wait support to defer abstime conversion

updates #2745

  • Property mode set to 100644
File size: 4.4 KB
Line 
1/**
2 * @file
3 *
4 * @brief POSIX Condition Variables Wait Support
5 * @ingroup POSIX_COND_VARS
6 */
7
8/*
9 *  COPYRIGHT (c) 1989-2014.
10 *  On-Line Applications Research Corporation (OAR).
11 *
12 *  The license and distribution terms for this file may be
13 *  found in the file LICENSE in this distribution or at
14 *  http://www.rtems.org/license/LICENSE.
15 */
16
17#if HAVE_CONFIG_H
18#include "config.h"
19#endif
20
21#include <rtems/posix/condimpl.h>
22#include <rtems/posix/posixapi.h>
23#include <rtems/score/assert.h>
24#include <rtems/score/statesimpl.h>
25#include <rtems/score/status.h>
26#include <rtems/score/threaddispatch.h>
27
28THREAD_QUEUE_OBJECT_ASSERT( POSIX_Condition_variables_Control, Wait_queue );
29
30int _POSIX_Condition_variables_Wait_support(
31  pthread_cond_t            *cond,
32  pthread_mutex_t           *mutex,
33  const struct timespec     *abstime
34)
35{
36  POSIX_Condition_variables_Control *the_cond;
37  Thread_queue_Context               queue_context;
38  int                                error;
39  int                                mutex_error;
40  Per_CPU_Control                   *cpu_self;
41  Thread_Control                    *executing;
42  Watchdog_Interval                  timeout;
43  bool                               already_timedout;
44  TOD_Absolute_timeout_conversion_results  status;
45
46  if ( mutex == NULL ) {
47    return EINVAL;
48  }
49
50  the_cond = _POSIX_Condition_variables_Get( cond, &queue_context );
51
52  if ( the_cond == NULL ) {
53    return EINVAL;
54  }
55
56  already_timedout = false;
57
58  if ( abstime != NULL ) {
59    /*
60     *  POSIX requires that blocking calls with timeouts that take
61     *  an absolute timeout must ignore issues with the absolute
62     *  time provided if the operation would otherwise succeed.
63     *  So we check the abstime provided, and hold on to whether it
64     *  is valid or not.  If it isn't correct and in the future,
65     *  then we do a polling operation and convert the UNSATISFIED
66     *  status into the appropriate error.
67     */
68    status = _TOD_Absolute_timeout_to_ticks( abstime, &timeout );
69    if ( status == TOD_ABSOLUTE_TIMEOUT_INVALID )
70      return EINVAL;
71
72    if ( status == TOD_ABSOLUTE_TIMEOUT_IS_IN_PAST ||
73        status == TOD_ABSOLUTE_TIMEOUT_IS_NOW ) {
74      already_timedout = true;
75    } else {
76      _Thread_queue_Context_set_relative_timeout( &queue_context, timeout );
77    }
78  } else {
79    _Thread_queue_Context_set_no_timeout( &queue_context );
80  }
81
82  _POSIX_Condition_variables_Acquire_critical( the_cond, &queue_context );
83
84  if (
85    the_cond->mutex != POSIX_CONDITION_VARIABLES_NO_MUTEX
86      && the_cond->mutex != *mutex
87  ) {
88    _POSIX_Condition_variables_Release( the_cond, &queue_context );
89    return EINVAL;
90  }
91
92  the_cond->mutex = *mutex;
93
94  cpu_self = _Thread_Dispatch_disable_critical( &queue_context.Lock_context );
95  executing = _Per_CPU_Get_executing( cpu_self );
96
97  if ( !already_timedout ) {
98    _Thread_queue_Context_set_expected_level( &queue_context, 2 );
99    _Thread_queue_Enqueue_critical(
100      &the_cond->Wait_queue.Queue,
101      POSIX_CONDITION_VARIABLES_TQ_OPERATIONS,
102      executing,
103      STATES_WAITING_FOR_CONDITION_VARIABLE,
104      &queue_context
105    );
106  } else {
107    _POSIX_Condition_variables_Release( the_cond, &queue_context );
108    executing->Wait.return_code = STATUS_TIMEOUT;
109  }
110
111  mutex_error = pthread_mutex_unlock( mutex );
112  if ( mutex_error != 0 ) {
113    /*
114     *  Historically, we ignored the unlock status since the behavior
115     *  is undefined by POSIX. But GNU/Linux returns EPERM in this
116     *  case, so we follow their lead.
117     */
118    _Assert( mutex_error == EINVAL || mutex_error == EPERM );
119    _Thread_queue_Extract( executing );
120    _Thread_Dispatch_enable( cpu_self );
121    return EPERM;
122  }
123
124  /*
125   *  Switch ourself out because we blocked as a result of the
126   *  _Thread_queue_Enqueue_critical().
127   */
128
129  _Thread_Dispatch_enable( cpu_self );
130
131  error = _POSIX_Get_error_after_wait( executing );
132
133  /*
134   *  If the thread is interrupted, while in the thread queue, by
135   *  a POSIX signal, then pthread_cond_wait returns spuriously,
136   *  according to the POSIX standard. It means that pthread_cond_wait
137   *  returns a success status, except for the fact that it was not
138   *  woken up a pthread_cond_signal() or a pthread_cond_broadcast().
139   */
140
141  if ( error == EINTR ) {
142    error = 0;
143  }
144
145  /*
146   *  When we get here the dispatch disable level is 0.
147   */
148
149  mutex_error = pthread_mutex_lock( mutex );
150  if ( mutex_error != 0 ) {
151    _Assert( mutex_error == EINVAL );
152    return EINVAL;
153  }
154
155  return error;
156}
Note: See TracBrowser for help on using the repository browser.