(loc_RunThread, empth_init, empth_threadMain, empth_yield)

(empth_select, empth_sleep, empth_wait_for_signal)
(empth_sem_wait) [EMPTH_W32]: Add the ability to wait additional
event in loc_RunThread() when acquiring the hThread mutex.

(empth_rwlock_t, empth_rwlock_create, empth_rwlock_destroy)
(empth_rwlock_wrlock, empth_rwlock_rdlock, empth_rwlock_unlock): New.

(empth_rwlock_t, empth_rwlock_create, empth_rwlock_destroy)
(empth_rwlock_wrlock, empth_rwlock_rdlock, empth_rwlock_unlock)
[EMPTH_W32]: WIN32 implementation.

(empth_rwlock_t, empth_rwlock_create, empth_rwlock_destroy)
(empth_rwlock_wrlock, empth_rwlock_rdlock, empth_rwlock_unlock)
(lwp_rwlock, lwp_rwlock_create, lwp_rwlock_destroy)
(lwp_rwlock_wrlock, lwp_rwlock_rdlock, lwp_rwlock_unlock)
[EMPTH_LWP]: LWP implementation.

(empth_rwlock_t, empth_rwlock_create, empth_rwlock_destroy)
(empth_rwlock_wrlock, empth_rwlock_rdlock, empth_rwlock_unlock)
[EMPTH_POSIX]: pthread implementation.
This commit is contained in:
Ron Koenderink 2007-01-15 12:45:35 +00:00
parent 8c65fbc2be
commit 71e0f98825
6 changed files with 409 additions and 14 deletions

View file

@ -31,7 +31,7 @@
* Sasha Mikheev
* Doug Hay, 1998
* Steve McClure, 1998
* Markus Armbruster, 2005-2006
* Markus Armbruster, 2005-2007
*/
/*
@ -74,6 +74,9 @@ typedef struct lwpProc empth_t;
/* empth_sem_t * represents a semaphore */
typedef struct lwpSem empth_sem_t;
/* empth_rwlock_t * represents a read-write lock */
typedef struct lwp_rwlock empth_rwlock_t;
/* Flags for empth_select(): whether to sleep on input or output */
#define EMPTH_FD_READ LWP_FD_READ
#define EMPTH_FD_WRITE LWP_FD_WRITE
@ -95,6 +98,7 @@ typedef struct lwpSem empth_sem_t;
typedef struct empth_t empth_t;
typedef struct empth_sem_t empth_sem_t;
typedef struct empth_rwlock_t empth_rwlock_t;
#endif /* EMPTH_POSIX */
@ -108,6 +112,7 @@ typedef struct empth_sem_t empth_sem_t;
typedef struct loc_Thread_t empth_t;
typedef struct loc_Sem_t empth_sem_t;
typedef struct loc_RWLock_t empth_rwlock_t;
void empth_request_shutdown(void);
#endif /* EMPTH_W32 */
@ -220,6 +225,42 @@ void empth_sem_signal(empth_sem_t *sem);
*/
void empth_sem_wait(empth_sem_t *sem);
/*
* Create a read-write lock.
* NAME is its name, it is used for debugging.
* Return the reade-write lock, or NULL on error.
*/
empth_rwlock_t *empth_rwlock_create(char *name);
/*
* Destroy RWLOCK.
*/
void empth_rwlock_destroy(empth_rwlock_t *rwlock);
/*
* Lock RWLOCK for writing.
* A read-write lock can be locked for writing only when it is
* unlocked. If this is not the case, put the current thread to sleep
* until it is.
*/
void empth_rwlock_wrlock(empth_rwlock_t *rwlock);
/*
* Lock RWLOCK for reading.
* A read-write lock can be locked for reading only when it is not
* locked for writing. If this is not the case, put the current
* thread to sleep until it is. Must not starve writers, and may
* sleep to avoid that.
*/
void empth_rwlock_rdlock(empth_rwlock_t *rwlock);
/*
* Unlock read-write lock RWLOCK.
* The current thread must hold RWLOCK.
* Wake up threads that can now lock it.
*/
void empth_rwlock_unlock(empth_rwlock_t *rwlock);
/*
* Stuff for implementations, not for clients.

View file

@ -31,6 +31,7 @@
struct lwpProc;
struct lwpSem;
struct lwp_rwlock;
#define LWP_FD_READ 0x1
#define LWP_FD_WRITE 0x2
@ -57,6 +58,12 @@ struct lwpSem *lwpCreateSem(char *name, int count);
void lwpSignal(struct lwpSem *);
void lwpWait(struct lwpSem *);
struct lwp_rwlock *lwp_rwlock_create(char *);
void lwp_rwlock_destroy(struct lwp_rwlock *);
void lwp_rwlock_wrlock(struct lwp_rwlock *);
void lwp_rwlock_rdlock(struct lwp_rwlock *);
void lwp_rwlock_unlock(struct lwp_rwlock *);
extern struct lwpProc *LwpCurrent;
#endif

View file

@ -29,7 +29,7 @@
*
* Known contributors to this file:
* Sasha Mikheev
* Markus Armbruster, 2006
* Markus Armbruster, 2006-2007
*/
#include <config.h>
@ -149,3 +149,33 @@ empth_sem_wait(empth_sem_t *sm)
{
lwpWait(sm);
}
empth_rwlock_t *
empth_rwlock_create(char *name)
{
return lwp_rwlock_create(name);
}
void
empth_rwlock_destroy(empth_rwlock_t *rwlock)
{
lwp_rwlock_destroy(rwlock);
}
void
empth_rwlock_wrlock(empth_rwlock_t *rwlock)
{
lwp_rwlock_wrlock(rwlock);
}
void
empth_rwlock_rdlock(empth_rwlock_t *rwlock)
{
lwp_rwlock_rdlock(rwlock);
}
void
empth_rwlock_unlock(empth_rwlock_t *rwlock)
{
lwp_rwlock_unlock(rwlock);
}

