summaryrefslogblamecommitdiffstats
path: root/cpukit/score/include/rtems/score/coremuteximpl.h
blob: 25094a49df714e343aa7b139f2a5273f95107075 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12
13
14













                                                           
                                         





                                    
                                  
                                      
                               
                                   
                                    









                         

                                                                   


                                                   




                                                     
 
 




                                                                              
                                                       

                                     

 
                                                                          


                                              

                                     

 
                                                                 

 



                                                           
                                           

 
   













                                                                       
                                                    

 
                                      

                                  
                              
                             
                                     
  
 




                                                  


                                              




                                                
                                            

 




                                               
                                                          

 

























                                                                       



                                                           
                                              










                                                                       



                                                                





























                                                                            















































                                                                    


                                              
          
              


                  
                           

 




                                                                            




























                                                                            



                 
                                                                                

                                            
                                           





                                              






                                                                   




                                                            
                             









                                                            
                             







                                       


                           

                                                         
                                        




                                                            





























                                                                       












                                                                  
                                                   













                                                                      


                                               









                                                                    










                                                                             









                                                                        



















                                                                      































































                                                                             
                                               








                                                                      







                         
/**
 * @file
 *
 * @ingroup ScoreMutex
 *
 * @brief CORE Mutex Implementation
 */

/*
 *  COPYRIGHT (c) 1989-2009.
 *  On-Line Applications Research Corporation (OAR).
 *
 *  The license and distribution terms for this file may be
 *  found in the file LICENSE in this distribution or at
 *  http://www.rtems.org/license/LICENSE.
 */

#ifndef _RTEMS_SCORE_COREMUTEXIMPL_H
#define _RTEMS_SCORE_COREMUTEXIMPL_H

#include <rtems/score/coremutex.h>
#include <rtems/score/chainimpl.h>
#include <rtems/score/schedulerimpl.h>
#include <rtems/score/status.h>
#include <rtems/score/threadimpl.h>
#include <rtems/score/threadqimpl.h>

