From 3ad9284357cc8eced9471541d36f75d66a2de6a5 Mon Sep 17 00:00:00 2001 From: Martine Lenders Date: Mon, 3 Aug 2015 18:52:32 +0200 Subject: [PATCH] sem: initial import of a lightweight semaphore layer --- Makefile.dep | 4 ++ sys/Makefile | 3 + sys/include/sem.h | 145 ++++++++++++++++++++++++++++++++++++++ sys/sem/Makefile | 1 + sys/sem/sem.c | 173 ++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 326 insertions(+) create mode 100644 sys/include/sem.h create mode 100644 sys/sem/Makefile create mode 100644 sys/sem/sem.c diff --git a/Makefile.dep b/Makefile.dep index d2eb6eb02a..39bd45a516 100644 --- a/Makefile.dep +++ b/Makefile.dep @@ -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 diff --git a/sys/Makefile b/sys/Makefile index efb6384dad..78f3755432 100644 --- a/sys/Makefile +++ b/sys/Makefile @@ -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}))) diff --git a/sys/include/sem.h b/sys/include/sem.h new file mode 100644 index 0000000000..6d647e4dcd --- /dev/null +++ b/sys/include/sem.h @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2015 Martine Lenders + * + * 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 + * @author Christian Mehlis + * @author René Kijewski + */ +#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 + * The Open Group Base Specifications Issue 7, sem_init() + * (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 + * The Open Group Base Specifications Issue 7, sem_destroy() + * + * + * @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_ */ +/** @} */ diff --git a/sys/sem/Makefile b/sys/sem/Makefile new file mode 100644 index 0000000000..48422e909a --- /dev/null +++ b/sys/sem/Makefile @@ -0,0 +1 @@ +include $(RIOTBASE)/Makefile.base diff --git a/sys/sem/sem.c b/sys/sem/sem.c new file mode 100644 index 0000000000..a4256069b7 --- /dev/null +++ b/sys/sem/sem.c @@ -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 + * @author Martine Lenders + * @author René Kijewski + */ + +#include +#include +#include + +#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; +} + +/** @} */