View file

@ -30,7 +30,7 @@
* Known contributors to this file:
* Doug Hay, 1998
* Steve McClure, 1998
* Ron Koenderink, 2004-2005
* Ron Koenderink, 2004-2007
*/
/*
@ -108,6 +108,53 @@ struct loc_Sem_t {
int count;
};
/************************
* loc_RWLock_t
*
* Invariants
* must hold at function call, return, sleep
* and resume from sleep.
*
* any state:
* nwrite >= 0
* nread >= 0
* if unlocked:
* can_read set
* can_write set
* nwrite == 0
* nread == 0
*
* if read-locked without writers contending:
* can_read set
* can_write clear
* nwrite == 0
* nread > 0
*
* if read-locked with writers contending:
* can_read clear
* can_write clear
* nwrite > 0 #writers blocked
* nread > 0
*
* if write-locked:
* can_read clear
* can_write clear
* nwrite > 0 #writers blocked + 1
* nread == 0
*
* To ensure consistency, state normally changes only while the
* thread changing it holds hThreadMutex.
*
*/
struct loc_RWLock_t {
char name[17]; /* The thread name, passed in at create time. */
HANDLE can_read; /* Manual event -- allows read locks */
HANDLE can_write; /* Auto-reset event -- allows write locks */
int nread; /* number of active readers */
int nwrite; /* total number of writers (active and waiting) */
};
/* This is the thread exclusion/non-premption mutex. */
/* The running thread has this MUTEX, and all others are */
/* either blocked on it, or waiting for some OS response. */
@ -202,8 +249,10 @@ loc_FreeThreadInfo(empth_t *pThread)
* info, and the thread owns the MUTEX sem.
*/
static void
loc_RunThisThread(void)
loc_RunThisThread(HANDLE hWaitObject)
{
HANDLE hWaitObjects[2];
empth_t *pThread = TlsGetValue(dwTLSIndex);
if (pThread->bKilled) {
@ -214,8 +263,11 @@ loc_RunThisThread(void)
}
}
/* Get the MUTEX semaphore, wait forever. */
WaitForSingleObject(hThreadMutex, INFINITE);
hWaitObjects[0] = hThreadMutex;
hWaitObjects[1] = hWaitObject;
WaitForMultipleObjects(hWaitObject ? 2 : 1, hWaitObjects,
TRUE, INFINITE);
if (!pCurThread) {
/* Set the globals to this thread. */
@ -306,7 +358,7 @@ empth_threadMain(void *pvData)
srand(now ^ (unsigned)pThread);
/* Switch to this thread context */
loc_RunThisThread();
loc_RunThisThread(NULL);
/* Run the thread. */
if (pThread->pfnEntry)
@ -375,7 +427,7 @@ empth_init(void **ctx_ptr, int flags)
TlsSetValue(dwTLSIndex, pThread);
/* Make this the running thread. */
loc_RunThisThread();
loc_RunThisThread(NULL);
logerror("NT pthreads initialized");
return 0;
@ -481,7 +533,7 @@ void
empth_yield(void)
{
loc_BlockThisThread();
loc_RunThisThread();
loc_RunThisThread(NULL);
}
/************************
@ -534,7 +586,7 @@ empth_select(int fd, int flags)
WSACloseEvent(hEventObject[0]);
loc_RunThisThread();
loc_RunThisThread(NULL);
}
/************************
@ -570,7 +622,7 @@ empth_sleep(time_t until)
loc_debug("sleep done. Waiting to run.");
loc_RunThisThread();
loc_RunThisThread(NULL);
}
/************************
@ -593,7 +645,7 @@ empth_wait_for_signal(void)
/* Get the MUTEX semaphore, wait the number of MS */
WaitForSingleObject(hShutdownEvent, INFINITE);
loc_RunThisThread();
loc_RunThisThread(NULL);
return 0;
}
@ -670,5 +722,81 @@ empth_sem_wait(empth_sem_t *pSem)
} else
ReleaseMutex(pSem->hMutex);
loc_RunThisThread();
loc_RunThisThread(NULL);
}
empth_rwlock_t *
empth_rwlock_create(char *name)
{
empth_rwlock_t *rwlock;
rwlock = malloc(sizeof(*rwlock));
if (!rwlock)
return NULL;
memset(rwlock, 0, sizeof(*rwlock));
strncpy(rwlock->name, name, sizeof(rwlock->name) - 1);
if ((rwlock->can_read = CreateEvent(NULL, TRUE, TRUE, NULL)) == NULL) {
logerror("rwlock_create: failed to create reader event %s at %s:%d",
name, __FILE__, __LINE__);
free(rwlock);
return NULL;
}
if ((rwlock->can_write = CreateEvent(NULL, FALSE, TRUE, NULL)) == NULL) {
logerror("rwlock_create: failed to create writer event %s at %s:%d",
name, __FILE__, __LINE__);
CloseHandle(rwlock->can_read);
free(rwlock);
return NULL;
}
return rwlock;
}
void
empth_rwlock_destroy(empth_rwlock_t *rwlock)
{
if (CANT_HAPPEN(rwlock->nread || rwlock->nwrite))
return;
CloseHandle(rwlock->can_read);
CloseHandle(rwlock->can_write);
free(rwlock);
}
void
empth_rwlock_wrlock(empth_rwlock_t *rwlock)
{
/* block any new readers */
ResetEvent(rwlock->can_read);
rwlock->nwrite++;
loc_BlockThisThread();
loc_RunThisThread(rwlock->can_write);
CANT_HAPPEN(rwlock->nread != 0);
}
void
empth_rwlock_rdlock(empth_rwlock_t *rwlock)
{
loc_BlockThisThread();
loc_RunThisThread(rwlock->can_read);
ResetEvent(rwlock->can_write);
rwlock->nread++;
}
void
empth_rwlock_unlock(empth_rwlock_t *rwlock)
{
if (CANT_HAPPEN(!rwlock->nread && !rwlock->nwrite))
return;
if (rwlock->nread) { /* holding read lock */
rwlock->nread--;
if (rwlock->nread == 0)
SetEvent(rwlock->can_write);
} else {
rwlock->nwrite--;
SetEvent(rwlock->can_write);
}
if (rwlock->nwrite == 0)
SetEvent(rwlock->can_read);
}

