Comtrol eCos User Manual

Page 67

Advertising
background image

Mutexes

In simple applications it may be possible to arrange the code such that priority inversion cannot occur, for example
by ensuring that a given mutex is never shared by threads running at different priority levels. However this may not
always be possible even at the application level. In addition mutexes may be used internally by underlying code,
for example the memory allocation package, so careful analysis of the whole system would be needed to be sure
that priority inversion cannot occur. Instead it is common practice to use one of two techniques: priority ceilings
and priority inheritance.

Priority ceilings involve associating a priority with each mutex. Usually this will match the highest priority thread
that will ever lock the mutex. When a thread running at a lower priority makes a successful call to

cyg_mutex_lock

or

cyg_mutex_trylock

its priority will be boosted to that of the mutex. For example, given the previous example

the priority associated with the mutex would be that of thread A, so for as long as it owns the mutex thread C will
run in preference to thread B. When C releases the mutex its priority drops to the normal value again, allowing A
to run and claim the mutex. Setting the priority for a mutex involves a call to

cyg_mutex_set_ceiling

, which

is typically called during initialization. It is possible to change the ceiling dynamically but this will only affect
subsequent lock operations, not the current owner of the mutex.

Priority ceilings are very suitable for simple applications, where for every thread in the system it is possible to
work out which mutexes will be accessed. For more complicated applications this may prove difficult, especially
if thread priorities change at run-time. An additional problem occurs for any mutexes outside the application,
for example used internally within eCos packages. A typical eCos package will be unaware of the details of the
various threads in the system, so it will have no way of setting suitable ceilings for its internal mutexes. If those
mutexes are not exported to application code then using priority ceilings may not be viable. The kernel does provide
a configuration option

CYGSEM_KERNEL_SYNCH_MUTEX_PRIORITY_INVERSION_PROTOCOL_DEFAULT_PRIORITY

that can be used to set the default priority ceiling for all mutexes, which may prove sufficient.

The alternative approach is to use priority inheritance: if a thread calls

cyg_mutex_lock

for a mutex that it cur-

rently owned by a lower-priority thread, then the owner will have its priority raised to that of the current thread.
Often this is more efficient than priority ceilings because priority boosting only happens when necessary, not for
every lock operation, and the required priority is determined at run-time rather than by static analysis. However
there are complications when multiple threads running at different priorities try to lock a single mutex, or when
the current owner of a mutex then tries to lock additional mutexes, and this makes the implementation significantly
more complicated than priority ceilings.

There

are

a

number

of

configuration

options

associated

with

priority

inversion.

First,

if

after

careful

analysis

it

is

known

that

priority

inversion

cannot

arise

then

the

component

CYGSEM_KERNEL_SYNCH_MUTEX_PRIORITY_INVERSION_PROTOCOL

can

be

disabled.

More

commonly

this

component

will

be

enabled,

and

one

of

either

CYGSEM_KERNEL_SYNCH_MUTEX_PRIORITY_INVERSION_PROTOCOL_INHERIT

or

CYGSEM_KERNEL_SYNCH_MUTEX_PRIORITY_INVERSION_PROTOCOL_CEILING

will be selected, so that one of

the two protocols is available for all mutexes. It is possible to select multiple protocols, so that some mutexes can
have priority ceilings while others use priority inheritance or no priority inversion protection at all. Obviously
this flexibility will add to the code size and to the cost of mutex operations. The default for all mutexes will
be controlled by

CYGSEM_KERNEL_SYNCH_MUTEX_PRIORITY_INVERSION_PROTOCOL_DEFAULT

, and can be

changed at run-time using

cyg_mutex_set_protocol

.

Priority inversion problems can also occur with other synchronization primitives such as semaphores. For example
there could be a situation where a high-priority thread A is waiting on a semaphore, a low-priority thread C needs
to do just a little bit more work before posting the semaphore, but a medium priority thread B is running and
preventing C from making progress. However a semaphore does not have the concept of an owner, so there is no
way for the system to know that it is thread C which would next post to the semaphore. Hence there is no way for
the system to boost the priority of C automatically and prevent the priority inversion. Instead situations like this

67

Advertising