]> git.pond.sub.org Git - empserver/blobdiff - src/lib/empthread/ntthread.c
Generation numbers to catch write back of stale copies
[empserver] / src / lib / empthread / ntthread.c
index a373a25c76d0de231bbf010c0be1bdad3bcd60fa..67ae3ef49966f330941dfc81495efb73d2f9b543 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *  Empire - A multi-player, client/server Internet based war game.
- *  Copyright (C) 1986-2000, 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 {
+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;
@@ -81,69 +77,95 @@ typedef struct {
     /* 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 *);
+    void (*pfnEntry) (void *);
 
     /* The system thread ID. */
-    unsigned long ulThreadID;    
+    unsigned long ulThreadID;
 
-    /* An Event sem that the thread will wait/sleep on. */
-    HANDLE  hThreadEvent;
-} loc_Thread_t;
+    /* An Mutex that the thread will wait/sleep on. */
+    HANDLE hThreadEvent;
+};
 
 
 /************************
- * 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 {
+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;
-
-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 an event used to wakeup the main thread */
+/* to start the shutdown sequence. */
+static HANDLE hShutdownEvent;
 
-    /* This is the thread startup event sem. */
-    /* We use this to lockstep when we are starting up threads. */
-    HANDLE  hThreadStartEvent;
+/* The Thread Local Storage index.  We store the pThread pointer */
+/* for each thread at this index. */
+static DWORD dwTLSIndex;
 
-    /* The Thread Local Storage index.  We store the pThread pointer */
-    /* for each thread at this index. */
-    DWORD  dwTLSIndex;
+/* The current running thread. */
+static empth_t *pCurThread;
 
-    /* The current running thread. */
-    loc_Thread_t *pCurThread;
+/* Ticks at start */
+static unsigned long ulTickAtStart;
 
-    /* Ticks at start */
-    unsigned long ulTickAtStart;
+/* Pointer out to global context.  "player". */
+/* From empth_init parameter. */
+static void **ppvUserData;
 
-    /* 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
@@ -157,52 +179,47 @@ loc_debug(const char *pszFmt, ...)
     unsigned long ulCurTick;
     unsigned long ulRunTick;
     unsigned long ulMs, ulSec, ulMin, ulHr;
-    loc_Thread_t *pThread =
-            (loc_Thread_t *) TlsGetValue(loc_GVAR.dwTLSIndex);
-    char    buf[1024];
-    
-    if ((loc_GVAR.flags & EMPTH_PRINT) != 0) {
-
-        /* Ticks are in milliseconds */
-        ulCurTick = GetTickCount();
-
-        ulRunTick = ulCurTick - loc_GVAR.ulTickAtStart;
-        ulMs = ulRunTick % 1000L;
-        ulSec = (ulRunTick / 1000L) % 60L;
-        ulMin = (ulRunTick / (60L * 1000L)) % 60L;
-        ulHr = (ulRunTick / (60L * 60L * 1000L));
-        
-        va_start(vaList, pszFmt);
-        vsprintf(buf, pszFmt, vaList);
-        va_end(vaList);
-
-        if (pThread) {
-            printf("%ld:%02ld:%02ld.%03ld %17s: %s\n",
-                    ulHr, ulMin, ulSec, ulMs,
-                    pThread->szName,
-                    buf);
-        } else {
-            printf("%ld:%02ld:%02ld.%03ld %17s: %s\n",
-                    ulHr, ulMin, ulSec, ulMs,
-                    "UNKNOWN",
-                    buf);
-        }
+    empth_t *pThread = TlsGetValue(dwTLSIndex);
+    char buf[1024];
+
+    if ((global_flags & EMPTH_PRINT) != 0) {
+
+       /* Ticks are in milliseconds */
+       ulCurTick = GetTickCount();
+
+       ulRunTick = ulCurTick - ulTickAtStart;
+       ulMs = ulRunTick % 1000L;
+       ulSec = (ulRunTick / 1000L) % 60L;
+       ulMin = (ulRunTick / (60L * 1000L)) % 60L;
+       ulHr = (ulRunTick / (60L * 60L * 1000L));
+
+       va_start(vaList, pszFmt);
+       vsprintf(buf, pszFmt, vaList);
+       va_end(vaList);
+
+       if (pThread) {
+           printf("%ld:%02ld:%02ld.%03ld %17s: %s\n",
+                  ulHr, ulMin, ulSec, ulMs, pThread->szName, buf);
+       } else {
+           printf("%ld:%02ld:%02ld.%03ld %17s: %s\n",
+                  ulHr, ulMin, ulSec, ulMs, "UNKNOWN", buf);
+       }
 
     }
 }
 
 /************************
  * 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));
-        free(pThread);
+       if (pThread->hThreadEvent)
+           CloseHandle(pThread->hThreadEvent);
+       if (pThread->szName != NULL)
+           free(pThread->szName);
+       free(pThread);
     }
 }
 
@@ -210,35 +227,30 @@ loc_FreeThreadInfo(loc_Thread_t *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) {
-        /* Set the globals to this thread. */
-        *loc_GVAR.ppvUserData = pThread->pvUserData;
+    WaitForMultipleObjects(hWaitObject ? 2 : 1, hWaitObjects,
+                          TRUE, INFINITE);
 
-        loc_GVAR.pCurThread = pThread;
+    if (!pCurThread) {
+       /* Set the globals to this thread. */
+       *ppvUserData = pThread->pvUserData;
+
+       pCurThread = pThread;
     } else {
-        /* Hmm, a problem, eh? */
-        logerror("RunThisThread, someone already running.");
+       /* Hmm, a problem, eh? */
+       logerror("RunThisThread, someone already running.");
     }
 }
 
