summaryrefslogblamecommitdiffstats
path: root/cpukit/posix/include/rtems/posix/muteximpl.h
blob: 833938bb2d25b93d6358e2bd7d397e7a92b1bdc1 (plain) (tree)
1
2
3
4
5
6
7
8
9








                                                                              
                            



                                                           
                                         

   


                                
                  
                    
 



                                   



                  














                                       

                                                                            





                                                    










                                                                 
   

                                           
                                                                 
 
                                                          



                                     










                                                              
    

                   






                                               








































































































                                                                    



                 





                                                           
 

































                                                         
 
 

                                                                         

 





























































































































                                                                   
 
 




































































                                                                    
 

                                          



                                                                              





                                      

                                                    

 
                                       

 


















                                                                        






                          
/**
 * @file
 *
 * @brief Private Inlined Routines for POSIX Mutex's.
 * 
 * This include file contains the static inline implementation of the private 
 * inlined routines for POSIX mutex's.
 */

/*  COPYRIGHT (c) 1989-2013.
 *  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_POSIX_MUTEXIMPL_H
#define _RTEMS_POSIX_MUTEXIMPL_H

#include <errno.h>
#include <pthread.h>

#include <rtems/score/percpu.h>
#include <rtems/score/muteximpl.h>
#include <rtems/score/threadimpl.h>

#ifdef __cplusplus
extern "C" {
#endif

typedef struct {
  unsigned long flags;
  Mutex_recursive_Control Recursive;
  Priority_Node Priority_ceiling;
  const Scheduler_Control *scheduler;
} POSIX_Mutex_Control;

#define POSIX_MUTEX_PROTOCOL_MASK 0x3UL

#define POSIX_MUTEX_RECURSIVE 0x4UL

#define POSIX_MUTEX_FLAGS_MASK 0x7UL

#define POSIX_MUTEX_MAGIC 0x961c13b8UL

#define POSIX_MUTEX_NO_PROTOCOL_TQ_OPERATIONS &_Thread_queue_Operations_FIFO

#define POSIX_MUTEX_PRIORITY_INHERIT_TQ_OPERATIONS \
  &_Thread_queue_Operations_priority_inherit

#define POSIX_MUTEX_PRIORITY_CEILING_TQ_OPERATIONS \
  &_Thread_queue_Operations_priority

/**
 * @brief Supported POSIX mutex protocols.
 *
 * Must be in synchronization with POSIX_Mutex_Control::protocol.
 */
typedef enum {
  POSIX_MUTEX_NO_PROTOCOL,
  POSIX_MUTEX_PRIORITY_INHERIT,
  POSIX_MUTEX_PRIORITY_CEILING
} POSIX_Mutex_Protocol;

/**
 *  The default mutex attributes structure.
 */
extern const pthread_mutexattr_t _POSIX_Mutex_Default_attributes;

RTEMS_INLINE_ROUTINE Thread_Control *_POSIX_Mutex_Acquire(
  POSIX_Mutex_Control  *the_mutex,
  Thread_queue_Context *queue_context
)
{
  ISR_Level       level;
  Thread_Control *executing;

  _Thread_queue_Context_initialize( queue_context );
  _Thread_queue_Context_ISR_disable( queue_context, level );
  _Thread_queue_Context_set_ISR_level( queue_context, level );
  executing = _Thread_Executing;
  _Thread_queue_Queue_acquire_critical(
    &the_mutex->Recursive.Mutex.Queue.Queue,
    &executing->Potpourri_stats,
    &queue_context->Lock_context.Lock_context
  );

  return executing;
}

RTEMS_INLINE_ROUTINE void _POSIX_Mutex_Release(
  POSIX_Mutex_Control  *the_mutex,
  Thread_queue_Context *queue_context
)
{
  _Thread_queue_Queue_release(
    &the_mutex->Recursive.Mutex.Queue.Queue,
    &queue_context->Lock_context.Lock_context
  );
}

