1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00

sys/sema: re-implement without IPC

This commit is contained in:
Victor Arino 2016-11-22 18:21:40 +01:00
parent 93abe83ddb
commit 791b68a295
2 changed files with 142 additions and 199 deletions

View File

@ -1,5 +1,6 @@
/*
* Copyright (C) 2015 Martine Lenders <mlenders@inf.fu-berlin.de>
* Copyright (C) 2016 TriaGnoSys GmbH
* 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
@ -18,13 +19,15 @@
* @author Martine Lenders <mlenders@inf.fu-berlin.de>
* @author Christian Mehlis <mehlis@inf.fu-berlin.de>
* @author René Kijewski <kijewski@inf.fu-berlin.de>
* @author Víctor Ariño <victor.arino@zii.aero>
*/
#ifndef SEM_H_
#define SEM_H_
#include "msg.h"
#include "priority_queue.h"
#include "timex.h"
#ifndef SEMA_H
#define SEMA_H
#include <stdint.h>
#include "mutex.h"
#ifdef __cplusplus
extern "C" {
@ -33,103 +36,89 @@ extern "C" {
/**
* @brief Creates semaphore statically.
*
* @param[in] value Initial value for the semaphore.
* @param[in] value Initial value for the semaphore (can't be 0). For a 0
* initialized semaphore @see SEMA_CREATE_LOCKED
*
* @return Statically initialized semaphore.
*/
#define SEMA_CREATE(value) { (value), PRIORITY_QUEUE_INIT }
#define SEMA_CREATE(value) { (value), SEMA_OK, MUTEX_INIT }
/**
* @brief Creates semaphore statically initialized to 0
* @return Statically initialized semaphore.
*/
#define SEMA_CREATE_LOCKED() { (0), SEMA_OK, MUTEX_INIT_LOCKED }
/**
* @brief A Semaphore states.
*/
typedef enum {
SEMA_OK = 0,
SEMA_DESTROY,
} sema_state_t;
/**
* @brief A Semaphore.
*/
typedef struct {
volatile unsigned int value; /**< value of the semaphore */
priority_queue_t queue; /**< list of threads waiting for the semaphore */
unsigned int value; /**< value of the semaphore */
sema_state_t state; /**< state of the semaphore */
mutex_t mutex; /**< mutex of the semaphore */
} sema_t;
/**
* @brief Creates semaphore dynamically.
*
* @pre `(sema != NULL)`
*
* @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] sema The created semaphore.
* @param[in] value Initial value for the semaphore.
*
* @return 0 on success.
* @return -EINVAL, if semaphore is invalid.
*/
int sema_create(sema_t *sema, unsigned int value);
void sema_create(sema_t *sema, unsigned int value);
/**
* @brief Destroys a semaphore.
*
* @pre `(sema != NULL)`
*
* @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] sema The semaphore to destroy.
* Destroying a semaphore upon which other threads are currently blocked
* will wake the other threads causing the @ref sema_wait (or
* @ref sema_wait_timed) to return error (-ECANCELLED).
*
* @return 0 on success.
* @return -EINVAL, if semaphore is invalid.
* @param[in] sema The semaphore to destroy.
*/
int sema_destroy(sema_t *sema);
void sema_destroy(sema_t *sema);
/**
* @brief Wait for a semaphore being posted.
*
* @pre Message queue of active thread is initialized (see @ref msg_init_queue()).
*
* @param[in] sema A semaphore.
* @param[in] timeout Time in microseconds until the semaphore times out. 0 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 sema_wait_timed_msg(sema_t *sema, uint64_t timeout, msg_t *msg);
/**
* @brief Wait for a semaphore being posted (without timeout).
*
* @param[in] sema 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 sema_wait_msg(sema_t *sema, msg_t *msg)
{
return sema_wait_timed_msg(sema, 0, msg);
}
/**
* @brief Wait for a semaphore being posted (dropping spurious messages).
* @details Any spurious messages received while waiting for the semaphore are silently dropped.
* @pre `(sema != NULL)`
*
* @param[in] sema A semaphore.
* @param[in] timeout Time in microseconds until the semaphore times out. 0 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 sema_wait_timed(sema_t *sema, uint64_t timeout);
/**
* @brief Wait for a semaphore being posted (without timeout, dropping spurious messages).
* @brief Wait for a semaphore being posted (without timeout).
*
* @pre `(sema != NULL)`
*
* @param[in] sema A semaphore.
*
* @return 0 on success
* @return -EINVAL, if semaphore is invalid.
* @return -ECANCELED, if the semaphore was destroyed.
*/
static inline int sema_wait(sema_t *sema)
@ -137,13 +126,27 @@ static inline int sema_wait(sema_t *sema)
return sema_wait_timed(sema, 0);
}
/**
* @brief Test if the semaphore is posted
*
* @pre `(sema != NULL)`
*
* This is a non-blocking alternative to @ref sema_wait.
*
* @return 0 on success
* @return -EAGAIN, if the semaphore is not posted.
* @return -ECANCELED, if the semaphore was destroyed.
*/
int sema_try_wait(sema_t *sema);
/**
* @brief Signal semaphore.
*
* @pre `(sema != NULL)`
*
* @param[in] sema A semaphore.
*
* @return 0, on success
* @return -EINVAL, if semaphore is invalid.
* @return -EOVERFLOW, if the semaphore's value would overflow.
*/
int sema_post(sema_t *sema);
@ -152,5 +155,5 @@ int sema_post(sema_t *sema);
}
#endif
#endif /* SEM_H_ */
#endif /* SEMA_H */
/** @} */

