source: rtems/cpukit/posix/src/condwaitsupp.c @ 7f4ee2b

5
Last change on this file since 7f4ee2b was 7f4ee2b, checked in by Sebastian Huber <sebastian.huber@…>, on 04/22/16 at 12:37:13

posix: Avoid Giant lock for condition variables

Update #2555.

  • Property mode set to 100644
File size: 3.7 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/muteximpl.h>
23#include <rtems/score/assert.h>
24#include <rtems/score/statesimpl.h>
25#include <rtems/score/threadimpl.h>
26
27THREAD_WAIT_QUEUE_OBJECT_ASSERT(
28  POSIX_Condition_variables_Control,
29  Wait_queue
30);
31
32int _POSIX_Condition_variables_Wait_support(
33  pthread_cond_t            *cond,
34  pthread_mutex_t           *mutex,
35  Watchdog_Interval          timeout,
36  bool                       already_timedout
37)
38{
39  POSIX_Condition_variables_Control *the_cond;
40  POSIX_Mutex_Control               *the_mutex;
41  ISR_lock_Context                   lock_context;
42  int                                status;
43  int                                mutex_status;
44  CORE_mutex_Status                  core_mutex_status;
45  Per_CPU_Control                   *cpu_self;
46  Thread_Control                    *executing;
47
48  if ( mutex == NULL ) {
49    return EINVAL;
50  }
51
52  the_cond = _POSIX_Condition_variables_Get( cond, &lock_context );
53
54  if ( the_cond == NULL ) {
55    return EINVAL;
56  }
57
58  _POSIX_Condition_variables_Acquire_critical( the_cond, &lock_context );
59
60  if (
61    the_cond->mutex != POSIX_CONDITION_VARIABLES_NO_MUTEX
62      && the_cond->mutex != *mutex
63  ) {
64    _POSIX_Condition_variables_Release( the_cond, &lock_context );
65    return EINVAL;
66  }
67
68  the_cond->mutex = *mutex;
69
70  cpu_self = _Thread_Dispatch_disable_critical( &lock_context );
71  executing = _Per_CPU_Get_executing( cpu_self );
72
73  /*
74   *  Historically, we ignored the unlock status since the behavior
75   *  is undefined by POSIX. But GNU/Linux returns EPERM in this
76   *  case, so we follow their lead.
77   */
78
79  the_mutex = _POSIX_Mutex_Get_no_protection( mutex );
80  if (
81    the_mutex == NULL
82      || !_CORE_mutex_Is_owner( &the_mutex->Mutex, executing )
83  ) {
84    _POSIX_Condition_variables_Release( the_cond, &lock_context );
85    _Thread_Dispatch_enable( cpu_self );
86    return EPERM;
87  }
88
89  if ( !already_timedout ) {
90    executing->Wait.return_code = 0;
91    _Thread_queue_Enqueue_critical(
92      &the_cond->Wait_queue.Queue,
93      POSIX_CONDITION_VARIABLES_TQ_OPERATIONS,
94      executing,
95      STATES_WAITING_FOR_CONDITION_VARIABLE,
96      timeout,
97      ETIMEDOUT,
98      &lock_context
99    );
100  } else {
101    _POSIX_Condition_variables_Release( the_cond, &lock_context );
102    executing->Wait.return_code = ETIMEDOUT;
103  }
104
105  _ISR_lock_ISR_disable( &lock_context );
106  core_mutex_status = _CORE_mutex_Surrender(
107    &the_mutex->Mutex,
108    NULL,
109    0,
110    &lock_context
111  );
112  _Assert( core_mutex_status == CORE_MUTEX_STATUS_SUCCESSFUL );
113  (void) core_mutex_status;
114
115  /*
116   *  Switch ourself out because we blocked as a result of the
117   *  _Thread_queue_Enqueue_critical().
118   */
119
120  _Thread_Dispatch_enable( cpu_self );
121
122  status = (int) executing->Wait.return_code;
123
124  /*
125   *  If the thread is interrupted, while in the thread queue, by
126   *  a POSIX signal, then pthread_cond_wait returns spuriously,
127   *  according to the POSIX standard. It means that pthread_cond_wait
128   *  returns a success status, except for the fact that it was not
129   *  woken up a pthread_cond_signal() or a pthread_cond_broadcast().
130   */
131
132  if ( status == EINTR ) {
133    status = 0;
134  }
135
136  /*
137   *  When we get here the dispatch disable level is 0.
138   */
139
140  mutex_status = pthread_mutex_lock( mutex );
141  if ( mutex_status != 0 ) {
142    return EINVAL;
143  }
144
145  return status;
146}
Note: See TracBrowser for help on using the repository browser.