Mutex

Mutex is short for “mutual exclusion”; a mutex provides mutually exclusive access to a shared resource. A mutex provides priority inheritance in order to prevent priority inversion. Priority inversion occurs when a higher priority task is waiting on a resource owned by a lower priority task. Using a mutex, the lower priority task will inherit the highest priority of any task waiting on the mutex.

Description

The first order of business when using a mutex is to declare the mutex globally. The mutex needs to be initialized before it is used (see the examples). It is generally a good idea to initialize the mutex before tasks start running in order to avoid a task possibly using the mutex before it is initialized.

When a task wants exclusive access to a shared resource it needs to obtain the mutex by calling os_mutex_pend(). If the mutex is currently owned by a different task (a lower priority task), the requesting task will be put to sleep and the owners priority will be elevated to the priority of the requesting task. Note that multiple tasks can request ownership and the current owner is elevated to the highest priority of any task waitin on the mutex. When the task is done using the shared resource, it needs to release the mutex by called os_mutex_release(). There needs to be one release per call to pend. Note that nested calls to os_mutex_pend() are allowed but there needs to be one release per pend.

The following example will illustrate how priority inheritance works. In this example, the task number is the same as its priority. Remember that the lower the number, the higher the priority (i.e. priority 0 is higher priority than priority 1). Suppose that task 5 gets ownership of a mutex but is preempted by task 4. Task 4 attempts to gain ownership of the mutex but cannot as it is owned by task 5. Task 4 is put to sleep and task 5 is temporarily raised to priority 4. Before task 5 can release the mutex, task 3 runs and attempts to acquire the mutex. At this point, both task 3 and task 4 are waiting on the mutex (sleeping). Task 5 now runs at priority 3 (the highest priority of all the tasks waiting on the mutex). When task 5 finally releases the mutex it will be preempted as two higher priority tasks are waiting for it.

Note that when multiple tasks are waiting on a mutex owned by another task, once the mutex is released the highest priority task waiting on the mutex is run.

API

os_error_t os_mutex_init(struct os_mutex *mu)

Create a mutex and initialize it.

Return

os_error_t OS_INVALID_PARM Mutex passed in was NULL. OS_OK no error.

Parameters
  • mu: Pointer to mutex

os_error_t os_mutex_release(struct os_mutex *mu)

Release a mutex.

Return

os_error_t OS_INVALID_PARM Mutex passed in was NULL. OS_BAD_MUTEX Mutex was not granted to current task (not owner). OS_OK No error

Parameters
  • mu: Pointer to the mutex to be released

os_error_t os_mutex_pend(struct os_mutex *mu, os_time_t timeout)

Pend (wait) for a mutex.

Return

os_error_t OS_INVALID_PARM Mutex passed in was NULL. OS_TIMEOUT Mutex was owned by another task and timeout=0 OS_OK no error.

Parameters
  • mu: Pointer to mutex.

  • timeout: Timeout, in os ticks. A timeout of 0 means do not wait if not available. A timeout of OS_TIMEOUT_NEVER means wait forever.

static inline uint16_t os_mutex_get_level(struct os_mutex *mu)

Get mutex lock count.

It can also be called from interrupt context to check if given mutex is taken.

Note

Function should be called from task owning the mutex (one that successfully called os_mutex_pend). Calling function from other task that does not own the mutex will return value that has little value to the caller since value can change at any time by other task.

Return

number of times lock was called from current task

Parameters
  • mu: Pointer to mutex.

struct os_mutex
#include <os_mutex.h>

OS mutex structure.

Public Members

uint8_t mu_prio

Mutex owner’s default priority.

uint16_t mu_level

Mutex call nesting level.

struct os_task *mu_owner

Task that owns the mutex.