]> git.pond.sub.org Git - empserver/blob - src/lib/empthread/ntthread.c
1f420fa78853c1c3850463f0c0a0876299d7d56f
[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_request_shutdown
281  *
282  * This wakes up the main thread so shutdown can proceed.
283  * This is done by signalling hShutdownEvent.
284  */
285 void
286 empth_request_shutdown(void)
287 {
288     SetEvent(hShutdownEvent);
289 }
290
291 /************************
292  * loc_BlockMainThread
293  *
294  * This blocks up the main thread.  loc_WakeupMainThread() is used
295  * wakeup the main so shutdown can proceed.
296  */
297 static void
298 loc_BlockMainThread(void)
299 {
300     /* Get the MUTEX semaphore, wait the number of MS */
301     WaitForSingleObject(hShutdownEvent, INFINITE);
302 }
303
304 /************************
305  * empth_threadMain
306  *
307  * This is the main line of each thread.
308  * This is really a static local func....
309  */
310 static void
311 empth_threadMain(void *pvData)
312 {
313     time_t now;
314
315     empth_t *pThread = pvData;
316
317     /* Out of here... */
318     if (!pvData)
319         return;
320
321     /* Store pThread on this thread. */
322     TlsSetValue(dwTLSIndex, pvData);
323
324     /* Get the ID of the thread. */
325     pThread->ulThreadID = GetCurrentThreadId();
326
327     /* Signal that the thread has started. */
328     SetEvent(hThreadStartEvent);
329
330     /* seed the rand() function */
331     time(&now);
332     srand(now ^ (unsigned)pThread);
333
334     /* Switch to this thread context */
335     loc_RunThisThread();
336
337     /* Run the thread. */
338     if (pThread->pfnEntry)
339         pThread->pfnEntry(pThread->pvUserData);
340
341     /* Kill the thread. */
342     empth_exit();
343 }
344
345 /************************
346  * empth_init
347  *
348  * Initialize the thread environment.
349  *
350  * This is called from the program main line.
351  */
352 int
353 empth_init(void **ctx_ptr, int flags)
354 {
355     empth_t *pThread = NULL;
356
357     ulTickAtStart = GetTickCount();
358     ppvUserData = ctx_ptr;
359     global_flags = flags;
360     dwTLSIndex = TlsAlloc();
361
362     /* Create the thread mutex sem. */
363     /* Initally unowned. */
364     hThreadMutex = CreateMutex(NULL, FALSE, NULL);
365     if (!hThreadMutex) {
366         logerror("Failed to create mutex %lu", GetLastError());
367         return 0;
368     }
369
370     /* Create the thread start event sem. */
371     /* Automatic state reset. */
372     hThreadStartEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
373     if (!hThreadStartEvent) {
374         logerror("Failed to create start event %lu", GetLastError());
375         return 0;
376     }
377
378     /* Create the shutdown event for the main thread. */
379     /* Manual reset */
380     hShutdownEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
381     if (!hShutdownEvent) {
382         logerror("Failed to create shutdown event %lu", GetLastError());
383         return 0;
384     }
385     SetConsoleCtrlHandler((PHANDLER_ROUTINE)loc_Exit_Handler, TRUE);
386
387     /* Create the global Thread context. */
388     pThread = malloc(sizeof(*pThread));
389     if (!pThread) {
390         logerror("not enough memory to create main thread.");
391         return 0;
392     }
393     memset(pThread, 0, sizeof(*pThread));
394
395     strncpy(pThread->szName, "Main", sizeof(pThread->szName) - 1);
396     strncpy(pThread->szDesc, "The main process",
397             sizeof(pThread->szDesc) - 1);
398     pThread->ulThreadID = GetCurrentThreadId();
399     pThread->bMainThread = TRUE;
400
401     TlsSetValue(dwTLSIndex, pThread);
402
403     /* Make this the running thread. */
404     loc_RunThisThread();
405
406     logerror("NT pthreads initialized");
407     return 0;
408 }
409
410
411 /************************
412  * empth_create
413  *
414  * Create a new thread.
415  *
416  * prio  - priority, not particularly useful in our context.
417  * entry - entry point function for thread.
418  * size  - stack size.
419  * flags - debug control.
420  *           LWP_STACKCHECK  - not needed
421  * name  - name of the thread, for debug.
422  * desc  - description of thread, for debug.
423  * ud    - "user data".  The "ctx_ptr" gets this value
424  *         when the thread is active.
425  *         It is also passed to the entry function...
426  */
427 empth_t *
428 empth_create(int prio, void (*entry)(void *), int size, int flags,
429              char *name, char *desc, void *ud)
430 {
431     empth_t *pThread = NULL;
432
433     loc_debug("creating new thread %s:%s", name, desc);
434
435     pThread = malloc(sizeof(*pThread));
436     if (!pThread) {
437         logerror("not enough memory to create thread: %s (%s)",
438                  name, desc);
439         return NULL;
440     }
441     memset(pThread, 0, sizeof(*pThread));
442
443     strncpy(pThread->szName, name, sizeof(pThread->szName) - 1);
444     strncpy(pThread->szDesc, desc, sizeof(pThread->szDesc) - 1);
445     pThread->pvUserData = ud;
446     pThread->pfnEntry = entry;
447     pThread->bMainThread = FALSE;
448
449     /* Create thread event sem, auto reset. */
450     pThread->hThreadEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
451
452     if (size < loc_MIN_THREAD_STACK)
453         size = loc_MIN_THREAD_STACK;
454
455     pThread->ulThreadID = _beginthread(empth_threadMain, size, pThread);
456     if (pThread->ulThreadID == -1) {
457         logerror("can not create thread: %s (%s): %s",
458                  name, desc, strerror(errno));
459         goto bad;
460     }
461
462     loc_debug("new thread id is %ld", pThread->ulThreadID);
463     empth_yield();
464     return pThread;
465
466   bad:
467     if (pThread) {
468         loc_FreeThreadInfo(pThread);
469     }
470     return NULL;
471 }
472
473
474 /************************
475  * empth_self
476  */
477 empth_t *
478 empth_self(void)
479 {
480     empth_t *pThread = TlsGetValue(dwTLSIndex);
481
482     return pThread;
483 }
484
485 /************************
486  * empth_exit
487  */
488 void
489 empth_exit(void)
490 {
491     empth_t *pThread = TlsGetValue(dwTLSIndex);
492
493     loc_BlockThisThread();
494
495     loc_debug("empth_exit");
496
497     if (pThread->bMainThread) {
498         loc_BlockMainThread();
499         loc_RunThisThread();
500         shutdwn(0);
501     } else {
502         TlsSetValue(dwTLSIndex, NULL);
503         loc_FreeThreadInfo(pThread);
504         _endthread();
505     }
506 }
507
508 /************************
509  * empth_yield
510  *
511  * Yield processing to another thread.
512  */
513 void
514 empth_yield(void)
515 {
516     loc_BlockThisThread();
517     loc_RunThisThread();
518 }
519
520 /************************
521  * empth_terminate
522  *
523  * Kill off the thread.
524  */
525 void
526 empth_terminate(empth_t *pThread)
527 {
528     loc_debug("killing thread %s", pThread->szName);
529     pThread->bKilled = TRUE;
530
531     SetEvent(pThread->hThreadEvent);
532 }
533
534 /************************
535  * empth_select
536  *
537  * Do a select on the given file.
538  * Wait for IO on it.
539  *
540  * This would be one of the main functions used within gen\io.c
541  */
542 void
543 empth_select(int fd, int flags)
544 {
545     WSAEVENT hEventObject[2];
546     empth_t *pThread = TlsGetValue(dwTLSIndex);
547
548     loc_debug("%s select on %d",
549               flags == EMPTH_FD_READ ? "read" : "write", fd);
550     loc_BlockThisThread();
551
552     hEventObject[0] = WSACreateEvent();
553     hEventObject[1] = pThread->hThreadEvent;
554
555     if (flags == EMPTH_FD_READ)
556         WSAEventSelect(fd, hEventObject[0], FD_READ | FD_ACCEPT | FD_CLOSE);
557     else if (flags == EMPTH_FD_WRITE)
558         WSAEventSelect(fd, hEventObject[0], FD_WRITE | FD_CLOSE);
559     else {
560         logerror("bad flag %d passed to empth_select", flags);
561         empth_exit();
562     }
563
564     WSAWaitForMultipleEvents(2, hEventObject, FALSE, WSA_INFINITE, FALSE);
565
566     WSAEventSelect(fd, hEventObject[0], 0);
567
568     WSACloseEvent(hEventObject[0]);
569
570     loc_RunThisThread();
571 }
572
573 /************************
574  * empth_wakeup
575  *
576  * Wake up the specified thread.
577  */
578 void
579 empth_wakeup(empth_t *pThread)
580 {
581     loc_debug("waking up thread %s", pThread->szName);
582
583     /* Let it run if it is blocked... */
584     SetEvent(pThread->hThreadEvent);
585 }
586
587 /************************
588  * empth_sleep
589  *
590  * Put the given thread to sleep...
591  */
592 void
593 empth_sleep(time_t until)
594 {
595     long lSec;
596
597     loc_BlockThisThread();
598
599     while ((lSec = until - time(0)) > 0) {
600         loc_debug("going to sleep %ld sec", lSec);
601         Sleep(lSec * 1000L);
602     }
603
604     loc_debug("sleep done. Waiting to run.");
605
606     loc_RunThisThread();
607 }
608
609
610 /************************
611  * empth_sem_create
612  *
613  * Create a signalling semaphore.
614  */
615 empth_sem_t *
616 empth_sem_create(char *name, int cnt)
617 {
618     empth_sem_t *pSem;
619
620     pSem = malloc(sizeof(*pSem));
621     if (!pSem) {
622         logerror("out of memory at %s:%d", __FILE__, __LINE__);
623         return NULL;
624     }
625
626     memset(pSem, 0, sizeof(pSem));
627     strncpy(pSem->szName, name, sizeof(pSem->szName) - 1);
628
629     pSem->hMutex = CreateMutex(NULL, FALSE, NULL);
630     pSem->hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
631     pSem->count = cnt;
632
633     return pSem;
634 }
635
636 /************************
637  * empth_sem_signal
638  *
639  * Hit/signal the specified semaphore.
640  */
641 void
642 empth_sem_signal(empth_sem_t *pSem)
643 {
644     loc_debug("signal on semaphore %s:%d", pSem->szName, pSem->count);
645
646     /* Wait for the Semaphore */
647     WaitForSingleObject(pSem->hMutex, INFINITE);
648
649     if (pSem->count++ < 0) {
650         SetEvent(pSem->hEvent);
651     }
652
653     ReleaseMutex(pSem->hMutex);
654 }
655
656 /************************
657  * empth_sem_wait
658  *
659  * Wait for the specified signal semaphore to be signaled.
660  */
661 void
662 empth_sem_wait(empth_sem_t *pSem)
663 {
664     empth_t *pThread = TlsGetValue(dwTLSIndex);
665
666     loc_debug("wait on semaphore %s:%d", pSem->szName, pSem->count);
667
668     /* Remove the thread from the running state. */
669     loc_BlockThisThread();
670
671     /* Wait for the Semaphore */
672     WaitForSingleObject(pSem->hMutex, INFINITE);
673     if (--pSem->count < 0) {
674         loc_debug("blocking");
675         ReleaseMutex(pSem->hMutex);
676
677         WaitForSingleObject(pSem->hEvent, INFINITE);
678
679         loc_debug("waking up");
680     } else
681         ReleaseMutex(pSem->hMutex);
682
683     loc_RunThisThread();
684 }