View file

@ -30,7 +30,8 @@
* Known contributors to this file:
* Sasha Mikheev
* Steve McClure, 1998
* Markus Armbruster, 2005-2006
* Markus Armbruster, 2005-2007
* Ron Koenderink, 2007
*/
/* Required for PTHREAD_STACK_MIN on some systems, e.g. Solaris: */
@ -75,6 +76,11 @@ struct empth_sem_t {
pthread_cond_t cnd_sem;
};
struct empth_rwlock_t {
char *name;
pthread_rwlock_t lock;
};
/* Thread-specific data key */
static pthread_key_t ctx_key;
@ -455,3 +461,52 @@ empth_sem_wait(empth_sem_t *sm)
} else
pthread_mutex_unlock(&sm->mtx_update);
}
empth_rwlock_t *
empth_rwlock_create(char *name)
{
empth_rwlock_t *rwlock;
rwlock = malloc(sizeof(*rwlock));
if (!rwlock)
return NULL;
if (pthread_rwlock_init(&rwlock->lock, NULL) != 0) {
free(rwlock);
return NULL;
}
rwlock->name = strdup(name);
return rwlock;
}
void
empth_rwlock_destroy(empth_rwlock_t *rwlock)
{
pthread_rwlock_destroy(&rwlock->lock);
free(rwlock);
}
void
empth_rwlock_wrlock(empth_rwlock_t *rwlock)
{
pthread_mutex_unlock(&mtx_ctxsw);
pthread_rwlock_wrlock(&rwlock->lock);
pthread_mutex_lock(&mtx_ctxsw);
empth_restorectx();
}
void
empth_rwlock_rdlock(empth_rwlock_t *rwlock)
{
pthread_mutex_unlock(&mtx_ctxsw);
pthread_rwlock_rdlock(&rwlock->lock);
pthread_mutex_lock(&mtx_ctxsw);
empth_restorectx();
}
void
empth_rwlock_unlock(empth_rwlock_t *rwlock)
{
pthread_rwlock_unlock(&rwlock->lock);
}

