

5 changed files with 326 additions and 0 deletions
@ -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_ */ |
||||
/** @} */ |
@ -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