/** * @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 #include #include #include #include #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 (POSIX_Mutex_Protocol) (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, const struct timespec *abstime, 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, const struct timespec *abstime, 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, abstime, 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_queue_Dispatch_disable( queue_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, const struct timespec *abstime, 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, abstime, 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_queue_Dispatch_disable( queue_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; } #define POSIX_MUTEX_ABSTIME_TRY_LOCK ((uintptr_t) 1) int _POSIX_Mutex_Lock_support( pthread_mutex_t *mutex, const struct timespec *abstime, Thread_queue_Enqueue_callout enqueue_callout ); 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 */