Mutexes

Mutexes are used to help prevent priority inversion. They work by temporarily raising the priority of the lower task holding the mutex to a higher priority so that it will run and release the mutex.

On uCOS priorities must be unique. Because of that each mutex has its own priority. Typically one higher then the highest one of the tasks that share the mutex.

It only bumps up the priority of a task if a higher priority task is waiting on the same mutex. The task’s priority reverts back when it calls Post on the mutex.

The process of temporarily increasing the priority of a task is often called priority boosting.

Priority Inversion

../_images/Priority_Inversion_Diagram.png

In this scenario there are three tasks, Task1, Task2, and Task3 with priorities 1, 2, and 3 respectively. Task1 and Task3 are sharing a function, use(), that is protected by a semaphore.

At first Task3 is running and acquires semaphore. Right after that Task1 is signaled and starts to run since it has higher priority than Task3. Task1 runs until it tries to acquire the semaphore but since Task3 has the semaphore Task1 will need to wait until Task3 releases it. That much is a given. It’s a shared resource and only one task at a time can use it. Everything is ok at this point.

But if Task2 is signaled it will preempt Task3 because it has a higher priority then Task3. Task3 is no longer running yet it still holds the semaphore that is blocking Task1.

Task1 is now completely blocked and it’s priority is essentially less Task2’s priority. Hence the name priority inversion. Task1 may be blocked indefinitely even though it has the highest priority. Mutexes are designed to help solve this problem.

A mutex is a binary semaphore that can detect the case where it is being held by lower priority task when a higher priority task needs to acquire it. When the higher priority task calls Pend, the mutex will boost the prority of the lower task that is holding it so that the lower task will run release the mutex. The priority boosting helps to ensure the lower task will not be preempt while it is hold a mutex needed by a higher priority task.

OSMutexCreate

OS_EVENT *OSMutexCreate(INT8U prio, INT8U *perr);
  • Creates a mutex which is a binary semaphore with priority boosting.
  • Has it’s own priority level.
  • It is used to help prevent priority inversion.
  • It reserves a priority level.
  • Mutex APIs can only be called from tasks. They can’t be used in an ISR.

OSMutexDel

OS_EVENT *OSMutexDel(OS_EVENT *pevent, INT8U opt, INT8U *perr);
  • Deletes a mutex
  • Frees the Event Control Block (ECB) used by the mutex
  • Requires an option
    • OS_DEL_NO_PEND - don’t delete if tasks are waiting on the mutex
    • OS_DEL_ALWAYS - always delete
  • If called with OS_DEL_ALWAYS, the mutex will be deleted even if tasks are waiting on it.
    • All the waiting tasks will become ready
    • All those tasks will think they have the mutex, which is bad!
  • Best practice is to first delete all tasks that use the mutex before deleting the mutex.

OSMutexPend

void OSMutexPend(OS_EVENT *pevent, INT16U timeout, INT8U *perr);
  • Must be called from a task.
  • If the mutex is available it returns immediately.
  • If not, it will but the task on the wait list.
  • When called, it compares the priority level of the caller to the current owner of the mutex. If the owner’s priority is lower then the caller, it will change the owner’s priority to its own priority. That is, the priority level given to the mutex when it was created.
  • When the task that owned the mutex release the mutex, the task’s priority level will revert back to it’s original value.

OSMutexPost

INT8U OSMutexPost(OS_EVENT *pevent);
  • Only the current owner of the mutex can call Post on the mutex.
  • The function checks to see if the caller actually owns the mutex.
    • If not it returns the error OS_ERR_NOT_MUTEX_OWNER
  • Check if the caller’s priority was raised. If so, the caller’s priority is returned to its original priority value.
  • The highest priority waiting task is removed from the wait list and made ready.
  • Calls the scheduler.

OSMutexAccept

BOOLEAN OSMutexAccept(OS_EVENT *pevent, INT8U *perr);
  • Must be called from a task.
  • If the mutex is available it returns with a 1.
  • If the mutex is not available it returns with a 0.
  • Does not block.

OSMutexQuery

INT8U OSMutexQuery(OS_EVENT *pevent, OS_MUTEX_DATA *p_mutex_data);
  • Caller must allocate a OS_MUTEX_DATA blcok and pass the pointer to the block.
  • Must be called from a task.
  • Copies the mutex info into the data block.
  • Copies the value of the priority inhertience priority, ie, the mutex’s own priority.
  • Copies the priority of the mutex’s owner if it is being held.
  • And 1 or 0 if the mutex is available or is being held.

Table Of Contents

Previous topic

Semaphores

Next topic

MailBoxes

This Page