Skip to content
Snippets Groups Projects
Commit 3f9faa65 authored by Deucе's avatar Deucе :ok_hand_tone4:
Browse files

Add rwlock wrappers.

Recursive read locks, non-recursive write locks.
parent 3208fdd1
No related branches found
No related tags found
1 merge request!455Update branch with changes from master
......@@ -40,6 +40,7 @@ MTOBJS = \
$(MTOBJODIR)$(DIRSEP)link_list$(OFILE) \
$(MTOBJODIR)$(DIRSEP)msg_queue$(OFILE) \
$(MTOBJODIR)$(DIRSEP)multisock$(OFILE) \
$(MTOBJODIR)$(DIRSEP)rwlockwrap$(OFILE) \
$(MTOBJODIR)$(DIRSEP)semwrap$(OFILE) \
$(MTOBJODIR)$(DIRSEP)netwrap$(OFILE) \
$(MTOBJODIR)$(DIRSEP)sockwrap$(OFILE) \
......
#include "rwlockwrap.h"
#ifdef _WIN32
bool
rwlock_init(rwlock_t *lock)
{
InitializeCriticalSection(&lock->lk);
InitializeCriticalSection(&lock->wlk);
readers = 0;
writers = 0;
writers_waiting = 0;
writer = (DWORD)-1;
}
bool
rwlock_rdlock(rwlock_t *lock)
{
DWORD obj;
EnterCriticalSection(&lock->lk);
while(lock->writers || lock->writers_waiting) {
LeaveCriticalSection(&lock->lk);
// Wait for current writer to release
EnterCriticalSection(&lock->wlk);
EnterCriticalSection(&lock->lk);
if (lock->writers || lock->writers_waiting) {
LeaveCriticalSection(&lock->lk);
LeaveCriticalSection(&lock->wlk);
/*
* Just in case thread scheduling is weird on
* Win32, allow time for a writer to grab wlk
*/
Sleep(1);
EnterCriticalSection(&lock->lk);
continue;
}
else {
lock->readers++;
LeaveCriticalSection(&lock->lk);
LeaveCriticalSection(&lock->wlk);
return true;
}
}
lock->readers++;
LeaveCriticalSection(&lock->lk);
return true;
}
bool
rwlock_tryrdlock(rwlock_t *lock)
{
bool ret = false;
EnterCriticalSection(&lock->lk);
if (lock->writers == 0 && lock->writers_waiting == 0) {
lock->readers++;
ret = true;
}
LeaveCriticalSection(&lock->lk);
return ret;
}
bool
rwlock_wrlock(rwlock_t *lock)
{
EnterCriticalSection(&lock->lk);
lock->writers_waiting++;
LeaveCriticalSection(&lock->lk);
EnterCriticalSection(&lock->wlk);
EnterCriticalSection(&lock->lk);
// No recursion
if (lock->writers == 0) {
lock->writers_waiting--;
lock->writers++;
lock->writer = GetCurrentThreadId();
LeaveCriticalSection(&lock->lk);
// Keep holding wlk
return true;
}
LeaveCriticalSection(&lock->lk);
LeaveCriticalSection(&lock->wlk);
return false;
}
bool
rwlock_trywrlock(rwlock_t *lock)
{
if (TryEnterCriticalSection(&lock->wlk)) {
EnterCriticalSection(&lock->lk);
// Prevent recursing on writer locks
if (lock->writers == 0) {
lock->writers++;
lock->writer = GetCurrentThreadId();
LeaveCriticalSection(&lock->lk);
return true;
}
LeaveCriticalSection(&lock->lk);
LeaveCriticalSection(&lock->wlk);
return false;
}
return false;
}
bool
rwlock_unlock(rwlock_t *lock)
{
bool ret = false;
EnterCriticalSection(&lock->lk);
if (lock->writers) {
if (lock->writer == GetCurrentThreadId()) {
lock->writers--;
LeaveCriticalSection(&lock->lk);
LeaveCriticalSection(&lock->wlk);
return true;
}
LeaveCriticalSection(&lock->lk);
return false;
}
lock->readers--;
return true;
}
#elif defined(__unix__)
// All macros...
#else
#error no rwlock wrapper for this platform
#endif
#ifndef RWLOCKWRAP_H
#define RWLOCKWRAP_H
#ifdef UNIX
#include <stdbool.h>
#if defined(__unix__)
#include <pthread.h>
#include <time.h>
typedef pthread_rwlock_t rwlock_t;
#define rwlock_init(lock) (pthread_rwlock_init(lock, NULL) == 0)
#define rwlock_rdlock(lock) (pthread_rwlock_rdlock(lock) == 0)
#define rwlock_tryrdlock(lock) (pthread_rwlock_tryrdlock(lock) == 0)
#define rwlock_wrlock(lock) (pthread_rwlock_wrlock(lock) == 0)
#define rwlock_trywrlock(lock) (pthread_rwlock_trywrlock(lock) == 0)
#define rwlock_unlock(lock) (pthread_rwlock_unlock(lock) == 0)
#elif defined(_WIN32)
#include "threadwrap.h"
typedef struct {
CRITICAL_SECTION lk; // Protects access to all elements
CRITICAL_SECTION wlk; // Locked by an active writer
unsigned readers;
unsigned writers;
unsigned writers_waiting;
DWORD writer;
} rwlock_t;
bool rwlock_init(rwlock_t *lock);
bool rwlock_rdlock(rwlock_t *lock);
bool rwlock_tryrdlock(rwlock_t *lock);
bool rwlock_wrlock(rwlock_t *lock);
bool rwlock_trywrlock(rwlock_t *lock);
bool rwlock_unlock(rwlock_t *lock);
#else
#error Not implemented
#endif
......
......@@ -10,6 +10,7 @@
#include "conwrap.h"
#include "dirwrap.h"
#include "filewrap.h"
#include "rwlockwrap.h"
#include "sockwrap.h"
#include "threadwrap.h"
#include "xpbeep.h"
......@@ -63,12 +64,12 @@ int main()
printf("Testing rwlocks...\n");
do {
pthread_rwlock_t lock;
if ((i=pthread_rwlock_init(&lock, NULL)) != 0) {
printf("pthread_rwlock_init() failed (%d)\n", i);
rwlock_t lock;
if (!rwlock_init(&lock)) {
printf("rwlock_init() failed (%d)\n", i);
continue;
}
// Start two copies of the ddlock thread...
// Start two copies of the rdlock thread...
if (_beginthread(rwlock_rdlock_thread, 0, &lock) == -1UL) {
printf("Unable to start rwlock_rdlock_thread\n");
continue;
......@@ -437,101 +438,80 @@ static void sopen_child_thread(void* arg)
static void rwlock_rdlock_thread(void *arg)
{
pthread_rwlock_t *lock = arg;
rwlock_t *lock = arg;
int locks = 0;
int i;
struct timespec abstime = {0};
// Grab the lock three times...
for (locks = 0; locks < 3; locks++) {
if ((i = pthread_rwlock_rdlock(lock)) != 0) {
if (!rwlock_rdlock(lock)) {
printf("Failed to obtain rdlock #%d\n", locks + 1);
break;
}
}
// Try to grab the lock (should succeed)
if ((i = pthread_rwlock_tryrdlock(lock)) == 0) {
if (rwlock_tryrdlock(lock)) {
locks++;
}
else {
printf("tryrdlock failed: %d\n", i);
printf("tryrdlock failed\n");
}
// Try to grab the lock before Jan 1st, 1970 (should succeed)
if ((i = pthread_rwlock_timedrdlock(lock, &abstime)) == 0) {
locks++;
}
else {
printf("timedrdlock failed: %d\n", i);
}
printf("Obtained %d locks (should be 5)\n", locks);
printf("Obtained %d locks (should be 4)\n", locks);
for (; locks > 0; locks--) {
SLEEP(1000);
if ((i = pthread_rwlock_unlock(lock)) != 0) {
if (!rwlock_unlock(lock)) {
printf("Failed to unlock rdlock #%d\n", locks);
}
}
SLEEP(1000);
// Try to grab the lock (should fail)
if ((i = pthread_rwlock_tryrdlock(lock)) == 0) {
printf("tryrdlock incorrectly succeeded: %d\n", i);
pthread_rwlock_unlock(lock);
}
// Try to grab the lock before Jan 1st, 1970 (should fail)
if ((i = pthread_rwlock_timedrdlock(lock, &abstime)) == 0) {
printf("timedrdlock incorrectly succeeded: %d\n", i);
pthread_rwlock_unlock(lock);
if (rwlock_tryrdlock(lock)) {
printf("tryrdlock incorrectly succeeded\n");
rwlock_unlock(lock);
}
// Wait up to five seconds for the lock (should succeed)
clock_gettime(CLOCK_REALTIME, &abstime);
abstime.tv_sec += 5;
printf("Waiting for lock\n");
if ((i = pthread_rwlock_timedrdlock(lock, &abstime)) == 0) {
if (rwlock_rdlock(lock)) {
locks++;
}
else {
printf("timedrdlock failed: %d\n", i);
printf("timedrdlock failed\n");
}
printf("Wait done\n");
SLEEP(1000);
for (; locks > 0; locks--) {
pthread_rwlock_unlock(lock);
rwlock_unlock(lock);
}
}
static void rwlock_wrlock_thread(void *arg)
{
pthread_rwlock_t *lock = arg;
int i;
struct timespec abstime = {0};
rwlock_t *lock = arg;
// Sleep to allow readers to grab locks...
SLEEP(1000);
// Try to grab lock (should fail)
if ((i = pthread_rwlock_trywrlock(lock)) == 0) {
if (rwlock_trywrlock(lock)) {
printf("trywrlock() incorrectly succeeded\n");
pthread_rwlock_unlock(lock);
rwlock_unlock(lock);
}
// Try to grab lock (should succeed)
printf("Waiting for write lock\n");
if ((i = pthread_rwlock_wrlock(lock)) != 0) {
printf("wrlock() failed %d\n", i);
if (!rwlock_wrlock(lock)) {
printf("wrlock() failed\n");
}
printf("wrlock Wait done\n");
// Enjoy the lock for a bit...
SLEEP(2000);
printf("wrlock unlocking\n");
pthread_rwlock_unlock(lock);
rwlock_unlock(lock);
SLEEP(100);
// Wait up to five seconds for the lock (should succeed)
clock_gettime(CLOCK_REALTIME, &abstime);
abstime.tv_sec += 5;
printf("Waiting up to 5 seconds for wrlock\n");
if ((i = pthread_rwlock_timedwrlock(lock, &abstime)) != 0) {
printf("timedrdlock failed: %d\n", i);
// Wait for the lock (should succeed)
printf("Waiting wrlock\n");
if (!rwlock_wrlock(lock)) {
printf("wrlock failed\n");
}
else {
SLEEP(1000);
pthread_rwlock_unlock(lock);
rwlock_unlock(lock);
}
printf("wrlock wait done\n");
}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment