diff --git a/src/xpdev/objects.mk b/src/xpdev/objects.mk index 03286ab638ec399566301a06cd5b0a60d15f4c86..5805098b35e16acc80ffc31859fbf4e8697c8aee 100644 --- a/src/xpdev/objects.mk +++ b/src/xpdev/objects.mk @@ -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) \ diff --git a/src/xpdev/rwlockwrap.c b/src/xpdev/rwlockwrap.c new file mode 100644 index 0000000000000000000000000000000000000000..cbeca40c4fc03d7ad642282792c92084caed312e --- /dev/null +++ b/src/xpdev/rwlockwrap.c @@ -0,0 +1,132 @@ +#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 diff --git a/src/xpdev/rwlockwrap.h b/src/xpdev/rwlockwrap.h index 6c7723a174bd13e74ded9cd5315ceb1ca1d4ac6a..79cea157cc0715b611011e937f9f5e2895b420d2 100644 --- a/src/xpdev/rwlockwrap.h +++ b/src/xpdev/rwlockwrap.h @@ -1,9 +1,40 @@ #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 diff --git a/src/xpdev/wraptest.c b/src/xpdev/wraptest.c index d3ead989c4c511825af701266446e829e3c03157..121afa67566c0b8e42bfb5d2f25f47fbf3856daa 100644 --- a/src/xpdev/wraptest.c +++ b/src/xpdev/wraptest.c @@ -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"); }