1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2025-01-18 12:52:44 +01:00

Merge pull request #1018 from Kijewski/pthread-rwlock

pthread: implement reader/writer lock
This commit is contained in:
René Kijewski 2014-04-18 16:27:39 +02:00
commit c87841b689
7 changed files with 737 additions and 37 deletions

View File

@ -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"

View File

@ -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 <errno.h>
#include <stdbool.h>
/* 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);

View File

@ -0,0 +1,58 @@
/**
* @ingroup pthread
*/
#include <errno.h>
/**
* @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);

View File

@ -0,0 +1,320 @@
/*
* Copyright (C) 2014 René Kijewski <rene.kijewski@fu-berlin.de>
*
* 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 <rene.kijewski@fu-berlin.de>
*
* @}
*/
#include "pthread.h"
#include "sched.h"
#include "vtimer.h"
#include <stdint.h>
#include <string.h>
#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;
}

View File

@ -0,0 +1,74 @@
/*
* Copyright (C) 2014 René Kijewski <rene.kijewski@fu-berlin.de>
*
* 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 <rene.kijewski@fu-berlin.de>
*
* @}
*/
#include "pthread.h"
#include <string.h>
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;
}

View File

@ -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

View File

@ -0,0 +1,132 @@
/*
* Copyright (C) 2014 René Kijewski <rene.kijewski@fu-berlin.de>
*
* 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 <rene.kijewski@fu-berlin.de>
*
* @}
*/
#include <pthread.h>
#include <stdio.h>
#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;
}