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;
}