]> git.pond.sub.org Git - empserver/blob - src/lib/empthread/ntthread.c
(empth_init) [_WIN32]: Change flags to global_flags to prevent
[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 /* This is the thread exclusion/non-premption mutex. */
111 /* The running thread has this MUTEX, and all others are */
112 /* either blocked on it, or waiting for some OS response. */
113 static HANDLE hThreadMutex;
114
115 /* This is the thread startup event sem. */
116 /* We use this to lockstep when we are starting up threads. */
117 static HANDLE hThreadStartEvent;
118
119 /* This is an event used to wakeup the main thread */
120 /* to start the shutdown sequence. */
121 static HANDLE hShutdownEvent;
122
123 /* The Thread Local Storage index.  We store the pThread pointer */
124 /* for each thread at this index. */
125 static DWORD dwTLSIndex;
126
127 /* The current running thread. */
128 static empth_t *pCurThread;
129
130 /* Ticks at start */
131 static unsigned long ulTickAtStart;
132
133 /* Pointer out to global context.  "player". */
134 /* From empth_init parameter. */
135 static void **ppvUserData;
136
137 /* Global flags.  From empth_init parameter. */
138 static int global_flags;
139
140
141 /************************
142  * loc_debug
143  *
144  * Print out the current thread's status??
145  */
146 static void
147 loc_debug(const char *pszFmt, ...)
148 {
149     va_list vaList;
150     unsigned long ulCurTick;
151     unsigned long ulRunTick;
152     unsigned long ulMs, ulSec, ulMin, ulHr;
153     empth_t *pThread = TlsGetValue(dwTLSIndex);
154     char buf[1024];
155
156     if ((global_flags & EMPTH_PRINT) != 0) {
157
158         /* Ticks are in milliseconds */
159         ulCurTick = GetTickCount();
160
161         ulRunTick = ulCurTick - ulTickAtStart;
162         ulMs = ulRunTick % 1000L;
163         ulSec = (ulRunTick / 1000L) % 60L;
164         ulMin = (ulRunTick / (60L * 1000L)) % 60L;
165         ulHr = (ulRunTick / (60L * 60L * 1000L));
166
167         va_start(vaList, pszFmt);
168         vsprintf(buf, pszFmt, vaList);
169         va_end(vaList);
170
171         if (pThread) {
172             printf("%ld:%02ld:%02ld.%03ld %17s: %s\n",
173                    ulHr, ulMin, ulSec, ulMs, pThread->szName, buf);
174         } else {
175             printf("%ld:%02ld:%02ld.%03ld %17s: %s\n",
176                    ulHr, ulMin, ulSec, ulMs, "UNKNOWN", buf);
177         }
178
179     }
180 }
181
182 /************************
183  * loc_FreeThreadInfo
184  *
185  */
186 static void
187 loc_FreeThreadInfo(empth_t *pThread)
188 {
189     if (pThread) {
190         if (pThread->hThreadEvent)
191             CloseHandle(pThread->hThreadEvent);
192         memset(pThread, 0, sizeof(*pThread));
193         free(pThread);
194     }
195 }
196
197 /************************
198  * loc_RunThisThread
199  *
200  * This thread wants to run.
201  * When this function returns, the globals are set to this thread
202  * info, and the thread owns the MUTEX sem.
203  */
204 static void
205 loc_RunThisThread()
206 {
207     empth_t *pThread = TlsGetValue(dwTLSIndex);
208
209     if (pThread->bKilled) {
210         if (!pThread->bMainThread) {
211             TlsSetValue(dwTLSIndex, NULL);
212             loc_FreeThreadInfo(pThread);
213             _endthread();
214         }
215     }
216
217     /* Get the MUTEX semaphore, wait forever. */
218     WaitForSingleObject(hThreadMutex, INFINITE);
219
220     if (!pCurThread) {
221         /* Set the globals to this thread. */
222         *ppvUserData = pThread->pvUserData;
223
224         pCurThread = pThread;
225     } else {
226         /* Hmm, a problem, eh? */
227         logerror("RunThisThread, someone already running.");
228     }
229 }
230
231 /************************
232  * loc_BlockThisThread
233  *
234  * This thread was running.  It no longer wants to.
235  */
236 static void
237 loc_BlockThisThread()
238 {
239     empth_t *pThread = TlsGetValue(dwTLSIndex);
240
241     if (pCurThread == pThread) {
242         /* Reset the globals back to original */
243
244         pCurThread = NULL;
245         *ppvUserData = NULL;
246
247         /* Release the MUTEX */
248         ReleaseMutex(hThreadMutex);
249     } else {
250         /* Hmm, this thread was not the running one. */
251         logerror("BlockThisThread, not running.");
252     }
253 }
254
255 /************************
256  * loc_Exit_Handler
257  *
258  * Ctrl-C, Ctrl-Break, Window-Closure, User-Logging-Off or
259  * System-Shutdown will initiate a shutdown.
260  * This is done by calling empth_request_shutdown()
261  */
262 static BOOL
263 loc_Exit_Handler(DWORD fdwCtrlType)
264 {
265     switch (fdwCtrlType) 
266     { 
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 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 int)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 %d", 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 %d", 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 %d", 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)", name,
438                  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", name, desc,
458                  strerror(errno));
459         goto bad;
460     }
461
462     loc_debug("new thread id is %ld", pThread->ulThreadID);
463     return pThread;
464
465   bad:
466     if (pThread) {
467         loc_FreeThreadInfo(pThread);
468     }
469     return NULL;
470 }
471
472
473 /************************
474  * empth_self
475  */
476 empth_t *
477 empth_self(void)
478 {
479     empth_t *pThread = TlsGetValue(dwTLSIndex);
480
481     return pThread;
482 }
483
484 /************************
485  * empth_exit
486  */
487 void
488 empth_exit(void)
489 {
490     empth_t *pThread = TlsGetValue(dwTLSIndex);
491
492     loc_BlockThisThread();
493
494     loc_debug("empth_exit");
495
496     if (pThread->bMainThread) {
497         loc_BlockMainThread();
498         loc_RunThisThread();
499         shutdwn(0);
500     } else {
501         TlsSetValue(dwTLSIndex, NULL);
502         loc_FreeThreadInfo(pThread);
503         _endthread();
504     }
505 }
506
507 /************************
508  * empth_yield
509  *
510  * Yield processing to another thread.
511  */
512 void
513 empth_yield(void)
514 {
515     loc_BlockThisThread();
516     loc_RunThisThread();
517 }
518
519 /************************
520  * empth_terminate
521  *
522  * Kill off the thread.
523  */
524 void
525 empth_terminate(empth_t *pThread)
526 {
527     loc_debug("killing thread %s", pThread->szName);
528     pThread->bKilled = TRUE;
529
530     SetEvent(pThread->hThreadEvent);
531 }
532
533 /************************
534  * empth_select
535  *
536  * Do a select on the given file.
537  * Wait for IO on it.
538  *
539  * This would be one of the main functions used within gen\io.c
540  */
541 void
542 empth_select(int fd, int flags)
543 {
544     WSAEVENT hEventObject[2];
545     empth_t *pThread = TlsGetValue(dwTLSIndex);
546
547     loc_debug("%s select on %d",
548               flags == EMPTH_FD_READ ? "read" : "write", fd);
549     loc_BlockThisThread();
550
551     hEventObject[0] = WSACreateEvent();
552     hEventObject[1] = pThread->hThreadEvent;
553
554     if (flags == EMPTH_FD_READ)
555         WSAEventSelect(fd, hEventObject[0], FD_READ | FD_ACCEPT | FD_CLOSE);
556     else if (flags == EMPTH_FD_WRITE)
557         WSAEventSelect(fd, hEventObject[0], FD_WRITE | FD_CLOSE);
558     else {
559         logerror("bad flag %d passed to empth_select", flags);
560         empth_exit();
561     }
562
563     WSAWaitForMultipleEvents(2, hEventObject, FALSE, WSA_INFINITE, FALSE);
564
565     WSAEventSelect(fd, hEventObject[0], 0);
566
567     WSACloseEvent(hEventObject[0]);
568
569     loc_RunThisThread();
570 }
571
572 /************************
573  * empth_alarm
574  */
575 void
576 empth_alarm(int sig)
577 {
578     empth_t *pThread = TlsGetValue(dwTLSIndex);
579
580     loc_debug("got alarm signal  %d", sig);
581
582     /* Let it run if it is blocked like... */
583     SetEvent(pThread->hThreadEvent);
584 }
585
586 /************************
587  * empth_wakeup
588  *
589  * Wake up the specified thread.
590  */
591 void
592 empth_wakeup(empth_t *pThread)
593 {
594     loc_debug("waking up thread %s", pThread->szName);
595
596     /* Let it run if it is blocked... */
597     SetEvent(pThread->hThreadEvent);
598 }
599
600 /************************
601  * empth_sleep
602  *
603  * Put the given thread to sleep...
604  */
605 void
606 empth_sleep(time_t until)
607 {
608     long lSec;
609
610     loc_BlockThisThread();
611
612     while ((lSec = until - time(0)) > 0) {
613         loc_debug("going to sleep %ld sec", lSec);
614         Sleep(lSec * 1000L);
615     }
616
617     loc_debug("sleep done. Waiting to run.");
618
619     loc_RunThisThread();
620 }
621
622
623 /************************
624  * empth_sem_create
625  *
626  * Create a signalling semaphore.
627  */
628 empth_sem_t *
629 empth_sem_create(char *name, int cnt)
630 {
631     empth_sem_t *pSem;
632
633     pSem = malloc(sizeof(*pSem));
634     if (!pSem) {
635         logerror("out of memory at %s:%d", __FILE__, __LINE__);
636         return NULL;
637     }
638
639     memset(pSem, 0, sizeof(pSem));
640     strncpy(pSem->szName, name, sizeof(pSem->szName) - 1);
641
642     pSem->hMutex = CreateMutex(NULL, FALSE, NULL);
643     pSem->hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
644     pSem->count = cnt;
645
646     return pSem;
647 }
648
649 /************************
650  * empth_sem_signal
651  *
652  * Hit/signal the specified semaphore.
653  */
654 void
655 empth_sem_signal(empth_sem_t *pSem)
656 {
657     loc_debug("signal on semaphore %s:%d", pSem->szName, pSem->count);
658
659     /* Wait for the Semaphore */
660     WaitForSingleObject(pSem->hMutex, INFINITE);
661
662     if (pSem->count++ < 0) {
663         SetEvent(pSem->hEvent);
664     }
665
666     ReleaseMutex(pSem->hMutex);
667 }
668
669 /************************
670  * empth_sem_wait
671  *
672  * Wait for the specified signal semaphore to be signaled.
673  */
674 void
675 empth_sem_wait(empth_sem_t *pSem)
676 {
677     empth_t *pThread = TlsGetValue(dwTLSIndex);
678
679     loc_debug("wait on semaphore %s:%d", pSem->szName, pSem->count);
680
681     /* Remove the thread from the running state. */
682     loc_BlockThisThread();
683
684     /* Wait for the Semaphore */
685     WaitForSingleObject(pSem->hMutex, INFINITE);
686     if (--pSem->count < 0) {
687         loc_debug("blocking");
688         ReleaseMutex(pSem->hMutex);
689
690         WaitForSingleObject(pSem->hEvent, INFINITE);
691
692         loc_debug("waking up");
693     } else
694         ReleaseMutex(pSem->hMutex);
695
696     loc_RunThisThread();
697 }
698
699 #endif /* _WIN32 */