diff --git a/sys/posix/pthread/include/pthread.h b/sys/posix/pthread/include/pthread.h index acd5e8e306..d0949e417f 100644 --- a/sys/posix/pthread/include/pthread.h +++ b/sys/posix/pthread/include/pthread.h @@ -15,6 +15,7 @@ #include "pthread_threading.h" #include "pthread_mutex_attr.h" #include "pthread_mutex.h" +#include "pthread_rwlock_attr.h" #include "pthread_rwlock.h" #include "pthread_spin.h" #include "pthread_barrier.h" diff --git a/sys/posix/pthread/include/pthread_rwlock.h b/sys/posix/pthread/include/pthread_rwlock.h index dd631575a9..b5b5b77219 100644 --- a/sys/posix/pthread/include/pthread_rwlock.h +++ b/sys/posix/pthread/include/pthread_rwlock.h @@ -2,57 +2,154 @@ * @ingroup pthread */ -typedef unsigned long int pthread_rwlock_t; -typedef unsigned long int pthread_rwlockattr_t; +#include "queue.h" +#include "tcb.h" -/* Initialize read-write lock RWLOCK using attributes ATTR, or use - the default values if later is NULL. */ -int pthread_rwlock_init(pthread_rwlock_t *rwlock, - const pthread_rwlockattr_t *attr); +#include +#include -/* Destroy read-write lock RWLOCK. */ +/** + * @brief A fair reader writer lock. + * @details The implementation ensures that readers and writers of the same priority + * won't starve each other. + * E.g. no new readers will get into the critical section + * if a writer of the same or a higher priority already waits for the lock. + */ +typedef struct pthread_rwlock +{ + /** + * @brief The current amount of reader inside the critical section. + * @details + * * `== 0`: no thread is in the critical section. + * * `> 0`: the number of readers currently in the critical section. + * * `< 0`: a writer is currently in the critical section. + */ + int readers; + + /** + * @brief Queue of waiting threads. + */ + queue_node_t queue; + + /** + * @brief Provides mutual exclusion on reading and writing on the structure. + */ + mutex_t mutex; +} pthread_rwlock_t; + +/** + * @brief Internal structure that stores one waiting thread. + */ +typedef struct __pthread_rwlock_waiter_node +{ + bool is_writer; /**< `false`: reader; `true`: writer */ + tcb_t *thread; /**< waiting thread */ + queue_node_t qnode; /**< Node to store in `pthread_rwlock_t::queue`. */ + bool continue_; /**< This is not a spurious wakeup. */ +} __pthread_rwlock_waiter_node_t; + +/** + * @brief Initialize a reader/writer lock. + * @details A zeroed out datum is initialized. + * @param[in,out] rwlock Lock to initialize. + * @param[in] attr Unused. + * @returns `0` on success. + * `EINVAL` if `rwlock == NULL`. + */ +int pthread_rwlock_init(pthread_rwlock_t *rwlock, const pthread_rwlockattr_t *attr); + +/** + * @brief Destroy a reader/writer lock. + * @details This is a no-op. + * Destroying a reader/writer lock while a thread holds it, or + * there are threads waiting for it causes undefined behavior. + * Datum must be reinitialized before using it again. + * @param[in] rwlock Lock to destroy. + * @returns `0` on success. + * `EINVAL` if `rwlock == NULL`. + * `EBUSY` if the lock was in use. + */ int pthread_rwlock_destroy(pthread_rwlock_t *rwlock); -/* Acquire read lock for RWLOCK. */ +/** + * @brief Lock a reader/writer lock for reading. + * @details This function may block. + * @param[in] rwlock Lock to acquire for reading. + * @returns `0` if the lock could be acquired. + * `EINVAL` if `rwlock == NULL`. + */ int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock); -/* Try to acquire read lock for RWLOCK. */ +/** + * @brief Try to lock a reader/writer lock for reader. + * @details This function won't block. + * @param[in] rwlock Lock to acquire for reading. + * @returns `0` if the lock could be acquired. + * `EBUSY` if acquiring the lock cannot be done without blocking. + * `EINVAL` if `rwlock == NULL`. + */ int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock); -/* Try to acquire read lock for RWLOCK or return after specfied time. */ -int pthread_rwlock_timedrdlock(pthread_rwlock_t *rwlock, - const struct timespec *abstime); +/** + * @brief Try to acquire a read lock in a given timeframe + * @param[in] rwlock Lock to acquire for reading. + * @param[in] abstime Maximum timestamp when to wakeup, absolute. + * @returns `0` if the lock could be acquired. + * `EDEADLK` if the lock could not be acquired in the given timeframe. + * `EINVAL` if `rwlock == NULL`. + */ +int pthread_rwlock_timedrdlock(pthread_rwlock_t *rwlock, const struct timespec *abstime); -/* Acquire write lock for RWLOCK. */ +/** + * @brief Lock a reader/writer lock for writing. + * @details This function may block. + * @param[in] rwlock Lock to acquire for writing. + * @returns `0` if the lock could be acquired. + * `EINVAL` if `rwlock == NULL`. + */ int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock); -/* Try to acquire write lock for RWLOCK. */ +/** + * @brief Try to lock a reader/writer lock for writing. + * @details This function won't block. + * @param[in] rwlock Lock to acquire for writing. + * @returns `0` if the lock could be acquired. + * `EBUSY` if acquiring the lock cannot be done without blocking. + * `EINVAL` if `rwlock == NULL`. + */ int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock); -/* Try to acquire write lock for RWLOCK or return after specfied time. */ -int pthread_rwlock_timedwrlock(pthread_rwlock_t *rwlock, - const struct timespec *abstime); +/** + * @brief Try to acquire a write lock in a given timeframe + * @param[in] rwlock Lock to acquire for writing. + * @param[in] abstime Maximum timestamp when to wakeup, absolute. + * @returns `0` if the lock could be acquired. + * `EDEADLK` if the lock could not be acquired in the given timeframe. + * `EINVAL` if `rwlock == NULL`. + */ +int pthread_rwlock_timedwrlock(pthread_rwlock_t *rwlock, const struct timespec *abstime); -/* Unlock RWLOCK. */ +/** + * @brief Unlock the reader/writer lock. + * @details Must only be used if the lock is currently held. + * You may release the lock out of another thread, but the *lock*, *operate*, *unlock* logic must not be broken. + * @param[in] rwlock Lock to release + * @returns `0` on success. + * `EPERM` if the lock was not held for any operation. + * `EINVAL` if `rwlock == NULL`. + */ int pthread_rwlock_unlock(pthread_rwlock_t *rwlock); -/* Functions for handling read-write lock attributes. */ +/** + * @brief Internal function to determine of the lock can be acquired for reading. + * @param[in] rwlock Lock to query. + * @returns `false` if locking for reading is possible without blocking. + */ +bool __pthread_rwlock_blocked_readingly(const pthread_rwlock_t *rwlock); -/* Initialize attribute object ATTR with default values. */ -int pthread_rwlockattr_init(pthread_rwlockattr_t *attr); - -/* Destroy attribute object ATTR. */ -int pthread_rwlockattr_destroy(pthread_rwlockattr_t *attr); - -/* Return current setting of process-shared attribute of ATTR in PSHARED. */ -int pthread_rwlockattr_getpshared(const pthread_rwlockattr_t *attr, - int *pshared); - -/* Set process-shared attribute of ATTR to PSHARED. */ -int pthread_rwlockattr_setpshared(pthread_rwlockattr_t *attr, int pshared); - -/* Return current setting of reader/writer preference. */ -int pthread_rwlockattr_getkind_np(const pthread_rwlockattr_t *attr, int *pref); - -/* Set reader/write preference. */ -int pthread_rwlockattr_setkind_np(pthread_rwlockattr_t *attr, int pref); +/** + * @brief Internal function to determine of the lock can be acquired for writing. + * @param[in] rwlock Lock to query. + * @returns `false` if locking for writing is possible without blocking. + */ +bool __pthread_rwlock_blocked_writingly(const pthread_rwlock_t *rwlock); diff --git a/sys/posix/pthread/include/pthread_rwlock_attr.h b/sys/posix/pthread/include/pthread_rwlock_attr.h new file mode 100644 index 0000000000..3e895b6c89 --- /dev/null +++ b/sys/posix/pthread/include/pthread_rwlock_attr.h @@ -0,0 +1,58 @@ +/** + * @ingroup pthread + */ + +#include + +/** + * @brief Attributes for a new reader/writer lock. + * @details The options set in this struct will be ignored by pthread_rwlock_init(). + */ +typedef struct pthread_rwlockattr +{ + /** + * @brief Whether to share lock with child processes. + * @details Valid values are `PTHREAD_PROCESS_SHARED` and `PTHREAD_PROCESS_PRIVATE`. + * Since RIOT is a single-process operating system, this value is ignored. + */ + int pshared; +} pthread_rwlockattr_t; + +/** + * @brief Initilize the attribute set with the defaults. + * @details Default value for pshared: `PTHREAD_PROCESS_PRIVATE`. + * A zeroed out datum is initialized. + * @param[in,out] attr Attribute set to initialize. + * @returns `0` on success. + * `EINVAL` if `attr == NULL`. + */ +int pthread_rwlockattr_init(pthread_rwlockattr_t *attr); + +/** + * @brief Destroy an attribute set. + * @details This function does nothing, don't bother calling it. + * @param[in,out] attr Attribute set to destroy. + * @returns `0` on success. + * `EINVAL` if `attr == NULL`. + */ +int pthread_rwlockattr_destroy(pthread_rwlockattr_t *attr); + +/** + * @brief Read whether to share the lock with child processes. + * @details There are not child processes in RIOT. + * @param[in] attr Attribute set to query. + * @param[out] pshared Either `PTHREAD_PROCESS_SHARED` or `PTHREAD_PROCESS_PRIVATE`. + * @returns `0` on success. + * `EINVAL` if `attr == NULL`. + */ +int pthread_rwlockattr_getpshared(const pthread_rwlockattr_t *attr, int *pshared); + +/** + * @brief Set whether to share the lock with child processes. + * @details There are not child processes in RIOT. + * @param[in,out] attr Attribute set to operate on. + * @param[in] pshared Either `PTHREAD_PROCESS_SHARED` or `PTHREAD_PROCESS_PRIVATE`. + * @returns `0` on success. + * `EINVAL` if `attr == NULL` or a wrong value for `pshared` was supplied. + */ +int pthread_rwlockattr_setpshared(pthread_rwlockattr_t *attr, int pshared); diff --git a/sys/posix/pthread/pthread_rwlock.c b/sys/posix/pthread/pthread_rwlock.c new file mode 100644 index 0000000000..f7033327c8 --- /dev/null +++ b/sys/posix/pthread/pthread_rwlock.c @@ -0,0 +1,320 @@ +/* + * Copyright (C) 2014 René Kijewski + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @ingroup pthread + * @{ + * + * @file + * @brief Implementation of a fair, POSIX conforming reader/writer lock. + * + * @author René Kijewski + * + * @} + */ + +#include "pthread.h" +#include "sched.h" +#include "vtimer.h" + +#include +#include + +#define ENABLE_DEBUG (0) +#include "debug.h" + +int pthread_rwlock_init(pthread_rwlock_t *rwlock, const pthread_rwlockattr_t *attr) +{ + (void) attr; + + if (rwlock == NULL) { + DEBUG("Thread %u: pthread_rwlock_%s(): rwlock=NULL supplied\n", thread_pid, "init"); + return EINVAL; + } + + memset(rwlock, 0, sizeof (*rwlock)); + return 0; +} + +int pthread_rwlock_destroy(pthread_rwlock_t *rwlock) +{ + if (rwlock == NULL) { + DEBUG("Thread %u: pthread_rwlock_%s(): rwlock=NULL supplied\n", thread_pid, "destroy"); + return EINVAL; + } + + /* do not unlock the mutex, no need */ + if ((mutex_trylock(&rwlock->mutex) == 0) || (rwlock->readers != 0)) { + return EBUSY; + } + + return 0; +} + +bool __pthread_rwlock_blocked_readingly(const pthread_rwlock_t *rwlock) +{ + if (rwlock->readers < 0) { + /* a writer holds the lock */ + return true; + } + + /* Determine if there is a writer waiting to get this lock who has a higher or the same priority: */ + + if (rwlock->queue.next == NULL) { + /* no waiting thread */ + return false; + } + + queue_node_t *qnode = rwlock->queue.next; + if (qnode->priority > active_thread->priority) { + /* the waiting thread has a lower priority */ + return false; + } + + /* if the waiting node is a writer, then we cannot enter the critical section (to prevent starving the writer) */ + __pthread_rwlock_waiter_node_t *waiting_node = (__pthread_rwlock_waiter_node_t *) qnode->data; + return waiting_node->is_writer; +} + +bool __pthread_rwlock_blocked_writingly(const pthread_rwlock_t *rwlock) +{ + /* if any thread holds the lock, then no writer may enter the critical section */ + return rwlock->readers != 0; +} + +static int pthread_rwlock_lock(pthread_rwlock_t *rwlock, + bool (*is_blocked)(const pthread_rwlock_t *rwlock), + bool is_writer, + int incr_when_held, + bool allow_spurious) +{ + if (rwlock == NULL) { + DEBUG("Thread %u: pthread_rwlock_%s(): is_writer=%u, allow_spurious=%u %s\n", + thread_pid, "lock", is_writer, allow_spurious, "rwlock=NULL"); + return EINVAL; + } + + mutex_lock(&rwlock->mutex); + if (!is_blocked(rwlock)) { + DEBUG("Thread %u: pthread_rwlock_%s(): is_writer=%u, allow_spurious=%u %s\n", + thread_pid, "lock", is_writer, allow_spurious, "is open"); + rwlock->readers += incr_when_held; + } + else { + DEBUG("Thread %u: pthread_rwlock_%s(): is_writer=%u, allow_spurious=%u %s\n", + thread_pid, "lock", is_writer, allow_spurious, "is locked"); + + /* queue for the lock */ + __pthread_rwlock_waiter_node_t waiting_node = { + .is_writer = is_writer, + .thread = (tcb_t *) active_thread, + .qnode = { + .next = NULL, + .data = (uintptr_t) &waiting_node, + .priority = active_thread->priority, + }, + .continue_ = false, + }; + queue_priority_add(&rwlock->queue, &waiting_node.qnode); + + while (1) { + /* wait to be unlocked, so this thread can try to acquire the lock again */ + mutex_unlock_and_sleep(&rwlock->mutex); + + mutex_lock(&rwlock->mutex); + if (waiting_node.continue_) { + /* pthread_rwlock_unlock() already set rwlock->readers */ + DEBUG("Thread %u: pthread_rwlock_%s(): is_writer=%u, allow_spurious=%u %s\n", + thread_pid, "lock", is_writer, allow_spurious, "continued"); + break; + } + else if (allow_spurious) { + DEBUG("Thread %u: pthread_rwlock_%s(): is_writer=%u, allow_spurious=%u %s\n", + thread_pid, "lock", is_writer, allow_spurious, "is timed out"); + queue_remove(&rwlock->queue, &waiting_node.qnode); + mutex_unlock(&rwlock->mutex); + return ETIMEDOUT; + } + } + } + mutex_unlock(&rwlock->mutex); + + return 0; +} + +static int pthread_rwlock_trylock(pthread_rwlock_t *rwlock, + bool (*is_blocked)(const pthread_rwlock_t *rwlock), + int incr_when_held) +{ + if (rwlock == NULL) { + DEBUG("Thread %u: pthread_rwlock_%s(): rwlock=NULL supplied\n", thread_pid, "trylock"); + return EINVAL; + } + else if (mutex_trylock(&rwlock->mutex) == 0) { + return EBUSY; + } + else if (is_blocked(rwlock)) { + mutex_unlock(&rwlock->mutex); + return EBUSY; + } + + rwlock->readers += incr_when_held; + + mutex_unlock(&rwlock->mutex); + return 0; +} + +static int pthread_rwlock_timedlock(pthread_rwlock_t *rwlock, + bool (*is_blocked)(const pthread_rwlock_t *rwlock), + bool is_writer, + int incr_when_held, + const struct timespec *abstime) +{ + timex_t now, then; + + then.seconds = abstime->tv_sec; + then.microseconds = abstime->tv_nsec / 1000u; + timex_normalize(&then); + + vtimer_now(&now); + + if (timex_cmp(then, now) <= 0) { + return ETIMEDOUT; + } + else { + timex_t reltime = timex_sub(then, now); + + vtimer_t timer; + vtimer_set_wakeup(&timer, reltime, active_thread->pid); + int result = pthread_rwlock_lock(rwlock, is_blocked, is_writer, incr_when_held, true); + if (result != ETIMEDOUT) { + vtimer_remove(&timer); + } + + return result; + } +} + +int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock) +{ + return pthread_rwlock_lock(rwlock, __pthread_rwlock_blocked_readingly, false, +1, false); +} + +int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock) +{ + return pthread_rwlock_lock(rwlock, __pthread_rwlock_blocked_writingly, true, -1, false); +} + +int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock) +{ + return pthread_rwlock_trylock(rwlock, __pthread_rwlock_blocked_readingly, +1); +} + +int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock) +{ + return pthread_rwlock_trylock(rwlock, __pthread_rwlock_blocked_writingly, -1); +} + +int pthread_rwlock_timedrdlock(pthread_rwlock_t *rwlock, const struct timespec *abstime) +{ + return pthread_rwlock_timedlock(rwlock, __pthread_rwlock_blocked_readingly, false, +1, abstime); +} + +int pthread_rwlock_timedwrlock(pthread_rwlock_t *rwlock, const struct timespec *abstime) +{ + return pthread_rwlock_timedlock(rwlock, __pthread_rwlock_blocked_writingly, true, -1, abstime); +} + +int pthread_rwlock_unlock(pthread_rwlock_t *rwlock) +{ + if (rwlock == NULL) { + DEBUG("Thread %u: pthread_rwlock_%s(): rwlock=NULL supplied\n", thread_pid, "unlock"); + return EINVAL; + } + + mutex_lock(&rwlock->mutex); + if (rwlock->readers == 0) { + /* the lock is open */ + DEBUG("Thread %u: pthread_rwlock_%s(): lock is open\n", thread_pid, "unlock"); + mutex_unlock(&rwlock->mutex); + return EPERM; + } + + if (rwlock->readers > 0) { + DEBUG("Thread %u: pthread_rwlock_%s(): release %s lock\n", thread_pid, "unlock", "read"); + --rwlock->readers; + } + else { + DEBUG("Thread %u: pthread_rwlock_%s(): release %s lock\n", thread_pid, "unlock", "write"); + rwlock->readers = 0; + } + + if (rwlock->readers != 0 || rwlock->queue.next == NULL) { + /* this thread was not the last reader, or no one is waiting to aquire the lock */ + DEBUG("Thread %u: pthread_rwlock_%s(): no one is waiting\n", thread_pid, "unlock"); + mutex_unlock(&rwlock->mutex); + return 0; + } + + /* wake up the next thread */ + queue_node_t *qnode = queue_remove_head(&rwlock->queue); + __pthread_rwlock_waiter_node_t *waiting_node = (__pthread_rwlock_waiter_node_t *) qnode->data; + waiting_node->continue_ = true; + uint16_t prio = qnode->priority; + sched_set_status(waiting_node->thread, STATUS_PENDING); + + if (waiting_node->is_writer) { + DEBUG("Thread %u: pthread_rwlock_%s(): continue %s %u\n", + thread_pid, "unlock", "writer", waiting_node->thread->pid); + --rwlock->readers; + } + else { + DEBUG("Thread %u: pthread_rwlock_%s(): continue %s %u\n", + thread_pid, "unlock", "reader", waiting_node->thread->pid); + ++rwlock->readers; + + /* wake up further readers */ + while (rwlock->queue.next) { + waiting_node = (__pthread_rwlock_waiter_node_t *) rwlock->queue.next->data; + if (waiting_node->is_writer) { + /* Not to be unfair to writers, we don't try to wake up readers that came after the first writer. */ + DEBUG("Thread %u: pthread_rwlock_%s(): continuing readers blocked by writer %u\n", + thread_pid, "unlock", waiting_node->thread->pid); + break; + } + waiting_node->continue_ = true; + DEBUG("Thread %u: pthread_rwlock_%s(): continue %s %u\n", + thread_pid, "unlock", "reader", waiting_node->thread->pid); + + /* wake up this reader */ + qnode = queue_remove_head(&rwlock->queue); + if (qnode->priority < prio) { + prio = qnode->priority; + } + sched_set_status(waiting_node->thread, STATUS_PENDING); + + ++rwlock->readers; + } + } + + mutex_unlock(&rwlock->mutex); + + /* yield if a woken up thread had a higher priority */ + sched_switch(active_thread->priority, prio); + return 0; +} diff --git a/sys/posix/pthread/pthread_rwlock_attr.c b/sys/posix/pthread/pthread_rwlock_attr.c new file mode 100644 index 0000000000..cd73ef6635 --- /dev/null +++ b/sys/posix/pthread/pthread_rwlock_attr.c @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2014 René Kijewski + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @ingroup pthread + * @{ + * + * @file + * @brief Implementation of a fair, POSIX conforming reader/writer lock (attribute set). + * + * @author René Kijewski + * + * @} + */ + +#include "pthread.h" + +#include + +int pthread_rwlockattr_init(pthread_rwlockattr_t *attr) +{ + if (attr == NULL) { + return EINVAL; + } + + memset(attr, 0, sizeof (*attr)); + return 0; +} + +int pthread_rwlockattr_destroy(pthread_rwlockattr_t *attr) +{ + if (attr == NULL) { + return EINVAL; + } + + (void) attr; + return 0; +} + +/* Return current setting of process-shared attribute of ATTR in PSHARED. */ +int pthread_rwlockattr_getpshared(const pthread_rwlockattr_t *attr, int *pshared) +{ + if (attr == NULL || pshared == NULL) { + return EINVAL; + } + + *pshared = attr->pshared; + return 0; +} + +int pthread_rwlockattr_setpshared(pthread_rwlockattr_t *attr, int pshared) +{ + if (attr == NULL || (pshared != PTHREAD_PROCESS_SHARED && pshared != PTHREAD_PROCESS_PRIVATE)) { + return EINVAL; + } + + attr->pshared = pshared; + return 0; +} diff --git a/tests/test_pthread_rwlock/Makefile b/tests/test_pthread_rwlock/Makefile new file mode 100644 index 0000000000..4cb40dfe58 --- /dev/null +++ b/tests/test_pthread_rwlock/Makefile @@ -0,0 +1,18 @@ +PROJECT = test_pthread_rwlock +include ../Makefile.tests_common + +USEMODULE += pthread +USEMODULE += vtimer +USEMODULE += random + +DISABLE_MODULE += auto_init + +CFLAGS += -DNATIVE_AUTO_EXIT + +# these boards provide too little RAM for the example to run +BOARD_BLACKLIST += chronos +BOARD_BLACKLIST += mbed_lpc1768 +BOARD_BLACKLIST += msb-430 +BOARD_BLACKLIST += msb-430h + +include $(RIOTBASE)/Makefile.include diff --git a/tests/test_pthread_rwlock/main.c b/tests/test_pthread_rwlock/main.c new file mode 100644 index 0000000000..8b3b51d687 --- /dev/null +++ b/tests/test_pthread_rwlock/main.c @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2014 René Kijewski + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @ingroup tests + * @{ + * + * @file + * @brief Test rwlock implementation. + * + * @author René Kijewski + * + * @} + */ + +#include +#include + +#include "kernel.h" +#include "random.h" +#include "sched.h" +#include "thread.h" +#include "vtimer.h" + +#define NUM_READERS_HIGH 2 +#define NUM_READERS_LOW 3 + +#define NUM_WRITERS_HIGH 1 +#define NUM_WRITERS_LOW 2 + +#define NUM_READERS (NUM_READERS_HIGH + NUM_READERS_LOW) +#define NUM_WRITERS (NUM_WRITERS_HIGH + NUM_WRITERS_LOW) +#define NUM_CHILDREN (NUM_READERS + NUM_WRITERS) + +#define NUM_ITERATIONS 5 + +#define RAND_SEED 0xC0FFEE + +static pthread_rwlock_t rwlock; +static volatile unsigned counter; + +#define PRINTF(FMT, ...) \ + printf("%c%u (prio=%u): " FMT "\n", active_thread->name[0], thread_pid, active_thread->priority, __VA_ARGS__) + +static void do_sleep(int factor) +{ + uint32_t timeout_us = (genrand_uint32() % 100000) * factor; + /* PRINTF("sleep for % 8i µs.", timeout_us); */ + vtimer_usleep(timeout_us); +} + +static void writer(void) +{ + /* PRINTF("%s", "start"); */ + for (int i = 0; i < NUM_ITERATIONS; ++i) { + pthread_rwlock_wrlock(&rwlock); + unsigned cur = ++counter; + do_sleep(3); /* simulate time that it takes to write the value */ + PRINTF("%i: write -> %2u (correct = %u)", i, cur, cur == counter); + pthread_rwlock_unlock(&rwlock); + do_sleep(2); + } + /* PRINTF("%s", "done"); */ +} + +static void reader(void) +{ + /* PRINTF("%s", "start"); */ + for (int i = 0; i < NUM_ITERATIONS; ++i) { + pthread_rwlock_rdlock(&rwlock); + unsigned cur = counter; + do_sleep(1); /* simulate time that it takes to read the value */ + PRINTF("%i: read <- %2u (correct = %u)", i, cur, cur == counter); + pthread_rwlock_unlock(&rwlock); + do_sleep(1); + } + /* PRINTF("%s", "done"); */ +} + +int main(void) +{ + static char stacks[NUM_CHILDREN][KERNEL_CONF_STACKSIZE_MAIN]; + + puts("Main start."); + + for (unsigned i = 0; i < NUM_CHILDREN; ++i) { + int prio; + void (*fun)(void); + const char *name; + + if (i < NUM_READERS) { + if (i < NUM_READERS_HIGH) { + prio = PRIORITY_MAIN + 1; + } + else { + prio = PRIORITY_MAIN + 2; + } + fun = reader; + name = "reader"; + } + else { + if (i - NUM_READERS < NUM_WRITERS_HIGH) { + prio = PRIORITY_MAIN + 1; + } + else { + prio = PRIORITY_MAIN + 2; + } + fun = writer; + name = "writer"; + } + + thread_create(stacks[i], sizeof (stacks[i]), prio, CREATE_WOUT_YIELD | CREATE_STACKTEST, fun, name); + } + + puts("Main done."); + return 0; +}