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