1
0
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:
Martine Lenders 2015-08-03 18:52:32 +02:00 committed by Martine Lenders
parent 6f264f46ad
commit 3ad9284357
5 changed files with 326 additions and 0 deletions

View File

@ -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

View File

@ -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
View 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
View File

@ -0,0 +1 @@
include $(RIOTBASE)/Makefile.base

173
sys/sem/sem.c Normal file
View 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;
}
/** @} */