Semaphores

A semaphore is a kernel object that implements a traditional counting semaphore.

Concepts

Any number of semaphores can be defined (limited only by available RAM). Each semaphore is referenced by its memory address.

A semaphore has the following key properties:

  • A count that indicates the number of times the semaphore can be taken. A count of zero indicates that the semaphore is unavailable.

  • A limit that indicates the maximum value the semaphore’s count can reach.

A semaphore must be initialized before it can be used. Its count must be set to a non-negative value that is less than or equal to its limit.

A semaphore may be given by a thread or an ISR. Giving the semaphore increments its count, unless the count is already equal to the limit.

A semaphore may be taken by a thread. Taking the semaphore decrements its count, unless the semaphore is unavailable (i.e. at zero). When a semaphore is unavailable a thread may choose to wait for it to be given. Any number of threads may wait on an unavailable semaphore simultaneously. When the semaphore is given, it is taken by the highest priority thread that has waited longest.

Note

The kernel does allow an ISR to take a semaphore, however the ISR must not attempt to wait if the semaphore is unavailable.

Implementation

Defining a Semaphore

A semaphore is defined using a variable of type k_sem. It must then be initialized by calling k_sem_init().

The following code defines a semaphore, then configures it as a binary semaphore by setting its count to 0 and its limit to 1.

struct k_sem my_sem;

k_sem_init(&my_sem, 0, 1);

Alternatively, a semaphore can be defined and initialized at compile time by calling K_SEM_DEFINE.

The following code has the same effect as the code segment above.

K_SEM_DEFINE(my_sem, 0, 1);

Giving a Semaphore

A semaphore is given by calling k_sem_give().

The following code builds on the example above, and gives the semaphore to indicate that a unit of data is available for processing by a consumer thread.

void input_data_interrupt_handler(void *arg)
{
    /* notify thread that data is available */
    k_sem_give(&my_sem);

    ...
}

Taking a Semaphore

A semaphore is taken by calling k_sem_take().

The following code builds on the example above, and waits up to 50 milliseconds for the semaphore to be given. A warning is issued if the semaphore is not obtained in time.

void consumer_thread(void)
{
    ...

    if (k_sem_take(&my_sem, K_MSEC(50)) != 0) {
        printk("Input data not available!");
    } else {
        /* fetch available data */
        ...
    }
    ...
}

Suggested Uses

Use a semaphore to control access to a set of resources by multiple threads.

Use a semaphore to synchronize processing between a producing and consuming threads or ISRs.

Configuration Options

Related configuration options:

  • None.

API Reference

group semaphore_apis

Defines

K_SEM_DEFINE(name, initial_count, count_limit)

Statically define and initialize a semaphore.

The semaphore can be accessed outside the module where it is defined using:

extern struct k_sem <name>; 

Parameters
  • name: Name of the semaphore.

  • initial_count: Initial semaphore count.

  • count_limit: Maximum permitted semaphore count.

Functions

int k_sem_init(struct k_sem *sem, unsigned int initial_count, unsigned int limit)

Initialize a semaphore.

This routine initializes a semaphore object, prior to its first use.

Parameters
  • sem: Address of the semaphore.

  • initial_count: Initial semaphore count.

  • limit: Maximum permitted semaphore count.

Return Value
  • 0: Semaphore created successfully

  • -EINVAL: Invalid values

int k_sem_take(struct k_sem *sem, k_timeout_t timeout)

Take a semaphore.

This routine takes sem.

Note

Can be called by ISRs, but timeout must be set to K_NO_WAIT.

Parameters
  • sem: Address of the semaphore.

  • timeout: Waiting period to take the semaphore, or one of the special values K_NO_WAIT and K_FOREVER.

Return Value
  • 0: Semaphore taken.

  • -EBUSY: Returned without waiting.

  • -EAGAIN: Waiting period timed out.

void k_sem_give(struct k_sem *sem)

Give a semaphore.

This routine gives sem, unless the semaphore is already at its maximum permitted count.

Note

Can be called by ISRs.

Return

N/A

Parameters
  • sem: Address of the semaphore.

void k_sem_reset(struct k_sem *sem)

Reset a semaphore’s count to zero.

This routine sets the count of sem to zero.

Return

N/A

Parameters
  • sem: Address of the semaphore.

unsigned int k_sem_count_get(struct k_sem *sem)

Get a semaphore’s count.

This routine returns the current count of sem.

Return

Current semaphore count.

Parameters
  • sem: Address of the semaphore.

User Mode Semaphore API Reference

The sys_sem exists in user memory working as counter semaphore for user mode thread when user mode enabled. When user mode isn’t enabled, sys_sem behaves like k_sem.

group user_semaphore_apis

Defines

SYS_SEM_DEFINE(_name, _initial_count, _count_limit)

Statically define and initialize a sys_sem.

The semaphore can be accessed outside the module where it is defined using:

extern struct sys_sem <name>; 

Route this to memory domains using K_APP_DMEM().

Parameters
  • _name: Name of the semaphore.

  • _initial_count: Initial semaphore count.

  • _count_limit: Maximum permitted semaphore count.

Functions

int sys_sem_init(struct sys_sem *sem, unsigned int initial_count, unsigned int limit)

Initialize a semaphore.

This routine initializes a semaphore instance, prior to its first use.

Parameters
  • sem: Address of the semaphore.

  • initial_count: Initial semaphore count.

  • limit: Maximum permitted semaphore count.

Return Value
  • 0: Initial success.

  • -EINVAL: Bad parameters, the value of limit should be located in (0, INT_MAX] and initial_count shouldn’t be greater than limit.

int sys_sem_give(struct sys_sem *sem)

Give a semaphore.

This routine gives sem, unless the semaphore is already at its maximum permitted count.

Parameters
  • sem: Address of the semaphore.

Return Value
  • 0: Semaphore given.

  • -EINVAL: Parameter address not recognized.

  • -EACCES: Caller does not have enough access.

  • -EAGAIN: Count reached Maximum permitted count and try again.

int sys_sem_take(struct sys_sem *sem, k_timeout_t timeout)

Take a sys_sem.

This routine takes sem.

Parameters
  • sem: Address of the sys_sem.

  • timeout: Waiting period to take the sys_sem, or one of the special values K_NO_WAIT and K_FOREVER.

Return Value
  • 0: sys_sem taken.

  • -EINVAL: Parameter address not recognized.

  • -ETIMEDOUT: Waiting period timed out.

  • -EACCES: Caller does not have enough access.

unsigned int sys_sem_count_get(struct sys_sem *sem)

Get sys_sem’s value.

This routine returns the current value of sem.

Return

Current value of sys_sem.

Parameters
  • sem: Address of the sys_sem.