* Sasha Mikheev
* Doug Hay, 1998
* Steve McClure, 1998
- * Markus Armbruster, 2005-2006
+ * Markus Armbruster, 2005-2007
*/
/*
/* 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
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 */
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 */
*/
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.
struct lwpProc;
struct lwpSem;
+struct lwp_rwlock;
#define LWP_FD_READ 0x1
#define LWP_FD_WRITE 0x2
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
*
* Known contributors to this file:
* Sasha Mikheev
- * Markus Armbruster, 2006
+ * Markus Armbruster, 2006-2007
*/
#include <config.h>
{
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);
+}
* Known contributors to this file:
* Doug Hay, 1998
* Steve McClure, 1998
- * Ron Koenderink, 2004-2005
+ * Ron Koenderink, 2004-2007
*/
/*
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. */
* 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) {
}
}
- /* 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. */
srand(now ^ (unsigned)pThread);
/* Switch to this thread context */
- loc_RunThisThread();
+ loc_RunThisThread(NULL);
/* Run the thread. */
if (pThread->pfnEntry)
TlsSetValue(dwTLSIndex, pThread);
/* Make this the running thread. */
- loc_RunThisThread();
+ loc_RunThisThread(NULL);
logerror("NT pthreads initialized");
return 0;
empth_yield(void)
{
loc_BlockThisThread();
- loc_RunThisThread();
+ loc_RunThisThread(NULL);
}
/************************
WSACloseEvent(hEventObject[0]);
- loc_RunThisThread();
+ loc_RunThisThread(NULL);
}
/************************
loc_debug("sleep done. Waiting to run.");
- loc_RunThisThread();
+ loc_RunThisThread(NULL);
}
/************************
/* Get the MUTEX semaphore, wait the number of MS */
WaitForSingleObject(hShutdownEvent, INFINITE);
- loc_RunThisThread();
+ loc_RunThisThread(NULL);
return 0;
}
} 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);
}
* 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: */
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;
} 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);
+}
--- /dev/null
+/*
+ * 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();
+ }
+}