Hi Here is the second iteration of the RW lock code for the Windows. It includes the fix for the cross-process RW lock.
Christian =================================================================== --- c:/projects/apr/include/apr_errno.h Sun Apr 8 06:19:36 2001 +++ c:/apr/include/apr_errno.h Sat Apr 28 17:22:56 2001 @@ -248,7 +248,7 @@ #define APR_EINCOMPLETE (APR_OS_START_ERROR + 22) #define APR_EABOVEROOT (APR_OS_START_ERROR + 23) #define APR_EBADPATH (APR_OS_START_ERROR + 24) - +#define APR_ELOCKTYPE (APR_OS_START_ERROR + 25) /* APR ERROR VALUE TESTS */ #define APR_STATUS_IS_ENOSTAT(s) ((s) == APR_ENOSTAT) --- c:/projects/apr/include/apr_lock.h Thu Feb 15 23:41:18 2001 +++ c:/apr/include/apr_lock.h Sun Apr 29 12:39:37 2001 @@ -70,6 +70,7 @@ typedef enum {APR_CROSS_PROCESS, APR_INTRAPROCESS, APR_LOCKALL} apr_lockscope_e; typedef enum {APR_MUTEX, APR_READWRITE} apr_locktype_e; +typedef enum {APR_READER, APR_WRITER} apr_readerwriterlock_e; typedef struct apr_lock_t apr_lock_t; @@ -103,13 +104,20 @@ apr_lockscope_e scope, const char *fname, apr_pool_t *cont); - /** * Lock a protected region. * @param lock The lock to set. - * @deffunc apr_status_t apr_lock_acquire(apr_lock_t *lock) */ APR_DECLARE(apr_status_t) apr_lock_acquire(apr_lock_t *lock); + +/** + * Lock a region with either a reader or writer lock. + * @param lock The lock to set. + * @param type The type of lock to acquire. + * @deffunc apr_status_t apr_lock_acquire_rw(apr_lock_t *lock, apr_readerwriterlock_e type) + */ +APR_DECLARE(apr_status_t) apr_lock_acquire_rw(apr_lock_t *lock, + apr_readerwriterlock_e type); /** * Unlock a protected region. --- c:/projects/apr/include/arch/win32/locks.h Thu Feb 15 23:41:24 2001 +++ c:/apr/include/arch/win32/locks.h Sun Apr 29 18:04:02 2001 @@ -57,6 +57,10 @@ #include "apr_lock.h" +#define DOING_NOTHING 0 +#define IS_READING 1 +#define IS_WRITING 2 + struct apr_lock_t { apr_pool_t *cntxt; apr_locktype_e type; @@ -64,6 +68,14 @@ HANDLE mutex; CRITICAL_SECTION section; char *fname; + /* Declarations used for the reader writer implementation */ + apr_uint32_t activeReaders; + apr_uint32_t activeWriters; + apr_uint32_t waitingReaders; + apr_uint32_t waitingWriters; + HANDLE blockedReader; + HANDLE blockedWriter; + apr_uint32_t currOperation; }; #endif /* LOCKS_H */ --- c:/projects/apr/locks/win32/locks.c Thu Feb 15 23:41:28 2001 +++ c:/apr/locks/win32/locks.c Sun Apr 29 18:08:36 2001 @@ -75,6 +75,13 @@ newlock->fname = apr_pstrdup(cont, fname); newlock->type = type; newlock->scope = scope; + newlock->type = type; + newlock->scope = scope; + newlock->activeReaders = 0; + newlock->activeWriters = 0; + newlock->waitingReaders = 0; + newlock->waitingWriters = 0; + newlock->currOperation = DOING_NOTHING; sec.nLength = sizeof(SECURITY_ATTRIBUTES); sec.lpSecurityDescriptor = NULL; @@ -85,10 +92,26 @@ sec.bInheritHandle = FALSE; } + if (newlock->type == APR_MUTEX) { + newlock->blockedReader = NULL; + newlock->blockedWriter = NULL; + } if (scope == APR_INTRAPROCESS) { InitializeCriticalSection(&newlock->section); + if (newlock->type == APR_READWRITE) { + newlock->blockedReader = CreateMutex(NULL, FALSE, NULL); + newlock->blockedWriter = CreateMutex(NULL, FALSE, NULL); + } } else { newlock->mutex = CreateMutex(&sec, FALSE, fname); + if (newlock->type == APR_READWRITE) { + char *tmp; + + tmp = apr_pstrcat( cont, fname, ".BlockedReader", NULL); + newlock->blockedReader = CreateMutex(&sec, FALSE, tmp); + tmp = apr_pstrcat( cont, fname, ".BlockedWriter", NULL); + newlock->blockedWriter = CreateMutex(&sec, FALSE, NULL); + } } *lock = newlock; return APR_SUCCESS; @@ -115,13 +138,20 @@ return APR_SUCCESS; } -APR_DECLARE(apr_status_t) apr_lock_acquire(apr_lock_t *lock) +/* + * This is private routine to get the lock + * It is made private because both the regular lock routines + * and the reader writer lock routines use it + */ +static apr_status_t get_lock(apr_lock_t * lock) { DWORD rv; + if (lock->scope == APR_INTRAPROCESS) { EnterCriticalSection(&lock->section); return APR_SUCCESS; - } else { + } + else { rv = WaitForSingleObject(lock->mutex, INFINITE); if (rv == WAIT_OBJECT_0 || rv == WAIT_ABANDONED) { @@ -131,12 +161,13 @@ return apr_get_os_error(); } -APR_DECLARE(apr_status_t) apr_lock_release(apr_lock_t *lock) +static apr_status_t release_lock(apr_lock_t * lock) { if (lock->scope == APR_INTRAPROCESS) { LeaveCriticalSection(&lock->section); return APR_SUCCESS; - } else { + } + else { if (ReleaseMutex(lock->mutex) == 0) { return apr_get_os_error(); } @@ -144,6 +175,107 @@ return APR_SUCCESS; } +APR_DECLARE(apr_status_t) apr_lock_acquire_rw(apr_lock_t *lock, + apr_readerwriterlock_e type) +{ + DWORD rv; + + if (lock->type == APR_MUTEX) { + return APR_ELOCKTYPE; + } + + rv = get_lock(lock); + if (rv != APR_SUCCESS) { + return rv; + } + + if (type == APR_WRITER) { + if (lock->activeReaders == 0 && lock->activeWriters == 0) { + /* there is no active reader or writer, OK to start writing */ + lock->activeWriters = 1; + lock->currOperation = IS_WRITING; + release_lock(lock); + } + else { + /* there is active readers or writer, hold on until free */ + lock->waitingWriters++; + release_lock(lock); + WaitForSingleObject(lock->blockedWriter, INFINITE); + } + } + else if (type == APR_READER) { + if (lock->activeWriters > 0 || lock->waitingWriters > 0) { + lock->waitingReaders++; + release_lock(lock); + WaitForSingleObject(lock->blockedReader, INFINITE); + } + else { + lock->activeReaders++; + lock->currOperation = IS_WRITING; + release_lock(lock); + } + } + return APR_SUCCESS; +} + +APR_DECLARE(apr_status_t) apr_lock_acquire(apr_lock_t * lock) +{ + /* ToDo: if the lock is a read write what is the default behaviour? + * Right now it is set to return an error + */ + if (lock->type == APR_READWRITE) { + return APR_ELOCKTYPE; + } + return get_lock(lock); +} + +APR_DECLARE(apr_status_t) apr_lock_release(apr_lock_t * lock) +{ + DWORD rv; + + if (lock->type == APR_MUTEX) { + return release_lock(lock); + } + else { + rv = get_lock(lock); + if (rv != APR_SUCCESS) { + return rv; + } + if (lock->currOperation == IS_READING) { + lock->activeReaders--; + /* last reader thread to finish reading needs + * to activate a waiting writer + */ + if (lock->activeReaders == 0 && lock->waitingWriters > 0) { + lock->activeWriters = 1; + lock->waitingWriters--; + ReleaseMutex(lock->blockedWriter); + } + } + else if (lock->currOperation == IS_WRITING) { + lock->activeWriters = 0; + if (lock->waitingReaders > 0) { + /* if there are waiting readers, release them all from read queue */ + while (lock->waitingReaders > 0) { + lock->waitingReaders--; + lock->activeReaders++; + ReleaseMutex(lock->blockedWriter); + } + } + else if (lock->waitingWriters > 0) { + /* no waiting reader and we have waiting writer, + * release 1 writer from write queue + */ + lock->waitingWriters--; + ReleaseMutex(lock->blockedWriter); + } + } + lock->currOperation = DOING_NOTHING; + release_lock(lock); + } + return APR_SUCCESS; +} + APR_DECLARE(apr_status_t) apr_lock_destroy(apr_lock_t *lock) { if (lock->scope == APR_INTRAPROCESS) { @@ -153,6 +285,11 @@ if (CloseHandle(lock->mutex) == 0) { return apr_get_os_error(); } + } + + if (lock->type == APR_READWRITE) { + CloseHandle(lock->blockedReader); + CloseHandle(lock->blockedWriter); } return APR_SUCCESS; }