/*
* Empire - A multi-player, client/server Internet based war game.
- * Copyright (C) 1986-2004, Dave Pare, Jeff Bailey, Thomas Ruschak,
+ * Copyright (C) 1986-2007, 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.
*
* ---
*
* Known contributors to this file:
* Doug Hay, 1998
* Steve McClure, 1998
+ * Ron Koenderink, 2004-2005
*/
/*
* 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 semaphore.
*/
+#include <config.h>
+
+#include <errno.h>
+#include <signal.h>
#include <stdio.h>
#include <sys/types.h>
-#include <signal.h>
-#include <errno.h>
-#include "misc.h"
-#include "empthread.h"
-#include "prototypes.h"
-
-#if defined(_WIN32) && defined(_EMPTH_WIN32)
+#include <time.h>
+#define WIN32
#include <winsock2.h>
+#undef NS_ALL
#include <windows.h>
#include <process.h>
-
+#include "misc.h"
+#include "empthread.h"
+#include "prototypes.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.
*/
-typedef struct loc_Thread_t {
+struct loc_Thread_t {
/* The thread name, passed in at create time. */
char szName[17];
/* An Event sem that the thread will wait/sleep on. */
HANDLE hThreadEvent;
-} loc_Thread_t;
+};
/************************
* loc_Sem_t
- *
- * The REAL empth_sem_t structure.
- * The external world only gets
- * a void pointer to this.
*/
-typedef struct empth_sem_t {
+struct loc_Sem_t {
char szName[17];
HANDLE hEvent;
/* The count variable */
int count;
-} loc_Sem_t;
+};
+
+/* 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;
-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;
+/* This is the thread startup event sem. */
+/* We use this to lockstep when we are starting up threads. */
+static HANDLE hThreadStartEvent;
- /* This is the thread startup event sem. */
- /* We use this to lockstep when we are starting up threads. */
- HANDLE hThreadStartEvent;
+/* This is an event used to wakeup the main thread */
+/* to start the shutdown sequence. */
+static HANDLE hShutdownEvent;
- /* The Thread Local Storage index. We store the pThread pointer */
- /* for each thread at this index. */
- DWORD dwTLSIndex;
+/* The Thread Local Storage index. We store the pThread pointer */
+/* for each thread at this index. */
+static DWORD dwTLSIndex;
- /* The current running thread. */
- loc_Thread_t *pCurThread;
+/* The current running thread. */
+static empth_t *pCurThread;
- /* Ticks at start */
- unsigned long ulTickAtStart;
+/* Ticks at start */
+static unsigned long ulTickAtStart;
- /* Pointer out to global context. "player". */
- /* From empth_init parameter. */
- char **ppvUserData;
+/* Pointer out to global context. "player". */
+/* From empth_init parameter. */
+static void **ppvUserData;
- /* Global flags. From empth_init parameter. */
- int flags;
-} loc_GVAR;
+/* Global flags. From empth_init parameter. */
+static int global_flags;
/************************
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)
* 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 sem.
*/
static void
-loc_RunThisThread()
+loc_RunThisThread(void)
{
- loc_Thread_t *pThread =
- (loc_Thread_t *)TlsGetValue(loc_GVAR.dwTLSIndex);
+ empth_t *pThread = TlsGetValue(dwTLSIndex);
if (pThread->bKilled) {
if (!pThread->bMainThread) {
- TlsSetValue(loc_GVAR.dwTLSIndex, NULL);
+ TlsSetValue(dwTLSIndex, NULL);
loc_FreeThreadInfo(pThread);
_endthread();
}
}
/* Get the MUTEX semaphore, wait forever. */
- WaitForSingleObject(loc_GVAR.hThreadMutex, INFINITE);
+ WaitForSingleObject(hThreadMutex, INFINITE);
- if (!loc_GVAR.pCurThread) {
+ 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
+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....
*/
-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);
+ SetEvent(hThreadStartEvent);
/* seed the rand() function */
time(&now);
- srand(now ^ (unsigned int)pThread);
+ srand(now ^ (unsigned)pThread);
/* Switch to this thread context */
loc_RunThisThread();
*
* 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. */
/* 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. */
/* 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((PHANDLER_ROUTINE)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;
pThread->ulThreadID = GetCurrentThreadId();
pThread->bMainThread = TRUE;
- TlsSetValue(loc_GVAR.dwTLSIndex, pThread);
+ TlsSetValue(dwTLSIndex, pThread);
/* Make this the running thread. */
loc_RunThisThread();
empth_create(int prio, void (*entry)(void *), int size, int flags,
char *name, char *desc, void *ud)
{
- loc_Thread_t *pThread = NULL;
+ empth_t *pThread = NULL;
loc_debug("creating new thread %s:%s", name, desc);
- 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 (%s)",
+ name, desc);
return NULL;
}
memset(pThread, 0, sizeof(*pThread));
if (size < loc_MIN_THREAD_STACK)
size = loc_MIN_THREAD_STACK;
- pThread->ulThreadID = _beginthread(empth_threadMain, size,
- (void *)pThread);
+ pThread->ulThreadID = _beginthread(empth_threadMain, size, pThread);
if (pThread->ulThreadID == -1) {
- logerror("can not create thread: %s (%s): %s", name, desc,
- strerror(errno));
+ logerror("can not create thread: %s (%s): %s",
+ name, desc, strerror(errno));
goto bad;
}
loc_debug("new thread id is %ld", pThread->ulThreadID);
+ empth_yield();
return pThread;
bad:
empth_t *
empth_self(void)
{
- loc_Thread_t *pThread =
- (loc_Thread_t *)TlsGetValue(loc_GVAR.dwTLSIndex);
+ empth_t *pThread = TlsGetValue(dwTLSIndex);
return pThread;
}
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");
+ loc_BlockThisThread();
- if (pThread->bMainThread) {
- /* The main line. Wait forever. */
- while (1) {
- if (!debug) {
- if (service_stopped())
- shutdwn(0);
- Sleep(3);
- } else {
- char buf[20];
- 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();
}
/************************
* Kill off the thread.
*/
void
-empth_terminate(empth_t *a)
+empth_terminate(empth_t *pThread)
{
- loc_Thread_t *pThread = (loc_Thread_t *)a;
-
loc_debug("killing thread %s", pThread->szName);
pThread->bKilled = TRUE;
* 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)
{
- 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;
+ WSAEVENT hEventObject[2];
+ empth_t *pThread = TlsGetValue(dwTLSIndex);
loc_debug("%s select on %d",
flags == EMPTH_FD_READ ? "read" : "write", fd);
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;
- }
+ hEventObject[0] = WSACreateEvent();
+ hEventObject[1] = pThread->hThreadEvent;
- 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;
- }
+ if (flags == EMPTH_FD_READ)
+ WSAEventSelect(fd, hEventObject[0], FD_READ | FD_ACCEPT | FD_CLOSE);
+ else if (flags == EMPTH_FD_WRITE)
+ WSAEventSelect(fd, hEventObject[0], FD_WRITE | FD_CLOSE);
+ else {
+ logerror("bad flag %d passed to empth_select", flags);
+ empth_exit();
}
- done:
- loc_RunThisThread();
-}
+ WSAWaitForMultipleEvents(2, hEventObject, FALSE, WSA_INFINITE, FALSE);
-/************************
- * empth_alarm
- */
-void
-empth_alarm(int sig)
-{
- loc_Thread_t *pThread =
- (loc_Thread_t *)TlsGetValue(loc_GVAR.dwTLSIndex);
+ WSAEventSelect(fd, hEventObject[0], 0);
- loc_debug("got alarm signal %d", sig);
+ WSACloseEvent(hEventObject[0]);
- /* Let it run if it is blocked like... */
- SetEvent(pThread->hThreadEvent);
+ loc_RunThisThread();
}
/************************
* 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)
+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);
+ long lSec;
loc_BlockThisThread();
- WaitForSingleObject(pThread->hThreadEvent, (ulSec * 1000));
+ while ((lSec = until - time(0)) > 0) {
+ loc_debug("going to sleep %ld sec", lSec);
+ Sleep(lSec * 1000L);
+ }
loc_debug("sleep done. Waiting to run.");
loc_RunThisThread();
}
+/************************
+ * empth_request_shutdown
+ *
+ * This wakes up empth_wait_for_signal() so shutdown can proceed.
+ * This is done by signalling hShutdownEvent.
+ */
+void
+empth_request_shutdown(void)
+{
+ SetEvent(hShutdownEvent);
+}
+
+int
+empth_wait_for_signal(void)
+{
+ loc_BlockThisThread();
+
+ /* Get the MUTEX semaphore, wait the number of MS */
+ WaitForSingleObject(hShutdownEvent, INFINITE);
+
+ loc_RunThisThread();
+ return 0;
+}
/************************
* empth_sem_create
empth_sem_t *
empth_sem_create(char *name, int cnt)
{
- loc_Sem_t *pSem;
+ empth_sem_t *pSem;
- pSem = (loc_Sem_t *)malloc(sizeof(*pSem));
+ pSem = malloc(sizeof(*pSem));
if (!pSem) {
logerror("out of memory at %s:%d", __FILE__, __LINE__);
return NULL;
* Hit/signal the specified semaphore.
*/
void
-empth_sem_signal(empth_sem_t *sm)
+empth_sem_signal(empth_sem_t *pSem)
{
- loc_Sem_t *pSem = (loc_Sem_t *)sm;
-
loc_debug("signal on semaphore %s:%d", pSem->szName, pSem->count);
/* Wait for the Semaphore */
/************************
* empth_sem_wait
*
- * Wait for the specified signal semaphore
- * to be signaled.
+ * Wait for the specified signal semaphore to be signaled.
*/
void
-empth_sem_wait(empth_sem_t *sm)
+empth_sem_wait(empth_sem_t *pSem)
{
- loc_Thread_t *pThread =
- (loc_Thread_t *)TlsGetValue(loc_GVAR.dwTLSIndex);
- loc_Sem_t *pSem = (loc_Sem_t *)sm;
+ empth_t *pThread = TlsGetValue(dwTLSIndex);
loc_debug("wait on semaphore %s:%d", pSem->szName, pSem->count);
loc_RunThisThread();
}
-
-#endif /* _WIN32 */