src/lib/empthread/ntthread.c

Go to the documentation of this file.
00001 /*
00002  *  Empire - A multi-player, client/server Internet based war game.
00003  *  Copyright (C) 1986-2007, Dave Pare, Jeff Bailey, Thomas Ruschak,
00004  *                           Ken Stevens, Steve McClure
00005  *
00006  *  This program is free software; you can redistribute it and/or modify
00007  *  it under the terms of the GNU General Public License as published by
00008  *  the Free Software Foundation; either version 2 of the License, or
00009  *  (at your option) any later version.
00010  *
00011  *  This program is distributed in the hope that it will be useful,
00012  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  *  GNU General Public License for more details.
00015  *
00016  *  You should have received a copy of the GNU General Public License
00017  *  along with this program; if not, write to the Free Software
00018  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00019  *
00020  *  ---
00021  *
00022  *  See files README, COPYING and CREDITS in the root of the source
00023  *  tree for related information and legal notices.  It is expected
00024  *  that future projects/authors will amend these files as needed.
00025  *
00026  *  ---
00027  *
00028  *  ntthread.c: Interface from Empire threads to Windows NT threads
00029  * 
00030  *  Known contributors to this file:
00031  *     Doug Hay, 1998
00032  *     Steve McClure, 1998
00033  *     Ron Koenderink, 2004-2007
00034  */
00035 
00036 /*
00037  * EMPTHREADs for Windows NT.
00038  *
00039  * Actually, threads for any Win32 platform, like Win95, Win98, WinCE,
00040  * and whatever other toy OSs are in our future from Microsoft.
00041  *
00042  * WIN32 has a full pre-emptive threading environment.  But Empire can
00043  * not handle pre-emptive threading.  Thus, we will use the threads,
00044  * but limit the preemption using a Mutex.
00045  */
00046 
00047 #include <config.h>
00048 
00049 #include <errno.h>
00050 #include <signal.h>
00051 #include <stdio.h>
00052 #include <stdarg.h>
00053 #include <sys/types.h>
00054 #include <time.h>
00055 #include <winsock2.h>
00056 #undef NS_ALL
00057 #include <windows.h>
00058 #include <process.h>
00059 /* Note: unistd.h(posixio.c) is not thread-safe.
00060  * It may be used *only* while holding hThreadMutex.
00061  */
00062 #include "unistd.h"
00063 #include "misc.h"
00064 #include "empthread.h"
00065 #include "prototypes.h"
00066 #include "server.h"
00067 
00068 #define loc_MIN_THREAD_STACK  16384
00069 
00070 /************************
00071  * loc_Thread
00072  */
00073 struct loc_Thread {
00074 
00075     /* The thread name, passed in at create time. */
00076     char szName[17];
00077 
00078     /* True if this is the main line, and not a real thread. */
00079     BOOL bMainThread;
00080 
00081     /* The user data passed in at create time. */
00082     void *pvUserData;
00083 
00084     /* True if this thread has been killed. */
00085     BOOL bKilled;
00086 
00087     /* The entry function for the thread. */
00088     void (*pfnEntry) (void *);
00089 
00090     /* The system thread ID. */
00091     unsigned long ulThreadID;
00092 
00093     /* An Mutex that the thread will wait/sleep on. */
00094     HANDLE hThreadEvent;
00095 };
00096 
00097 
00098 /************************
00099  * loc_RWLock
00100  *
00101  * Invariants
00102  *      must hold at function call, return, sleep
00103  *      and resume from sleep.
00104  *
00105  * any state:
00106  *      nwrite >= 0
00107  *      nread >= 0
00108 
00109  * if unlocked:
00110  *      can_read set
00111  *      can_write set
00112  *      nwrite == 0
00113  *      nread == 0
00114  *
00115  * if read-locked without writers contending:
00116  *      can_read set
00117  *      can_write clear
00118  *      nwrite == 0
00119  *      nread > 0
00120  *
00121  * if read-locked with writers contending:
00122  *      can_read clear
00123  *      can_write clear
00124  *      nwrite > 0    #writers blocked
00125  *      nread > 0
00126  *
00127  * if write-locked:
00128  *      can_read clear
00129  *      can_write clear
00130  *      nwrite > 0    #writers blocked + 1
00131  *      nread == 0
00132  *
00133  * To ensure consistency, state normally changes only while the
00134  * thread changing it holds hThreadMutex.
00135  *
00136  */
00137 struct loc_RWLock {
00138     char name[17];      /* The thread name, passed in at create time. */
00139     HANDLE can_read;    /* Manual event -- allows read locks */
00140     HANDLE can_write;   /* Auto-reset event -- allows write locks */
00141     int nread;          /* number of active readers */
00142     int nwrite;         /* total number of writers (active and waiting) */
00143 };
00144 
00145 /* This is the thread exclusion/non-premption mutex. */
00146 /* The running thread has this MUTEX, and all others are */
00147 /* either blocked on it, or waiting for some OS response. */
00148 static HANDLE hThreadMutex;
00149 
00150 /* This is the thread startup event. */
00151 /* We use this to lockstep when we are starting up threads. */
00152 static HANDLE hThreadStartEvent;
00153 
00154 /* This is an event used to wakeup the main thread */
00155 /* to start the shutdown sequence. */
00156 static HANDLE hShutdownEvent;
00157 
00158 /* The Thread Local Storage index.  We store the pThread pointer */
00159 /* for each thread at this index. */
00160 static DWORD dwTLSIndex;
00161 
00162 /* The current running thread. */
00163 static empth_t *pCurThread;
00164 
00165 /* Ticks at start */
00166 static unsigned long ulTickAtStart;
00167 
00168 /* Pointer out to global context.  "player". */
00169 /* From empth_init parameter. */
00170 static void **ppvUserData;
00171 
00172 /* Global flags.  From empth_init parameter. */
00173 static int global_flags;
00174 
00175 
00176 /************************
00177  * loc_debug
00178  *
00179  * Print out the current thread's status??
00180  */
00181 static void
00182 loc_debug(const char *pszFmt, ...)
00183 {
00184     va_list vaList;
00185     unsigned long ulCurTick;
00186     unsigned long ulRunTick;
00187     unsigned long ulMs, ulSec, ulMin, ulHr;
00188     empth_t *pThread = TlsGetValue(dwTLSIndex);
00189     char buf[1024];
00190 
00191     if ((global_flags & EMPTH_PRINT) != 0) {
00192 
00193         /* Ticks are in milliseconds */
00194         ulCurTick = GetTickCount();
00195 
00196         ulRunTick = ulCurTick - ulTickAtStart;
00197         ulMs = ulRunTick % 1000L;
00198         ulSec = (ulRunTick / 1000L) % 60L;
00199         ulMin = (ulRunTick / (60L * 1000L)) % 60L;
00200         ulHr = (ulRunTick / (60L * 60L * 1000L));
00201 
00202         va_start(vaList, pszFmt);
00203         vsprintf(buf, pszFmt, vaList);
00204         va_end(vaList);
00205 
00206         if (pThread) {
00207             printf("%ld:%02ld:%02ld.%03ld %17s: %s\n",
00208                    ulHr, ulMin, ulSec, ulMs, pThread->szName, buf);
00209         } else {
00210             printf("%ld:%02ld:%02ld.%03ld %17s: %s\n",
00211                    ulHr, ulMin, ulSec, ulMs, "UNKNOWN", buf);
00212         }
00213 
00214     }
00215 }
00216 
00217 /************************
00218  * loc_FreeThreadInfo
00219  */
00220 static void
00221 loc_FreeThreadInfo(empth_t *pThread)
00222 {
00223     if (pThread) {
00224         if (pThread->hThreadEvent)
00225             CloseHandle(pThread->hThreadEvent);
00226         memset(pThread, 0, sizeof(*pThread));
00227         free(pThread);
00228     }
00229 }
00230 
00231 /************************
00232  * loc_RunThisThread
00233  *
00234  * This thread wants to run.
00235  * When this function returns, the globals are set to this thread
00236  * info, and the thread owns the MUTEX.
00237  */
00238 static void
00239 loc_RunThisThread(HANDLE hWaitObject)
00240 {
00241     HANDLE hWaitObjects[2];
00242 
00243     empth_t *pThread = TlsGetValue(dwTLSIndex);
00244 
00245     if (pThread->bKilled) {
00246         if (!pThread->bMainThread) {
00247             TlsSetValue(dwTLSIndex, NULL);
00248             loc_FreeThreadInfo(pThread);
00249             _endthread();
00250         }
00251     }
00252 
00253     hWaitObjects[0] = hThreadMutex;
00254     hWaitObjects[1] = hWaitObject;
00255 
00256     WaitForMultipleObjects(hWaitObject ? 2 : 1, hWaitObjects,
00257                            TRUE, INFINITE);
00258 
00259     if (!pCurThread) {
00260         /* Set the globals to this thread. */
00261         *ppvUserData = pThread->pvUserData;
00262 
00263         pCurThread = pThread;
00264     } else {
00265         /* Hmm, a problem, eh? */
00266         logerror("RunThisThread, someone already running.");
00267     }
00268 }
00269 
00270 /************************
00271  * loc_BlockThisThread
00272  *
00273  * This thread was running.  It no longer wants to.
00274  */
00275 static void
00276 loc_BlockThisThread(void)
00277 {
00278     empth_t *pThread = TlsGetValue(dwTLSIndex);
00279 
00280     if (pCurThread == pThread) {
00281         /* Reset the globals back to original */
00282 
00283         pCurThread = NULL;
00284         *ppvUserData = NULL;
00285 
00286         /* Release the MUTEX */
00287         ReleaseMutex(hThreadMutex);
00288     } else {
00289         /* Hmm, this thread was not the running one. */
00290         logerror("BlockThisThread, not running.");
00291     }
00292 }
00293 
00294 /************************
00295  * loc_Exit_Handler
00296  *
00297  * Ctrl-C, Ctrl-Break, Window-Closure, User-Logging-Off or
00298  * System-Shutdown will initiate a shutdown.
00299  * This is done by calling empth_request_shutdown()
00300  */
00301 static BOOL WINAPI
00302 loc_Exit_Handler(DWORD fdwCtrlType)
00303 {
00304     switch (fdwCtrlType) { 
00305         case CTRL_C_EVENT:
00306         case CTRL_CLOSE_EVENT:
00307         case CTRL_BREAK_EVENT: 
00308         case CTRL_LOGOFF_EVENT: 
00309         case CTRL_SHUTDOWN_EVENT: 
00310             empth_request_shutdown();
00311             return TRUE;
00312         default:
00313             return FALSE;
00314     }
00315 }
00316 
00317 /************************
00318  * empth_threadMain
00319  *
00320  * This is the main line of each thread.
00321  * This is really a static local func....
00322  * Note: As the POSIX compatibility layer is not thread safe
00323  * this function can not open or create any files or sockets until
00324  * loc_RunThisThread() is called
00325  */
00326 static void
00327 empth_threadMain(void *pvData)
00328 {
00329     empth_t *pThread = pvData;
00330 
00331     /* Out of here... */
00332     if (!pvData)
00333         return;
00334 
00335     /* Store pThread on this thread. */
00336     TlsSetValue(dwTLSIndex, pvData);
00337 
00338     /* Get the ID of the thread. */
00339     pThread->ulThreadID = GetCurrentThreadId();
00340 
00341     /* Signal that the thread has started. */
00342     SetEvent(hThreadStartEvent);
00343 
00344     /* Switch to this thread context */
00345     loc_RunThisThread(NULL);
00346 
00347     /* Run the thread. */
00348     if (pThread->pfnEntry)
00349         pThread->pfnEntry(pThread->pvUserData);
00350 
00351     /* Kill the thread. */
00352     empth_exit();
00353 }
00354 
00355 /************************
00356  * empth_init
00357  *
00358  * Initialize the thread environment.
00359  *
00360  * This is called from the program main line.
00361  */
00362 int
00363 empth_init(void **ctx_ptr, int flags)
00364 {
00365     empth_t *pThread = NULL;
00366 
00367     ulTickAtStart = GetTickCount();
00368     ppvUserData = ctx_ptr;
00369     global_flags = flags;
00370     dwTLSIndex = TlsAlloc();
00371 
00372     /* Create the thread mutex. */
00373     /* Initally unowned. */
00374     hThreadMutex = CreateMutex(NULL, FALSE, NULL);
00375     if (!hThreadMutex) {
00376         logerror("Failed to create mutex %lu", GetLastError());
00377         return 0;
00378     }
00379 
00380     /* Create the thread start event. */
00381     /* Automatic state reset. */
00382     hThreadStartEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
00383     if (!hThreadStartEvent) {
00384         logerror("Failed to create start event %lu", GetLastError());
00385         return 0;
00386     }
00387 
00388     /* Create the shutdown event for the main thread. */
00389     /* Manual reset */
00390     hShutdownEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
00391     if (!hShutdownEvent) {
00392         logerror("Failed to create shutdown event %lu", GetLastError());
00393         return 0;
00394     }
00395     SetConsoleCtrlHandler(loc_Exit_Handler, TRUE);
00396 
00397     /* Create the global Thread context. */
00398     pThread = malloc(sizeof(*pThread));
00399     if (!pThread) {
00400         logerror("not enough memory to create main thread.");
00401         return 0;
00402     }
00403     memset(pThread, 0, sizeof(*pThread));
00404 
00405     strncpy(pThread->szName, "Main", sizeof(pThread->szName) - 1);
00406     pThread->ulThreadID = GetCurrentThreadId();
00407     pThread->bMainThread = TRUE;
00408 
00409     TlsSetValue(dwTLSIndex, pThread);
00410 
00411     /* Make this the running thread. */
00412     loc_RunThisThread(NULL);
00413 
00414     logerror("NT pthreads initialized");
00415     return 0;
00416 }
00417 
00418 
00419 /************************
00420  * empth_create
00421  *
00422  * Create a new thread.
00423  *
00424  * entry - entry point function for thread.
00425  * size  - stack size.
00426  * flags - debug control.
00427  *           LWP_STACKCHECK  - not needed
00428  * name  - name of the thread, for debug.
00429  * ud    - "user data".  The "ctx_ptr" gets this value
00430  *         when the thread is active.
00431  *         It is also passed to the entry function...
00432  */
00433 empth_t *
00434 empth_create(void (*entry)(void *), int size, int flags,
00435              char *name, void *ud)
00436 {
00437     empth_t *pThread = NULL;
00438 
00439     loc_debug("creating new thread %s", name);
00440 
00441     pThread = malloc(sizeof(*pThread));
00442     if (!pThread) {
00443         logerror("not enough memory to create thread %s", name);
00444         return NULL;
00445     }
00446     memset(pThread, 0, sizeof(*pThread));
00447 
00448     strncpy(pThread->szName, name, sizeof(pThread->szName) - 1);
00449     pThread->pvUserData = ud;
00450     pThread->pfnEntry = entry;
00451     pThread->bMainThread = FALSE;
00452 
00453     /* Create thread event, auto reset. */
00454     pThread->hThreadEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
00455 
00456     if (size < loc_MIN_THREAD_STACK)
00457         size = loc_MIN_THREAD_STACK;
00458 
00459     pThread->ulThreadID = _beginthread(empth_threadMain, size, pThread);
00460     if (pThread->ulThreadID == 1L) {
00461         logerror("can not create thread: %s: %s", name, strerror(errno));
00462         goto bad;
00463     }
00464 
00465     loc_debug("new thread id is %ld", pThread->ulThreadID);
00466     empth_yield();
00467     return pThread;
00468 
00469   bad:
00470     if (pThread) {
00471         loc_FreeThreadInfo(pThread);
00472     }
00473     return NULL;
00474 }
00475 
00476 
00477 /************************
00478  * empth_self
00479  */
00480 empth_t *
00481 empth_self(void)
00482 {
00483     empth_t *pThread = TlsGetValue(dwTLSIndex);
00484 
00485     return pThread;
00486 }
00487 
00488 /************************
00489  * empth_exit
00490  */
00491 void
00492 empth_exit(void)
00493 {
00494     empth_t *pThread = TlsGetValue(dwTLSIndex);
00495 
00496     loc_debug("empth_exit");
00497     loc_BlockThisThread();
00498 
00499     TlsSetValue(dwTLSIndex, NULL);
00500     loc_FreeThreadInfo(pThread);
00501     _endthread();
00502 }
00503 
00504 /************************
00505  * empth_yield
00506  *
00507  * Yield processing to another thread.
00508  */
00509 void
00510 empth_yield(void)
00511 {
00512     loc_BlockThisThread();
00513     loc_RunThisThread(NULL);
00514 }
00515 
00516 /************************
00517  * empth_terminate
00518  *
00519  * Kill off the thread.
00520  */
00521 void
00522 empth_terminate(empth_t *pThread)
00523 {
00524     loc_debug("killing thread %s", pThread->szName);
00525     pThread->bKilled = TRUE;
00526 
00527     SetEvent(pThread->hThreadEvent);
00528 }
00529 
00530 /************************
00531  * empth_select
00532  *
00533  * Do a select on the given file.
00534  * Wait for IO on it.
00535  *
00536  * This would be one of the main functions used within gen\io.c
00537  */
00538 void
00539 empth_select(int fd, int flags)
00540 {
00541     int handle;
00542     WSAEVENT hEventObject[2];
00543     empth_t *pThread = TlsGetValue(dwTLSIndex);
00544 
00545     loc_debug("%s select on %d",
00546               flags == EMPTH_FD_READ ? "read" : "write", fd);
00547     loc_BlockThisThread();
00548 
00549     hEventObject[0] = WSACreateEvent();
00550     hEventObject[1] = pThread->hThreadEvent;
00551 
00552     handle = posix_fd2socket(fd);
00553     CANT_HAPPEN(handle < 0);
00554 
00555     if (flags == EMPTH_FD_READ)
00556         WSAEventSelect(handle, hEventObject[0], FD_READ | FD_ACCEPT | FD_CLOSE);
00557     else if (flags == EMPTH_FD_WRITE)
00558         WSAEventSelect(handle, hEventObject[0], FD_WRITE | FD_CLOSE);
00559     else {
00560         logerror("bad flag %d passed to empth_select", flags);
00561         empth_exit();
00562     }
00563 
00564     WSAWaitForMultipleEvents(2, hEventObject, FALSE, WSA_INFINITE, FALSE);
00565 
00566     WSAEventSelect(handle, hEventObject[0], 0);
00567 
00568     WSACloseEvent(hEventObject[0]);
00569 
00570     loc_RunThisThread(NULL);
00571 }
00572 
00573 /************************
00574  * empth_wakeup
00575  *
00576  * Wake up the specified thread.
00577  */
00578 void
00579 empth_wakeup(empth_t *pThread)
00580 {
00581     loc_debug("waking up thread %s", pThread->szName);
00582 
00583     /* Let it run if it is blocked... */
00584     SetEvent(pThread->hThreadEvent);
00585 }
00586 
00587 /************************
00588  * empth_sleep
00589  *
00590  * Put the given thread to sleep...
00591  */
00592 int
00593 empth_sleep(time_t until)
00594 {
00595     long lSec;
00596     empth_t *pThread = TlsGetValue(dwTLSIndex);
00597     int iReturn = 0;
00598 
00599     while (!iReturn && ((lSec = until - time(0)) > 0)) {
00600         loc_BlockThisThread();
00601         loc_debug("going to sleep %ld sec", lSec);
00602 
00603         if (WaitForSingleObject(pThread->hThreadEvent, lSec * 1000L) !=
00604             WAIT_TIMEOUT)
00605             iReturn = -1;
00606 
00607         loc_debug("sleep done. Waiting to run.");
00608         loc_RunThisThread(NULL);
00609     }
00610     return iReturn;
00611 }
00612 
00613 /************************
00614  * empth_request_shutdown
00615  *
00616  * This wakes up empth_wait_for_signal() so shutdown can proceed.
00617  * This is done by signalling hShutdownEvent.
00618  */
00619 void
00620 empth_request_shutdown(void)
00621 {
00622     SetEvent(hShutdownEvent);
00623 }
00624 
00625 int
00626 empth_wait_for_signal(void)
00627 {
00628     loc_BlockThisThread();
00629     loc_RunThisThread(hShutdownEvent);
00630     return SIGTERM;
00631 }
00632 
00633 empth_rwlock_t *
00634 empth_rwlock_create(char *name)
00635 {
00636     empth_rwlock_t *rwlock;
00637 
00638     rwlock = malloc(sizeof(*rwlock));
00639     if (!rwlock)
00640         return NULL;
00641 
00642     memset(rwlock, 0, sizeof(*rwlock));
00643     strncpy(rwlock->name, name, sizeof(rwlock->name) - 1);
00644 
00645     if ((rwlock->can_read = CreateEvent(NULL, TRUE, TRUE, NULL)) == NULL) {
00646         logerror("rwlock_create: failed to create reader event %s at %s:%d",
00647             name, __FILE__, __LINE__);
00648         free(rwlock);
00649         return NULL;
00650     }
00651 
00652     if ((rwlock->can_write = CreateEvent(NULL, FALSE, TRUE, NULL)) == NULL) {
00653         logerror("rwlock_create: failed to create writer event %s at %s:%d",
00654             name, __FILE__, __LINE__);
00655         CloseHandle(rwlock->can_read);
00656         free(rwlock);
00657         return NULL;
00658     }
00659     return rwlock;
00660 }
00661 
00662 void
00663 empth_rwlock_destroy(empth_rwlock_t *rwlock)
00664 {
00665     if (CANT_HAPPEN(rwlock->nread || rwlock->nwrite))
00666         return;
00667     CloseHandle(rwlock->can_read);
00668     CloseHandle(rwlock->can_write);
00669     free(rwlock);
00670 }
00671 
00672 void
00673 empth_rwlock_wrlock(empth_rwlock_t *rwlock)
00674 {
00675     /* block any new readers */
00676     ResetEvent(rwlock->can_read);
00677     rwlock->nwrite++;
00678     loc_BlockThisThread();
00679     loc_RunThisThread(rwlock->can_write);
00680     CANT_HAPPEN(rwlock->nread != 0);
00681 }
00682 
00683 void
00684 empth_rwlock_rdlock(empth_rwlock_t *rwlock)
00685 {
00686     loc_BlockThisThread();
00687     loc_RunThisThread(rwlock->can_read);
00688     ResetEvent(rwlock->can_write);
00689     rwlock->nread++;
00690 }
00691 
00692 void
00693 empth_rwlock_unlock(empth_rwlock_t *rwlock)
00694 {
00695     if (CANT_HAPPEN(!rwlock->nread && !rwlock->nwrite))
00696         return;
00697    if (rwlock->nread) { /* holding read lock */
00698         rwlock->nread--;
00699         if (rwlock->nread == 0)
00700             SetEvent(rwlock->can_write);
00701     } else {
00702         rwlock->nwrite--;
00703         SetEvent(rwlock->can_write);
00704     }
00705     if (rwlock->nwrite == 0)
00706         SetEvent(rwlock->can_read);
00707 }

Generated on Fri Mar 28 11:01:14 2008 for empserver by  doxygen 1.5.2