diff --git a/cpu/msp430-common/include/msp430_types.h b/cpu/msp430-common/include/msp430_types.h index c5e205c51d..e6ad5d9fe5 100644 --- a/cpu/msp430-common/include/msp430_types.h +++ b/cpu/msp430-common/include/msp430_types.h @@ -19,4 +19,7 @@ struct timeval { time_t tv_usec; }; +/* TODO: remove once msp430 libc supports clockid_t */ +typedef int clockid_t; + #endif /* MSP430_TYPES_H */ diff --git a/sys/posix/pthread/include/pthread_cond.h b/sys/posix/pthread/include/pthread_cond.h index 1fae48f628..a829d6e630 100644 --- a/sys/posix/pthread/include/pthread_cond.h +++ b/sys/posix/pthread/include/pthread_cond.h @@ -1,54 +1,140 @@ -/* Functions for handling conditional variables. */ +/* + * Copyright (C) 2014 Hamburg University of Applied Sciences (HAW) + * + * This file subject to the terms and conditions of the GNU Lesser General + * Public License. See the file LICENSE in the top level directory for more + * details. + */ -typedef unsigned long int pthread_cond_t; -typedef unsigned long int pthread_condattr_t; +/** + * @defgroup + * @brief + * @ingroup sys + * @{ + * + * @file condition_variable.h + * @brief RIOT POSIX condition variable API + * + * @author Martin Landsmann + */ -/* Initialize condition variable COND using attributes ATTR, or use - the default values if later is NULL. */ -int pthread_cond_init(pthread_cond_t *cond, - const pthread_condattr_t *cond_attr); +#ifndef _CONDITION_VARIABLE_H +#define _CONDITION_VARIABLE_H -/* Destroy condition variable COND. */ -int pthread_cond_destroy(pthread_cond_t *cond); +#include +#include "mutex.h" -/* Wake up one thread waiting for condition variable COND. */ -int pthread_cond_signal(pthread_cond_t *cond); +#if defined(CPU_CC430) || defined(CPU_MSP430X16X) +# include "msp430_types.h" +#endif -/* Wake up all threads waiting for condition variables COND. */ -int pthread_cond_broadcast(pthread_cond_t *cond); +/** + * @note condition attributes are currently NOT USED in RIOT condition variables + */ +typedef struct pthread_condattr_t { + int __dummy; +} pthread_condattr_t; -/* Wait for condition variable COND to be signaled or broadcast. - MUTEX is assumed to be locked before. +typedef struct pthread_cond_t { + /* fields are managed by cv functions, don't touch */ + queue_node_t queue; /**< Threads currently waiting to be signaled. */ +} pthread_cond_t; - This function is a cancellation point and therefore not marked with. */ -int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex); +/** + * @brief Initializes a condition attribute variable object using default values + * @param[in, out] attr pre-allocated condition attribute variable structure. + * @return returns 0 on success, an errorcode otherwise. + */ +int pthread_cond_condattr_init(struct pthread_condattr_t *attr); -/* Wait for condition variable COND to be signaled or broadcast until - ABSTIME. MUTEX is assumed to be locked before. ABSTIME is an - absolute time specification; zero is the beginning of the epoch - (00:00:00 GMT, January 1, 1970). +/** + * @brief Uninitializes a condition attribute variable object + * @param[in, out] attr pre-allocated condition attribute variable structure. + * @return returns 0 on success, an errorcode otherwise. + */ +int pthread_cond_condattr_destroy(struct pthread_condattr_t *attr); - This function is a cancellation point and therefore not marked with. */ -int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, - const struct timespec *abstime); - -/* Functions for handling condition variable attributes. */ - -/* Initialize condition variable attribute ATTR. */ -int pthread_condattr_init(pthread_condattr_t *attr); - -/* Destroy condition variable attribute ATTR. */ -int pthread_condattr_destroy(pthread_condattr_t *attr); - -/* Get the process-shared flag of the condition variable attribute ATTR. */ +/** + * @brief Get the process-shared attribute in an initialised attributes object referenced by attr + * @note NOT USED since RIOT is a single process OS + * @param[in] attr pre-allocated condition attribute variable structure. + * @param[out] pshared the pre-allocated process-shared variable. + * @return returns 0 on success, an errorcode otherwise. + */ int pthread_condattr_getpshared(const pthread_condattr_t *attr, int *pshared); -/* Set the process-shared flag of the condition variable attribute ATTR. */ +/** + * @brief Set the process-shared attribute in an initialised attributes object referenced by attr + * @note NOT USED since RIOT is a single process OS + * @param[in, out] attr pre-allocated condition attribute variable structure. + * @param[in] pshared pshared the pre-allocated process-shared variable. + * @return returns 0 on success, an errorcode otherwise. + */ int pthread_condattr_setpshared(pthread_condattr_t *attr, int pshared); -/* Get the clock selected for the conditon variable attribute ATTR. */ -int pthread_condattr_getclock(const pthread_condattr_t *attr, - clockid_t *clock_id); +/** + * @brief Get the clock selected for the conditon variable attribute attr. + * @note currently NOT USED in RIOT. + * @param[in] attr pre-allocated condition attribute variable structure. + * @param[out] clock_id the clock ID that is used to measure the timeout service + * @return returns 0 on success, an errorcode otherwise. + */ +int pthread_condattr_getclock(const pthread_condattr_t *attr, clockid_t *clock_id); -/* Set the clock selected for the conditon variable attribute ATTR. */ +/** + * @brief Set the clock selected for the conditon variable attribute ATTR. + * @note currently NOT USED in RIOT. + * @param[in, out] attr pre-allocated condition attribute variable structure. + * @param[in] clock_id the clock ID that shall be used to measure the timeout service + * @return returns 0 on success, an errorcode otherwise. + */ int pthread_condattr_setclock(pthread_condattr_t *attr, clockid_t clock_id); + +/** + * @brief Initializes a condition variable object + * @param[in, out] cond pre-allocated condition variable structure. + * @param[in] attr pre-allocated condition attribute variable structure. + * @return returns 0 on success, an errorcode otherwise. + */ +int pthread_cond_init(struct pthread_cond_t *cond, struct pthread_condattr_t *attr); + +/** + * @brief Destroy the condition variable cond + * @param[in, out] cond pre-allocated condition variable structure. + * @return returns 0 on success, an errorcode otherwise. + */ +int pthread_cond_destroy(struct pthread_cond_t *cond); + +/** + * @brief blocks the calling thread until the specified condition cond is signalled + * @param[in, out] cond pre-allocated condition variable structure. + * @param[in, out] mutex pre-allocated mutex variable structure. + * @return returns 0 on success, an errorcode otherwise. + */ +int pthread_cond_wait(struct pthread_cond_t *cond, struct mutex_t *mutex); + +/** + * @brief blocks the calling thread until the specified condition cond is signalled + * @param[in, out] cond pre-allocated condition variable structure. + * @param[in, out] mutex pre-allocated mutex variable structure. + * @param[in] abstime pre-allocated timeout. + * @return returns 0 on success, an errorcode otherwise. + */ +int pthread_cond_timedwait(struct pthread_cond_t *cond, struct mutex_t *mutex, const struct timespec *abstime); + +/** + * @brief unblock at least one of the threads that are blocked on the specified condition variable cond + * @param[in, out] cond pre-allocated condition variable structure. + * @return returns 0 on success, an errorcode otherwise. + */ +int pthread_cond_signal(struct pthread_cond_t *cond); + +/** + * @brief unblock all threads that are currently blocked on the specified condition variable cond + * @param[in, out] cond pre-allocated condition variable structure. + * @return returns 0 on success, an errorcode otherwise. + */ +int pthread_cond_broadcast(struct pthread_cond_t *cond); + +/** @} */ +#endif /* _CONDITION_VARIABLE_H */ diff --git a/sys/posix/pthread/pthread_cond.c b/sys/posix/pthread/pthread_cond.c new file mode 100644 index 0000000000..b27ff1d88b --- /dev/null +++ b/sys/posix/pthread/pthread_cond.c @@ -0,0 +1,198 @@ +/* + * Copyright (C) 2014 Hamburg University of Applied Sciences (HAW) + * + * This file is subject to the terms and conditions of the GNU Lesser General + * Public License. See the file LICENSE in the top level directory for more + * details. + */ + +/** + * @ingroup sys + * @{ + * + * @file pthread_cond.c + * @brief Condition variable implementation + * + * @author Martin Landsmann + * @author René Kijewski + * + * @} + */ + +#include "pthread_cond.h" +#include "thread.h" +#include "vtimer.h" +#include "sched.h" +#include "irq.h" +#include "debug.h" + +struct vtimer_t timer; + +int pthread_cond_condattr_destroy(struct pthread_condattr_t *attr) +{ + if (attr != NULL) { + DEBUG("pthread_cond_condattr_destroy: currently attributes are not supported.\n"); + } + + return 0; +} + +int pthread_cond_condattr_init(struct pthread_condattr_t *attr) +{ + if (attr != NULL) { + DEBUG("pthread_cond_condattr_init: currently attributes are not supported.\n"); + } + + return 0; +} + +int pthread_condattr_getpshared(const pthread_condattr_t *attr, int *pshared) +{ + (void)attr; + (void)pshared; + + return 0; +} + +int pthread_condattr_setpshared(pthread_condattr_t *attr, int pshared) +{ + (void)attr; + (void)pshared; + + return 0; +} + +int pthread_condattr_getclock(const pthread_condattr_t *attr, clockid_t *clock_id) +{ + (void)attr; + (void)clock_id; + + return 0; +} + +int pthread_condattr_setclock(pthread_condattr_t *attr, clockid_t clock_id) +{ + (void)attr; + (void)clock_id; + + return 0; +} + +int pthread_cond_init(struct pthread_cond_t *cond, struct pthread_condattr_t *attr) +{ + if (attr != NULL) { + DEBUG("pthread_cond_init: currently attributes are not supported.\n"); + } + + cond->queue.priority = 0; + cond->queue.data = 0; + cond->queue.next = NULL; + return 0; +} + +int pthread_cond_destroy(struct pthread_cond_t *cond) +{ + pthread_cond_init(cond, NULL); + return 0; +} + +int pthread_cond_wait(struct pthread_cond_t *cond, struct mutex_t *mutex) +{ + queue_node_t n; + n.priority = active_thread->priority; + n.data = active_thread->pid; + n.next = NULL; + + /* the signaling thread may not hold the mutex, the queue is not thread safe */ + unsigned old_state = disableIRQ(); + queue_priority_add(&(cond->queue), &n); + restoreIRQ(old_state); + + mutex_unlock_and_sleep(mutex); + + if (n.data != -1u) { + /* on signaling n.data is set to -1u */ + /* if it isn't set, then the wakeup is either spurious or a timer wakeup */ + old_state = disableIRQ(); + queue_remove(&(cond->queue), &n); + restoreIRQ(old_state); + } + + mutex_lock(mutex); + return 0; +} + +int pthread_cond_timedwait(struct pthread_cond_t *cond, struct mutex_t *mutex, const struct timespec *abstime) +{ + timex_t now, then, reltime; + + vtimer_now(&now); + then.seconds = abstime->tv_sec; + then.microseconds = abstime->tv_nsec / 1000u; + reltime = timex_sub(then, now); + + vtimer_t timer; + vtimer_set_wakeup(&timer, reltime, active_thread->pid); + int result = pthread_cond_wait(cond, mutex); + vtimer_remove(&timer); + + return result; +} + +int pthread_cond_signal(struct pthread_cond_t *cond) +{ + unsigned old_state = disableIRQ(); + + queue_node_t *head = queue_remove_head(&(cond->queue)); + int other_prio = -1; + if (head != NULL) { + tcb_t *other_thread = (tcb_t *) sched_threads[head->data]; + if (other_thread) { + other_prio = other_thread->priority; + sched_set_status(other_thread, STATUS_PENDING); + } + head->data = -1u; + } + + restoreIRQ(old_state); + + if (other_prio >= 0) { + sched_switch(active_thread->priority, other_prio); + } + + return 0; +} + +static int max_prio(int a, int b) +{ + return (a < 0) ? b : ((a < b) ? a : b); +} + +int pthread_cond_broadcast(struct pthread_cond_t *cond) +{ + unsigned old_state = disableIRQ(); + + int other_prio = -1; + + while (1) { + queue_node_t *head = queue_remove_head(&(cond->queue)); + if (head == NULL) { + break; + } + + tcb_t *other_thread = (tcb_t *) sched_threads[head->data]; + if (other_thread) { + other_prio = max_prio(other_prio, other_thread->priority); + sched_set_status(other_thread, STATUS_PENDING); + } + head->data = -1u; + } + + restoreIRQ(old_state); + + if (other_prio >= 0) { + sched_switch(active_thread->priority, other_prio); + } + + return 0; +} diff --git a/tests/test_pthread_condition_variable/Makefile b/tests/test_pthread_condition_variable/Makefile new file mode 100644 index 0000000000..941edc9950 --- /dev/null +++ b/tests/test_pthread_condition_variable/Makefile @@ -0,0 +1,10 @@ +export PROJECT = test_condition_variable +include ../Makefile.tests_common + +USEMODULE += posix +USEMODULE += pthread +USEMODULE += vtimer + +CFLAGS += -DNATIVE_AUTO_EXIT + +include $(RIOTBASE)/Makefile.include diff --git a/tests/test_pthread_condition_variable/main.c b/tests/test_pthread_condition_variable/main.c new file mode 100644 index 0000000000..c77f440d78 --- /dev/null +++ b/tests/test_pthread_condition_variable/main.c @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2014 Hamburg University of Applied Sciences (HAW) + * + * This file is subject to the terms and conditions of the GNU Lesser General + * Public License. See the file LICENSE in the top level directory for more + * details. + */ + +/** + * @ingroup tests + * @{ + * + * @file + * @brief simple condition variable test application + * + * @author Martin Landsmann + * + * @} + */ + +#include +#include "pthread_cond.h" +#include "thread.h" +#include "mutex.h" + +static mutex_t mutex; +static struct pthread_cond_t cv; +static volatile int is_finished; +static volatile long count; +static volatile long expected_value; +static char stack[KERNEL_CONF_STACKSIZE_MAIN]; + +/** + * @brief This thread tries to lock the mutex to enter the critical section. + * Then it signals one waiting thread to check the condition and it goes to sleep again + * If is_finished is set to 1 second_thread ends + */ +static void second_thread(void) +{ + while (1) { + mutex_lock(&mutex); + + if (is_finished == 1) { + break; + } + + pthread_cond_signal(&cv); + mutex_unlock_and_sleep(&mutex); + } +} + +int main(void) +{ + count = 0; + is_finished = 0; + expected_value = 1000*1000; + mutex_init(&mutex); + pthread_cond_init(&cv, NULL); + + int pid = thread_create(stack, + KERNEL_CONF_STACKSIZE_MAIN, + PRIORITY_MAIN - 1, + CREATE_WOUT_YIELD | CREATE_STACKTEST, + second_thread, + "second_thread"); + + while (1) { + mutex_lock(&mutex); + thread_wakeup(pid); + count++; + + if ((count % 100000) == 0) { + printf("Still alive alternated [count: %ldk] times.\n", count / 1000); + } + + if (count == expected_value) { + puts("condition fulfilled."); + is_finished = 1; + mutex_unlock(&mutex); + return 0; + } + + pthread_cond_wait(&cv, &mutex); + mutex_unlock(&mutex); + } +}