View File

@ -1,5 +1,6 @@
/*
* Copyright (C) 2013-15 Freie Universität Berlin
* Copyright (C) 2016 TriaGnoSys GmbH
* 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
@ -11,169 +12,108 @@
*
* @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>
* @author Víctor Ariño <victor.arino@zii.aero>
*/
#include <assert.h>
#include <errno.h>
#include <limits.h>
#include "irq.h"
#include "msg.h"
#include "xtimer.h"
#include "assert.h"
#include "sema.h"
#include "xtimer.h"
#define ENABLE_DEBUG (0)
#include "debug.h"
#define MSG_SIGNAL (0x0501)
#define MSG_TIMEOUT (0x0502)
#define MSG_DESTROYED (0x0503)
int sema_create(sema_t *sema, unsigned int value)
void sema_create(sema_t *sema, unsigned int value)
{
if (sema == NULL) {
return -EINVAL;
}
assert(sema != NULL);
sema->value = value;
/* waiters for the mutex */
sema->queue.first = NULL;
sema->state = SEMA_OK;
mutex_init(&sema->mutex);
if (value == 0) {
mutex_lock(&sema->mutex);
}
}
void sema_destroy(sema_t *sema)
{
assert(sema != NULL);
sema->state = SEMA_DESTROY;
mutex_unlock(&sema->mutex);
}
int sema_try_wait(sema_t *sema)
{
assert(sema != NULL);
int ret = -ECANCELED;
if (sema->state == SEMA_OK) {
ret = -EAGAIN;
unsigned old = irq_disable();
if (sema->value > 0) {
--sema->value;
ret = 0;
}
irq_restore(old);
}
return ret;
}
int sema_wait_timed(sema_t *sema, uint64_t us)
{
assert(sema != NULL);
if (sema->state != SEMA_OK) {
return -ECANCELED;
}
unsigned old = irq_disable();
if (sema->value == 0) {
irq_restore(old);
int timeout = xtimer_mutex_lock_timeout(&sema->mutex, us);
if (sema->state != SEMA_OK) {
mutex_unlock(&sema->mutex);
return -ECANCELED;
}
if (timeout) {
return -ETIMEDOUT;
}
old = irq_disable();
}
unsigned int value = --sema->value;
irq_restore(old);
if (value > 0) {
mutex_unlock(&sema->mutex);
}
return 0;
}
int sema_destroy(sema_t *sema)
{
unsigned int old_state;
priority_queue_node_t *next;
if (sema == NULL) {
return -EINVAL;
}
old_state = irq_disable();
while ((next = priority_queue_remove_head(&sema->queue)) != NULL) {
msg_t msg;
kernel_pid_t pid = (kernel_pid_t)next->data;
msg.type = MSG_DESTROYED;
msg.content.ptr = sema;
msg_send_int(&msg, pid);
}
irq_restore(old_state);
return 0;
}
int sema_wait_timed_msg(sema_t *sema, uint64_t timeout, msg_t *msg)
{
unsigned old_state;
msg_t timeout_msg;
xtimer_t timeout_timer;
if (sema == NULL) {
return -EINVAL;
}
if (timeout != 0) {
old_state = irq_disable();
timeout_timer.target = 0, timeout_timer.long_target = 0;
timeout_msg.type = MSG_TIMEOUT;
timeout_msg.content.ptr = sema;
/* we will stay in the same stack context so we can use timeout_msg */
xtimer_set_msg64(&timeout_timer, timeout, &timeout_msg, sched_active_pid);
irq_restore(old_state);
}
while (1) {
priority_queue_node_t n;
unsigned value;
old_state = irq_disable();
value = sema->value;
if (value != 0) {
sema->value = value - 1;
irq_restore(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(&sema->queue, &n);
DEBUG("sema_wait: %" PRIkernel_pid ": Adding node to semaphore queue: prio: %" PRIu32 "\n",
sched_active_thread->pid, sched_active_thread->priority);
irq_restore(old_state);
msg_receive(msg);
old_state = irq_disable();
if (timeout != 0) {
xtimer_remove(&timeout_timer);
}
priority_queue_remove(&sema->queue, &n);
irq_restore(old_state);
if (msg->content.ptr != sema) {
return -EAGAIN;
}
switch (msg->type) {
case MSG_SIGNAL:
continue;
case MSG_TIMEOUT:
return -ETIMEDOUT;
case MSG_DESTROYED:
return -ECANCELED;
default:
return -EAGAIN;
}
}
}
int sema_wait_timed(sema_t *sema, uint64_t timeout)
{
int result;
do {
msg_t msg;
result = sema_wait_timed_msg(sema, timeout, &msg);
DEBUG("sema_wait: %" PRIkernel_pid ": Discarding message from %" PRIkernel_pid "\n",
sched_active_thread->pid, msg->sender_pid);
} while (result == -EAGAIN);
return result;
}
int sema_post(sema_t *sema)
{
unsigned int old_state, value;
priority_queue_node_t *next;
assert(sema != NULL);
if (sema == NULL) {
return -EINVAL;
}
old_state = irq_disable();
value = sema->value;
if (value == UINT_MAX) {
irq_restore(old_state);
unsigned old = irq_disable();
if (sema->value == UINT_MAX) {
irq_restore(old);
return -EOVERFLOW;
}
++sema->value;
next = priority_queue_remove_head(&sema->queue);
if (next) {
uint16_t prio = (uint16_t)next->priority;
kernel_pid_t pid = (kernel_pid_t) next->data;
msg_t msg;
DEBUG("sema_post: %" PRIkernel_pid ": waking up %" PRIkernel_pid "\n",
sched_active_thread->pid, next_process->pid);
msg.type = MSG_SIGNAL;
msg.content.ptr = sema;
msg_send_int(&msg, pid);
irq_restore(old_state);
sched_switch(prio);
}
else {
irq_restore(old_state);
unsigned value = sema->value++;
irq_restore(old);
if (value == 0) {
mutex_unlock(&sema->mutex);
}
return 0;
}
/** @} */