From 9a5a8a2452994b809fc8f493d2e20b6f4f891320 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Kijewski?= Date: Thu, 17 Apr 2014 14:20:46 +0200 Subject: [PATCH] Add pthread_rwlock test --- sys/posix/pthread/pthread_rwlock.c | 30 +++++++ tests/test_pthread_rwlock/Makefile | 18 ++++ tests/test_pthread_rwlock/main.c | 132 +++++++++++++++++++++++++++++ 3 files changed, 180 insertions(+) create mode 100644 tests/test_pthread_rwlock/Makefile create mode 100644 tests/test_pthread_rwlock/main.c diff --git a/sys/posix/pthread/pthread_rwlock.c b/sys/posix/pthread/pthread_rwlock.c index 5b6a6b5af6..f7033327c8 100644 --- a/sys/posix/pthread/pthread_rwlock.c +++ b/sys/posix/pthread/pthread_rwlock.c @@ -35,11 +35,15 @@ #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; } @@ -50,6 +54,7 @@ int pthread_rwlock_init(pthread_rwlock_t *rwlock, const pthread_rwlockattr_t *at 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; } @@ -99,14 +104,21 @@ static int pthread_rwlock_lock(pthread_rwlock_t *rwlock, 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, @@ -127,9 +139,13 @@ static int pthread_rwlock_lock(pthread_rwlock_t *rwlock, 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; @@ -146,6 +162,7 @@ static int pthread_rwlock_trylock(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) { @@ -226,25 +243,30 @@ int pthread_rwlock_timedwrlock(pthread_rwlock_t *rwlock, const struct timespec * 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; } @@ -257,9 +279,13 @@ int pthread_rwlock_unlock(pthread_rwlock_t *rwlock) 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 */ @@ -267,9 +293,13 @@ int pthread_rwlock_unlock(pthread_rwlock_t *rwlock) 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); 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; +}