]> git.pond.sub.org Git - empserver/blob - src/lib/empthread/ntthread.c
(loc_Thread, loc_Sem_t): Remove typedef, use the typedef name defined
[empserver] / src / lib / empthread / ntthread.c
1 /*
2  *  Empire - A multi-player, client/server Internet based war game.
3  *  Copyright (C) 1986-2005, Dave Pare, Jeff Bailey, Thomas Ruschak,
4  *                           Ken Stevens, Steve McClure
5  *
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.
10  *
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.
15  *
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
19  *
20  *  ---
21  *
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.
25  *
26  *  ---
27  *
28  *  ntthread.c: Interface from Empire threads to Windows NT threads
29  * 
30  *  Known contributors to this file:
31  *     Doug Hay, 1998
32  *     Steve McClure, 1998
33  */
34
35 /*
36  * EMPTHREADs for Windows NT.
37  *
38  * Actually, threads for any Win32 platform, like Win95, Win98, WinCE,
39  * and whatever other toy OSs are in our future from Microsoft.
40  *
41  * WIN32 has a full pre-emptive threading environment.  But Empire can
42  * not handle pre-emptive threading.  Thus, we will use the threads,
43  * but limit the preemption using a Mutex semaphore.
44  *
45  */
46
47 #include <stdio.h>
48 #include <sys/types.h>
49 #include <signal.h>
50 #include <errno.h>
51 #include "misc.h"
52 #include "empthread.h"
53 #include "prototypes.h"
54
55 #if defined(_WIN32) && defined(_EMPTH_WIN32)
56 #define WIN32
57 #include <winsock2.h>
58 #undef NS_ALL
59 #include <windows.h>
60 #include <process.h>
61
62 #define loc_MIN_THREAD_STACK  16384
63
64 /************************
65  * loc_Thread_t
66  */
67 struct loc_Thread_t {
68
69     /* The thread name, passed in at create time. */
70     char szName[17];
71     /* The thread description, passed in at create time. */
72     char szDesc[80];
73
74     /* True if this is the main line, and not a real thread. */
75     BOOL bMainThread;
76
77     /* The user data passed in at create time. */
78     void *pvUserData;
79
80     /* True if this thread has been killed. */
81     BOOL bKilled;
82
83     /* The entry function for the thread. */
84     void (*pfnEntry) (void *);
85
86     /* The system thread ID. */
87     unsigned long ulThreadID;
88
89     /* An Event sem that the thread will wait/sleep on. */
90     HANDLE hThreadEvent;
91 };
92
93
94 /************************
95  * loc_Sem_t
96  */
97 struct loc_Sem_t {
98
99     char szName[17];
100
101     /* An exclusion semaphore for this sem. */
102     HANDLE hMutex;
103     /* An Event sem that the thread(s) will sleep on. */
104
105     HANDLE hEvent;
106     /* The count variable */
107     int count;
108 };
109
110 static struct {
111     /* This is the thread exclusion/non-premption mutex. */
112     /* The running thread has this MUTEX, and all others are */
113     /* either blocked on it, or waiting for some OS response. */
114     HANDLE hThreadMutex;
115
116     /* This is the thread startup event sem. */
117     /* We use this to lockstep when we are starting up threads. */
118     HANDLE hThreadStartEvent;
119
120     /* This is an event used to wakeup the main thread */
121     /* to start the shutdown sequence. */
122     HANDLE hShutdownEvent;
123
124     /* The Thread Local Storage index.  We store the pThread pointer */
125     /* for each thread at this index. */
126     DWORD dwTLSIndex;
127
128     /* The current running thread. */
129     empth_t *pCurThread;
130
131     /* Ticks at start */
132     unsigned long ulTickAtStart;
133
134     /* Pointer out to global context.  "player". */
135     /* From empth_init parameter. */
136     void **ppvUserData;
137
138     /* Global flags.  From empth_init parameter. */
139     int flags;
140 } loc_GVAR;
141
142
143 /************************
144  * loc_debug
145  *
146  * Print out the current thread's status??
147  */
148 static void
149 loc_debug(const char *pszFmt, ...)
150 {
151     va_list vaList;
152     unsigned long ulCurTick;
153     unsigned long ulRunTick;
154     unsigned long ulMs, ulSec, ulMin, ulHr;
155     empth_t *pThread = TlsGetValue(loc_GVAR.dwTLSIndex);
156     char buf[1024];
157
158     if ((loc_GVAR.flags & EMPTH_PRINT) != 0) {
159
160         /* Ticks are in milliseconds */
161         ulCurTick = GetTickCount();
162
163         ulRunTick = ulCurTick - loc_GVAR.ulTickAtStart;
164         ulMs = ulRunTick % 1000L;
165         ulSec = (ulRunTick / 1000L) % 60L;
166         ulMin = (ulRunTick / (60L * 1000L)) % 60L;
167         ulHr = (ulRunTick / (60L * 60L * 1000L));
168
169         va_start(vaList, pszFmt);
170         vsprintf(buf, pszFmt, vaList);
171         va_end(vaList);
172
173         if (pThread) {
174             printf("%ld:%02ld:%02ld.%03ld %17s: %s\n",
175                    ulHr, ulMin, ulSec, ulMs, pThread->szName, buf);
176         } else {
177             printf("%ld:%02ld:%02ld.%03ld %17s: %s\n",
178                    ulHr, ulMin, ulSec, ulMs, "UNKNOWN", buf);
179         }
180
181     }
182 }
183
184 /************************
185  * loc_FreeThreadInfo
186  *
187  */
188 static void
189 loc_FreeThreadInfo(empth_t *pThread)
190 {
191     if (pThread) {
192         if (pThread->hThreadEvent)
193             CloseHandle(pThread->hThreadEvent);
194         memset(pThread, 0, sizeof(*pThread));
195         free(pThread);
196     }
197 }
198
199 /************************
200  * loc_RunThisThread
201  *
202  * This thread wants to run.
203  * When this function returns, the globals are set to this thread
204  * info, and the thread owns the MUTEX sem.
205  */
206 static void
207 loc_RunThisThread()
208 {
209     empth_t *pThread = TlsGetValue(loc_GVAR.dwTLSIndex);
210
211     if (pThread->bKilled) {
212         if (!pThread->bMainThread) {
213             TlsSetValue(loc_GVAR.dwTLSIndex, NULL);
214             loc_FreeThreadInfo(pThread);
215             _endthread();
216         }
217     }
218
219     /* Get the MUTEX semaphore, wait forever. */
220     WaitForSingleObject(loc_GVAR.hThreadMutex, INFINITE);
221
222     if (!loc_GVAR.pCurThread) {
223         /* Set the globals to this thread. */
224         *loc_GVAR.ppvUserData = pThread->pvUserData;
225
226         loc_GVAR.pCurThread = pThread;
227     } else {
228         /* Hmm, a problem, eh? */
229         logerror("RunThisThread, someone already running.");
230     }
231 }
232
233 /************************
234  * loc_BlockThisThread
235  *
236  * This thread was running.  It no longer wants to.
237  */
238 static void
239 loc_BlockThisThread()
240 {
241     empth_t *pThread = TlsGetValue(loc_GVAR.dwTLSIndex);
242
243     if (loc_GVAR.pCurThread == pThread) {
244         /* Reset the globals back to original */
245
246         loc_GVAR.pCurThread = NULL;
247         *loc_GVAR.ppvUserData = NULL;
248
249         /* Release the MUTEX */
250         ReleaseMutex(loc_GVAR.hThreadMutex);
251     } else {
252         /* Hmm, this thread was not the running one. */
253         logerror("BlockThisThread, not running.");
254     }
255 }
256
257 /************************
258  * loc_Exit_Handler
259  *
260  * Ctrl-C, Ctrl-Break, Window-Closure, User-Logging-Off or
261  * System-Shutdown will initiate a shutdown.
262  * This is done by calling empth_request_shutdown()
263  */
264 static BOOL
265 loc_Exit_Handler(DWORD fdwCtrlType)
266 {
267     switch (fdwCtrlType) 
268     { 
269         case CTRL_C_EVENT:
270         case CTRL_CLOSE_EVENT:
271         case CTRL_BREAK_EVENT: 
272         case CTRL_LOGOFF_EVENT: 
273         case CTRL_SHUTDOWN_EVENT: 
274             empth_request_shutdown();
275             return TRUE;
276         default:
277             return FALSE;
278     }
279 }
280
281 /************************
282  * empth_request_shutdown
283  *
284  * This wakes up the main thread so shutdown can proceed.
285  * This is done by signalling hShutdownEvent.
286  */
287 void
288 empth_request_shutdown(void)
289 {
290     SetEvent(loc_GVAR.hShutdownEvent);
291 }
292
293 /************************
294  * loc_BlockMainThread
295  *
296  * This blocks up the main thread.  loc_WakeupMainThread() is used
297  * wakeup the main so shutdown can proceed.
298  */
299 static void
300 loc_BlockMainThread(void)
301 {
302     /* Get the MUTEX semaphore, wait the number of MS */
303     WaitForSingleObject(loc_GVAR.hShutdownEvent, INFINITE);
304 }
305
306 /************************
307  * empth_threadMain
308  *
309  * This is the main line of each thread.
310  * This is really a static local func....
311  */
312 void
313 empth_threadMain(void *pvData)
314 {
315     time_t now;
316
317     empth_t *pThread = pvData;
318
319     /* Out of here... */
320     if (!pvData)
321         return;
322
323     /* Store pThread on this thread. */
324     TlsSetValue(loc_GVAR.dwTLSIndex, pvData);
325
326     /* Get the ID of the thread. */
327     pThread->ulThreadID = GetCurrentThreadId();
328
329     /* Signal that the thread has started. */
330     SetEvent(loc_GVAR.hThreadStartEvent);
331
332     /* seed the rand() function */
333     time(&now);
334     srand(now ^ (unsigned int)pThread);
335
336     /* Switch to this thread context */
337     loc_RunThisThread();
338
339     /* Run the thread. */
340     if (pThread->pfnEntry)
341         pThread->pfnEntry(pThread->pvUserData);
342
343     /* Kill the thread. */
344     empth_exit();
345 }
346
347 /************************
348  * empth_init
349  *
350  * Initialize the thread environment.
351  *
352  * This is called from the program main line.
353  */
354 int
355 empth_init(void **ctx_ptr, int flags)
356 {
357     empth_t *pThread = NULL;
358
359     loc_GVAR.ulTickAtStart = GetTickCount();
360     loc_GVAR.ppvUserData = ctx_ptr;
361     loc_GVAR.flags = flags;
362     loc_GVAR.dwTLSIndex = TlsAlloc();
363
364     /* Create the thread mutex sem. */
365     /* Initally unowned. */
366     loc_GVAR.hThreadMutex = CreateMutex(NULL, FALSE, NULL);
367     if (!loc_GVAR.hThreadMutex) {
368         logerror("Failed to create mutex %d", GetLastError());
369         return 0;
370     }
371
372     /* Create the thread start event sem. */
373     /* Automatic state reset. */
374     loc_GVAR.hThreadStartEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
375     if (!loc_GVAR.hThreadStartEvent) {
376         logerror("Failed to create start event %d", GetLastError());
377         return 0;
378     }
379
380     /* Create the shutdown event for the main thread. */
381     /* Manual reset */
382     loc_GVAR.hShutdownEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
383     if (!loc_GVAR.hShutdownEvent) {
384         logerror("Failed to create shutdown event %d", GetLastError());
385         return 0;
386     }
387     SetConsoleCtrlHandler((PHANDLER_ROUTINE)loc_Exit_Handler, TRUE);
388
389     /* Create the global Thread context. */
390     pThread = malloc(sizeof(*pThread));
391     if (!pThread) {
392         logerror("not enough memory to create main thread.");
393         return 0;
394     }
395     memset(pThread, 0, sizeof(*pThread));
396
397     strncpy(pThread->szName, "Main", sizeof(pThread->szName) - 1);
398     strncpy(pThread->szDesc, "The main process",
399             sizeof(pThread->szDesc) - 1);
400     pThread->ulThreadID = GetCurrentThreadId();
401     pThread->bMainThread = TRUE;
402
403     TlsSetValue(loc_GVAR.dwTLSIndex, pThread);
404
405     /* Make this the running thread. */
406     loc_RunThisThread();
407
408     logerror("NT pthreads initialized");
409     return 0;
410 }
411
412
413 /************************
414  * empth_create
415  *
416  * Create a new thread.
417  *
418  * prio  - priority, not particularly useful in our context.
419  * entry - entry point function for thread.
420  * size  - stack size.
421  * flags - debug control.
422  *           LWP_STACKCHECK  - not needed
423  * name  - name of the thread, for debug.
424  * desc  - description of thread, for debug.
425  * ud    - "user data".  The "ctx_ptr" gets this value
426  *         when the thread is active.
427  *         It is also passed to the entry function...
428  */
429 empth_t *
430 empth_create(int prio, void (*entry)(void *), int size, int flags,
431              char *name, char *desc, void *ud)
432 {
433     empth_t *pThread = NULL;
434
435     loc_debug("creating new thread %s:%s", name, desc);
436
437     pThread = malloc(sizeof(*pThread));
438     if (!pThread) {
439         logerror("not enough memory to create thread: %s (%s)", name,
440                  desc);
441         return NULL;
442     }
443     memset(pThread, 0, sizeof(*pThread));
444
445     strncpy(pThread->szName, name, sizeof(pThread->szName) - 1);
446     strncpy(pThread->szDesc, desc, sizeof(pThread->szDesc) - 1);
447     pThread->pvUserData = ud;
448     pThread->pfnEntry = entry;
449     pThread->bMainThread = FALSE;
450
451     /* Create thread event sem, auto reset. */
452     pThread->hThreadEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
453
454     if (size < loc_MIN_THREAD_STACK)
455         size = loc_MIN_THREAD_STACK;
456
457     pThread->ulThreadID = _beginthread(empth_threadMain, size, pThread);
458     if (pThread->ulThreadID == -1) {
459         logerror("can not create thread: %s (%s): %s", name, desc,
460                  strerror(errno));
461         goto bad;
462     }
463
464     loc_debug("new thread id is %ld", pThread->ulThreadID);
465     return pThread;
466
467   bad:
468     if (pThread) {
469         loc_FreeThreadInfo(pThread);
470     }
471     return NULL;
472 }
473
474
475 /************************
476  * empth_self
477  */
478 empth_t *
479 empth_self(void)
480 {
481     empth_t *pThread = TlsGetValue(loc_GVAR.dwTLSIndex);
482
483     return pThread;
484 }
485
486 /************************
487  * empth_exit
488  */
489 void
490 empth_exit(void)
491 {
492     empth_t *pThread = TlsGetValue(loc_GVAR.dwTLSIndex);
493
494     loc_BlockThisThread();
495
496     loc_debug("empth_exit");
497
498     if (pThread->bMainThread) {
499         loc_BlockMainThread();
500         loc_RunThisThread();
501         shutdwn(0);
502     } else {
503         TlsSetValue(loc_GVAR.dwTLSIndex, NULL);
504         loc_FreeThreadInfo(pThread);
505         _endthread();
506     }
507 }
508
509 /************************
510  * empth_yield
511  *
512  * Yield processing to another thread.
513  */
514 void
515 empth_yield(void)
516 {
517     loc_BlockThisThread();
518     loc_RunThisThread();
519 }
520
521 /************************
522  * empth_terminate
523  *
524  * Kill off the thread.
525  */
526 void
527 empth_terminate(empth_t *pThread)
528 {
529     loc_debug("killing thread %s", pThread->szName);
530     pThread->bKilled = TRUE;
531
532     SetEvent(pThread->hThreadEvent);
533 }
534
535 /************************
536  * empth_select
537  *
538  * Do a select on the given file.
539  * Wait for IO on it.
540  *
541  * This would be one of the main functions used within gen\io.c
542  */
543 void
544 empth_select(int fd, int flags)
545 {
546     WSAEVENT hEventObject[2];
547     empth_t *pThread = TlsGetValue(loc_GVAR.dwTLSIndex);
548
549     loc_debug("%s select on %d",
550               flags == EMPTH_FD_READ ? "read" : "write", fd);
551     loc_BlockThisThread();
552
553     hEventObject[0] = WSACreateEvent();
554     hEventObject[1] = pThread->hThreadEvent;
555
556     if (flags == EMPTH_FD_READ)
557         WSAEventSelect(fd, hEventObject[0], FD_READ | FD_ACCEPT | FD_CLOSE);
558     else if (flags == EMPTH_FD_WRITE)
559         WSAEventSelect(fd, hEventObject[0], FD_WRITE | FD_CLOSE);
560     else {
561         logerror("bad flag %d passed to empth_select", flags);
562         empth_exit();
563     }
564
565     WSAWaitForMultipleEvents(2, hEventObject, FALSE, WSA_INFINITE, FALSE);
566
567     WSAEventSelect(fd, hEventObject[0], 0);
568
569     WSACloseEvent(hEventObject[0]);
570
571     loc_RunThisThread();
572 }
573
574 /************************
575  * empth_alarm
576  */
577 void
578 empth_alarm(int sig)
579 {
580     empth_t *pThread = TlsGetValue(loc_GVAR.dwTLSIndex);
581
582     loc_debug("got alarm signal  %d", sig);
583
584     /* Let it run if it is blocked like... */
585     SetEvent(pThread->hThreadEvent);
586 }
587
588 /************************
589  * empth_wakeup
590  *
591  * Wake up the specified thread.
592  */
593 void
594 empth_wakeup(empth_t *pThread)
595 {
596     loc_debug("waking up thread %s", pThread->szName);
597
598     /* Let it run if it is blocked... */
599     SetEvent(pThread->hThreadEvent);
600 }
601
602 /************************
603  * empth_sleep
604  *
605  * Put the given thread to sleep...
606  */
607 void
608 empth_sleep(time_t until)
609 {
610     long lSec;
611
612     loc_BlockThisThread();
613
614     while ((lSec = until - time(0)) > 0) {
615         loc_debug("going to sleep %ld sec", lSec);
616         Sleep(lSec * 1000L);
617     }
618
619     loc_debug("sleep done. Waiting to run.");
620
621     loc_RunThisThread();
622 }
623
624
625 /************************
626  * empth_sem_create
627  *
628  * Create a signalling semaphore.
629  */
630 empth_sem_t *
631 empth_sem_create(char *name, int cnt)
632 {
633     empth_sem_t *pSem;
634
635     pSem = malloc(sizeof(*pSem));
636     if (!pSem) {
637         logerror("out of memory at %s:%d", __FILE__, __LINE__);
638         return NULL;
639     }
640
641     memset(pSem, 0, sizeof(pSem));
642     strncpy(pSem->szName, name, sizeof(pSem->szName) - 1);
643
644     pSem->hMutex = CreateMutex(NULL, FALSE, NULL);
645     pSem->hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
646     pSem->count = cnt;
647
648     return pSem;
649 }
650
651 /************************
652  * empth_sem_signal
653  *
654  * Hit/signal the specified semaphore.
655  */
656 void
657 empth_sem_signal(empth_sem_t *pSem)
658 {
659     loc_debug("signal on semaphore %s:%d", pSem->szName, pSem->count);
660
661     /* Wait for the Semaphore */
662     WaitForSingleObject(pSem->hMutex, INFINITE);
663
664     if (pSem->count++ < 0) {
665         SetEvent(pSem->hEvent);
666     }
667
668     ReleaseMutex(pSem->hMutex);
669 }
670
671 /************************
672  * empth_sem_wait
673  *
674  * Wait for the specified signal semaphore to be signaled.
675  */
676 void
677 empth_sem_wait(empth_sem_t *pSem)
678 {
679     empth_t *pThread = TlsGetValue(loc_GVAR.dwTLSIndex);
680
681     loc_debug("wait on semaphore %s:%d", pSem->szName, pSem->count);
682
683     /* Remove the thread from the running state. */
684     loc_BlockThisThread();
685
686     /* Wait for the Semaphore */
687     WaitForSingleObject(pSem->hMutex, INFINITE);
688     if (--pSem->count < 0) {
689         loc_debug("blocking");
690         ReleaseMutex(pSem->hMutex);
691
692         WaitForSingleObject(pSem->hEvent, INFINITE);
693
694         loc_debug("waking up");
695     } else
696         ReleaseMutex(pSem->hMutex);
697
698     loc_RunThisThread();
699 }
700
701 #endif /* _WIN32 */