134
src/lib/lwp/rwlock.c Normal file
View file

@ -0,0 +1,134 @@
/*
* Empire - A multi-player, client/server Internet based war game.
* Copyright (C) 1994-2007, Dave Pare, Jeff Bailey, Thomas Ruschak,
* Ken Stevens, Steve McClure
* Copyright (C) 1991-3 Stephen Crane
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* ---
*
* See files README, COPYING and CREDITS in the root of the source
* tree for related information and legal notices. It is expected
* that future projects/authors will amend these files as needed.
*
* ---
*
* rwlock.c: Read-write locks
*
* Known contributors to this file:
* Ron Koenderink, 2007
* Markus Armbruster, 2007
*/
#include <config.h>
#include <stdlib.h>
#include <string.h>
#include "lwp.h"
#include "lwpint.h"
struct lwp_rwlock {
/*
* Lock counter
* 0: unlocked
* -1: locked for writing
* >0: locked for reading that many times
*/
int count;
struct lwpQueue rq; /* read lock sleepers */
struct lwpQueue wq; /* write lock sleepers */
char *name;
};
struct lwp_rwlock *
lwp_rwlock_create(char *name)
{
struct lwp_rwlock *rwlock;
rwlock = malloc(sizeof(*rwlock));
if (!rwlock)
return NULL;
memset(rwlock, 0, sizeof(*rwlock));
rwlock->name = strdup(name);
return rwlock;
}
void
lwp_rwlock_destroy(struct lwp_rwlock *rwlock)
{
if (CANT_HAPPEN(rwlock->count))
return;
free(rwlock);
}
void
lwp_rwlock_wrlock(struct lwp_rwlock *rwlock)
{
if (rwlock->count) {
lwpAddTail(&rwlock->wq, LwpCurrent);
lwpStatus(LwpCurrent, "blocked to acquire rwlock %s for writing",
rwlock->name);
lwpReschedule();
}
CANT_HAPPEN(rwlock->count != 0);
rwlock->count = -1;
lwpStatus(LwpCurrent, "acquired rwlock %s for writing", rwlock->name);
}
void
lwp_rwlock_rdlock(struct lwp_rwlock *rwlock)
{
if (rwlock->count < 0 || rwlock->wq.head) {
lwpStatus(LwpCurrent, "blocked to acquire rwlock %s for reading",
rwlock->name);
lwpAddTail(&rwlock->rq, LwpCurrent);
lwpReschedule();
}
CANT_HAPPEN(rwlock->count < 0);
rwlock->count++;
lwpStatus(LwpCurrent, "acquired rwlock %s for reading", rwlock->name);
}
void
lwp_rwlock_unlock(struct lwp_rwlock *rwlock)
{
struct lwpProc *p;
lwpStatus(LwpCurrent, "unlocking rwlock %s", rwlock->name);
if (CANT_HAPPEN(rwlock->count == 0))
return;
if (rwlock->count < 0)
rwlock->count = 0;
else
rwlock->count--;
if (rwlock->count == 0 && rwlock->wq.head) {
p = lwpGetFirst(&rwlock->wq);
lwpStatus(p, "wake up next writer of rwlock %s", rwlock->name);
} else if (rwlock->count >= 0 && rwlock->rq.head && !rwlock->wq.head) {
p = lwpGetFirst(&rwlock->rq);
lwpStatus(p, "wake up next reader of rwlock %s", rwlock->name);
} else
return;
lwpReady(p);
if (LwpCurrent->pri < p->pri) {
lwpStatus(LwpCurrent, "yielding to thread with higher priority");
lwpYield();
}
}