/*
* Empire - A multi-player, client/server Internet based war game.
- * Copyright (C) 1986-2004, Dave Pare, Jeff Bailey, Thomas Ruschak,
+ * Copyright (C) 1986-2009, Dave Pare, Jeff Bailey, Thomas Ruschak,
* Ken Stevens, Steve McClure
*
* This program is free software; you can redistribute it and/or modify
*
* ---
*
- * See the "LEGAL", "LICENSE", "CREDITS" and "README" files for all the
- * related information and legal notices. It is expected that any future
- * projects/authors will amend these files as needed.
+ * 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.
*
* ---
*
* ntthread.c: Interface from Empire threads to Windows NT threads
- *
+ *
* Known contributors to this file:
* Doug Hay, 1998
* Steve McClure, 1998
+ * Ron Koenderink, 2004-2009
*/
/*
* EMPTHREADs for Windows NT.
*
- * Actually, threads for any Win32 platform,
- * like Win95, Win98, WinCE, and whatever other
- * toy OSs are in our future from Microsoft.
- *
- * WIN32 has a full pre-emptive threading environment.
- * But, Empire can not handle pre-emptive threading.
- * Thus, we will use the threads, but limit the preemption
- * using a Mutex semaphore.
+ * Actually, threads for any Win32 platform, like Win95, Win98, WinCE,
+ * and whatever other toy OSs are in our future from Microsoft.
*
+ * WIN32 has a full pre-emptive threading environment. But Empire can
+ * not handle pre-emptive threading. Thus, we will use the threads,
+ * but limit the preemption using a Mutex.
*/
+#include <config.h>
+
+#include <errno.h>
+#include <signal.h>
#include <stdio.h>
+#include <stdarg.h>
#include <sys/types.h>
-#include <signal.h>
-#include <errno.h>
+#include <time.h>
+#include <windows.h>
+#include <process.h>
#include "misc.h"
#include "empthread.h"
+#include "file.h"
#include "prototypes.h"
-
-#if defined(_WIN32) && defined(_EMPTH_WIN32)
-#include <windows.h>
-#include <process.h>
-
+#include "server.h"
+#include "sys/socket.h"
#define loc_MIN_THREAD_STACK 16384
/************************
- * loc_Thread_t
- *
- * The REAL empth_t thread structure.
- * The external world only gets
- * a void pointer to this.
+ * loc_Thread
*/
-typedef struct loc_Thread_t {
+struct loc_Thread {
/* The thread name, passed in at create time. */
- char szName[17];
- /* The thread description, passed in at create time. */
- char szDesc[80];
+ char *szName;
/* True if this is the main line, and not a real thread. */
BOOL bMainThread;
/* The user data passed in at create time. */
void *pvUserData;
- /* True if this thread has been killed. */
- BOOL bKilled;
-
/* The entry function for the thread. */
void (*pfnEntry) (void *);
/* The system thread ID. */
unsigned long ulThreadID;
- /* An Event sem that the thread will wait/sleep on. */
+ /* An Mutex that the thread will wait/sleep on. */
HANDLE hThreadEvent;
-} loc_Thread_t;
+};
/************************
- * loc_Sem_t
+ * loc_RWLock
+ *
+ * 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.
*
- * The REAL empth_sem_t structure.
- * The external world only gets
- * a void pointer to this.
*/
-typedef struct empth_sem_t {
+struct loc_RWLock {
+ char *name; /* The lock 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) */
+};
- char szName[17];
+/* 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. */
+static HANDLE hThreadMutex;
- /* An exclusion semaphore for this sem. */
- HANDLE hMutex;
- /* An Event sem that the thread(s) will sleep on. */
+/* This is the thread startup event. */
+/* We use this to lockstep when we are starting up threads. */
+static HANDLE hThreadStartEvent;
- HANDLE hEvent;
- /* The count variable */
- int count;
-} loc_Sem_t;
+/* This is an event used to wakeup the main thread */
+/* to start the shutdown sequence. */
+static HANDLE hShutdownEvent;
-static struct {
- /* 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. */
- HANDLE hThreadMutex;
+/* The Thread Local Storage index. We store the pThread pointer */
+/* for each thread at this index. */
+static DWORD dwTLSIndex;
- /* This is the thread startup event sem. */
- /* We use this to lockstep when we are starting up threads. */
- HANDLE hThreadStartEvent;
+/* The current running thread. */
+static empth_t *pCurThread;
- /* The Thread Local Storage index. We store the pThread pointer */
- /* for each thread at this index. */
- DWORD dwTLSIndex;
+/* Ticks at start */
+static unsigned long ulTickAtStart;
- /* The current running thread. */
- loc_Thread_t *pCurThread;
+/* Pointer out to global context. "player". */
+/* From empth_init parameter. */
+static void **ppvUserData;
- /* Ticks at start */
- unsigned long ulTickAtStart;
-
- /* Pointer out to global context. "player". */
- /* From empth_init parameter. */
- char **ppvUserData;
-
- /* Global flags. From empth_init parameter. */
- int flags;
-} loc_GVAR;
+/* Global flags. From empth_init parameter. */
+static int global_flags;
+static void loc_debug(const char *, ...) ATTRIBUTE((format(printf, 1, 2)));
/************************
* loc_debug
unsigned long ulCurTick;
unsigned long ulRunTick;
unsigned long ulMs, ulSec, ulMin, ulHr;
- loc_Thread_t *pThread =
- (loc_Thread_t *)TlsGetValue(loc_GVAR.dwTLSIndex);
+ empth_t *pThread = TlsGetValue(dwTLSIndex);
char buf[1024];
- if ((loc_GVAR.flags & EMPTH_PRINT) != 0) {
+ if ((global_flags & EMPTH_PRINT) != 0) {
/* Ticks are in milliseconds */
ulCurTick = GetTickCount();
- ulRunTick = ulCurTick - loc_GVAR.ulTickAtStart;
+ ulRunTick = ulCurTick - ulTickAtStart;
ulMs = ulRunTick % 1000L;
ulSec = (ulRunTick / 1000L) % 60L;
ulMin = (ulRunTick / (60L * 1000L)) % 60L;
/************************
* loc_FreeThreadInfo
- *
*/
static void
-loc_FreeThreadInfo(loc_Thread_t *pThread)
+loc_FreeThreadInfo(empth_t *pThread)
{
if (pThread) {
if (pThread->hThreadEvent)
CloseHandle(pThread->hThreadEvent);
- memset(pThread, 0, sizeof(*pThread));
+ if (pThread->szName != NULL)
+ free(pThread->szName);
free(pThread);
}
}
* loc_RunThisThread
*
* This thread wants to run.
- * When this function returns, the
- * globals are set to this thread info,
- * and the thread owns the MUTEX sem.
+ * When this function returns, the globals are set to this thread
+ * info, and the thread owns the MUTEX.
*/
static void
-loc_RunThisThread()
+loc_RunThisThread(HANDLE hWaitObject)
{
- loc_Thread_t *pThread =
- (loc_Thread_t *)TlsGetValue(loc_GVAR.dwTLSIndex);
-
- if (pThread->bKilled) {
- if (!pThread->bMainThread) {
- TlsSetValue(loc_GVAR.dwTLSIndex, NULL);
- loc_FreeThreadInfo(pThread);
- _endthread();
- }
- }
+ HANDLE hWaitObjects[2];
+
+ empth_t *pThread = TlsGetValue(dwTLSIndex);
- /* Get the MUTEX semaphore, wait forever. */
- WaitForSingleObject(loc_GVAR.hThreadMutex, INFINITE);
+ hWaitObjects[0] = hThreadMutex;
+ hWaitObjects[1] = hWaitObject;
- if (!loc_GVAR.pCurThread) {
+ WaitForMultipleObjects(hWaitObject ? 2 : 1, hWaitObjects,
+ TRUE, INFINITE);
+
+ if (!pCurThread) {
/* Set the globals to this thread. */
- *loc_GVAR.ppvUserData = pThread->pvUserData;
+ *ppvUserData = pThread->pvUserData;
- loc_GVAR.pCurThread = pThread;
+ pCurThread = pThread;
} else {
/* Hmm, a problem, eh? */
logerror("RunThisThread, someone already running.");
* This thread was running. It no longer wants to.
*/
static void
-loc_BlockThisThread()
+loc_BlockThisThread(void)
{
- loc_Thread_t *pThread =
- (loc_Thread_t *)TlsGetValue(loc_GVAR.dwTLSIndex);
+ empth_t *pThread = TlsGetValue(dwTLSIndex);
- if (loc_GVAR.pCurThread == pThread) {
+ if (pCurThread == pThread) {
/* Reset the globals back to original */
- loc_GVAR.pCurThread = NULL;
- *loc_GVAR.ppvUserData = NULL;
+ pCurThread = NULL;
+ *ppvUserData = NULL;
/* Release the MUTEX */
- ReleaseMutex(loc_GVAR.hThreadMutex);
+ ReleaseMutex(hThreadMutex);
} else {
/* Hmm, this thread was not the running one. */
logerror("BlockThisThread, not running.");
}
}
-
/************************
- * loc_SleepThisThread
+ * loc_Exit_Handler
+ *
+ * Ctrl-C, Ctrl-Break, Window-Closure, User-Logging-Off or
+ * System-Shutdown will initiate a shutdown.
+ * This is done by calling empth_request_shutdown()
*/
-static void
-loc_SleepThisThread(unsigned long ulMillisecs)
+static BOOL WINAPI
+loc_Exit_Handler(DWORD fdwCtrlType)
{
- loc_Thread_t *pThread =
- (loc_Thread_t *)TlsGetValue(loc_GVAR.dwTLSIndex);
-
- /* Make sure the event thread is clean. */
- ResetEvent(pThread->hThreadEvent);
-
- /* Get the MUTEX semaphore, wait the number of MS */
- WaitForSingleObject(pThread->hThreadEvent, ulMillisecs);
+ switch (fdwCtrlType) {
+ case CTRL_C_EVENT:
+ case CTRL_CLOSE_EVENT:
+ case CTRL_BREAK_EVENT:
+ case CTRL_LOGOFF_EVENT:
+ case CTRL_SHUTDOWN_EVENT:
+ empth_request_shutdown();
+ return TRUE;
+ default:
+ return FALSE;
+ }
}
-
/************************
* empth_threadMain
*
* This is the main line of each thread.
* This is really a static local func....
+ * Note: As the POSIX compatibility layer is not thread safe
+ * this function can not open or create any files or sockets until
+ * loc_RunThisThread() is called
*/
-void
+static void
empth_threadMain(void *pvData)
{
- time_t now;
-
- loc_Thread_t *pThread = (loc_Thread_t *)pvData;
+ empth_t *pThread = pvData;
/* Out of here... */
if (!pvData)
return;
/* Store pThread on this thread. */
- TlsSetValue(loc_GVAR.dwTLSIndex, pvData);
+ TlsSetValue(dwTLSIndex, pvData);
/* Get the ID of the thread. */
pThread->ulThreadID = GetCurrentThreadId();
/* Signal that the thread has started. */
- SetEvent(loc_GVAR.hThreadStartEvent);
-
- /* seed the rand() function */
- time(&now);
- srand(now ^ (unsigned int)pThread);
+ SetEvent(hThreadStartEvent);
/* Switch to this thread context */
- loc_RunThisThread();
+ loc_RunThisThread(NULL);
/* Run the thread. */
if (pThread->pfnEntry)
*
* Initialize the thread environment.
*
- * This is called from the program
- * main line.
+ * This is called from the program main line.
*/
int
-empth_init(char **ctx_ptr, int flags)
+empth_init(void **ctx_ptr, int flags)
{
- loc_Thread_t *pThread = NULL;
+ empth_t *pThread = NULL;
- loc_GVAR.ulTickAtStart = GetTickCount();
- loc_GVAR.ppvUserData = ctx_ptr;
- loc_GVAR.flags = flags;
- loc_GVAR.dwTLSIndex = TlsAlloc();
+ ulTickAtStart = GetTickCount();
+ ppvUserData = ctx_ptr;
+ global_flags = flags;
+ dwTLSIndex = TlsAlloc();
- /* Create the thread mutex sem. */
+ /* Create the thread mutex. */
/* Initally unowned. */
- loc_GVAR.hThreadMutex = CreateMutex(NULL, FALSE, NULL);
- if (!loc_GVAR.hThreadMutex) {
- logerror("Failed to create mutex");
+ hThreadMutex = CreateMutex(NULL, FALSE, NULL);
+ if (!hThreadMutex) {
+ logerror("Failed to create mutex %lu", GetLastError());
return 0;
}
- /* Create the thread start event sem. */
+ /* Create the thread start event. */
/* Automatic state reset. */
- loc_GVAR.hThreadStartEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
- if (!loc_GVAR.hThreadStartEvent) {
- logerror("Failed to create mutex");
+ hThreadStartEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+ if (!hThreadStartEvent) {
+ logerror("Failed to create start event %lu", GetLastError());
return 0;
}
+ /* Create the shutdown event for the main thread. */
+ /* Manual reset */
+ hShutdownEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+ if (!hShutdownEvent) {
+ logerror("Failed to create shutdown event %lu", GetLastError());
+ return 0;
+ }
+ SetConsoleCtrlHandler(loc_Exit_Handler, TRUE);
+
/* Create the global Thread context. */
- pThread = (loc_Thread_t *)malloc(sizeof(*pThread));
+ pThread = malloc(sizeof(*pThread));
if (!pThread) {
logerror("not enough memory to create main thread.");
return 0;
}
memset(pThread, 0, sizeof(*pThread));
- strncpy(pThread->szName, "Main", sizeof(pThread->szName) - 1);
- strncpy(pThread->szDesc, "The main process",
- sizeof(pThread->szDesc) - 1);
+ pThread->szName = strdup("Main");
pThread->ulThreadID = GetCurrentThreadId();
pThread->bMainThread = TRUE;
- TlsSetValue(loc_GVAR.dwTLSIndex, pThread);
+ TlsSetValue(dwTLSIndex, pThread);
/* Make this the running thread. */
- loc_RunThisThread();
+ loc_RunThisThread(NULL);
logerror("NT pthreads initialized");
return 0;
*
* Create a new thread.
*
- * prio - priority, not particularly useful in our context.
* entry - entry point function for thread.
* size - stack size.
* flags - debug control.
* LWP_STACKCHECK - not needed
* name - name of the thread, for debug.
- * desc - description of thread, for debug.
* ud - "user data". The "ctx_ptr" gets this value
* when the thread is active.
* It is also passed to the entry function...
*/
empth_t *
-empth_create(int prio, void (*entry)(void *), int size, int flags,
- char *name, char *desc, void *ud)
+empth_create(void (*entry)(void *), int size, int flags,
+ char *name, void *ud)
{
- loc_Thread_t *pThread = NULL;
+ empth_t *pThread = NULL;
- loc_debug("creating new thread %s:%s", name, desc);
+ loc_debug("creating new thread %s", name);
+ ef_make_stale();
- pThread = (loc_Thread_t *)malloc(sizeof(*pThread));
+ pThread = malloc(sizeof(*pThread));
if (!pThread) {
- logerror("not enough memory to create thread: %s (%s)", name,
- desc);
+ logerror("not enough memory to create thread %s", name);
return NULL;
}
memset(pThread, 0, sizeof(*pThread));
- strncpy(pThread->szName, name, sizeof(pThread->szName) - 1);
- strncpy(pThread->szDesc, desc, sizeof(pThread->szDesc) - 1);
+ pThread->szName = strdup(name);
pThread->pvUserData = ud;
pThread->pfnEntry = entry;
pThread->bMainThread = FALSE;
- /* Create thread event sem, auto reset. */
+ /* Create thread event, auto reset. */
pThread->hThreadEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
if (size < loc_MIN_THREAD_STACK)
size = loc_MIN_THREAD_STACK;
- pThread->ulThreadID = _beginthread(empth_threadMain, size,
- (void *)pThread);
- if (pThread->ulThreadID == -1) {
- logerror("can not create thread: %s (%s): %s", name, desc,
- strerror(errno));
+ pThread->ulThreadID = _beginthread(empth_threadMain, size, pThread);
+ if (pThread->ulThreadID == 1L || pThread->ulThreadID == 0L) {
+ logerror("can not create thread: %s: %s", name, strerror(errno));
goto bad;
}
loc_debug("new thread id is %ld", pThread->ulThreadID);
+ empth_yield();
return pThread;
- bad:
+bad:
if (pThread) {
loc_FreeThreadInfo(pThread);
}
empth_t *
empth_self(void)
{
- loc_Thread_t *pThread =
- (loc_Thread_t *)TlsGetValue(loc_GVAR.dwTLSIndex);
+ empth_t *pThread = TlsGetValue(dwTLSIndex);
return pThread;
}
+/************************
+ * empth_name
+ */
+char *
+empth_name(empth_t *thread)
+{
+ return thread->szName;
+}
+
+/************************
+ * empth_set_name
+ * Set the thread name
+ */
+void
+empth_set_name(empth_t *thread, char *name)
+{
+ if (thread->szName != NULL)
+ free(thread->szName);
+ thread->szName = strdup(name);
+}
+
/************************
* empth_exit
*/
void
empth_exit(void)
{
- loc_Thread_t *pThread =
- (loc_Thread_t *)TlsGetValue(loc_GVAR.dwTLSIndex);
-
- loc_BlockThisThread();
+ empth_t *pThread = TlsGetValue(dwTLSIndex);
loc_debug("empth_exit");
+ ef_make_stale();
+ loc_BlockThisThread();
- if (pThread->bMainThread) {
- char buf[20];
- /* The main line. Wait forever. */
- while (1) {
- printf("\nEmpire Server>");
- fgets(buf, sizeof(buf), stdin);
- if (!strnicmp(buf, "quit", 4))
- shutdwn(0);
- }
- } else {
- TlsSetValue(loc_GVAR.dwTLSIndex, NULL);
- loc_FreeThreadInfo(pThread);
- _endthread();
- }
+ TlsSetValue(dwTLSIndex, NULL);
+ loc_FreeThreadInfo(pThread);
+ _endthread();
}
/************************
void
empth_yield(void)
{
+ ef_make_stale();
loc_BlockThisThread();
- loc_RunThisThread();
-}
-
-/************************
- * empth_terminate
- *
- * Kill off the thread.
- */
-void
-empth_terminate(empth_t *a)
-{
- loc_Thread_t *pThread = (loc_Thread_t *)a;
-
- loc_debug("killing thread %s", pThread->szName);
- pThread->bKilled = TRUE;
-
- SetEvent(pThread->hThreadEvent);
+ Sleep(0);
+ loc_RunThisThread(NULL);
}
/************************
* Do a select on the given file.
* Wait for IO on it.
*
- * This would be one of the main functions used within
- * gen\io.c
+ * This would be one of the main functions used within gen\io.c
*/
-void
-empth_select(int fd, int flags)
+int
+empth_select(int fd, int flags, struct timeval *timeout)
{
- loc_Thread_t *pThread =
- (loc_Thread_t *)TlsGetValue(loc_GVAR.dwTLSIndex);
- fd_set readmask;
- fd_set writemask;
- struct lwpProc *proc;
- struct timeval tv;
- int n;
+ SOCKET sock;
+ WSAEVENT hEventObject[2];
+ long events;
+ DWORD result, msec;
+ empth_t *pThread = TlsGetValue(dwTLSIndex);
+ int res;
loc_debug("%s select on %d",
flags == EMPTH_FD_READ ? "read" : "write", fd);
+ ef_make_stale();
loc_BlockThisThread();
- while (1) {
- tv.tv_sec = 1000000;
- tv.tv_usec = 0;
-
- FD_ZERO(&readmask);
- FD_ZERO(&writemask);
-
- switch (flags) {
- case EMPTH_FD_READ:
- FD_SET(fd, &readmask);
- break;
- case EMPTH_FD_WRITE:
- FD_SET(fd, &writemask);
- break;
- default:
- logerror("bad flag %d passed to empth_select", flags);
- empth_exit();
- }
-
- n = select(fd + 1, &readmask, &writemask, (fd_set *) 0, &tv);
-
- if (n < 0) {
- if (errno == EINTR) {
- /* go handle the signal */
- loc_debug("select broken by signal");
- goto done;
- return;
- }
- /* strange but we dont get EINTR on select broken by signal */
- loc_debug("select failed (%s)", strerror(errno));
- goto done;
- return;
- }
-
- if (flags == EMPTH_FD_READ && FD_ISSET(fd, &readmask)) {
- loc_debug("input ready");
- break;
- }
- if (flags == EMPTH_FD_WRITE && FD_ISSET(fd, &writemask)) {
- loc_debug("output ready");
- break;
- }
+ hEventObject[0] = WSACreateEvent();
+ hEventObject[1] = pThread->hThreadEvent;
+
+ sock = w32_fd2socket(fd);
+ CANT_HAPPEN(sock == (SOCKET)-1);
+
+ events = 0;
+ if (flags & EMPTH_FD_READ)
+ events |= FD_READ | FD_ACCEPT | FD_CLOSE;
+ if (flags & EMPTH_FD_WRITE)
+ events |= FD_WRITE | FD_CLOSE;
+ WSAEventSelect(sock, hEventObject[0], events);
+
+ if (timeout)
+ msec = timeout->tv_sec * 1000L + timeout->tv_usec / 1000L;
+ else
+ msec = WSA_INFINITE;
+ result = WSAWaitForMultipleEvents(2, hEventObject, FALSE, msec, FALSE);
+
+ switch (result) {
+ case WSA_WAIT_EVENT_0:
+ res = 1;
+ break;
+ case WSA_WAIT_EVENT_0 + 1:
+ case WSA_WAIT_TIMEOUT:
+ res = 0;
+ break;
+ case WSA_WAIT_FAILED:
+ w32_set_winsock_errno();
+ res = -1;
+ break;
+ default:
+ CANT_REACH();
+ errno = EINVAL;
+ res = -1;
}
- done:
- loc_RunThisThread();
-}
+ WSAEventSelect(sock, hEventObject[0], 0);
-/************************
- * empth_alarm
- */
-void
-empth_alarm(int sig)
-{
- loc_Thread_t *pThread =
- (loc_Thread_t *)TlsGetValue(loc_GVAR.dwTLSIndex);
-
- loc_debug("got alarm signal %d", sig);
+ WSACloseEvent(hEventObject[0]);
- /* Let it run if it is blocked like... */
- SetEvent(pThread->hThreadEvent);
+ loc_RunThisThread(NULL);
+ return res;
}
/************************
* Wake up the specified thread.
*/
void
-empth_wakeup(empth_t *a)
+empth_wakeup(empth_t *pThread)
{
- loc_Thread_t *pThread = (loc_Thread_t *)a;
-
loc_debug("waking up thread %s", pThread->szName);
/* Let it run if it is blocked... */
*
* Put the given thread to sleep...
*/
-void
-empth_sleep(long until)
+int
+empth_sleep(time_t until)
{
- loc_Thread_t *pThread =
- (loc_Thread_t *)TlsGetValue(loc_GVAR.dwTLSIndex);
- unsigned long ulSec;
-
- ulSec = until - time(0);
-
- loc_debug("going to sleep %ld sec", ulSec);
+ time_t now;
+ long lSec;
+ empth_t *pThread = TlsGetValue(dwTLSIndex);
+ DWORD result;
+ ef_make_stale();
loc_BlockThisThread();
- WaitForSingleObject(pThread->hThreadEvent, (ulSec * 1000));
+ do {
+ now = time(NULL);
+ lSec = until >= now ? until - now : 0;
+ loc_debug("going to sleep %ld sec", lSec);
+ result = WaitForSingleObject(pThread->hThreadEvent, lSec * 1000L);
+ } while (result != WAIT_TIMEOUT && result != WAIT_OBJECT_0);
loc_debug("sleep done. Waiting to run.");
+ loc_RunThisThread(NULL);
- loc_RunThisThread();
+ return result == WAIT_TIMEOUT ? 0 : -1;
}
-
/************************
- * empth_sem_create
+ * empth_request_shutdown
*
- * Create a signalling semaphore.
+ * This wakes up empth_wait_for_signal() so shutdown can proceed.
+ * This is done by signalling hShutdownEvent.
*/
-empth_sem_t *
-empth_sem_create(char *name, int cnt)
+void
+empth_request_shutdown(void)
{
- loc_Sem_t *pSem;
-
- pSem = (loc_Sem_t *)malloc(sizeof(*pSem));
- if (!pSem) {
- logerror("out of memory at %s:%d", __FILE__, __LINE__);
- return NULL;
- }
-
- memset(pSem, 0, sizeof(pSem));
- strncpy(pSem->szName, name, sizeof(pSem->szName) - 1);
-
- pSem->hMutex = CreateMutex(NULL, FALSE, NULL);
- pSem->hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
- pSem->count = cnt;
+ SetEvent(hShutdownEvent);
+}
- return pSem;
+int
+empth_wait_for_signal(void)
+{
+ ef_make_stale();
+ loc_BlockThisThread();
+ loc_RunThisThread(hShutdownEvent);
+ return SIGTERM;
}
-/************************
- * empth_sem_signal
- *
- * Hit/signal the specified semaphore.
- */
-void
-empth_sem_signal(empth_sem_t *sm)
+empth_rwlock_t *
+empth_rwlock_create(char *name)
{
- loc_Sem_t *pSem = (loc_Sem_t *)sm;
+ empth_rwlock_t *rwlock;
- loc_debug("signal on semaphore %s:%d", pSem->szName, pSem->count);
+ rwlock = malloc(sizeof(*rwlock));
+ if (!rwlock)
+ return NULL;
- /* Wait for the Semaphore */
- WaitForSingleObject(pSem->hMutex, INFINITE);
+ memset(rwlock, 0, sizeof(*rwlock));
+ rwlock->name = strdup(name);
- if (pSem->count++ < 0) {
- SetEvent(pSem->hEvent);
+ 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->name);
+ free(rwlock);
+ return NULL;
}
- ReleaseMutex(pSem->hMutex);
+ 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__);
+ free(rwlock->name);
+ CloseHandle(rwlock->can_read);
+ free(rwlock);
+ return NULL;
+ }
+ return rwlock;
}
-/************************
- * empth_sem_wait
- *
- * Wait for the specified signal semaphore
- * to be signaled.
- */
void
-empth_sem_wait(empth_sem_t *sm)
+empth_rwlock_destroy(empth_rwlock_t *rwlock)
{
- loc_Thread_t *pThread =
- (loc_Thread_t *)TlsGetValue(loc_GVAR.dwTLSIndex);
- loc_Sem_t *pSem = (loc_Sem_t *)sm;
-
- loc_debug("wait on semaphore %s:%d", pSem->szName, pSem->count);
+ if (CANT_HAPPEN(rwlock->nread || rwlock->nwrite))
+ return;
+ if (rwlock->name != NULL)
+ free(rwlock->name);
+ CloseHandle(rwlock->can_read);
+ CloseHandle(rwlock->can_write);
+ free(rwlock);
+}
- /* Remove the thread from the running state. */
+void
+empth_rwlock_wrlock(empth_rwlock_t *rwlock)
+{
+ ef_make_stale();
+ /* block any new readers */
+ ResetEvent(rwlock->can_read);
+ rwlock->nwrite++;
loc_BlockThisThread();
+ loc_RunThisThread(rwlock->can_write);
+ CANT_HAPPEN(rwlock->nread != 0);
+}
- /* Wait for the Semaphore */
- WaitForSingleObject(pSem->hMutex, INFINITE);
- if (--pSem->count < 0) {
- loc_debug("blocking");
- ReleaseMutex(pSem->hMutex);
-
- WaitForSingleObject(pSem->hEvent, INFINITE);
-
- loc_debug("waking up");
- } else
- ReleaseMutex(pSem->hMutex);
-
- loc_RunThisThread();
+void
+empth_rwlock_rdlock(empth_rwlock_t *rwlock)
+{
+ ef_make_stale();
+ loc_BlockThisThread();
+ loc_RunThisThread(rwlock->can_read);
+ ResetEvent(rwlock->can_write);
+ rwlock->nread++;
}
-#endif /* _WIN32 */
+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);
+}