source: rtems/cpukit/score/src/coremutexsurrender.c @ bbd6d27a

5
Last change on this file since bbd6d27a was 8f96581, checked in by Sebastian Huber <sebastian.huber@…>, on 04/01/16 at 09:38:47

score: Rework MP thread queue callout support

The thread queue implementation was heavily reworked to support SMP.
This broke the multiprocessing support of the thread queues. This is
fixed by this patch.

A thread proxy is unblocked due to three reasons

1) timeout,
2) request satisfaction, and
3) extraction.

In case 1) no MPCI message must be sent. This is ensured via the
_Thread_queue_MP_callout_do_nothing() callout set during
_Thread_MP_Allocate_proxy().

In case 2) and 3) an MPCI message must be sent. In case we interrupt
the blocking operation during _Thread_queue_Enqueue_critical(), then
this message must be sent by the blocking thread. For this the new
fields Thread_Proxy_control::thread_queue_callout and
Thread_Proxy_control::thread_queue_id are used.

Delete the individual API MP callout types and use
Thread_queue_MP_callout throughout. This type is only defined in
multiprocessing configurations. Prefix the multiprocessing parameters
with mp_ to ease code review. Multiprocessing specific parameters are
optional due to use of a similar macro pattern. There is no overhead
for non-multiprocessing configurations.

  • Property mode set to 100644