RTEMS_INLINE_ROUTINE POSIX_Mutex_Protocol _POSIX_Mutex_Get_protocol(
  unsigned long flags
)
{
  return flags & POSIX_MUTEX_PROTOCOL_MASK;
}

RTEMS_INLINE_ROUTINE bool _POSIX_Mutex_Is_recursive(
  unsigned long flags
)
{
  return ( flags & POSIX_MUTEX_RECURSIVE ) != 0;
}

RTEMS_INLINE_ROUTINE Thread_Control *_POSIX_Mutex_Get_owner(
  const POSIX_Mutex_Control *the_mutex
)
{
  return the_mutex->Recursive.Mutex.Queue.Queue.owner;
}

RTEMS_INLINE_ROUTINE bool _POSIX_Mutex_Is_locked(
  const POSIX_Mutex_Control *the_mutex
)
{
  return _POSIX_Mutex_Get_owner( the_mutex ) != NULL;
}

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

RTEMS_INLINE_ROUTINE void _POSIX_Mutex_Set_owner(
  POSIX_Mutex_Control *the_mutex,
  Thread_Control      *owner
)
{
  the_mutex->Recursive.Mutex.Queue.Queue.owner = owner;
}

RTEMS_INLINE_ROUTINE bool _POSIX_Mutex_Is_owner(
  const POSIX_Mutex_Control *the_mutex,
  const Thread_Control      *the_thread
)
{
  return _POSIX_Mutex_Get_owner( the_mutex ) == the_thread;
}

static Status_Control _POSIX_Mutex_Lock_nested(
  POSIX_Mutex_Control *the_mutex,
  unsigned long        flags
)
{

  if ( _POSIX_Mutex_Is_recursive( flags ) ) {
    ++the_mutex->Recursive.nest_level;
    return STATUS_SUCCESSFUL;
  } else {
    return STATUS_NESTING_NOT_ALLOWED;
  }
}

RTEMS_INLINE_ROUTINE Status_Control _POSIX_Mutex_Seize(
  POSIX_Mutex_Control           *the_mutex,
  unsigned long                  flags,
  const Thread_queue_Operations *operations,
  Thread_Control                *executing,
  bool                           wait,
  Thread_queue_Context          *queue_context
)
{
  Thread_Control *owner;

  owner = _POSIX_Mutex_Get_owner( the_mutex );

  if ( owner == NULL ) {
    _POSIX_Mutex_Set_owner( the_mutex, executing );
    _Thread_Resource_count_increment( executing );
    _POSIX_Mutex_Release( the_mutex, queue_context );
    return STATUS_SUCCESSFUL;
  }

  if ( owner == executing ) {
    Status_Control status;

    status = _POSIX_Mutex_Lock_nested( the_mutex, flags );
    _POSIX_Mutex_Release( the_mutex, queue_context );
    return status;
  }

  return _POSIX_Mutex_Seize_slow(
    the_mutex,
    operations,
    executing,
    wait,
    queue_context
  );
}

