You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@apr.apache.org by Christian Gross <Ch...@yahoo.de> on 2001/04/29 01:16:36 UTC

[PATCH] Read Write Locks for Windows Platform

Here is an implementation of Windows based Reader Writer locks.

I tested them a bit and they passed that.  However these changes
should be looked at.

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	Sat Apr 28 16:35:47 2001
@@ -103,13 +103,23 @@
                                           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);
+
+/**
+ * Acquire a lock as a reader
+ * @param lock The lock to set
+ */
+APR_DECLARE(apr_status_t) apr_lock_acquire_read(apr_lock_t *lock);
+
+/**
+ * Acquire a lock as a writer
+ * @param lock The lock to set
+ */
+APR_DECLARE(apr_status_t) apr_lock_acquire_write(apr_lock_t *lock);
 
 /**
  * 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	Sat Apr 28 17:25:14 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	Sat Apr 28 19:04:58 2001
@@ -57,137 +57,276 @@
 #include "win32/locks.h"
 #include "apr_portable.h"
 
-APR_DECLARE(apr_status_t) apr_lock_create(apr_lock_t **lock, 
-                                          apr_locktype_e type, 
-                                          apr_lockscope_e scope, 
-                                          const char *fname,
-                                          apr_pool_t *cont)
+APR_DECLARE(apr_status_t) apr_lock_create(apr_lock_t ** lock,
+					  apr_locktype_e type,
+					  apr_lockscope_e scope,
+					  const char *fname,
+					  apr_pool_t * cont)
 {
     apr_lock_t *newlock;
     SECURITY_ATTRIBUTES sec;
 
-    newlock = (apr_lock_t *)apr_palloc(cont, sizeof(apr_lock_t));
+    newlock = (apr_lock_t *) apr_palloc(cont, sizeof(apr_lock_t));
 
     newlock->cntxt = cont;
     /* ToDo:  How to handle the case when no context is available? 
-    *         How to cleanup the storage properly?
-    */
+       *         How to cleanup the storage properly?
+     */
     newlock->fname = apr_pstrdup(cont, fname);
     newlock->type = type;
     newlock->scope = scope;
+    newlock->activeReaders = 0;
+    newlock->activeWriters = 0;
+    newlock->waitingReaders = 0;
+    newlock->waitingWriters = 0;
+    newlock->currOperation = DOING_NOTHING;
+    if (newlock->type == APR_READWRITE) {
+        newlock->blockedReader = CreateMutex(NULL, FALSE, NULL);
+	    newlock->blockedWriter = CreateMutex(NULL, FALSE, NULL);
+    }
+    else {
+        newlock->blockedReader = NULL;
+        newlock->blockedWriter = NULL;
+    }
+
     sec.nLength = sizeof(SECURITY_ATTRIBUTES);
     sec.lpSecurityDescriptor = NULL;
 
     if (scope == APR_CROSS_PROCESS || scope == APR_LOCKALL) {
-        sec.bInheritHandle = TRUE;
+		sec.bInheritHandle = TRUE;
     }
     else {
-        sec.bInheritHandle = FALSE;
+		sec.bInheritHandle = FALSE;
     }
 
     if (scope == APR_INTRAPROCESS) {
-        InitializeCriticalSection(&newlock->section);
-    } else {
-        newlock->mutex = CreateMutex(&sec, FALSE, fname);
+		InitializeCriticalSection(&newlock->section);
+    }
+    else {
+		newlock->mutex = CreateMutex(&sec, FALSE, fname);
     }
     *lock = newlock;
     return APR_SUCCESS;
 }
 
-APR_DECLARE(apr_status_t) apr_lock_child_init(apr_lock_t **lock, 
-                                              const char *fname, 
-                                              apr_pool_t *cont)
+APR_DECLARE(apr_status_t) apr_lock_child_init(apr_lock_t ** lock,
+					      const char *fname,
+					      apr_pool_t * cont)
 {
     /* This routine should not be called (and OpenMutex will fail if
called) 
      * on a INTRAPROCESS lock
      */
-    (*lock) = (apr_lock_t *)apr_palloc(cont, sizeof(apr_lock_t));
+    (*lock) = (apr_lock_t *) apr_palloc(cont, sizeof(apr_lock_t));
 
     if ((*lock) == NULL) {
-        return APR_ENOMEM;
+		return APR_ENOMEM;
     }
     (*lock)->fname = apr_pstrdup(cont, fname);
     (*lock)->mutex = OpenMutex(MUTEX_ALL_ACCESS, TRUE, fname);
-    
+
     if ((*lock)->mutex == NULL) {
-        return apr_get_os_error();
+		return apr_get_os_error();
     }
     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 {
-        rv = WaitForSingleObject(lock->mutex, INFINITE);
-
-        if (rv == WAIT_OBJECT_0 || rv == WAIT_ABANDONED) {
-            return APR_SUCCESS;
-        }
+		EnterCriticalSection(&lock->section);
+		return APR_SUCCESS;
+    }
+    else {
+		rv = WaitForSingleObject(lock->mutex, INFINITE);
+
+		if (rv == WAIT_OBJECT_0 || rv == WAIT_ABANDONED) {
+			return APR_SUCCESS;
+		}
     }
     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 {
-        if (ReleaseMutex(lock->mutex) == 0) {
-            return apr_get_os_error();
-        }
+		LeaveCriticalSection(&lock->section);
+		return APR_SUCCESS;
+    }
+    else {
+		if (ReleaseMutex(lock->mutex) == 0) {
+			return apr_get_os_error();
+		}
+    }
+    return APR_SUCCESS;
+}
+
+APR_DECLARE(apr_status_t) apr_lock_acquire_write(apr_lock_t * lock)
+{
+    DWORD rv;
+
+    if (lock->type == APR_MUTEX) {
+		return APR_ELOCKTYPE;
+    }
+
+    rv = get_lock(lock);
+    if (rv != APR_SUCCESS) {
+		return rv;
+    }
+
+    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);
+    }
+    return APR_SUCCESS;
+}
+
+APR_DECLARE(apr_status_t) apr_lock_acquire_read(apr_lock_t * lock)
+{
+    DWORD rv;
+
+    if (lock->type == APR_MUTEX) {
+		return APR_ELOCKTYPE;
+    }
+
+    rv = get_lock(lock);
+    if (rv != APR_SUCCESS) {
+		return rv;
+    }
+    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_destroy(apr_lock_t *lock)
+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) {
-        DeleteCriticalSection(&lock->section);
-        return APR_SUCCESS;
-    } else {
-        if (CloseHandle(lock->mutex) == 0) {
-            return apr_get_os_error();
-        }
+		DeleteCriticalSection(&lock->section);
+    }
+    else {
+		if (CloseHandle(lock->mutex) == 0) {
+			return apr_get_os_error();
+		}
+    }
+
+    if (lock->type == APR_READWRITE) {
+		CloseHandle(lock->blockedReader);
+		CloseHandle(lock->blockedWriter);
     }
     return APR_SUCCESS;
 }
 
-APR_DECLARE(apr_status_t) apr_lock_data_get(apr_lock_t *lock, const
char *key,
-                                           void *data)
+APR_DECLARE(apr_status_t) apr_lock_data_get(apr_lock_t * lock,
+					    const char *key, void
*data)
 {
     return apr_pool_userdata_get(data, key, lock->cntxt);
 }
 
-APR_DECLARE(apr_status_t) apr_lock_data_set(apr_lock_t *lock, void
*data,
-                                           const char *key,
-                                           apr_status_t (*cleanup)
(void *))
+APR_DECLARE(apr_status_t) apr_lock_data_set(apr_lock_t * lock, void
*data,
+					    const char *key,
+					    apr_status_t(*cleanup)
(void *))
 {
     return apr_pool_userdata_set(data, key, cleanup, lock->cntxt);
 }
 
-APR_DECLARE(apr_status_t) apr_os_lock_get(apr_os_lock_t *thelock,
-                                          apr_lock_t *lock)
+APR_DECLARE(apr_status_t) apr_os_lock_get(apr_os_lock_t * thelock,
+					  apr_lock_t * lock)
 {
     *thelock = lock->mutex;
     return APR_SUCCESS;
 }
 
-APR_DECLARE(apr_status_t) apr_os_lock_put(apr_lock_t **lock,
-                                          apr_os_lock_t *thelock,
-                                          apr_pool_t *cont)
+APR_DECLARE(apr_status_t) apr_os_lock_put(apr_lock_t ** lock,
+					  apr_os_lock_t * thelock,
+					  apr_pool_t * cont)
 {
     if (cont == NULL) {
-        return APR_ENOPOOL;
+		return APR_ENOPOOL;
     }
     if ((*lock) == NULL) {
-        (*lock) = (apr_lock_t *)apr_palloc(cont, sizeof(apr_lock_t));
-        (*lock)->cntxt = cont;
+		(*lock) = (apr_lock_t *) apr_palloc(cont,
sizeof(apr_lock_t));
+		(*lock)->cntxt = cont;
     }
     (*lock)->mutex = *thelock;
     return APR_SUCCESS;
-}    
+}