2 * Empire - A multi-player, client/server Internet based war game.
3 * Copyright (C) 1986-2004, Dave Pare, Jeff Bailey, Thomas Ruschak,
4 * Ken Stevens, Steve McClure
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 * See the "LEGAL", "LICENSE", "CREDITS" and "README" files for all the
23 * related information and legal notices. It is expected that any future
24 * projects/authors will amend these files as needed.
28 * ntthread.c: Interface from Empire threads to Windows NT threads
30 * Known contributors to this file:
36 * EMPTHREADs for Windows NT.
38 * Actually, threads for any Win32 platform,
39 * like Win95, Win98, WinCE, and whatever other
40 * toy OSs are in our future from Microsoft.
42 * WIN32 has a full pre-emptive threading environment.
43 * But, Empire can not handle pre-emptive threading.
44 * Thus, we will use the threads, but limit the preemption
45 * using a Mutex semaphore.
50 #include <sys/types.h>
54 #include "empthread.h"
55 #include "prototypes.h"
57 #if defined(_WIN32) && defined(_EMPTH_WIN32)
64 #define loc_MIN_THREAD_STACK 16384
66 /************************
69 * The REAL empth_t thread structure.
70 * The external world only gets
71 * a void pointer to this.
73 typedef struct loc_Thread_t {
75 /* The thread name, passed in at create time. */
77 /* The thread description, passed in at create time. */
80 /* True if this is the main line, and not a real thread. */
83 /* The user data passed in at create time. */
86 /* True if this thread has been killed. */
89 /* The entry function for the thread. */
90 void (*pfnEntry) (void *);
92 /* The system thread ID. */
93 unsigned long ulThreadID;
95 /* An Event sem that the thread will wait/sleep on. */
100 /************************
103 * The REAL empth_sem_t structure.
104 * The external world only gets
105 * a void pointer to this.
107 typedef struct empth_sem_t {
111 /* An exclusion semaphore for this sem. */
113 /* An Event sem that the thread(s) will sleep on. */
116 /* The count variable */
121 /* This is the thread exclusion/non-premption mutex. */
122 /* The running thread has this MUTEX, and all others are */
123 /* either blocked on it, or waiting for some OS response. */
126 /* This is the thread startup event sem. */
127 /* We use this to lockstep when we are starting up threads. */
128 HANDLE hThreadStartEvent;
130 /* The Thread Local Storage index. We store the pThread pointer */
131 /* for each thread at this index. */
134 /* The current running thread. */
135 loc_Thread_t *pCurThread;
138 unsigned long ulTickAtStart;
140 /* Pointer out to global context. "player". */
141 /* From empth_init parameter. */
144 /* Global flags. From empth_init parameter. */
149 /************************
152 * Print out the current thread's status??
155 loc_debug(const char *pszFmt, ...)
158 unsigned long ulCurTick;
159 unsigned long ulRunTick;
160 unsigned long ulMs, ulSec, ulMin, ulHr;
161 loc_Thread_t *pThread =
162 (loc_Thread_t *)TlsGetValue(loc_GVAR.dwTLSIndex);
165 if ((loc_GVAR.flags & EMPTH_PRINT) != 0) {
167 /* Ticks are in milliseconds */
168 ulCurTick = GetTickCount();
170 ulRunTick = ulCurTick - loc_GVAR.ulTickAtStart;
171 ulMs = ulRunTick % 1000L;
172 ulSec = (ulRunTick / 1000L) % 60L;
173 ulMin = (ulRunTick / (60L * 1000L)) % 60L;
174 ulHr = (ulRunTick / (60L * 60L * 1000L));
176 va_start(vaList, pszFmt);
177 vsprintf(buf, pszFmt, vaList);
181 printf("%ld:%02ld:%02ld.%03ld %17s: %s\n",
182 ulHr, ulMin, ulSec, ulMs, pThread->szName, buf);
184 printf("%ld:%02ld:%02ld.%03ld %17s: %s\n",
185 ulHr, ulMin, ulSec, ulMs, "UNKNOWN", buf);
191 /************************
196 loc_FreeThreadInfo(loc_Thread_t *pThread)
199 if (pThread->hThreadEvent)
200 CloseHandle(pThread->hThreadEvent);
201 memset(pThread, 0, sizeof(*pThread));
206 /************************
209 * This thread wants to run.
210 * When this function returns, the
211 * globals are set to this thread info,
212 * and the thread owns the MUTEX sem.
217 loc_Thread_t *pThread =
218 (loc_Thread_t *)TlsGetValue(loc_GVAR.dwTLSIndex);
220 if (pThread->bKilled) {
221 if (!pThread->bMainThread) {
222 TlsSetValue(loc_GVAR.dwTLSIndex, NULL);
223 loc_FreeThreadInfo(pThread);
228 /* Get the MUTEX semaphore, wait forever. */
229 WaitForSingleObject(loc_GVAR.hThreadMutex, INFINITE);
231 if (!loc_GVAR.pCurThread) {
232 /* Set the globals to this thread. */
233 *loc_GVAR.ppvUserData = pThread->pvUserData;
235 loc_GVAR.pCurThread = pThread;
237 /* Hmm, a problem, eh? */
238 logerror("RunThisThread, someone already running.");
242 /************************
243 * loc_BlockThisThread
245 * This thread was running. It no longer wants to.
248 loc_BlockThisThread()
250 loc_Thread_t *pThread =
251 (loc_Thread_t *)TlsGetValue(loc_GVAR.dwTLSIndex);
253 if (loc_GVAR.pCurThread == pThread) {
254 /* Reset the globals back to original */
256 loc_GVAR.pCurThread = NULL;
257 *loc_GVAR.ppvUserData = NULL;
259 /* Release the MUTEX */
260 ReleaseMutex(loc_GVAR.hThreadMutex);
262 /* Hmm, this thread was not the running one. */
263 logerror("BlockThisThread, not running.");
268 /************************
269 * loc_SleepThisThread
272 loc_SleepThisThread(unsigned long ulMillisecs)
274 loc_Thread_t *pThread =
275 (loc_Thread_t *)TlsGetValue(loc_GVAR.dwTLSIndex);
277 /* Make sure the event thread is clean. */
278 ResetEvent(pThread->hThreadEvent);
280 /* Get the MUTEX semaphore, wait the number of MS */
281 WaitForSingleObject(pThread->hThreadEvent, ulMillisecs);
285 /************************
288 * This is the main line of each thread.
289 * This is really a static local func....
292 empth_threadMain(void *pvData)
296 loc_Thread_t *pThread = (loc_Thread_t *)pvData;
302 /* Store pThread on this thread. */
303 TlsSetValue(loc_GVAR.dwTLSIndex, pvData);
305 /* Get the ID of the thread. */
306 pThread->ulThreadID = GetCurrentThreadId();
308 /* Signal that the thread has started. */
309 SetEvent(loc_GVAR.hThreadStartEvent);
311 /* seed the rand() function */
313 srand(now ^ (unsigned int)pThread);
315 /* Switch to this thread context */
318 /* Run the thread. */
319 if (pThread->pfnEntry)
320 pThread->pfnEntry(pThread->pvUserData);
322 /* Kill the thread. */
326 /************************
329 * Initialize the thread environment.
331 * This is called from the program
335 empth_init(char **ctx_ptr, int flags)
337 loc_Thread_t *pThread = NULL;
339 loc_GVAR.ulTickAtStart = GetTickCount();
340 loc_GVAR.ppvUserData = ctx_ptr;
341 loc_GVAR.flags = flags;
342 loc_GVAR.dwTLSIndex = TlsAlloc();
344 /* Create the thread mutex sem. */
345 /* Initally unowned. */
346 loc_GVAR.hThreadMutex = CreateMutex(NULL, FALSE, NULL);
347 if (!loc_GVAR.hThreadMutex) {
348 logerror("Failed to create mutex");
352 /* Create the thread start event sem. */
353 /* Automatic state reset. */
354 loc_GVAR.hThreadStartEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
355 if (!loc_GVAR.hThreadStartEvent) {
356 logerror("Failed to create mutex");
360 /* Create the global Thread context. */
361 pThread = (loc_Thread_t *)malloc(sizeof(*pThread));
363 logerror("not enough memory to create main thread.");
366 memset(pThread, 0, sizeof(*pThread));
368 strncpy(pThread->szName, "Main", sizeof(pThread->szName) - 1);
369 strncpy(pThread->szDesc, "The main process",
370 sizeof(pThread->szDesc) - 1);
371 pThread->ulThreadID = GetCurrentThreadId();
372 pThread->bMainThread = TRUE;
374 TlsSetValue(loc_GVAR.dwTLSIndex, pThread);
376 /* Make this the running thread. */
379 logerror("NT pthreads initialized");
384 /************************
387 * Create a new thread.
389 * prio - priority, not particularly useful in our context.
390 * entry - entry point function for thread.
392 * flags - debug control.
393 * LWP_STACKCHECK - not needed
394 * name - name of the thread, for debug.
395 * desc - description of thread, for debug.
396 * ud - "user data". The "ctx_ptr" gets this value
397 * when the thread is active.
398 * It is also passed to the entry function...
401 empth_create(int prio, void (*entry)(void *), int size, int flags,
402 char *name, char *desc, void *ud)
404 loc_Thread_t *pThread = NULL;
406 loc_debug("creating new thread %s:%s", name, desc);
408 pThread = (loc_Thread_t *)malloc(sizeof(*pThread));
410 logerror("not enough memory to create thread: %s (%s)", name,
414 memset(pThread, 0, sizeof(*pThread));
416 strncpy(pThread->szName, name, sizeof(pThread->szName) - 1);
417 strncpy(pThread->szDesc, desc, sizeof(pThread->szDesc) - 1);
418 pThread->pvUserData = ud;
419 pThread->pfnEntry = entry;
420 pThread->bMainThread = FALSE;
422 /* Create thread event sem, auto reset. */
423 pThread->hThreadEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
425 if (size < loc_MIN_THREAD_STACK)
426 size = loc_MIN_THREAD_STACK;
428 pThread->ulThreadID = _beginthread(empth_threadMain, size,
430 if (pThread->ulThreadID == -1) {
431 logerror("can not create thread: %s (%s): %s", name, desc,
436 loc_debug("new thread id is %ld", pThread->ulThreadID);
441 loc_FreeThreadInfo(pThread);
447 /************************
453 loc_Thread_t *pThread =
454 (loc_Thread_t *)TlsGetValue(loc_GVAR.dwTLSIndex);
459 /************************
465 loc_Thread_t *pThread =
466 (loc_Thread_t *)TlsGetValue(loc_GVAR.dwTLSIndex);
468 loc_BlockThisThread();
470 loc_debug("empth_exit");
472 if (pThread->bMainThread) {
473 /* The main line. Wait forever. */
476 if (service_stopped())
481 printf("\nEmpire Server>");
482 fgets(buf, sizeof(buf), stdin);
483 if (!strnicmp(buf, "quit", 4))
488 TlsSetValue(loc_GVAR.dwTLSIndex, NULL);
489 loc_FreeThreadInfo(pThread);
494 /************************
497 * Yield processing to another thread.
502 loc_BlockThisThread();
506 /************************
509 * Kill off the thread.
512 empth_terminate(empth_t *a)
514 loc_Thread_t *pThread = (loc_Thread_t *)a;
516 loc_debug("killing thread %s", pThread->szName);
517 pThread->bKilled = TRUE;
519 SetEvent(pThread->hThreadEvent);
522 /************************
525 * Do a select on the given file.
528 * This would be one of the main functions used within
532 empth_select(int fd, int flags)
534 loc_Thread_t *pThread =
535 (loc_Thread_t *)TlsGetValue(loc_GVAR.dwTLSIndex);
538 struct lwpProc *proc;
542 loc_debug("%s select on %d",
543 flags == EMPTH_FD_READ ? "read" : "write", fd);
544 loc_BlockThisThread();
555 FD_SET(fd, &readmask);
558 FD_SET(fd, &writemask);
561 logerror("bad flag %d passed to empth_select", flags);
565 n = select(fd + 1, &readmask, &writemask, (fd_set *) 0, &tv);
568 if (errno == EINTR) {
569 /* go handle the signal */
570 loc_debug("select broken by signal");
574 /* strange but we dont get EINTR on select broken by signal */
575 loc_debug("select failed (%s)", strerror(errno));
580 if (flags == EMPTH_FD_READ && FD_ISSET(fd, &readmask)) {
581 loc_debug("input ready");
584 if (flags == EMPTH_FD_WRITE && FD_ISSET(fd, &writemask)) {
585 loc_debug("output ready");
594 /************************
600 loc_Thread_t *pThread =
601 (loc_Thread_t *)TlsGetValue(loc_GVAR.dwTLSIndex);
603 loc_debug("got alarm signal %d", sig);
605 /* Let it run if it is blocked like... */
606 SetEvent(pThread->hThreadEvent);
609 /************************
612 * Wake up the specified thread.
615 empth_wakeup(empth_t *a)
617 loc_Thread_t *pThread = (loc_Thread_t *)a;
619 loc_debug("waking up thread %s", pThread->szName);
621 /* Let it run if it is blocked... */
622 SetEvent(pThread->hThreadEvent);
625 /************************
628 * Put the given thread to sleep...
631 empth_sleep(long until)
633 loc_Thread_t *pThread =
634 (loc_Thread_t *)TlsGetValue(loc_GVAR.dwTLSIndex);
637 ulSec = until - time(0);
639 loc_debug("going to sleep %ld sec", ulSec);
641 loc_BlockThisThread();
643 WaitForSingleObject(pThread->hThreadEvent, (ulSec * 1000));
645 loc_debug("sleep done. Waiting to run.");
651 /************************
654 * Create a signalling semaphore.
657 empth_sem_create(char *name, int cnt)
661 pSem = (loc_Sem_t *)malloc(sizeof(*pSem));
663 logerror("out of memory at %s:%d", __FILE__, __LINE__);
667 memset(pSem, 0, sizeof(pSem));
668 strncpy(pSem->szName, name, sizeof(pSem->szName) - 1);
670 pSem->hMutex = CreateMutex(NULL, FALSE, NULL);
671 pSem->hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
677 /************************
680 * Hit/signal the specified semaphore.
683 empth_sem_signal(empth_sem_t *sm)
685 loc_Sem_t *pSem = (loc_Sem_t *)sm;
687 loc_debug("signal on semaphore %s:%d", pSem->szName, pSem->count);
689 /* Wait for the Semaphore */
690 WaitForSingleObject(pSem->hMutex, INFINITE);
692 if (pSem->count++ < 0) {
693 SetEvent(pSem->hEvent);
696 ReleaseMutex(pSem->hMutex);
699 /************************
702 * Wait for the specified signal semaphore
706 empth_sem_wait(empth_sem_t *sm)
708 loc_Thread_t *pThread =
709 (loc_Thread_t *)TlsGetValue(loc_GVAR.dwTLSIndex);
710 loc_Sem_t *pSem = (loc_Sem_t *)sm;
712 loc_debug("wait on semaphore %s:%d", pSem->szName, pSem->count);
714 /* Remove the thread from the running state. */
715 loc_BlockThisThread();
717 /* Wait for the Semaphore */
718 WaitForSingleObject(pSem->hMutex, INFINITE);
719 if (--pSem->count < 0) {
720 loc_debug("blocking");
721 ReleaseMutex(pSem->hMutex);
723 WaitForSingleObject(pSem->hEvent, INFINITE);
725 loc_debug("waking up");
727 ReleaseMutex(pSem->hMutex);