CMSIS-RTOS2
Version 2.2.0
Real-Time Operating System: API and RTX Reference Implementation
|
Access shared resources simultaneously from different threads. More...
Data Structures | |
struct | osSemaphoreAttr_t |
Attributes structure for semaphore. More... | |
Typedefs | |
typedef void * | osSemaphoreId_t |
Functions | |
osSemaphoreId_t | osSemaphoreNew (uint32_t max_count, uint32_t initial_count, const osSemaphoreAttr_t *attr) |
Create and Initialize a Semaphore object. More... | |
const char * | osSemaphoreGetName (osSemaphoreId_t semaphore_id) |
Get name of a Semaphore object. More... | |
osStatus_t | osSemaphoreAcquire (osSemaphoreId_t semaphore_id, uint32_t timeout) |
Acquire a Semaphore token or timeout if no tokens are available. More... | |
osStatus_t | osSemaphoreRelease (osSemaphoreId_t semaphore_id) |
Release a Semaphore token up to the initial maximum count. More... | |
uint32_t | osSemaphoreGetCount (osSemaphoreId_t semaphore_id) |
Get current Semaphore token count. More... | |
osStatus_t | osSemaphoreDelete (osSemaphoreId_t semaphore_id) |
Delete a Semaphore object. More... | |
Semaphores are used to manage and protect access to shared resources. Semaphores are very similar to Mutexes. Whereas a Mutex permits just one thread to access a shared resource at a time, a semaphore can be used to permit a fixed number of threads/ISRs to access a pool of shared resources. Using semaphores, access to a group of identical peripherals can be managed (for example multiple DMA channels).
A semaphore object should be initialized to the maximum number of available tokens. This number of available resources is specified as parameter of the osSemaphoreNew function. Each time a semaphore token is obtained with osSemaphoreAcquire (in available state), the semaphore count is decremented. When the semaphore count is 0 (i.e. depleted state), no more semaphore tokens can be obtained. The thread/ISR that tries to obtain the semaphore token needs to wait until the next token is free. Semaphores are released with osSemaphoreRelease incrementing the semaphore count.
Due to their flexibility, semaphores cover a wide range of synchronizing applications. At the same time, they are perhaps the most challenging RTOS object to understand. The following explains a use case for semaphores, taken from the book The Little Book Of Semaphores by Allen B. Downey which is available for free download.
Non-binary Semaphore (Multiplex)
A multiplex limits the number of threads that can access a critical section of code. For example, this could be a function accessing DMA resources which can only support a limited number of calls.
To allow multiple threads to run the function, initialize a semaphore to the maximum number of threads that can be allowed. The number of tokens in the semaphore represents the number of additional threads that may enter. If this number is zero, then the next thread trying to access the function will have to wait until one of the other threads exits and releases its token. When all threads have exited the token number is back to n. The following example shows the code for one of the threads that might access the resource:
Producer/Consumer Semaphore
The producer-consumer problem can be solved using two semaphores.
A first semaphore (empty_id) counts down the available (empty) buffers, i.e. the producer thread can wait for available buffer slots by acquiring from this one.
A second semaphore (filled_id) counts up the used (filled) buffers, i.e. the consumer thread can wait for available data by acquiring from this one.
It is crucial for the correct behaviour that the threads acquire and release on both semaphores in the given sequence. According to this example one can have multiple producer and/or consumer threads running concurrently.
struct osSemaphoreAttr_t |
Specifies the following attributes for the osSemaphoreNew function.
Data Fields | ||
---|---|---|
const char * | name |
name of the semaphore Pointer to a constant string with a human readable name (displayed during debugging) of the semaphore object. Default: NULL no name specified. |
uint32_t | attr_bits |
attribute bits Reserved for future use (must be set to '0' for future compatibility). |
void * | cb_mem |
memory for control block Pointer to a memory for the semaphore control block object. Refer to Static Object Memory for more information. Default: NULL to use Automatic Dynamic Allocation for the semaphore control block. |
uint32_t | cb_size |
size of provided memory for control block The size (in bytes) of memory block passed with cb_mem. For RTX, the minimum value is defined with osRtxSemaphoreCbSize (higher values are permitted). Default: 0 as the default is no memory provided with cb_mem. |
osSemaphoreId_t osSemaphoreNew | ( | uint32_t | max_count, |
uint32_t | initial_count, | ||
const osSemaphoreAttr_t * | attr | ||
) |
[in] | max_count | maximum number of available tokens. |
[in] | initial_count | initial number of available tokens. |
[in] | attr | semaphore attributes; NULL: default values. |
The function osSemaphoreNew creates and initializes a semaphore object that is used to manage access to shared resources and returns the pointer to the semaphore object identifier or NULL in case of an error. It can be safely called before the RTOS is started (call to osKernelStart), but not before it is initialized (call to osKernelInitialize).
The parameter max_count specifies the maximum number of available tokens. A max_count value of 1 creates a binary semaphore.
The parameter initial_count sets the initial number of available tokens.
The parameter attr specifies additional semaphore attributes. Default attributes will be used if set to NULL.
Code Example
const char * osSemaphoreGetName | ( | osSemaphoreId_t | semaphore_id | ) |
[in] | semaphore_id | semaphore ID obtained by osSemaphoreNew. |
The function osSemaphoreGetName returns the pointer to the name string of the semaphore identified by parameter semaphore_id or NULL in case of an error.
osStatus_t osSemaphoreAcquire | ( | osSemaphoreId_t | semaphore_id, |
uint32_t | timeout | ||
) |
[in] | semaphore_id | semaphore ID obtained by osSemaphoreNew. |
[in] | timeout | Timeout Value or 0 in case of no time-out. |
The blocking function osSemaphoreAcquire waits until a token of the semaphore object specified by parameter semaphore_id becomes available. If a token is available, the function instantly returns and decrements the token count.
The parameter timeout specifies how long the system waits to acquire the token. While the system waits, the thread that is calling this function is put into the BLOCKED state. The parameter timeout can have the following values:
Possible osStatus_t return values:
Code Example
Refer to osSemaphoreNew.
osStatus_t osSemaphoreRelease | ( | osSemaphoreId_t | semaphore_id | ) |
[in] | semaphore_id | semaphore ID obtained by osSemaphoreNew. |
The function osSemaphoreRelease releases a token of the semaphore object specified by parameter semaphore_id. Tokens can only be released up to the maximum count specified at creation time, see osSemaphoreNew. Other threads that currently wait for a token of this semaphore object will be put into the READY state.
Possible osStatus_t return values:
Code Example
Refer to osSemaphoreNew.
uint32_t osSemaphoreGetCount | ( | osSemaphoreId_t | semaphore_id | ) |
[in] | semaphore_id | semaphore ID obtained by osSemaphoreNew. |
The function osSemaphoreGetCount returns the number of available tokens of the semaphore object specified by parameter semaphore_id. In case of an error it returns 0.
osStatus_t osSemaphoreDelete | ( | osSemaphoreId_t | semaphore_id | ) |
[in] | semaphore_id | semaphore ID obtained by osSemaphoreNew. |
The function osSemaphoreDelete deletes a semaphore object specified by parameter semaphore_id. It releases internal memory obtained for semaphore handling. After this call, the semaphore_id is no longer valid and cannot be used. The semaphore may be created again using the function osSemaphoreNew.
Possible osStatus_t return values: