Return-Path: Delivered-To: apmail-apr-dev-archive@apr.apache.org Received: (qmail 28868 invoked by uid 500); 28 Apr 2001 23:14:48 -0000 Mailing-List: contact dev-help@apr.apache.org; run by ezmlm Precedence: bulk List-Post: List-Help: List-Unsubscribe: List-Subscribe: Delivered-To: mailing list dev@apr.apache.org Received: (qmail 28856 invoked from network); 28 Apr 2001 23:14:47 -0000 From: Christian Gross To: dev@apr.apache.org Subject: [PATCH] Read Write Locks for Windows Platform Date: Sat, 28 Apr 2001 19:16:36 -0400 Message-ID: X-Mailer: Forte Agent 1.8/32.548 MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Transfer-Encoding: quoted-printable X-Spam-Rating: h31.sny.collab.net 1.6.2 0/1000/N 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 =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --- 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) =20 /* APR ERROR VALUE TESTS */ #define APR_STATUS_IS_ENOSTAT(s) ((s) =3D=3D 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); =20 /** * 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 @@ =20 #include "apr_lock.h" =20 +#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; }; =20 #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" =20 -APR_DECLARE(apr_status_t) apr_lock_create(apr_lock_t **lock,=20 - apr_locktype_e type,=20 - apr_lockscope_e scope,=20 - 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; =20 - newlock =3D (apr_lock_t *)apr_palloc(cont, sizeof(apr_lock_t)); + newlock =3D (apr_lock_t *) apr_palloc(cont, sizeof(apr_lock_t)); =20 newlock->cntxt =3D cont; /* ToDo: How to handle the case when no context is available?=20 - * How to cleanup the storage properly? - */ + * How to cleanup the storage properly? + */ newlock->fname =3D apr_pstrdup(cont, fname); newlock->type =3D type; newlock->scope =3D scope; + newlock->activeReaders =3D 0; + newlock->activeWriters =3D 0; + newlock->waitingReaders =3D 0; + newlock->waitingWriters =3D 0; + newlock->currOperation =3D DOING_NOTHING; + if (newlock->type =3D=3D APR_READWRITE) { + newlock->blockedReader =3D CreateMutex(NULL, FALSE, NULL); + newlock->blockedWriter =3D CreateMutex(NULL, FALSE, NULL); + } + else { + newlock->blockedReader =3D NULL; + newlock->blockedWriter =3D NULL; + } + sec.nLength =3D sizeof(SECURITY_ATTRIBUTES); sec.lpSecurityDescriptor =3D NULL; =20 if (scope =3D=3D APR_CROSS_PROCESS || scope =3D=3D APR_LOCKALL) { - sec.bInheritHandle =3D TRUE; + sec.bInheritHandle =3D TRUE; } else { - sec.bInheritHandle =3D FALSE; + sec.bInheritHandle =3D FALSE; } =20 if (scope =3D=3D APR_INTRAPROCESS) { - InitializeCriticalSection(&newlock->section); - } else { - newlock->mutex =3D CreateMutex(&sec, FALSE, fname); + InitializeCriticalSection(&newlock->section); + } + else { + newlock->mutex =3D CreateMutex(&sec, FALSE, fname); } *lock =3D newlock; return APR_SUCCESS; } =20 -APR_DECLARE(apr_status_t) apr_lock_child_init(apr_lock_t **lock,=20 - const char *fname,=20 - 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)=20 * on a INTRAPROCESS lock */ - (*lock) =3D (apr_lock_t *)apr_palloc(cont, sizeof(apr_lock_t)); + (*lock) =3D (apr_lock_t *) apr_palloc(cont, sizeof(apr_lock_t)); =20 if ((*lock) =3D=3D NULL) { - return APR_ENOMEM; + return APR_ENOMEM; } (*lock)->fname =3D apr_pstrdup(cont, fname); (*lock)->mutex =3D OpenMutex(MUTEX_ALL_ACCESS, TRUE, fname); - =20 + if ((*lock)->mutex =3D=3D NULL) { - return apr_get_os_error(); + return apr_get_os_error(); } return APR_SUCCESS; } =20 -APR_DECLARE(apr_status_t) apr_lock_acquire(apr_lock_t *lock) +/* + * This is private routine to get the lock=20 + * 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 =3D=3D APR_INTRAPROCESS) { - EnterCriticalSection(&lock->section); - return APR_SUCCESS; - } else { - rv =3D WaitForSingleObject(lock->mutex, INFINITE); - - if (rv =3D=3D WAIT_OBJECT_0 || rv =3D=3D WAIT_ABANDONED) { - return APR_SUCCESS; - } + EnterCriticalSection(&lock->section); + return APR_SUCCESS; + } + else { + rv =3D WaitForSingleObject(lock->mutex, INFINITE); + + if (rv =3D=3D WAIT_OBJECT_0 || rv =3D=3D WAIT_ABANDONED) { + return APR_SUCCESS; + } } return apr_get_os_error(); } =20 -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 =3D=3D APR_INTRAPROCESS) { - LeaveCriticalSection(&lock->section); - return APR_SUCCESS; - } else { - if (ReleaseMutex(lock->mutex) =3D=3D 0) { - return apr_get_os_error(); - } + LeaveCriticalSection(&lock->section); + return APR_SUCCESS; + } + else { + if (ReleaseMutex(lock->mutex) =3D=3D 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 =3D=3D APR_MUTEX) { + return APR_ELOCKTYPE; + } + + rv =3D get_lock(lock); + if (rv !=3D APR_SUCCESS) { + return rv; + } + + if (lock->activeReaders =3D=3D 0 && lock->activeWriters =3D=3D 0) { + /* there is no active reader or writer, OK to start writing */ + lock->activeWriters =3D 1; + lock->currOperation =3D 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 =3D=3D APR_MUTEX) { + return APR_ELOCKTYPE; + } + + rv =3D get_lock(lock); + if (rv !=3D 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 =3D IS_WRITING; + release_lock(lock); } return APR_SUCCESS; } =20 -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 =3D=3D 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 =3D=3D APR_MUTEX) { + return release_lock(lock); + } + else { + rv =3D get_lock(lock); + if (rv !=3D APR_SUCCESS) { + return rv; + } + if (lock->currOperation =3D=3D IS_READING) { + lock->activeReaders--; + /* last reader thread to finish reading needs=20 + * to activate a waiting writer=20 + */ + if (lock->activeReaders =3D=3D 0 && lock->waitingWriters > 0) { + lock->activeWriters =3D 1; + lock->waitingWriters--; + ReleaseMutex(lock->blockedWriter); + } + } + else if (lock->currOperation =3D=3D IS_WRITING) { + lock->activeWriters =3D 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,=20 + * release 1 writer from write queue + */ + lock->waitingWriters--; + ReleaseMutex(lock->blockedWriter); + } + } + lock->currOperation =3D DOING_NOTHING; + release_lock(lock); + } + return APR_SUCCESS; +} + +APR_DECLARE(apr_status_t) apr_lock_destroy(apr_lock_t * lock) { if (lock->scope =3D=3D APR_INTRAPROCESS) { - DeleteCriticalSection(&lock->section); - return APR_SUCCESS; - } else { - if (CloseHandle(lock->mutex) =3D=3D 0) { - return apr_get_os_error(); - } + DeleteCriticalSection(&lock->section); + } + else { + if (CloseHandle(lock->mutex) =3D=3D 0) { + return apr_get_os_error(); + } + } + + if (lock->type =3D=3D APR_READWRITE) { + CloseHandle(lock->blockedReader); + CloseHandle(lock->blockedWriter); } return APR_SUCCESS; } =20 -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); } =20 -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); } =20 -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 =3D lock->mutex; return APR_SUCCESS; } =20 -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 =3D=3D NULL) { - return APR_ENOPOOL; + return APR_ENOPOOL; } if ((*lock) =3D=3D NULL) { - (*lock) =3D (apr_lock_t *)apr_palloc(cont, sizeof(apr_lock_t)); - (*lock)->cntxt =3D cont; + (*lock) =3D (apr_lock_t *) apr_palloc(cont, sizeof(apr_lock_t)); + (*lock)->cntxt =3D cont; } (*lock)->mutex =3D *thelock; return APR_SUCCESS; -} =20 +}