mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
sem: initial import of a lightweight semaphore layer
This commit is contained in:
parent
6f264f46ad
commit
3ad9284357
@ -301,6 +301,10 @@ ifneq (,$(filter posix,$(USEMODULE)))
|
||||
USEMODULE += vtimer
|
||||
endif
|
||||
|
||||
ifneq (,$(filter sem,$(USEMODULE)))
|
||||
USEMODULE += vtimer
|
||||
endif
|
||||
|
||||
ifneq (,$(filter vtimer,$(USEMODULE)))
|
||||
USEMODULE += xtimer
|
||||
USEMODULE += timex
|
||||
|
@ -68,6 +68,9 @@ endif
|
||||
ifneq (,$(filter netopt,$(USEMODULE)))
|
||||
DIRS += net/crosslayer/netopt
|
||||
endif
|
||||
ifneq (,$(filter sem,$(USEMODULE)))
|
||||
DIRS += sem
|
||||
endif
|
||||
|
||||
DIRS += $(dir $(wildcard $(addsuffix /Makefile, ${USEMODULE})))
|
||||
|
||||
|
145
sys/include/sem.h
Normal file
145
sys/include/sem.h
Normal file
@ -0,0 +1,145 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Martine Lenders <mlenders@inf.fu-berlin.de>
|
||||
*
|
||||
* 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_sem Semaphores
|
||||
* @ingroup sys
|
||||
* @brief Lightweight semaphore implementation
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Semaphore definitions
|
||||
*
|
||||
* @author Martine Lenders <mlenders@inf.fu-berlin.de>
|
||||
* @author Christian Mehlis <mehlis@inf.fu-berlin.de>
|
||||
* @author René Kijewski <kijewski@inf.fu-berlin.de>
|
||||
*/
|
||||
#ifndef SEM_H_
|
||||
#define SEM_H_
|
||||
|
||||
#include "priority_queue.h"
|
||||
#include "timex.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief A Semaphore.
|
||||
*/
|
||||
typedef struct {
|
||||
volatile unsigned int value; /**< value of the semaphore */
|
||||
priority_queue_t queue; /**< list of threads waiting for the semaphore */
|
||||
} sem_t;
|
||||
|
||||
/**
|
||||
* @brief Creates semaphore.
|
||||
*
|
||||
* @see <a href="http://pubs.opengroup.org/onlinepubs/9699919799/functions/sem_init.html">
|
||||
* The Open Group Base Specifications Issue 7, sem_init()
|
||||
* </a> (without `pshared` parameter)
|
||||
*
|
||||
* @param[out] sem The created semaphore.
|
||||
* @param[in] value Initial value for the semaphore.
|
||||
*
|
||||
* @return 0 on success.
|
||||
* @return -EINVAL, if semaphore is invalid.
|
||||
*/
|
||||
int sem_create(sem_t *sem, unsigned int value);
|
||||
|
||||
/**
|
||||
* @brief Destroys a semaphore.
|
||||
*
|
||||
* @see <a href="http://pubs.opengroup.org/onlinepubs/9699919799/functions/sem_destroy.html">
|
||||
* The Open Group Base Specifications Issue 7, sem_destroy()
|
||||
* </a>
|
||||
*
|
||||
* @param[in] sem The semaphore to destroy.
|
||||
*
|
||||
* @return 0 on success.
|
||||
* @return -EINVAL, if semaphore is invalid.
|
||||
*/
|
||||
int sem_destroy(sem_t *sem);
|
||||
|
||||
/**
|
||||
* @brief Wait for a semaphore being posted.
|
||||
*
|
||||
* @pre Message queue of active thread is initialized (see @ref msg_init_queue()).
|
||||
*
|
||||
* @param[in] sem A semaphore.
|
||||
* @param[in] timeout Time until the semaphore times out. NULL for no timeout.
|
||||
* @param[out] msg Container for a spurious message during the timed wait (result == -EAGAIN).
|
||||
*
|
||||
* @return 0 on success
|
||||
* @return -EINVAL, if semaphore is invalid.
|
||||
* @return -ETIMEDOUT, if the semaphore times out.
|
||||
* @return -ECANCELED, if the semaphore was destroyed.
|
||||
* @return -EAGAIN, if the thread received a message while waiting for the lock.
|
||||
*/
|
||||
int sem_wait_timed_msg(sem_t *sem, timex_t *timeout, msg_t *msg);
|
||||
|
||||
/**
|
||||
* @brief Wait for a semaphore being posted (without timeout).
|
||||
*
|
||||
* @param[in] sem A semaphore.
|
||||
* @param[out] msg Container for a spurious message during the timed wait (result == -EAGAIN).
|
||||
*
|
||||
* @return 0 on success
|
||||
* @return -EINVAL, if semaphore is invalid.
|
||||
* @return -ECANCELED, if the semaphore was destroyed.
|
||||
* @return -EAGAIN, if the thread received a message while waiting for the lock.
|
||||
*/
|
||||
static inline int sem_wait_msg(sem_t *sem, msg_t *msg)
|
||||
{
|
||||
return sem_wait_timed_msg(sem, NULL, msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Wait for a semaphore being posted (dropping spurious messages).
|
||||
* @details Any spurious messages received while waiting for the semaphore are silently dropped.
|
||||
*
|
||||
* @param[in] sem A semaphore.
|
||||
* @param[in] timeout Time until the semaphore times out. NULL for no timeout.
|
||||
*
|
||||
* @return 0 on success
|
||||
* @return -EINVAL, if semaphore is invalid.
|
||||
* @return -ETIMEDOUT, if the semaphore times out.
|
||||
* @return -ECANCELED, if the semaphore was destroyed.
|
||||
*/
|
||||
int sem_wait_timed(sem_t *sem, timex_t *timeout);
|
||||
|
||||
/**
|
||||
* @brief Wait for a semaphore being posted (without timeout, dropping spurious messages).
|
||||
*
|
||||
* @param[in] sem A semaphore.
|
||||
*
|
||||
* @return 0 on success
|
||||
* @return -EINVAL, if semaphore is invalid.
|
||||
* @return -ECANCELED, if the semaphore was destroyed.
|
||||
*/
|
||||
static inline int sem_wait(sem_t *sem)
|
||||
{
|
||||
return sem_wait_timed(sem, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Signal semaphore.
|
||||
*
|
||||
* @param[in] sem A semaphore.
|
||||
*
|
||||
* @return -EINVAL, if semaphore is invalid.
|
||||
* @return -EOVERFLOW, if the semaphore's value would overflow.
|
||||
*/
|
||||
int sem_post(sem_t *sem);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* SEM_H_ */
|
||||
/** @} */
|
1
sys/sem/Makefile
Normal file
1
sys/sem/Makefile
Normal file
@ -0,0 +1 @@
|
||||
include $(RIOTBASE)/Makefile.base
|
173
sys/sem/sem.c
Normal file
173
sys/sem/sem.c
Normal file
@ -0,0 +1,173 @@
|
||||
/*
|
||||
* Copyright (C) 2013-15 Freie Universität Berlin
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
*
|
||||
* @author Christian Mehlis <mehlis@inf.fu-berlin.de>
|
||||
* @author Martine Lenders <mlenders@inf.fu-berlin.de>
|
||||
* @author René Kijewski <kijewski@inf.fu-berlin.de>
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include "irq.h"
|
||||
#include "msg.h"
|
||||
#include "vtimer.h"
|
||||
|
||||
#include "sem.h"
|
||||
|
||||
#define ENABLE_DEBUG (0)
|
||||
#include "debug.h"
|
||||
|
||||
#define MSG_SIGNAL (0x0501)
|
||||
#define MSG_TIMEOUT (0x0502)
|
||||
#define MSG_DESTROYED (0x0503)
|
||||
|
||||
int sem_create(sem_t *sem, unsigned int value)
|
||||
{
|
||||
if (sem == NULL) {
|
||||
#ifdef MODULE_POSIX_SEMAPHORE
|
||||
errno = EINVAL;
|
||||
#endif
|
||||
return -EINVAL;
|
||||
}
|
||||
sem->value = value;
|
||||
/* waiters for the mutex */
|
||||
sem->queue.first = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sem_destroy(sem_t *sem)
|
||||
{
|
||||
unsigned int old_state;
|
||||
priority_queue_node_t *next;
|
||||
if (sem == NULL) {
|
||||
#ifdef MODULE_POSIX_SEMAPHORE
|
||||
errno = EINVAL;
|
||||
#endif
|
||||
return -EINVAL;
|
||||
}
|
||||
old_state = disableIRQ();
|
||||
while ((next = priority_queue_remove_head(&sem->queue)) != NULL) {
|
||||
msg_t msg;
|
||||
kernel_pid_t pid = (kernel_pid_t)next->data;
|
||||
msg.type = MSG_DESTROYED;
|
||||
msg.content.ptr = (void *) sem;
|
||||
msg_send_int(&msg, pid);
|
||||
}
|
||||
restoreIRQ(old_state);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sem_wait_timed_msg(sem_t *sem, timex_t *timeout, msg_t *msg)
|
||||
{
|
||||
if (sem == NULL) {
|
||||
return -EINVAL;
|
||||
}
|
||||
while (1) {
|
||||
unsigned old_state = disableIRQ();
|
||||
priority_queue_node_t n;
|
||||
vtimer_t timeout_timer;
|
||||
|
||||
unsigned value = sem->value;
|
||||
if (value != 0) {
|
||||
sem->value = value - 1;
|
||||
restoreIRQ(old_state);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* I'm going blocked */
|
||||
n.priority = (uint32_t)sched_active_thread->priority;
|
||||
n.data = (unsigned int)sched_active_pid;
|
||||
n.next = NULL;
|
||||
priority_queue_add(&sem->queue, &n);
|
||||
|
||||
DEBUG("sem_wait: %" PRIkernel_pid ": Adding node to semaphore queue: prio: %" PRIu32 "\n",
|
||||
sched_active_thread->pid, sched_active_thread->priority);
|
||||
|
||||
if (timeout != NULL) {
|
||||
vtimer_set_msg(&timeout_timer, *timeout, sched_active_pid,
|
||||
MSG_TIMEOUT, sem);
|
||||
}
|
||||
|
||||
restoreIRQ(old_state);
|
||||
msg_receive(msg);
|
||||
|
||||
if (timeout != NULL) {
|
||||
vtimer_remove(&timeout_timer); /* remove timer just to be sure */
|
||||
}
|
||||
|
||||
if (msg->content.ptr != (void *) sem) {
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
switch (msg->type) {
|
||||
case MSG_SIGNAL:
|
||||
continue;
|
||||
case MSG_TIMEOUT:
|
||||
return -ETIMEDOUT;
|
||||
case MSG_DESTROYED:
|
||||
return -ECANCELED;
|
||||
default:
|
||||
return -EAGAIN;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int sem_wait_timed(sem_t *sem, timex_t *timeout)
|
||||
{
|
||||
int result;
|
||||
do {
|
||||
msg_t msg;
|
||||
result = sem_wait_timed_msg(sem, timeout, &msg);
|
||||
DEBUG("sem_wait: %" PRIkernel_pid ": Discarding message from %" PRIkernel_pid "\n",
|
||||
sched_active_thread->pid, msg->sender_pid);
|
||||
} while (result == -EAGAIN);
|
||||
return result;
|
||||
}
|
||||
|
||||
int sem_post(sem_t *sem)
|
||||
{
|
||||
unsigned int old_state, value;
|
||||
priority_queue_node_t *next;
|
||||
if (sem == NULL) {
|
||||
return -EINVAL;
|
||||
}
|
||||
old_state = disableIRQ();
|
||||
value = sem->value;
|
||||
if (value == UINT_MAX) {
|
||||
restoreIRQ(old_state);
|
||||
return -EOVERFLOW;
|
||||
}
|
||||
++sem->value;
|
||||
next = priority_queue_remove_head(&sem->queue);
|
||||
if (next) {
|
||||
uint16_t prio = (uint16_t)next->priority;
|
||||
kernel_pid_t pid = (kernel_pid_t) next->data;
|
||||
msg_t msg;
|
||||
DEBUG("sem_post: %" PRIkernel_pid ": waking up %" PRIkernel_pid "\n",
|
||||
sched_active_thread->pid, next_process->pid);
|
||||
msg.type = MSG_SIGNAL;
|
||||
msg.content.ptr = (void *) sem;
|
||||
msg_send_int(&msg, pid);
|
||||
restoreIRQ(old_state);
|
||||
sched_switch(prio);
|
||||
}
|
||||
else {
|
||||
restoreIRQ(old_state);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** @} */
|
Loading…
Reference in New Issue
Block a user