CORE_mutex_Status _CORE_mutex_Initialize( CORE_mutex_Control *the_mutex, Thread_Control *executing, const CORE_mutex_Attributes *the_mutex_attributes, bool initially_locked ) { /* Add this to the RTEMS environment later ????????? rtems_assert( initial_lock == CORE_MUTEX_LOCKED || initial_lock == CORE_MUTEX_UNLOCKED ); */ the_mutex->Attributes = *the_mutex_attributes; if ( initially_locked ) { the_mutex->nest_count = 1; the_mutex->holder = executing; if ( _CORE_mutex_Is_inherit_priority( &the_mutex->Attributes ) || _CORE_mutex_Is_priority_ceiling( &the_mutex->Attributes ) ) { Priority_Control ceiling = the_mutex->Attributes.priority_ceiling; /* * The mutex initialization is only protected by the allocator lock in * general. Disable thread dispatching before the priority check to * prevent interference with priority inheritance. */ _Thread_Disable_dispatch(); if ( executing->current_priority < ceiling ) { _Thread_Enable_dispatch(); return CORE_MUTEX_STATUS_CEILING_VIOLATED; } #ifdef __RTEMS_STRICT_ORDER_MUTEX__ _Chain_Prepend_unprotected( &executing->lock_mutex, &the_mutex->queue.lock_queue ); the_mutex->queue.priority_before = executing->current_priority; #endif executing->resource_count++; _Thread_Change_priority( executing, ceiling, false ); _Thread_Enable_dispatch(); } } else { the_mutex->nest_count = 0; the_mutex->holder = NULL; } _Thread_queue_Initialize( &the_mutex->Wait_queue, _CORE_mutex_Is_fifo( the_mutex_attributes ) ? THREAD_QUEUE_DISCIPLINE_FIFO : THREAD_QUEUE_DISCIPLINE_PRIORITY, STATES_WAITING_FOR_MUTEX, CORE_MUTEX_TIMEOUT ); return CORE_MUTEX_STATUS_SUCCESSFUL; }
CORE_mutex_Status _CORE_mutex_Initialize( CORE_mutex_Control *the_mutex, CORE_mutex_Attributes *the_mutex_attributes, uint32_t initial_lock ) { /* Add this to the RTEMS environment later ????????? rtems_assert( initial_lock == CORE_MUTEX_LOCKED || initial_lock == CORE_MUTEX_UNLOCKED ); */ the_mutex->Attributes = *the_mutex_attributes; the_mutex->lock = initial_lock; the_mutex->blocked_count = 0; if ( initial_lock == CORE_MUTEX_LOCKED ) { the_mutex->nest_count = 1; the_mutex->holder = _Thread_Executing; the_mutex->holder_id = _Thread_Executing->Object.id; if ( _CORE_mutex_Is_inherit_priority( &the_mutex->Attributes ) || _CORE_mutex_Is_priority_ceiling( &the_mutex->Attributes ) ) { if ( _Thread_Executing->current_priority < the_mutex->Attributes.priority_ceiling ) return CORE_MUTEX_STATUS_CEILING_VIOLATED; #ifdef __RTEMS_STRICT_ORDER_MUTEX__ _Chain_Prepend_unprotected( &_Thread_Executing->lock_mutex, &the_mutex->queue.lock_queue ); the_mutex->queue.priority_before = _Thread_Executing->current_priority; #endif _Thread_Executing->resource_count++; } } else { the_mutex->nest_count = 0; the_mutex->holder = NULL; the_mutex->holder_id = 0; } _Thread_queue_Initialize( &the_mutex->Wait_queue, _CORE_mutex_Is_fifo( the_mutex_attributes ) ? THREAD_QUEUE_DISCIPLINE_FIFO : THREAD_QUEUE_DISCIPLINE_PRIORITY, STATES_WAITING_FOR_MUTEX, CORE_MUTEX_TIMEOUT ); return CORE_MUTEX_STATUS_SUCCESSFUL; }
CORE_mutex_Status _CORE_mutex_Surrender( CORE_mutex_Control *the_mutex, #if defined(RTEMS_MULTIPROCESSING) Objects_Id id, CORE_mutex_API_mp_support_callout api_mutex_mp_support #else Objects_Id id __attribute__((unused)), CORE_mutex_API_mp_support_callout api_mutex_mp_support __attribute__((unused)) #endif ) { Thread_Control *the_thread; Thread_Control *holder; holder = the_mutex->holder; /* * The following code allows a thread (or ISR) other than the thread * which acquired the mutex to release that mutex. This is only * allowed when the mutex in quetion is FIFO or simple Priority * discipline. But Priority Ceiling or Priority Inheritance mutexes * must be released by the thread which acquired them. */ if ( the_mutex->Attributes.only_owner_release ) { if ( !_Thread_Is_executing( holder ) ) return CORE_MUTEX_STATUS_NOT_OWNER_OF_RESOURCE; } /* XXX already unlocked -- not right status */ if ( !the_mutex->nest_count ) return CORE_MUTEX_STATUS_SUCCESSFUL; the_mutex->nest_count--; if ( the_mutex->nest_count != 0 ) { /* * All error checking is on the locking side, so if the lock was * allowed to acquired multiple times, then we should just deal with * that. The RTEMS_DEBUG is just a validation. */ #if defined(RTEMS_DEBUG) switch ( the_mutex->Attributes.lock_nesting_behavior ) { case CORE_MUTEX_NESTING_ACQUIRES: return CORE_MUTEX_STATUS_SUCCESSFUL; #if defined(RTEMS_POSIX_API) case CORE_MUTEX_NESTING_IS_ERROR: /* should never occur */ return CORE_MUTEX_STATUS_NESTING_NOT_ALLOWED; #endif case CORE_MUTEX_NESTING_BLOCKS: /* Currently no API exercises this behavior. */ break; } #else /* must be CORE_MUTEX_NESTING_ACQUIRES or we wouldn't be here */ return CORE_MUTEX_STATUS_SUCCESSFUL; #endif } /* * Formally release the mutex before possibly transferring it to a * blocked thread. */ if ( _CORE_mutex_Is_inherit_priority( &the_mutex->Attributes ) || _CORE_mutex_Is_priority_ceiling( &the_mutex->Attributes ) ) { CORE_mutex_Status pop_status = _CORE_mutex_Pop_priority( the_mutex, holder ); if ( pop_status != CORE_MUTEX_STATUS_SUCCESSFUL ) return pop_status; holder->resource_count--; /* * 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 ( holder->resource_count == 0 && holder->real_priority != holder->current_priority ) { _Thread_Change_priority( holder, holder->real_priority, true ); } } the_mutex->holder = NULL; the_mutex->holder_id = 0; /* * Now we check if another thread was waiting for this mutex. If so, * transfer the mutex to that thread. */ if ( ( the_thread = _Thread_queue_Dequeue( &the_mutex->Wait_queue ) ) ) { #if defined(RTEMS_MULTIPROCESSING) if ( !_Objects_Is_local_id( the_thread->Object.id ) ) { the_mutex->holder = NULL; the_mutex->holder_id = the_thread->Object.id; the_mutex->nest_count = 1; ( *api_mutex_mp_support)( the_thread, id ); } else #endif { the_mutex->holder = the_thread; the_mutex->holder_id = the_thread->Object.id; the_mutex->nest_count = 1; switch ( the_mutex->Attributes.discipline ) { case CORE_MUTEX_DISCIPLINES_FIFO: case CORE_MUTEX_DISCIPLINES_PRIORITY: break; case CORE_MUTEX_DISCIPLINES_PRIORITY_INHERIT: _CORE_mutex_Push_priority( the_mutex, the_thread ); the_thread->resource_count++; break; case CORE_MUTEX_DISCIPLINES_PRIORITY_CEILING: _CORE_mutex_Push_priority( the_mutex, the_thread ); the_thread->resource_count++; if (the_mutex->Attributes.priority_ceiling < the_thread->current_priority){ _Thread_Change_priority( the_thread, the_mutex->Attributes.priority_ceiling, false ); } break; } } } else the_mutex->lock = CORE_MUTEX_UNLOCKED; return CORE_MUTEX_STATUS_SUCCESSFUL; }
CORE_mutex_Status _CORE_mutex_Surrender( CORE_mutex_Control *the_mutex, #if defined(RTEMS_MULTIPROCESSING) Objects_Id id, CORE_mutex_API_mp_support_callout api_mutex_mp_support, #else Objects_Id id __attribute__((unused)), CORE_mutex_API_mp_support_callout api_mutex_mp_support __attribute__((unused)), #endif ISR_lock_Context *lock_context ) { Thread_Control *the_thread; Thread_Control *holder; holder = the_mutex->holder; /* * The following code allows a thread (or ISR) other than the thread * which acquired the mutex to release that mutex. This is only * allowed when the mutex in quetion is FIFO or simple Priority * discipline. But Priority Ceiling or Priority Inheritance mutexes * must be released by the thread which acquired them. */ if ( the_mutex->Attributes.only_owner_release ) { if ( !_Thread_Is_executing( holder ) ) { _ISR_lock_ISR_enable( lock_context ); return CORE_MUTEX_STATUS_NOT_OWNER_OF_RESOURCE; } } _Thread_queue_Acquire_critical( &the_mutex->Wait_queue, lock_context ); /* XXX already unlocked -- not right status */ if ( !the_mutex->nest_count ) { _Thread_queue_Release( &the_mutex->Wait_queue, lock_context ); return CORE_MUTEX_STATUS_SUCCESSFUL; } the_mutex->nest_count--; if ( the_mutex->nest_count != 0 ) { /* * All error checking is on the locking side, so if the lock was * allowed to acquired multiple times, then we should just deal with * that. The RTEMS_DEBUG is just a validation. */ #if defined(RTEMS_DEBUG) switch ( the_mutex->Attributes.lock_nesting_behavior ) { case CORE_MUTEX_NESTING_ACQUIRES: _Thread_queue_Release( &the_mutex->Wait_queue, lock_context ); return CORE_MUTEX_STATUS_SUCCESSFUL; #if defined(RTEMS_POSIX_API) case CORE_MUTEX_NESTING_IS_ERROR: /* should never occur */ _Thread_queue_Release( &the_mutex->Wait_queue, lock_context ); return CORE_MUTEX_STATUS_NESTING_NOT_ALLOWED; #endif case CORE_MUTEX_NESTING_BLOCKS: /* Currently no API exercises this behavior. */ break; } #else _Thread_queue_Release( &the_mutex->Wait_queue, lock_context ); /* must be CORE_MUTEX_NESTING_ACQUIRES or we wouldn't be here */ return CORE_MUTEX_STATUS_SUCCESSFUL; #endif } /* * Formally release the mutex before possibly transferring it to a * blocked thread. */ if ( _CORE_mutex_Is_inherit_priority( &the_mutex->Attributes ) || _CORE_mutex_Is_priority_ceiling( &the_mutex->Attributes ) ) { CORE_mutex_Status pop_status = _CORE_mutex_Pop_priority( the_mutex, holder ); if ( pop_status != CORE_MUTEX_STATUS_SUCCESSFUL ) { _Thread_queue_Release( &the_mutex->Wait_queue, lock_context ); return pop_status; } holder->resource_count--; } the_mutex->holder = NULL; /* * Now we check if another thread was waiting for this mutex. If so, * transfer the mutex to that thread. */ if ( ( the_thread = _Thread_queue_First_locked( &the_mutex->Wait_queue ) ) ) { /* * 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. */ _Thread_queue_Extract_locked( &the_mutex->Wait_queue, the_thread ); #if defined(RTEMS_MULTIPROCESSING) _Thread_Dispatch_disable(); if ( _Objects_Is_local_id( the_thread->Object.id ) ) #endif { the_mutex->holder = the_thread; the_mutex->nest_count = 1; switch ( the_mutex->Attributes.discipline ) { case CORE_MUTEX_DISCIPLINES_FIFO: case CORE_MUTEX_DISCIPLINES_PRIORITY: break; case CORE_MUTEX_DISCIPLINES_PRIORITY_INHERIT: _CORE_mutex_Push_priority( the_mutex, the_thread ); the_thread->resource_count++; break; case CORE_MUTEX_DISCIPLINES_PRIORITY_CEILING: _CORE_mutex_Push_priority( the_mutex, the_thread ); the_thread->resource_count++; _Thread_Raise_priority( the_thread, the_mutex->Attributes.priority_ceiling ); break; } } _Thread_queue_Unblock_critical( &the_mutex->Wait_queue, the_thread, lock_context ); #if defined(RTEMS_MULTIPROCESSING) if ( !_Objects_Is_local_id( the_thread->Object.id ) ) { the_mutex->holder = NULL; the_mutex->nest_count = 1; ( *api_mutex_mp_support)( the_thread, id ); } _Thread_Dispatch_enable( _Per_CPU_Get() ); #endif } else { _Thread_queue_Release( &the_mutex->Wait_queue, lock_context ); } /* * 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( holder ) ) { /* * Ensure that the holder resource count is visible to all other processors * and that we read the latest priority restore hint. */ _Atomic_Fence( ATOMIC_ORDER_ACQ_REL ); if ( holder->priority_restore_hint ) { Per_CPU_Control *cpu_self; cpu_self = _Thread_Dispatch_disable(); _Thread_Restore_priority( holder ); _Thread_Dispatch_enable( cpu_self ); } } return CORE_MUTEX_STATUS_SUCCESSFUL; }
CORE_mutex_Status _CORE_mutex_Initialize( CORE_mutex_Control *the_mutex, Thread_Control *executing, const CORE_mutex_Attributes *the_mutex_attributes, bool initially_locked ) { /* Add this to the RTEMS environment later ????????? rtems_assert( initial_lock == CORE_MUTEX_LOCKED || initial_lock == CORE_MUTEX_UNLOCKED ); */ the_mutex->Attributes = *the_mutex_attributes; if ( initially_locked ) { bool is_priority_ceiling = _CORE_mutex_Is_priority_ceiling( &the_mutex->Attributes ); the_mutex->nest_count = 1; the_mutex->holder = executing; if ( is_priority_ceiling || _CORE_mutex_Is_inherit_priority( &the_mutex->Attributes ) ) { Priority_Control ceiling = the_mutex->Attributes.priority_ceiling; Per_CPU_Control *cpu_self; /* The mutex initialization is only protected by the allocator lock */ cpu_self = _Thread_Dispatch_disable(); /* * The test to check for a ceiling violation is a bit arbitrary. In case * this thread is the owner of a priority inheritance mutex, then it may * get a higher priority later or anytime on SMP configurations. */ if ( is_priority_ceiling && executing->current_priority < ceiling ) { /* * There is no need to undo the previous work since this error aborts * the object creation. */ _Thread_Dispatch_enable( cpu_self ); return CORE_MUTEX_STATUS_CEILING_VIOLATED; } #ifdef __RTEMS_STRICT_ORDER_MUTEX__ _Chain_Prepend_unprotected( &executing->lock_mutex, &the_mutex->queue.lock_queue ); the_mutex->queue.priority_before = executing->current_priority; #endif executing->resource_count++; if ( is_priority_ceiling ) { _Thread_Raise_priority( executing, ceiling ); } _Thread_Dispatch_enable( cpu_self ); } } else { the_mutex->nest_count = 0; the_mutex->holder = NULL; } _Thread_queue_Initialize( &the_mutex->Wait_queue ); if ( _CORE_mutex_Is_priority( the_mutex_attributes ) ) { the_mutex->operations = &_Thread_queue_Operations_priority; } else { the_mutex->operations = &_Thread_queue_Operations_FIFO; } return CORE_MUTEX_STATUS_SUCCESSFUL; }