File size: 6.9 KB
Line 
1/**
2 * @file
3 *
4 * @brief Surrender the Mutex
5 * @ingroup ScoreMutex
6 */
7
8/*
9 *  COPYRIGHT (c) 1989-2006.
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/system.h>
22#include <rtems/score/isr.h>
23#include <rtems/score/coremuteximpl.h>
24#include <rtems/score/thread.h>
25
26#ifdef __RTEMS_STRICT_ORDER_MUTEX__
27  static inline void _CORE_mutex_Push_priority(
28    CORE_mutex_Control *mutex,
29    Thread_Control *thread
30  )
31  {
32    _Chain_Prepend_unprotected(
33      &thread->lock_mutex,
34      &mutex->queue.lock_queue
35    );
36    mutex->queue.priority_before = thread->current_priority;
37  }
38
39  static inline CORE_mutex_Status _CORE_mutex_Pop_priority(
40    CORE_mutex_Control *mutex,
41    Thread_Control *holder
42  )
43  {
44    /*
45     *  Check whether the holder release the mutex in LIFO order if not return
46     *  error code.
47     */
48    if ( _Chain_First( &holder->lock_mutex ) != &mutex->queue.lock_queue ) {
49      mutex->nest_count++;
50
51      return CORE_MUTEX_RELEASE_NOT_ORDER;
52    }
53
54    /*
55     *  This pops the first node from the list.
56     */
57    _Chain_Get_first_unprotected( &holder->lock_mutex );
58
59    if ( mutex->queue.priority_before != holder->current_priority )
60      _Thread_Change_priority( holder, mutex->queue.priority_before, true );
61
62    return CORE_MUTEX_STATUS_SUCCESSFUL;
63  }
64#else
65  #define _CORE_mutex_Push_priority( mutex, thread ) ((void) 0)
66
67  #define _CORE_mutex_Pop_priority( mutex, thread ) \
68    CORE_MUTEX_STATUS_SUCCESSFUL
69#endif
70
71CORE_mutex_Status _CORE_mutex_Do_surrender(
72  CORE_mutex_Control      *the_mutex,
73#if defined(RTEMS_MULTIPROCESSING)
74  Thread_queue_MP_callout  mp_callout,
75  Objects_Id               mp_id,
76#endif
77  ISR_lock_Context        *lock_context
78)
79{
80  Thread_Control *the_thread;
81  Thread_Control *holder;
82
83  holder = the_mutex->holder;
84
85  /*
86   *  The following code allows a thread (or ISR) other than the thread
87   *  which acquired the mutex to release that mutex.  This is only
88   *  allowed when the mutex in quetion is FIFO or simple Priority
89   *  discipline.  But Priority Ceiling or Priority Inheritance mutexes
90   *  must be released by the thread which acquired them.
91   */
92
93  if ( the_mutex->Attributes.only_owner_release ) {
94    if ( !_Thread_Is_executing( holder ) ) {
95      _ISR_lock_ISR_enable( lock_context );
96      return CORE_MUTEX_STATUS_NOT_OWNER_OF_RESOURCE;
97    }
98  }
99
100  _Thread_queue_Acquire_critical( &the_mutex->Wait_queue, lock_context );
101
102  /* XXX already unlocked -- not right status */
103
104  if ( !the_mutex->nest_count ) {
105    _Thread_queue_Release( &the_mutex->Wait_queue, lock_context );
106    return CORE_MUTEX_STATUS_SUCCESSFUL;
107  }
108
109  the_mutex->nest_count--;
110
111  if ( the_mutex->nest_count != 0 ) {
112    /*
113     *  All error checking is on the locking side, so if the lock was
114     *  allowed to acquired multiple times, then we should just deal with
115     *  that.  The RTEMS_DEBUG is just a validation.
116     */
117    #if defined(RTEMS_DEBUG)
118      switch ( the_mutex->Attributes.lock_nesting_behavior ) {
119        case CORE_MUTEX_NESTING_ACQUIRES:
120          _Thread_queue_Release( &the_mutex->Wait_queue, lock_context );
121          return CORE_MUTEX_STATUS_SUCCESSFUL;
122        #if defined(RTEMS_POSIX_API)
123          case CORE_MUTEX_NESTING_IS_ERROR:
124            /* should never occur */
125            _Thread_queue_Release( &the_mutex->Wait_queue, lock_context );
126            return CORE_MUTEX_STATUS_NESTING_NOT_ALLOWED;
127        #endif
128        case CORE_MUTEX_NESTING_BLOCKS:
129          /* Currently no API exercises this behavior. */
130          break;
131      }
132    #else
133      _Thread_queue_Release( &the_mutex->Wait_queue, lock_context );
134      /* must be CORE_MUTEX_NESTING_ACQUIRES or we wouldn't be here */
135      return CORE_MUTEX_STATUS_SUCCESSFUL;
136    #endif
137  }
138
139  /*
140   *  Formally release the mutex before possibly transferring it to a
141   *  blocked thread.
142   */
143  if ( _CORE_mutex_Is_inherit_priority( &the_mutex->Attributes ) ||
144       _CORE_mutex_Is_priority_ceiling( &the_mutex->Attributes ) ) {
145    CORE_mutex_Status pop_status =
146      _CORE_mutex_Pop_priority( the_mutex, holder );
147
148    if ( pop_status != CORE_MUTEX_STATUS_SUCCESSFUL ) {
149      _Thread_queue_Release( &the_mutex->Wait_queue, lock_context );
150      return pop_status;
151    }
152
153    holder->resource_count--;
154  }
155  the_mutex->holder = NULL;
156
157  /*
158   *  Now we check if another thread was waiting for this mutex.  If so,
159   *  transfer the mutex to that thread.
160   */
161  if (
162    ( the_thread = _Thread_queue_First_locked(
163        &the_mutex->Wait_queue,
164        the_mutex->operations
165      )
166    )
167  ) {
168    bool unblock;
169
170    the_mutex->holder     = the_thread;
171    the_mutex->nest_count = 1;
172
173    /*
174     * We must extract the thread now since this will restore its default
175     * thread lock.  This is necessary to avoid a deadlock in the
176     * _Thread_Change_priority() below due to a recursive thread queue lock
177     * acquire.
178     */
179    unblock = _Thread_queue_Extract_locked(
180      &the_mutex->Wait_queue.Queue,
181      the_mutex->operations,
182      the_thread,
183      mp_callout,
184      mp_id
185    );
186
187#if defined(RTEMS_MULTIPROCESSING)
188    if ( _Objects_Is_local_id( the_thread->Object.id ) )
189#endif
190    {
191      switch ( the_mutex->Attributes.discipline ) {
192        case CORE_MUTEX_DISCIPLINES_FIFO:
193        case CORE_MUTEX_DISCIPLINES_PRIORITY:
194          break;
195        case CORE_MUTEX_DISCIPLINES_PRIORITY_INHERIT:
196          _CORE_mutex_Push_priority( the_mutex, the_thread );
197          the_thread->resource_count++;
198          _Thread_queue_Boost_priority( &the_mutex->Wait_queue.Queue, the_thread );
199          break;
200        case CORE_MUTEX_DISCIPLINES_PRIORITY_CEILING:
201          _CORE_mutex_Push_priority( the_mutex, the_thread );
202          the_thread->resource_count++;
203          _Thread_Raise_priority(
204            the_thread,
205            the_mutex->Attributes.priority_ceiling
206          );
207          break;
208      }
209    }
210
211    _Thread_queue_Unblock_critical(
212      unblock,
213      &the_mutex->Wait_queue.Queue,
214      the_thread,
215      mp_callout,
216      mp_id,
217      lock_context
218    );
219  } else {
220    _Thread_queue_Release( &the_mutex->Wait_queue, lock_context );
221  }
222
223  /*
224   *  Whether or not someone is waiting for the mutex, an
225   *  inherited priority must be lowered if this is the last
226   *  mutex (i.e. resource) this task has.
227   */
228  if ( !_Thread_Owns_resources( holder ) ) {
229    /*
230     * Ensure that the holder resource count is visible to all other processors
231     * and that we read the latest priority restore hint.
232     */
233    _Atomic_Fence( ATOMIC_ORDER_ACQ_REL );
234
235    if ( holder->priority_restore_hint ) {
236      Per_CPU_Control *cpu_self;
237
238      cpu_self = _Thread_Dispatch_disable();
239      _Thread_Restore_priority( holder );
240      _Thread_Dispatch_enable( cpu_self );
241    }
242  }
243
244  return CORE_MUTEX_STATUS_SUCCESSFUL;
245}
Note: See TracBrowser for help on using the repository browser.