From 3ad9284357cc8eced9471541d36f75d66a2de6a5 Mon Sep 17 00:00:00 2001
From: Martine Lenders <mlenders@inf.fu-berlin.de>
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 <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_ */
+/** @} */
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 <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;
+}
+
+/** @} */
-- 
GitLab