#ifdef __cplusplus
extern "C" {
#endif

/**
 * @addtogroup ScoreMutex
 */
/**@{**/

#define CORE_MUTEX_TQ_OPERATIONS &_Thread_queue_Operations_priority

#define CORE_MUTEX_TQ_PRIORITY_INHERIT_OPERATIONS \
  &_Thread_queue_Operations_priority_inherit

RTEMS_INLINE_ROUTINE void _CORE_mutex_Initialize(
  CORE_mutex_Control *the_mutex
)
{
  _Thread_queue_Initialize( &the_mutex->Wait_queue );
}

RTEMS_INLINE_ROUTINE void _CORE_mutex_Destroy( CORE_mutex_Control *the_mutex )
{
  _Thread_queue_Destroy( &the_mutex->Wait_queue );
}

RTEMS_INLINE_ROUTINE void _CORE_mutex_Acquire_critical(
  CORE_mutex_Control   *the_mutex,
  Thread_queue_Context *queue_context
)
{
  _Thread_queue_Acquire_critical( &the_mutex->Wait_queue, queue_context );
}

RTEMS_INLINE_ROUTINE void _CORE_mutex_Release(
  CORE_mutex_Control   *the_mutex,
  Thread_queue_Context *queue_context
)
{
  _Thread_queue_Release( &the_mutex->Wait_queue, queue_context );
}

RTEMS_INLINE_ROUTINE Thread_Control *_CORE_mutex_Get_owner(
  const CORE_mutex_Control *the_mutex
)
{
  return the_mutex->Wait_queue.Queue.owner;
}

/**
 * @brief Is mutex locked.
 *
 * This routine returns true if the mutex specified is locked and false
 * otherwise.
 *
 * @param[in] the_mutex is the mutex to check.
 *
 * @retval true The mutex is locked.
 * @retval false The mutex is not locked.
 */
RTEMS_INLINE_ROUTINE bool _CORE_mutex_Is_locked(
  const CORE_mutex_Control *the_mutex
)
{
  return _CORE_mutex_Get_owner( the_mutex ) != NULL;
}

Status_Control _CORE_mutex_Seize_slow(
  CORE_mutex_Control   *the_mutex,
  Thread_Control       *executing,
  Thread_Control       *owner,
  bool                  wait,
  Thread_queue_Context *queue_context
);

Status_Control _CORE_mutex_Seize_no_protocol_slow(
  CORE_mutex_Control            *the_mutex,
  const Thread_queue_Operations *operations,
  Thread_Control                *executing,
  bool                           wait,
  Thread_queue_Context          *queue_context
);

RTEMS_INLINE_ROUTINE void _CORE_mutex_Set_owner(
  CORE_mutex_Control *the_mutex,
  Thread_Control     *owner
)
{
  the_mutex->Wait_queue.Queue.owner = owner;
}

RTEMS_INLINE_ROUTINE bool _CORE_mutex_Is_owner(
  const CORE_mutex_Control *the_mutex,
  const Thread_Control     *the_thread
)
{
  return _CORE_mutex_Get_owner( the_mutex ) == the_thread;
}

RTEMS_INLINE_ROUTINE void _CORE_mutex_Restore_priority(
  Thread_Control *executing
)
{
  /*
   *  Whether or not someone is waiting for the mutex, an
   *  inherited priority must be lowered if this is the last
   *  mutex (i.e. resource) this task has.
   */
  if ( !_Thread_Owns_resources( executing ) ) {
    /*
     * Ensure that the executing resource count is visible to all other
     * processors and that we read the latest priority restore hint.
     */
    _Atomic_Fence( ATOMIC_ORDER_ACQ_REL );

    if ( executing->priority_restore_hint ) {
      Per_CPU_Control *cpu_self;

      cpu_self = _Thread_Dispatch_disable();
      _Thread_Restore_priority( executing );
      _Thread_Dispatch_enable( cpu_self );
    }
  }
}

RTEMS_INLINE_ROUTINE void _CORE_recursive_mutex_Initialize(
  CORE_recursive_mutex_Control *the_mutex
)
{
  _CORE_mutex_Initialize( &the_mutex->Mutex );
  the_mutex->nest_level = 0;
}

RTEMS_INLINE_ROUTINE Status_Control _CORE_recursive_mutex_Seize_nested(
  CORE_recursive_mutex_Control *the_mutex
)
{
  ++the_mutex->nest_level;
  return STATUS_SUCCESSFUL;
}

RTEMS_INLINE_ROUTINE Status_Control _CORE_recursive_mutex_Seize(
  CORE_recursive_mutex_Control  *the_mutex,
  Thread_Control                *executing,
  bool                           wait,
  Status_Control              ( *nested )( CORE_recursive_mutex_Control * ),
  Thread_queue_Context          *queue_context
)
{
  Thread_Control *owner;

  _CORE_mutex_Acquire_critical( &the_mutex->Mutex, queue_context );

  owner = _CORE_mutex_Get_owner( &the_mutex->Mutex );

  if ( owner == NULL ) {
    _CORE_mutex_Set_owner( &the_mutex->Mutex, executing );
    ++executing->resource_count;
    _CORE_mutex_Release( &the_mutex->Mutex, queue_context );
    return STATUS_SUCCESSFUL;
  }

  if ( owner == executing ) {
    Status_Control status;

    status = ( *nested )( the_mutex );
    _CORE_mutex_Release( &the_mutex->Mutex, queue_context );
    return status;
  }

  return _CORE_mutex_Seize_slow(
    &the_mutex->Mutex,
    executing,
    owner,
    wait,
    queue_context
  );
}

RTEMS_INLINE_ROUTINE Status_Control _CORE_recursive_mutex_Surrender(
  CORE_recursive_mutex_Control *the_mutex,
  Thread_Control               *executing,
  Thread_queue_Context         *queue_context
)
{
  unsigned int        nest_level;
  Thread_queue_Heads *heads;
  bool                keep_priority;

  _CORE_mutex_Acquire_critical( &the_mutex->Mutex, queue_context );

  if ( !_CORE_mutex_Is_owner( &the_mutex->Mutex, executing ) ) {
    _CORE_mutex_Release( &the_mutex->Mutex, queue_context );
    return STATUS_NOT_OWNER;
  }

  nest_level = the_mutex->nest_level;

  if ( nest_level > 0 ) {
    the_mutex->nest_level = nest_level - 1;
    _CORE_mutex_Release( &the_mutex->Mutex, queue_context );
    return STATUS_SUCCESSFUL;
  }

  --executing->resource_count;
  _CORE_mutex_Set_owner( &the_mutex->Mutex, NULL );

  /*
   * Ensure that the owner resource count is visible to all other
   * processors and that we read the latest priority restore
   * hint.
   */
  _Atomic_Fence( ATOMIC_ORDER_ACQ_REL );

  heads = the_mutex->Mutex.Wait_queue.Queue.heads;
  keep_priority = _Thread_Owns_resources( executing )
    || !executing->priority_restore_hint;

  if ( heads == NULL && keep_priority ) {
    _CORE_mutex_Release( &the_mutex->Mutex, queue_context );
    return STATUS_SUCCESSFUL;
  }

  _Thread_queue_Surrender(
    &the_mutex->Mutex.Wait_queue.Queue,
    CORE_MUTEX_TQ_PRIORITY_INHERIT_OPERATIONS,
    heads,
    executing,
    keep_priority,
    queue_context
  );
  return STATUS_SUCCESSFUL;
}

RTEMS_INLINE_ROUTINE Status_Control _CORE_recursive_mutex_Seize_no_protocol(
  CORE_recursive_mutex_Control  *the_mutex,
  const Thread_queue_Operations *operations,
  Thread_Control                *executing,
  bool                           wait,
  Status_Control              ( *nested )( CORE_recursive_mutex_Control * ),
  Thread_queue_Context          *queue_context
)
{
  Thread_Control *owner;

  _CORE_mutex_Acquire_critical( &the_mutex->Mutex, queue_context );

  owner = _CORE_mutex_Get_owner( &the_mutex->Mutex );

  if ( owner == NULL ) {
    _CORE_mutex_Set_owner( &the_mutex->Mutex, executing );
    _CORE_mutex_Release( &the_mutex->Mutex, queue_context );
    return STATUS_SUCCESSFUL;
  }

  if ( owner == executing ) {
    Status_Control status;

    status = ( *nested )( the_mutex );
    _CORE_mutex_Release( &the_mutex->Mutex, queue_context );
    return status;
  }

  return _CORE_mutex_Seize_no_protocol_slow(
    &the_mutex->Mutex,
    operations,
    executing,
    wait,
    queue_context
  );
}

RTEMS_INLINE_ROUTINE Status_Control _CORE_recursive_mutex_Surrender_no_protocol(
  CORE_recursive_mutex_Control  *the_mutex,
  const Thread_queue_Operations *operations,
  Thread_Control                *executing,
  Thread_queue_Context          *queue_context
)
{
  unsigned int    nest_level;
  Thread_Control *new_owner;

  _CORE_mutex_Acquire_critical( &the_mutex->Mutex, queue_context );

  if ( !_CORE_mutex_Is_owner( &the_mutex->Mutex, executing ) ) {
    _CORE_mutex_Release( &the_mutex->Mutex, queue_context );
    return STATUS_NOT_OWNER;
  }

  nest_level = the_mutex->nest_level;

  if ( nest_level > 0 ) {
    the_mutex->nest_level = nest_level - 1;
    _CORE_mutex_Release( &the_mutex->Mutex, queue_context );
    return STATUS_SUCCESSFUL;
  }

  new_owner = _Thread_queue_First_locked(
    &the_mutex->Mutex.Wait_queue,
    operations
  );
  _CORE_mutex_Set_owner( &the_mutex->Mutex, new_owner );

  if ( new_owner == NULL ) {
    _CORE_mutex_Release( &the_mutex->Mutex, queue_context );
    return STATUS_SUCCESSFUL;
  }

  _Thread_queue_Extract_critical(
    &the_mutex->Mutex.Wait_queue.Queue,
    operations,
    new_owner,
    queue_context
  );
  return STATUS_SUCCESSFUL;
}

RTEMS_INLINE_ROUTINE void _CORE_ceiling_mutex_Initialize(
  CORE_ceiling_mutex_Control *the_mutex,
  const Scheduler_Control    *scheduler,
  Priority_Control            priority_ceiling
)
{
  _CORE_recursive_mutex_Initialize( &the_mutex->Recursive );
  the_mutex->priority_ceiling = priority_ceiling;
#if defined(RTEMS_SMP)
  the_mutex->scheduler = scheduler;
#endif
}

RTEMS_INLINE_ROUTINE const Scheduler_Control *
_CORE_ceiling_mutex_Get_scheduler(
  const CORE_ceiling_mutex_Control *the_mutex
)
{
#if defined(RTEMS_SMP)
  return the_mutex->scheduler;
#else
  return _Scheduler_Get_by_CPU_index( 0 );
#endif
}

RTEMS_INLINE_ROUTINE void _CORE_ceiling_mutex_Set_priority(
  CORE_ceiling_mutex_Control *the_mutex,
  Priority_Control            priority_ceiling
)
{
  the_mutex->priority_ceiling = priority_ceiling;
}

RTEMS_INLINE_ROUTINE Priority_Control _CORE_ceiling_mutex_Get_priority(
  const CORE_ceiling_mutex_Control *the_mutex
)
{
  return the_mutex->priority_ceiling;
}

RTEMS_INLINE_ROUTINE Status_Control _CORE_ceiling_mutex_Set_owner(
  CORE_ceiling_mutex_Control *the_mutex,
  Thread_Control             *owner,
  Thread_queue_Context       *queue_context
)
{
  Priority_Control  priority_ceiling;
  Priority_Control  current_priority;
  Per_CPU_Control  *cpu_self;

  priority_ceiling = the_mutex->priority_ceiling;
  current_priority = _Thread_Get_priority( owner );

  if ( current_priority < priority_ceiling ) {
    _CORE_mutex_Release( &the_mutex->Recursive.Mutex, queue_context );
    return STATUS_MUTEX_CEILING_VIOLATED;
  }

  _CORE_mutex_Set_owner( &the_mutex->Recursive.Mutex, owner );
  ++owner->resource_count;

  if ( current_priority == priority_ceiling ) {
    _CORE_mutex_Release( &the_mutex->Recursive.Mutex, queue_context );
    return STATUS_SUCCESSFUL;
  }

  cpu_self = _Thread_Dispatch_disable_critical(
    &queue_context->Lock_context.Lock_context
  );
  _CORE_mutex_Release( &the_mutex->Recursive.Mutex, queue_context );
  _Thread_Raise_priority( owner, priority_ceiling );
  _Thread_Dispatch_enable( cpu_self );
  return STATUS_SUCCESSFUL;
}

RTEMS_INLINE_ROUTINE Status_Control _CORE_ceiling_mutex_Seize(
  CORE_ceiling_mutex_Control    *the_mutex,
  Thread_Control                *executing,
  bool                           wait,
  Status_Control              ( *nested )( CORE_recursive_mutex_Control * ),
  Thread_queue_Context          *queue_context
)
{
  Thread_Control *owner;

  _CORE_mutex_Acquire_critical( &the_mutex->Recursive.Mutex, queue_context );

  owner = _CORE_mutex_Get_owner( &the_mutex->Recursive.Mutex );

  if ( owner == NULL ) {
#if defined(RTEMS_SMP)
    if (
      _Scheduler_Get_own( executing )
        != _CORE_ceiling_mutex_Get_scheduler( the_mutex )
    ) {
      _CORE_mutex_Release( &the_mutex->Recursive.Mutex, queue_context );
      return STATUS_NOT_DEFINED;
    }
#endif

    return _CORE_ceiling_mutex_Set_owner(
      the_mutex,
      executing,
      queue_context
    );
  }

  if ( owner == executing ) {
    Status_Control status;

    status = ( *nested )( &the_mutex->Recursive );
    _CORE_mutex_Release( &the_mutex->Recursive.Mutex, queue_context );
    return status;
  }

  return _CORE_mutex_Seize_no_protocol_slow(
    &the_mutex->Recursive.Mutex,
    CORE_MUTEX_TQ_OPERATIONS,
    executing,
    wait,
    queue_context
  );
}

RTEMS_INLINE_ROUTINE Status_Control _CORE_ceiling_mutex_Surrender(
  CORE_ceiling_mutex_Control *the_mutex,
  Thread_Control             *executing,
  Thread_queue_Context       *queue_context
)
{
  unsigned int    nest_level;
  Thread_Control *new_owner;

  _CORE_mutex_Acquire_critical( &the_mutex->Recursive.Mutex, queue_context );

  if ( !_CORE_mutex_Is_owner( &the_mutex->Recursive.Mutex, executing ) ) {
    _CORE_mutex_Release( &the_mutex->Recursive.Mutex, queue_context );
    return STATUS_NOT_OWNER;
  }

  nest_level = the_mutex->Recursive.nest_level;

  if ( nest_level > 0 ) {
    the_mutex->Recursive.nest_level = nest_level - 1;
    _CORE_mutex_Release( &the_mutex->Recursive.Mutex, queue_context );
    return STATUS_SUCCESSFUL;
  }

  --executing->resource_count;

  new_owner = _Thread_queue_First_locked(
    &the_mutex->Recursive.Mutex.Wait_queue,
    CORE_MUTEX_TQ_OPERATIONS
  );
  _CORE_mutex_Set_owner( &the_mutex->Recursive.Mutex, new_owner );

  if ( new_owner != NULL ) {
    bool unblock;

    /*
     * We must extract the thread now since this will restore its default
     * thread lock.  This is necessary to avoid a deadlock in the
     * _Thread_Change_priority() below due to a recursive thread queue lock
     * acquire.
     */
    unblock = _Thread_queue_Extract_locked(
      &the_mutex->Recursive.Mutex.Wait_queue.Queue,
      CORE_MUTEX_TQ_OPERATIONS,
      new_owner,
      queue_context
    );

#if defined(RTEMS_MULTIPROCESSING)
    if ( _Objects_Is_local_id( new_owner->Object.id ) )
#endif
    {
      ++new_owner->resource_count;
      _Thread_Raise_priority( new_owner, the_mutex->priority_ceiling );
    }

    _Thread_queue_Unblock_critical(
      unblock,
      &the_mutex->Recursive.Mutex.Wait_queue.Queue,
      new_owner,
      &queue_context->Lock_context.Lock_context
    );
  } else {
    _CORE_mutex_Release( &the_mutex->Recursive.Mutex, queue_context );
  }

  _CORE_mutex_Restore_priority( executing );
  return STATUS_SUCCESSFUL;
}

/** @} */

#ifdef __cplusplus
}
#endif

#endif
/* end of include file */