@@ -248,72 +260,80 @@ loc_RunThisThread()
  * 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) {
-        /* Reset the globals back to original */
+    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);
+       /* Release the MUTEX */
+       ReleaseMutex(hThreadMutex);
     } else {
-        /* Hmm, this thread was not the running one. */
-        logerror("BlockThisThread, not running.");
+       /* 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)
 {
-    loc_Thread_t *pThread = (loc_Thread_t *)pvData;
+    empth_t *pThread = pvData;
 
     /* Out of here... */
-    if (!pvData) return;
+    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);
 
     /* Switch to this thread context */
-    loc_RunThisThread();
+    loc_RunThisThread(NULL);
 
     /* Run the thread. */
     if (pThread->pfnEntry)
-        pThread->pfnEntry(pThread->pvUserData);
+       pThread->pfnEntry(pThread->pvUserData);
 
     /* Kill the thread. */
     empth_exit();
@@ -324,52 +344,59 @@ empth_threadMain(void *pvData)
  *
  * 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");
-        return 0;
+    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");
-        return 0;
+    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;
+       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;
@@ -381,58 +408,55 @@ empth_init (char **ctx_ptr, int flags)
  *
  * 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)(), 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);
-        return NULL;
+       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));
-        goto bad;
+       size = loc_MIN_THREAD_STACK;
+
+    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:
     if (pThread) {
-        loc_FreeThreadInfo(pThread);
+       loc_FreeThreadInfo(pThread);
     }
     return NULL;
 }
@@ -444,74 +468,61 @@ bad:
 empth_t *
 empth_self(void)
 {
-    loc_Thread_t *pThread =
-            (loc_Thread_t *) TlsGetValue(loc_GVAR.dwTLSIndex);
+    empth_t *pThread = TlsGetValue(dwTLSIndex);
 
     return pThread;
 }
 
 /************************
- * empth_exit
+ * empth_name
  */
-void
-empth_exit (void)
+char *
+empth_name(empth_t *thread)
 {
-       s_char *getlogfile();
-
-    loc_Thread_t *pThread =
-            (loc_Thread_t *) TlsGetValue(loc_GVAR.dwTLSIndex);
-    
-    loc_BlockThisThread();
-
-    loc_debug("empth_exit");
+    return thread->szName;
+}
 
-    if (pThread->bMainThread) {
-        char buf[20];
-        /* The main line.  Wait forever. */
-               if (getlogfile() == (s_char *)0)
-                       loginit();
-               printf("log file: %s\n", getlogfile());
-        while (1) {
-            printf("\nEmpire Server>");
-               fgets(buf, sizeof(buf), stdin);
-               if (!strnicmp(buf, "quit", 4))
-                       break;
-        }
-        TlsSetValue(loc_GVAR.dwTLSIndex, NULL);
-        loc_FreeThreadInfo(pThread);
-    } else {
-        TlsSetValue(loc_GVAR.dwTLSIndex, NULL);
-        loc_FreeThreadInfo(pThread);
-        _endthread();
-    }
+/************************
+ * 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_yield
- *
- * Yield processing to another thread.
+ * empth_exit
  */
 void
-empth_yield (void)
+empth_exit(void)
 {
+    empth_t *pThread = TlsGetValue(dwTLSIndex);
+
+    loc_debug("empth_exit");
+    ef_make_stale();
     loc_BlockThisThread();
-    loc_RunThisThread();
+
+    TlsSetValue(dwTLSIndex, NULL);
+    loc_FreeThreadInfo(pThread);
+    _endthread();
 }
 
 /************************
- * empth_terminate
+ * empth_yield
  *
- * Kill off the thread.
+ * Yield processing to another thread.
  */
 void
-empth_terminate(empth_t *a)
+empth_yield(void)
 {
-    loc_Thread_t *pThread = (loc_Thread_t *) a;
-
-    loc_debug("killing thread %s", pThread->szName);
-    pThread->bKilled = TRUE;
-
-    SetEvent(pThread->hThreadEvent);
+    ef_make_stale();
+    loc_BlockThisThread();
+    Sleep(0);
+    loc_RunThisThread(NULL);
 }
 
 /************************
@@ -520,85 +531,66 @@ empth_terminate(empth_t *a)
  * 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 );
+             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;
 
-done:
-    loc_RunThisThread();
-}
+    sock = w32_fd2socket(fd);
+    CANT_HAPPEN(sock == (SOCKET)-1);
 
-/************************
- * empth_alarm
- */
-emp_sig_t
-empth_alarm(int sig)
-{
-    loc_Thread_t *pThread =
-            (loc_Thread_t *) TlsGetValue(loc_GVAR.dwTLSIndex);
+    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;
+    }
 
-    loc_debug("got alarm signal  %d", sig);
+    WSAEventSelect(sock, hEventObject[0], 0);
 
-    /* Let it run if it is blocked like... */
-    SetEvent(pThread->hThreadEvent);
+    WSACloseEvent(hEventObject[0]);
+
+    loc_RunThisThread(NULL);
+    return res;
 }
 
 /************************
@@ -607,10 +599,8 @@ empth_alarm(int sig)
  * 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... */
@@ -622,107 +612,129 @@ empth_wakeup(empth_t *a)
  *
  * 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);
+void
+empth_rwlock_rdlock(empth_rwlock_t *rwlock)
+{
+    ef_make_stale();
+    loc_BlockThisThread();
+    loc_RunThisThread(rwlock->can_read);
+    ResetEvent(rwlock->can_write);
+    rwlock->nread++;
+}
 
-        loc_debug("waking up");
+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);
     }
-    else
-        ReleaseMutex(pSem->hMutex);
-
-    loc_RunThisThread();
+    if (rwlock->nwrite == 0)
+       SetEvent(rwlock->can_read);
 }
-
-#endif  /* _WIN32 */