Browse Source

add pthread condition variable implementation

dev/timer
Martin 9 years ago
parent
commit
619039e0e2
  1. 3
      cpu/msp430-common/include/msp430_types.h
  2. 184
      sys/posix/pthread/include/pthread_cond.h
  3. 198
      sys/posix/pthread/pthread_cond.c
  4. 10
      tests/test_pthread_condition_variable/Makefile
  5. 86
      tests/test_pthread_condition_variable/main.c

3
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 */

184
sys/posix/pthread/include/pthread_cond.h

@ -1,54 +1,140 @@
/* Functions for handling conditional variables. */
typedef unsigned long int pthread_cond_t;
typedef unsigned long int pthread_condattr_t;
/* 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);
/* Destroy condition variable COND. */
int pthread_cond_destroy(pthread_cond_t *cond);
/* Wake up one thread waiting for condition variable COND. */
int pthread_cond_signal(pthread_cond_t *cond);
/* Wake up all threads waiting for condition variables COND. */
int pthread_cond_broadcast(pthread_cond_t *cond);
/* Wait for condition variable COND to be signaled or broadcast.
MUTEX is assumed to be locked before.
This function is a cancellation point and therefore not marked with. */
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
/* 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).
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. */
/*
* 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.
*/
/**
* @defgroup
* @brief
* @ingroup sys
* @{
*
* @file condition_variable.h
* @brief RIOT POSIX condition variable API
*
* @author Martin Landsmann <martin.landsmann@haw-hamburg.de>
*/
#ifndef _CONDITION_VARIABLE_H
#define _CONDITION_VARIABLE_H
#include <time.h>
#include "mutex.h"
#if defined(CPU_CC430) || defined(CPU_MSP430X16X)
# include "msp430_types.h"
#endif
/**
* @note condition attributes are currently NOT USED in RIOT condition variables
*/
typedef struct pthread_condattr_t {
int __dummy;
} pthread_condattr_t;
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;
/**
* @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);
/**
* @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);
/**
* @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);
/* Set the clock selected for the conditon variable attribute ATTR. */
/**
* @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);
/**
* @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 */

198
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 <martin.landsmann@haw-hamburg.de>
* @author René Kijewski <rene.kijewski@fu-berlin.de>
*
* @}
*/
#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;
}

10
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

86
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 <martin.landsmann@haw-hamburg.de>
*
* @}
*/
#include <stdio.h>
#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);
}
}
Loading…
Cancel
Save