1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00
RIOT/sys/include/sema_inv.h
Benjamin Valentin 5602587dd7 sys/sema_inv: add inverse Semaphore
It is often desiderable to sync on multiple threads, e.g. there can be a controller
thread that waits for `n` worker threads to finish their job.
An inverse semaphore provides an easy primitive to implement this pattern.

After being initialized with a value `n` (in counter mode), a call to `sema_inv_wait()`
will block until each of the `n` threads has called `sema_inv_post()` exactly once.

There are situations where workers might post an event more than once
(unless additional state is introduced).
For this case, the alternative mask mode is provided.
Here the inverse semaphore is initialized with a bit mask, each worker can clear one
or multiple bits with `sema_inv_post_mask()`. A worker can clear it's bit multiple times.
2021-01-17 00:17:58 +01:00

152 lines
3.9 KiB
C

/*
* Copyright (C) 2021 ML!PA Consulting GmbH
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @defgroup sys_sema_inv inverse Semaphores
* @ingroup sys
* @brief Lightweight inverse semaphore implementation
* @{
*
* @file
* @brief Inverse Semaphore definitions
*
* Inverse Semaphores can be used to synchronize on multiple
* threads / objects.
*
* The inverse semaphore can be used eiher in counter or in
* mask mode.
*
* ### Counter Mode ###
*
* In this mode the inverse Semaphore is initialized with a
* counter variable `n`.
* After `n` calls to @ref sema_inv_post, the waiting thread
* is unblocked.
*
* ### Mask Mode ###
*
* In this mode the inverse Semaphore is initialized with a
* bit mask `n`.
* A call to @ref sema_inv_post_mask clears one or multiple bits.
* Clearing the same bit multiple times has no effect.
* The thread is unblocked if all bits have been cleared.
*
* @author Benjamin Valentin <benjamin.valentin@ml-pa.com>
*/
#ifndef SEMA_INV_H
#define SEMA_INV_H
#include "atomic_utils.h"
#include "mutex.h"
#ifdef MODULE_XTIMER
#include "xtimer.h"
#endif
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief An Inverse Semaphore.
*/
typedef struct {
uint32_t value; /**< value of the semaphore */
mutex_t lock; /**< mutex of the semaphore */
} sema_inv_t;
/**
* @brief Signal semaphore (counter mode).
*
* Decrements the semaphore counter by one.
* If the counter reaches zero, the waiting thread is woken.
*
* @param s an inverse semaphore
*
* @retval `true` the value of the semaphore has reached zero
* and the waiting thread has been woken
* @retval `false` the semaphore has not reached zero yet
*/
bool sema_inv_post(sema_inv_t *s);
/**
* @brief Signal semaphore (mask mode).
*
* Clears the bits specified by @p mask from the semaphore value.
* If the value reaches zero, the waiting thread is woken.
*
* @param s an inverse semaphore
* @param mask bit mask to clear from the semaphore value
*
* @retval `true` the value of the semaphore has reached zero
* and the waiting thread has been woken
* @retval `false` the semaphore has not reached zero yet
*/
bool sema_inv_post_mask(sema_inv_t *s, uint32_t mask);
/**
* @brief Initialize an inverse semaphore
*
* @param s an inverse semaphore
* @param value start value, either a counter or a bit mask
*/
static inline void sema_inv_init(sema_inv_t *s, uint32_t value)
{
const mutex_t locked = MUTEX_INIT_LOCKED;
s->lock = locked;
s->value = value;
}
/**
* @brief Wait for the inverse semaphore value to reach zero.
*
* @param s An inverse semaphore.
*/
static inline void sema_inv_wait(sema_inv_t *s)
{
mutex_lock(&s->lock);
}
/**
* @brief Check if the inverse semaphore value has reached zero.
*
* @param s An inverse semaphore.
*
* @return 1 if the semaphore value has reached zero
* 0 otherwise
*/
static inline int sema_inv_try_wait(sema_inv_t *s)
{
return mutex_trylock(&s->lock);
}
#if defined(MODULE_XTIMER) || DOXYGEN
/**
* @brief Wait for the inverse semaphore value to reach zero or
* a timeout being reached.
*
* @param s An inverse semaphore.
* @param us Time in microseconds until the semaphore times out.
*
* @return 0 if the semaphore value has reached zero
* -1 when the timeout occurred
*/
static inline int sema_inv_wait_timeout(sema_inv_t *s, uint32_t us)
{
return xtimer_mutex_lock_timeout(&s->lock, us);
}
#endif
#ifdef __cplusplus
}
#endif
#endif /* SEMA_INV_H */
/** @} */