Priority inversion – Comtrol eCos User Manual
Page 66

Mutexes
Sections of code like the above which involve manipulating shared data are generally known as critical regions.
Code should claim a lock before entering a critical region and release the lock when leaving. Mutexes provide an
appropriate synchronization primitive for this.
static volatile int counter = 0;
static cyg_mutex_t
lock;
void
process_event(void)
{
...
cyg_mutex_lock(&lock);
counter++;
cyg_mutex_unlock(&lock);
}
A mutex must be initialized before it can be used, by calling
cyg_mutex_init
. This takes a pointer to a
cyg_mutex_t data structure which is typically statically allocated, and may be part of a larger data structure. If a
mutex is no longer required and there are no threads waiting on it then
cyg_mutex_destroy
can be used.
The main functions for using a mutex are
cyg_mutex_lock
and
cyg_mutex_unlock
. In normal operation
cyg_mutex_lock
will return success after claiming the mutex lock, blocking if another thread currently owns the
mutex. However the lock operation may fail if other code calls
cyg_mutex_release
or
cyg_thread_release
,
so if these functions may get used then it is important to check the return value. The current owner of a mutex
should call
cyg_mutex_unlock
when a lock is no longer required. This operation must be performed by the
owner, not by another thread.
cyg_mutex_trylock
is a variant of
cyg_mutex_lock
that will always return immediately, returning success or
failure as appropriate. This function is rarely useful. Typical code locks a mutex just before entering a critical
region, so if the lock cannot be claimed then there may be nothing else for the current thread to do. Use of this
function may also cause a form of priority inversion if the owner owner runs at a lower priority, because the
priority inheritance code will not be triggered. Instead the current thread continues running, preventing the owner
from getting any cpu time, completing the critical region, and releasing the mutex.
cyg_mutex_release
can be used to wake up all threads that are currently blocked inside a call to
cyg_mutex_lock
for a specific mutex. These lock calls will return failure. The current mutex owner is not
affected.
Priority Inversion
The use of mutexes gives rise to a problem known as priority inversion. In a typical scenario this requires three
threads A, B, and C, running at high, medium and low priority respectively. Thread A and thread B are temporarily
blocked waiting for some event, so thread C gets a chance to run, needs to enter a critical region, and locks a mutex.
At this point threads A and B are woken up - the exact order does not matter. Thread A needs to claim the same
mutex but has to wait until C has left the critical region and can release the mutex. Meanwhile thread B works on
something completely different and can continue running without problems. Because thread C is running a lower
priority than B it will not get a chance to run until B blocks for some reason, and hence thread A cannot run either.
The overall effect is that a high-priority thread A cannot proceed because of a lower priority thread B, and priority
inversion has occurred.
66