RTEMS_INLINE_ROUTINE Status_Control _POSIX_Mutex_Surrender(
  POSIX_Mutex_Control           *the_mutex,
  const Thread_queue_Operations *operations,
  Thread_Control                *executing,
  Thread_queue_Context          *queue_context
)
{
  unsigned int        nest_level;
  Thread_queue_Heads *heads;

  if ( !_POSIX_Mutex_Is_owner( the_mutex, executing ) ) {
    _POSIX_Mutex_Release( the_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;
    _POSIX_Mutex_Release( the_mutex, queue_context );
    return STATUS_SUCCESSFUL;
  }

  _Thread_Resource_count_decrement( executing );
  _POSIX_Mutex_Set_owner( the_mutex, NULL );

  heads = the_mutex->Recursive.Mutex.Queue.Queue.heads;

  if ( heads == NULL ) {
    _POSIX_Mutex_Release( the_mutex, queue_context );
    return STATUS_SUCCESSFUL;
  }

  _Thread_queue_Surrender(
    &the_mutex->Recursive.Mutex.Queue.Queue,
    heads,
    executing,
    queue_context,
    operations
  );
  return STATUS_SUCCESSFUL;
}

RTEMS_INLINE_ROUTINE const Scheduler_Control *_POSIX_Mutex_Get_scheduler(
  const POSIX_Mutex_Control *the_mutex
)
{
#if defined(RTEMS_SMP)
  return the_mutex->scheduler;
#else
  return &_Scheduler_Table[ 0 ];
#endif
}

RTEMS_INLINE_ROUTINE void _POSIX_Mutex_Set_priority(
  POSIX_Mutex_Control  *the_mutex,
  Priority_Control      priority_ceiling,
  Thread_queue_Context *queue_context
)
{
  Thread_Control *owner;

  owner = _POSIX_Mutex_Get_owner( the_mutex );

  if ( owner != NULL ) {
    _Thread_Wait_acquire( owner, queue_context );
    _Thread_Priority_change(
      owner,
      &the_mutex->Priority_ceiling,
      priority_ceiling,
      false,
      queue_context
    );
    _Thread_Wait_release( owner, queue_context );
  } else {
    the_mutex->Priority_ceiling.priority = priority_ceiling;
  }
}

RTEMS_INLINE_ROUTINE Priority_Control _POSIX_Mutex_Get_priority(
  const POSIX_Mutex_Control *the_mutex
)
{
  return the_mutex->Priority_ceiling.priority;
}

RTEMS_INLINE_ROUTINE Status_Control _POSIX_Mutex_Ceiling_set_owner(
  POSIX_Mutex_Control  *the_mutex,
  Thread_Control       *owner,
  Thread_queue_Context *queue_context
)
{
  ISR_lock_Context  lock_context;
  Scheduler_Node   *scheduler_node;
  Per_CPU_Control  *cpu_self;

  _Thread_Wait_acquire_default_critical( owner, &lock_context );

  scheduler_node = _Thread_Scheduler_get_home_node( owner );

  if (
    _Priority_Get_priority( &scheduler_node->Wait.Priority )
      < the_mutex->Priority_ceiling.priority
  ) {
    _Thread_Wait_release_default_critical( owner, &lock_context );
    _POSIX_Mutex_Release( the_mutex, queue_context );
    return STATUS_MUTEX_CEILING_VIOLATED;
  }

  _POSIX_Mutex_Set_owner( the_mutex, owner );
  _Thread_Resource_count_increment( owner );
  _Thread_Priority_add(
    owner,
    &the_mutex->Priority_ceiling,
    queue_context
  );
  _Thread_Wait_release_default_critical( owner, &lock_context );

  cpu_self = _Thread_Dispatch_disable_critical(
    &queue_context->Lock_context.Lock_context
  );
  _POSIX_Mutex_Release( the_mutex, queue_context );
  _Thread_Priority_update( queue_context );
  _Thread_Dispatch_enable( cpu_self );
  return STATUS_SUCCESSFUL;
}

RTEMS_INLINE_ROUTINE Status_Control _POSIX_Mutex_Ceiling_seize(
  POSIX_Mutex_Control  *the_mutex,
  unsigned long         flags,
  Thread_Control       *executing,
  bool                  wait,
  Thread_queue_Context *queue_context
)
{
  Thread_Control *owner;

  owner = _POSIX_Mutex_Get_owner( the_mutex );

  if ( owner == NULL ) {
#if defined(RTEMS_SMP)
    if (
      _Thread_Scheduler_get_home( executing )
        != _POSIX_Mutex_Get_scheduler( the_mutex )
    ) {
      _POSIX_Mutex_Release( the_mutex, queue_context );
      return STATUS_NOT_DEFINED;
    }
#endif

    _Thread_queue_Context_clear_priority_updates( queue_context );
    return _POSIX_Mutex_Ceiling_set_owner(
      the_mutex,
      executing,
      queue_context
    );
  }

  if ( owner == executing ) {
    Status_Control status;

    status = _POSIX_Mutex_Lock_nested( the_mutex, flags );
    _POSIX_Mutex_Release( the_mutex, queue_context );
    return status;
  }

  return _POSIX_Mutex_Seize_slow(
    the_mutex,
    POSIX_MUTEX_PRIORITY_CEILING_TQ_OPERATIONS,
    executing,
    wait,
    queue_context
  );
}

RTEMS_INLINE_ROUTINE Status_Control _POSIX_Mutex_Ceiling_surrender(
  POSIX_Mutex_Control  *the_mutex,
  Thread_Control       *executing,
  Thread_queue_Context *queue_context
)
{
  unsigned int        nest_level;
  ISR_lock_Context    lock_context;
  Per_CPU_Control    *cpu_self;
  Thread_queue_Heads *heads;

  if ( !_POSIX_Mutex_Is_owner( the_mutex, executing ) ) {
    _POSIX_Mutex_Release( the_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;
    _POSIX_Mutex_Release( the_mutex, queue_context );
    return STATUS_SUCCESSFUL;
  }

  _Thread_Resource_count_decrement( executing );

  _Thread_queue_Context_clear_priority_updates( queue_context );
  _Thread_Wait_acquire_default_critical( executing, &lock_context );
  _Thread_Priority_remove(
    executing,
    &the_mutex->Priority_ceiling,
    queue_context
  );
  _Thread_Wait_release_default_critical( executing, &lock_context );

  cpu_self = _Thread_Dispatch_disable_critical(
    &queue_context->Lock_context.Lock_context
  );

  heads = the_mutex->Recursive.Mutex.Queue.Queue.heads;

  if ( heads != NULL ) {
    const Thread_queue_Operations *operations;
    Thread_Control                *new_owner;

    operations = POSIX_MUTEX_PRIORITY_CEILING_TQ_OPERATIONS;
    new_owner = ( *operations->first )( heads );
    _POSIX_Mutex_Set_owner( the_mutex, new_owner );
    _Thread_Resource_count_increment( new_owner );
    _Thread_Priority_add(
      new_owner,
      &the_mutex->Priority_ceiling,
      queue_context
    );
    _Thread_queue_Extract_critical(
      &the_mutex->Recursive.Mutex.Queue.Queue,
      operations,
      new_owner,
      queue_context
    );
  } else {
    _POSIX_Mutex_Set_owner( the_mutex, NULL );
    _POSIX_Mutex_Release( the_mutex, queue_context );
  }

  _Thread_Priority_update( queue_context );
  _Thread_Dispatch_enable( cpu_self );
  return STATUS_SUCCESSFUL;
}

/**
 *  @brief POSIX Mutex Lock Support Method
 *
 *  A support routine which implements guts of the blocking, non-blocking, and
 *  timed wait version of mutex lock.
 */
int _POSIX_Mutex_Lock_support(
  pthread_mutex_t           *mutex,
  bool                       blocking,
  Watchdog_Interval          timeout
);

static inline POSIX_Mutex_Control *_POSIX_Mutex_Get(
  pthread_mutex_t *mutex
)
{
  return (POSIX_Mutex_Control *) mutex;
}

bool _POSIX_Mutex_Auto_initialization( POSIX_Mutex_Control *the_mutex );

#define POSIX_MUTEX_VALIDATE_OBJECT( the_mutex, flags ) \
  do { \
    if ( ( the_mutex ) == NULL ) { \
      return EINVAL; \
    } \
    flags = ( the_mutex )->flags; \
    if ( \
      ( ( (uintptr_t) ( the_mutex ) ^ POSIX_MUTEX_MAGIC ) \
          & ~POSIX_MUTEX_FLAGS_MASK ) \
        != ( flags & ~POSIX_MUTEX_FLAGS_MASK ) \
    ) { \
      if ( !_POSIX_Mutex_Auto_initialization( the_mutex ) ) { \
        return EINVAL; \
      } \
    } \
  } while ( 0 )

#ifdef __cplusplus
}
#endif

#endif
/*  end of include file */