source: rtems/cpukit/score/include/rtems/score/coremuteximpl.h @ 33e250c9

5
Last change on this file since 33e250c9 was 33e250c9, checked in by Sebastian Huber <sebastian.huber@…>, on 05/27/16 at 13:41:41

score: Rework CORE priority ceiling mutex

Rework seize and surrender methods to use CORE_ceiling_mutex_Control.
This eliminates CORE_mutex_Disciplines.

  • Property mode set to 100644
File size: 15.9 KB
Line 
1/**
2 * @file
3 *
4 * @ingroup ScoreMutex
5 *
6 * @brief CORE Mutex Implementation
7 */
8
9/*
10 *  COPYRIGHT (c) 1989-2009.
11 *  On-Line Applications Research Corporation (OAR).
12 *
13 *  The license and distribution terms for this file may be
14 *  found in the file LICENSE in this distribution or at
15 *  http://www.rtems.org/license/LICENSE.
16 */
17
18#ifndef _RTEMS_SCORE_COREMUTEXIMPL_H
19#define _RTEMS_SCORE_COREMUTEXIMPL_H
20
21#include <rtems/score/coremutex.h>
22#include <rtems/score/chainimpl.h>
23#include <rtems/score/status.h>
24#include <rtems/score/threadimpl.h>
25#include <rtems/score/threadqimpl.h>
26
27#ifdef __cplusplus
28extern "C" {
29#endif
30
31/**
32 * @addtogroup ScoreMutex
33 */
34/**@{**/
35
36#define CORE_MUTEX_TQ_OPERATIONS &_Thread_queue_Operations_priority
37
38/**
39 *  @brief Initializes the mutex based on the parameters passed.
40 *
41 *  This routine initializes the mutex based on the parameters passed.
42 *
43 *  @param[in,out] the_mutex is the mutex to initalize
44 *  @param[in,out] executing The currently executing thread.
45 *  @param[in] the_mutex_attributes is the attributes associated with this
46 *         mutex instance
47 *  @param[in] initially_locked If true, then the mutex is initially locked by
48 *  the executing thread.
49 *
50 *  @retval This method returns STATUS_SUCCESSFUL if successful.
51 */
52Status_Control _CORE_mutex_Initialize(
53  CORE_mutex_Control           *the_mutex,
54  Thread_Control               *executing,
55  const CORE_mutex_Attributes  *the_mutex_attributes,
56  bool                          initially_locked
57);
58
59RTEMS_INLINE_ROUTINE void _CORE_mutex_Destroy( CORE_mutex_Control *the_mutex )
60{
61  _Thread_queue_Destroy( &the_mutex->Wait_queue );
62}
63
64RTEMS_INLINE_ROUTINE void _CORE_mutex_Acquire_critical(
65  CORE_mutex_Control   *the_mutex,
66  Thread_queue_Context *queue_context
67)
68{
69  _Thread_queue_Acquire_critical(
70    &the_mutex->Wait_queue,
71    &queue_context->Lock_context
72  );
73}
74
75RTEMS_INLINE_ROUTINE void _CORE_mutex_Release(
76  CORE_mutex_Control   *the_mutex,
77  Thread_queue_Context *queue_context
78)
79{
80  _Thread_queue_Release(
81    &the_mutex->Wait_queue,
82    &queue_context->Lock_context
83  );
84}
85
86/**
87 *  @brief Performs the blocking portion of a mutex obtain.
88 *
89 *  This routine performs the blocking portion of a mutex obtain.
90 *  It is an actual subroutine and is not implemented as something
91 *  that may be inlined.
92 *
93 *  @param[in,out] the_mutex is the mutex to attempt to lock
94 *  @param[in,out] executing The currently executing thread.
95 *  @param[in] timeout is the maximum number of ticks to block
96 *  @param[in] lock_context is the interrupt level
97 */
98Status_Control _CORE_mutex_Seize_interrupt_blocking(
99  CORE_mutex_Control   *the_mutex,
100  Thread_Control       *executing,
101  Watchdog_Interval     timeout,
102  Thread_queue_Context *queue_context
103);
104
105RTEMS_INLINE_ROUTINE Thread_Control *_CORE_mutex_Get_owner(
106  const CORE_mutex_Control *the_mutex
107)
108{
109  return the_mutex->holder;
110}
111
112/**
113 * @brief Is mutex locked.
114 *
115 * This routine returns true if the mutex specified is locked and false
116 * otherwise.
117 *
118 * @param[in] the_mutex is the mutex to check.
119 *
120 * @retval true The mutex is locked.
121 * @retval false The mutex is not locked.
122 */
123RTEMS_INLINE_ROUTINE bool _CORE_mutex_Is_locked(
124  const CORE_mutex_Control *the_mutex
125)
126{
127  return _CORE_mutex_Get_owner( the_mutex ) != NULL;
128}
129
130/**
131 *  @brief Attempt to receive a unit from the_mutex.
132 *
133 *  This routine attempts to receive a unit from the_mutex.
134 *  If a unit is available or if the wait flag is false, then the routine
135 *  returns.  Otherwise, the calling task is blocked until a unit becomes
136 *  available.
137 *
138 *  @param[in,out] executing The currently executing thread.
139 *  @param[in,out] the_mutex is the mutex to attempt to lock
140 *  @param[in] queue_context is the interrupt level
141 *
142 *  @retval STATUS_UNAVAILABLE The mutex is already locked.
143 *  @retval other Otherwise.
144 */
145RTEMS_INLINE_ROUTINE Status_Control _CORE_mutex_Seize_interrupt_trylock(
146  CORE_mutex_Control   *the_mutex,
147  Thread_Control       *executing,
148  Thread_queue_Context *queue_context
149)
150{
151  /* disabled when you get here */
152
153  if ( !_CORE_mutex_Is_locked( the_mutex ) ) {
154    the_mutex->holder     = executing;
155    the_mutex->nest_count = 1;
156    ++executing->resource_count;
157
158    _CORE_mutex_Release( the_mutex, queue_context );
159    return STATUS_SUCCESSFUL;
160  }
161
162  /*
163   *  At this point, we know the mutex was not available.  If this thread
164   *  is the thread that has locked the mutex, let's see if we are allowed
165   *  to nest access.
166   */
167  if ( _Thread_Is_executing( the_mutex->holder ) ) {
168    switch ( the_mutex->Attributes.lock_nesting_behavior ) {
169      case CORE_MUTEX_NESTING_ACQUIRES:
170        the_mutex->nest_count++;
171        _CORE_mutex_Release( the_mutex, queue_context );
172        return STATUS_SUCCESSFUL;
173      #if defined(RTEMS_POSIX_API)
174        case CORE_MUTEX_NESTING_IS_ERROR:
175          _CORE_mutex_Release( the_mutex, queue_context );
176          return STATUS_NESTING_NOT_ALLOWED;
177      #endif
178    }
179  }
180
181  /*
182   *  The mutex is not available and the caller must deal with the possibility
183   *  of blocking.
184   */
185  return STATUS_UNAVAILABLE;
186}
187
188/**
189 *  @brief Attempt to obtain the mutex.
190 *
191 *  This routine attempts to obtain the mutex.  If the mutex is available,
192 *  then it will return immediately.  Otherwise, it will invoke the
193 *  support routine @a _Core_mutex_Seize_interrupt_blocking.
194 *
195 *  @param[in] the_mutex is the mutex to attempt to lock
196 *  @param[in] wait is true if the thread is willing to wait
197 *  @param[in] timeout is the maximum number of ticks to block
198 *  @param[in] queue_context is a temporary variable used to contain the ISR
199 *         disable level cookie
200 *
201 *  @note If the mutex is called from an interrupt service routine,
202 *        with context switching disabled, or before multitasking,
203 *        then a fatal error is generated.
204 *
205 *  The logic on this routine is as follows:
206 *
207 *  * If incorrect system state
208 *      return an error
209 *  * If mutex is available without any contention or blocking
210 *      obtain it with interrupts disabled and returned
211 *  * If the caller is willing to wait
212 *      then they are blocked.
213 */
214RTEMS_INLINE_ROUTINE Status_Control _CORE_mutex_Seize(
215  CORE_mutex_Control   *the_mutex,
216  Thread_Control       *executing,
217  bool                  wait,
218  Watchdog_Interval     timeout,
219  Thread_queue_Context *queue_context
220)
221{
222  Status_Control status;
223
224  _CORE_mutex_Acquire_critical( the_mutex, queue_context );
225
226  status = _CORE_mutex_Seize_interrupt_trylock(
227    the_mutex,
228    executing,
229    queue_context
230  );
231
232  if ( status != STATUS_UNAVAILABLE ) {
233    return status;
234  }
235
236  if ( !wait ) {
237    _CORE_mutex_Release( the_mutex, queue_context );
238    return status;
239  }
240
241  return _CORE_mutex_Seize_interrupt_blocking(
242    the_mutex,
243    executing,
244    timeout,
245    queue_context
246  );
247}
248
249Status_Control _CORE_mutex_Seize_no_protocol_slow(
250  CORE_mutex_Control            *the_mutex,
251  const Thread_queue_Operations *operations,
252  Thread_Control                *executing,
253  bool                           wait,
254  Watchdog_Interval              timeout,
255  Thread_queue_Context          *queue_context
256);
257
258Status_Control _CORE_mutex_Surrender(
259  CORE_mutex_Control   *the_mutex,
260  Thread_queue_Context *queue_context
261);
262
263RTEMS_INLINE_ROUTINE void _CORE_mutex_Set_owner(
264  CORE_mutex_Control *the_mutex,
265  Thread_Control     *owner
266)
267{
268  the_mutex->holder = owner;
269}
270
271RTEMS_INLINE_ROUTINE bool _CORE_mutex_Is_owner(
272  const CORE_mutex_Control *the_mutex,
273  const Thread_Control     *the_thread
274)
275{
276  return _CORE_mutex_Get_owner( the_mutex ) == the_thread;
277}
278
279RTEMS_INLINE_ROUTINE void _CORE_mutex_Restore_priority(
280  Thread_Control *executing
281)
282{
283  /*
284   *  Whether or not someone is waiting for the mutex, an
285   *  inherited priority must be lowered if this is the last
286   *  mutex (i.e. resource) this task has.
287   */
288  if ( !_Thread_Owns_resources( executing ) ) {
289    /*
290     * Ensure that the executing resource count is visible to all other
291     * processors and that we read the latest priority restore hint.
292     */
293    _Atomic_Fence( ATOMIC_ORDER_ACQ_REL );
294
295    if ( executing->priority_restore_hint ) {
296      Per_CPU_Control *cpu_self;
297
298      cpu_self = _Thread_Dispatch_disable();
299      _Thread_Restore_priority( executing );
300      _Thread_Dispatch_enable( cpu_self );
301    }
302  }
303}
304
305RTEMS_INLINE_ROUTINE void _CORE_recursive_mutex_Initialize(
306  CORE_recursive_mutex_Control *the_mutex
307)
308{
309  _Thread_queue_Initialize( &the_mutex->Mutex.Wait_queue );
310  the_mutex->Mutex.holder = NULL;
311  the_mutex->nest_level = 0;
312}
313
314RTEMS_INLINE_ROUTINE Status_Control _CORE_recursive_mutex_Seize_nested(
315  CORE_recursive_mutex_Control *the_mutex
316)
317{
318  ++the_mutex->nest_level;
319  return STATUS_SUCCESSFUL;
320}
321
322RTEMS_INLINE_ROUTINE Status_Control _CORE_recursive_mutex_Seize_no_protocol(
323  CORE_recursive_mutex_Control  *the_mutex,
324  const Thread_queue_Operations *operations,
325  Thread_Control                *executing,
326  bool                           wait,
327  Watchdog_Interval              timeout,
328  Status_Control              ( *nested )( CORE_recursive_mutex_Control * ),
329  Thread_queue_Context          *queue_context
330)
331{
332  Thread_Control *owner;
333
334  _CORE_mutex_Acquire_critical( &the_mutex->Mutex, queue_context );
335
336  owner = _CORE_mutex_Get_owner( &the_mutex->Mutex );
337
338  if ( owner == NULL ) {
339    _CORE_mutex_Set_owner( &the_mutex->Mutex, executing );
340    _CORE_mutex_Release( &the_mutex->Mutex, queue_context );
341    return STATUS_SUCCESSFUL;
342  }
343
344  if ( owner == executing ) {
345    Status_Control status;
346
347    status = ( *nested )( the_mutex );
348    _CORE_mutex_Release( &the_mutex->Mutex, queue_context );
349    return status;
350  }
351
352  return _CORE_mutex_Seize_no_protocol_slow(
353    &the_mutex->Mutex,
354    operations,
355    executing,
356    wait,
357    timeout,
358    queue_context
359  );
360}
361
362RTEMS_INLINE_ROUTINE void
363_CORE_recursive_mutex_Surrender_no_protocol_finalize(
364  CORE_recursive_mutex_Control  *the_mutex,
365  const Thread_queue_Operations *operations,
366  Thread_queue_Context          *queue_context
367)
368{
369  unsigned int    nest_level;
370  Thread_Control *new_owner;
371
372  nest_level = the_mutex->nest_level;
373
374  if ( nest_level > 0 ) {
375    the_mutex->nest_level = nest_level - 1;
376    _CORE_mutex_Release( &the_mutex->Mutex, queue_context );
377    return;
378  }
379
380  new_owner = _Thread_queue_First_locked(
381    &the_mutex->Mutex.Wait_queue,
382    operations
383  );
384  _CORE_mutex_Set_owner( &the_mutex->Mutex, new_owner );
385
386  if ( new_owner == NULL ) {
387    _CORE_mutex_Release( &the_mutex->Mutex, queue_context );
388    return;
389  }
390
391  _Thread_queue_Extract_critical(
392    &the_mutex->Mutex.Wait_queue.Queue,
393    operations,
394    new_owner,
395    queue_context
396  );
397}
398
399RTEMS_INLINE_ROUTINE Status_Control _CORE_recursive_mutex_Surrender_no_protocol(
400  CORE_recursive_mutex_Control  *the_mutex,
401  const Thread_queue_Operations *operations,
402  Thread_Control                *executing,
403  Thread_queue_Context          *queue_context
404)
405{
406  _CORE_mutex_Acquire_critical( &the_mutex->Mutex, queue_context );
407
408  if ( !_CORE_mutex_Is_owner( &the_mutex->Mutex, executing ) ) {
409    _CORE_mutex_Release( &the_mutex->Mutex, queue_context );
410    return STATUS_NOT_OWNER;
411  }
412
413  _CORE_recursive_mutex_Surrender_no_protocol_finalize(
414    the_mutex,
415    operations,
416    queue_context
417  );
418  return STATUS_SUCCESSFUL;
419}
420
421/*
422 * The Classic no protocol recursive mutex has the nice property that everyone
423 * can release it.
424 */
425RTEMS_INLINE_ROUTINE void _CORE_recursive_mutex_Surrender_no_protocol_classic(
426  CORE_recursive_mutex_Control  *the_mutex,
427  const Thread_queue_Operations *operations,
428  Thread_queue_Context          *queue_context
429)
430{
431  _CORE_mutex_Acquire_critical( &the_mutex->Mutex, queue_context );
432  _CORE_recursive_mutex_Surrender_no_protocol_finalize(
433    the_mutex,
434    operations,
435    queue_context
436  );
437}
438
439RTEMS_INLINE_ROUTINE void _CORE_ceiling_mutex_Initialize(
440  CORE_ceiling_mutex_Control *the_mutex,
441  Priority_Control            priority_ceiling
442)
443{
444  _CORE_recursive_mutex_Initialize( &the_mutex->Recursive );
445  the_mutex->priority_ceiling = priority_ceiling;
446}
447
448RTEMS_INLINE_ROUTINE Status_Control _CORE_ceiling_mutex_Set_owner(
449  CORE_ceiling_mutex_Control *the_mutex,
450  Thread_Control             *owner,
451  Thread_queue_Context       *queue_context
452)
453{
454  Priority_Control  priority_ceiling;
455  Priority_Control  current_priority;
456  Per_CPU_Control  *cpu_self;
457
458  priority_ceiling = the_mutex->priority_ceiling;
459  current_priority = owner->current_priority;
460
461  if ( current_priority < priority_ceiling ) {
462    _CORE_mutex_Release( &the_mutex->Recursive.Mutex, queue_context );
463    return STATUS_MUTEX_CEILING_VIOLATED;
464  }
465
466  _CORE_mutex_Set_owner( &the_mutex->Recursive.Mutex, owner );
467  ++owner->resource_count;
468
469  if ( current_priority == priority_ceiling ) {
470    _CORE_mutex_Release( &the_mutex->Recursive.Mutex, queue_context );
471    return STATUS_SUCCESSFUL;
472  }
473
474  cpu_self = _Thread_Dispatch_disable_critical( &queue_context->Lock_context );
475  _CORE_mutex_Release( &the_mutex->Recursive.Mutex, queue_context );
476  _Thread_Raise_priority( owner, priority_ceiling );
477  _Thread_Dispatch_enable( cpu_self );
478  return STATUS_SUCCESSFUL;
479}
480
481RTEMS_INLINE_ROUTINE Status_Control _CORE_ceiling_mutex_Seize(
482  CORE_ceiling_mutex_Control    *the_mutex,
483  Thread_Control                *executing,
484  bool                           wait,
485  Watchdog_Interval              timeout,
486  Status_Control              ( *nested )( CORE_recursive_mutex_Control * ),
487  Thread_queue_Context          *queue_context
488)
489{
490  Thread_Control *owner;
491
492  _CORE_mutex_Acquire_critical( &the_mutex->Recursive.Mutex, queue_context );
493
494  owner = _CORE_mutex_Get_owner( &the_mutex->Recursive.Mutex );
495
496  if ( owner == NULL ) {
497    return _CORE_ceiling_mutex_Set_owner(
498      the_mutex,
499      executing,
500      queue_context
501    );
502  }
503
504  if ( owner == executing ) {
505    Status_Control status;
506
507    status = ( *nested )( &the_mutex->Recursive );
508    _CORE_mutex_Release( &the_mutex->Recursive.Mutex, queue_context );
509    return status;
510  }
511
512  return _CORE_mutex_Seize_no_protocol_slow(
513    &the_mutex->Recursive.Mutex,
514    CORE_MUTEX_TQ_OPERATIONS,
515    executing,
516    wait,
517    timeout,
518    queue_context
519  );
520}
521
522RTEMS_INLINE_ROUTINE Status_Control _CORE_ceiling_mutex_Surrender(
523  CORE_ceiling_mutex_Control *the_mutex,
524  Thread_Control             *executing,
525  Thread_queue_Context       *queue_context
526)
527{
528  unsigned int    nest_level;
529  Thread_Control *new_owner;
530
531  _CORE_mutex_Acquire_critical( &the_mutex->Recursive.Mutex, queue_context );
532
533  if ( !_CORE_mutex_Is_owner( &the_mutex->Recursive.Mutex, executing ) ) {
534    _CORE_mutex_Release( &the_mutex->Recursive.Mutex, queue_context );
535    return STATUS_NOT_OWNER;
536  }
537
538  nest_level = the_mutex->Recursive.nest_level;
539
540  if ( nest_level > 0 ) {
541    the_mutex->Recursive.nest_level = nest_level - 1;
542    _CORE_mutex_Release( &the_mutex->Recursive.Mutex, queue_context );
543    return STATUS_SUCCESSFUL;
544  }
545
546  --executing->resource_count;
547
548  new_owner = _Thread_queue_First_locked(
549    &the_mutex->Recursive.Mutex.Wait_queue,
550    CORE_MUTEX_TQ_OPERATIONS
551  );
552  _CORE_mutex_Set_owner( &the_mutex->Recursive.Mutex, new_owner );
553
554  if ( new_owner != NULL ) {
555    bool unblock;
556
557    /*
558     * We must extract the thread now since this will restore its default
559     * thread lock.  This is necessary to avoid a deadlock in the
560     * _Thread_Change_priority() below due to a recursive thread queue lock
561     * acquire.
562     */
563    unblock = _Thread_queue_Extract_locked(
564      &the_mutex->Recursive.Mutex.Wait_queue.Queue,
565      CORE_MUTEX_TQ_OPERATIONS,
566      new_owner,
567      queue_context
568    );
569
570#if defined(RTEMS_MULTIPROCESSING)
571    if ( _Objects_Is_local_id( new_owner->Object.id ) )
572#endif
573    {
574      ++new_owner->resource_count;
575      _Thread_Raise_priority( new_owner, the_mutex->priority_ceiling );
576    }
577
578    _Thread_queue_Unblock_critical(
579      unblock,
580      &the_mutex->Recursive.Mutex.Wait_queue.Queue,
581      new_owner,
582      &queue_context->Lock_context
583    );
584  } else {
585    _CORE_mutex_Release( &the_mutex->Recursive.Mutex, queue_context );
586  }
587
588  _CORE_mutex_Restore_priority( executing );
589  return STATUS_SUCCESSFUL;
590}
591
592/** @} */
593
594#ifdef __cplusplus
595}
596#endif
597
598#endif
599/* end of include file */
Note: See TracBrowser for help on using the repository browser.