]> git.pond.sub.org Git - empserver/blob - src/lib/empthread/ntthread.c
(EMPTH_LWP, EMPTH_POSIX, EMPTH_W32): Define in config.h instead of
[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 <config.h>
48
49 #include <stdio.h>
50 #include <sys/types.h>
51 #include <signal.h>
52 #include <errno.h>
53 #include "misc.h"
54 #include "empthread.h"
55 #include "prototypes.h"
56
57 #define WIN32
58 #include <winsock2.h>
59 #undef NS_ALL
60 #include <windows.h>
61 #include <process.h>
62
63 #define loc_MIN_THREAD_STACK  16384
64
65 /************************
66  * loc_Thread_t
67  */
68 struct loc_Thread_t {
69
70     /* The thread name, passed in at create time. */
71     char szName[17];
72     /* The thread description, passed in at create time. */
73     char szDesc[80];
74
75     /* True if this is the main line, and not a real thread. */
76     BOOL bMainThread;
77
78     /* The user data passed in at create time. */
79     void *pvUserData;
80
81     /* True if this thread has been killed. */
82     BOOL bKilled;
83
84     /* The entry function for the thread. */
85     void (*pfnEntry) (void *);
86
87     /* The system thread ID. */
88     unsigned long ulThreadID;
89
90     /* An Event sem that the thread will wait/sleep on. */
91     HANDLE hThreadEvent;
92 };
93
94
95 /************************
96  * loc_Sem_t
97  */
98 struct loc_Sem_t {
99
100     char szName[17];
101
102     /* An exclusion semaphore for this sem. */
103     HANDLE hMutex;
104     /* An Event sem that the thread(s) will sleep on. */
105
106     HANDLE hEvent;
107     /* The count variable */
108     int count;
109 };
110
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 static HANDLE hThreadMutex;
115
116 /* This is the thread startup event sem. */
117 /* We use this to lockstep when we are starting up threads. */
118 static HANDLE hThreadStartEvent;
119
120 /* This is an event used to wakeup the main thread */
121 /* to start the shutdown sequence. */
122 static HANDLE hShutdownEvent;
123
124 /* The Thread Local Storage index.  We store the pThread pointer */
125 /* for each thread at this index. */
126 static DWORD dwTLSIndex;
127
128 /* The current running thread. */
129 static empth_t *pCurThread;
130
131 /* Ticks at start */
132 static unsigned long ulTickAtStart;
133
134 /* Pointer out to global context.  "player". */
135 /* From empth_init parameter. */
136 static void **ppvUserData;
137
138 /* Global flags.  From empth_init parameter. */
139 static int global_flags;
140
141
142 /************************
143  * loc_debug
144  *
145  * Print out the current thread's status??
146  */
147 static void
148 loc_debug(const char *pszFmt, ...)
149 {
150     va_list vaList;
151     unsigned long ulCurTick;
152     unsigned long ulRunTick;
153     unsigned long ulMs, ulSec, ulMin, ulHr;
154     empth_t *pThread = TlsGetValue(dwTLSIndex);
155     char buf[1024];
156
157     if ((global_flags & EMPTH_PRINT) != 0) {
158
159         /* Ticks are in milliseconds */
160         ulCurTick = GetTickCount();
161
162         ulRunTick = ulCurTick - ulTickAtStart;
163         ulMs = ulRunTick % 1000L;
164         ulSec = (ulRunTick / 1000L) % 60L;
165         ulMin = (ulRunTick / (60L * 1000L)) % 60L;
166         ulHr = (ulRunTick / (60L * 60L * 1000L));
167
168         va_start(vaList, pszFmt);
169         vsprintf(buf, pszFmt, vaList);
170         va_end(vaList);
171
172         if (pThread) {
173             printf("%ld:%02ld:%02ld.%03ld %17s: %s\n",
174                    ulHr, ulMin, ulSec, ulMs, pThread->szName, buf);
175         } else {
176             printf("%ld:%02ld:%02ld.%03ld %17s: %s\n",
177                    ulHr, ulMin, ulSec, ulMs, "UNKNOWN", buf);
178         }
179
180     }
181 }
182
183 /************************
184  * loc_FreeThreadInfo
185  *
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()
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()
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     { 
268         case CTRL_C_EVENT:
269         case CTRL_CLOSE_EVENT:
270         case CTRL_BREAK_EVENT: 
271         case CTRL_LOGOFF_EVENT: 
272         case CTRL_SHUTDOWN_EVENT: 
273             empth_request_shutdown();
274             return TRUE;
275         default:
276             return FALSE;
277     }
278 }
279
280 /************************
281  * empth_request_shutdown
282  *
283  * This wakes up the main thread so shutdown can proceed.
284  * This is done by signalling hShutdownEvent.
285  */
286 void
287 empth_request_shutdown(void)
288 {
289     SetEvent(hShutdownEvent);
290 }
291
292 /************************
293  * loc_BlockMainThread
294  *
295  * This blocks up the main thread.  loc_WakeupMainThread() is used
296  * wakeup the main so shutdown can proceed.
297  */
298 static void
299 loc_BlockMainThread(void)
300 {
301     /* Get the MUTEX semaphore, wait the number of MS */
302     WaitForSingleObject(hShutdownEvent, INFINITE);
303 }
304
305 /************************
306  * empth_threadMain
307  *
308  * This is the main line of each thread.
309  * This is really a static local func....
310  */
311 void
312 empth_threadMain(void *pvData)
313 {
314     time_t now;
315
316     empth_t *pThread = pvData;
317
318     /* Out of here... */
319     if (!pvData)
320         return;
321
322     /* Store pThread on this thread. */
323     TlsSetValue(dwTLSIndex, pvData);
324
325     /* Get the ID of the thread. */
326     pThread->ulThreadID = GetCurrentThreadId();
327
328     /* Signal that the thread has started. */
329     SetEvent(hThreadStartEvent);
330
331     /* seed the rand() function */
332     time(&now);
333     srand(now ^ (unsigned int)pThread);
334
335     /* Switch to this thread context */
336     loc_RunThisThread();
337
338     /* Run the thread. */
339     if (pThread->pfnEntry)
340         pThread->pfnEntry(pThread->pvUserData);
341
342     /* Kill the thread. */
343     empth_exit();
344 }
345
346 /************************
347  * empth_init
348  *
349  * Initialize the thread environment.
350  *
351  * This is called from the program main line.
352  */
353 int
354 empth_init(void **ctx_ptr, int flags)
355 {
356     empth_t *pThread = NULL;
357
358     ulTickAtStart = GetTickCount();
359     ppvUserData = ctx_ptr;
360     global_flags = flags;
361     dwTLSIndex = TlsAlloc();
362
363     /* Create the thread mutex sem. */
364     /* Initally unowned. */
365     hThreadMutex = CreateMutex(NULL, FALSE, NULL);
366     if (!hThreadMutex) {
367         logerror("Failed to create mutex %d", GetLastError());
368         return 0;
369     }
370
371     /* Create the thread start event sem. */
372     /* Automatic state reset. */
373     hThreadStartEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
374     if (!hThreadStartEvent) {
375         logerror("Failed to create start event %d", GetLastError());
376         return 0;
377     }
378
379     /* Create the shutdown event for the main thread. */
380     /* Manual reset */
381     hShutdownEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
382     if (!hShutdownEvent) {
383         logerror("Failed to create shutdown event %d", GetLastError());
384         return 0;
385     }
386     SetConsoleCtrlHandler((PHANDLER_ROUTINE)loc_Exit_Handler, TRUE);
387
388     /* Create the global Thread context. */
389     pThread = malloc(sizeof(*pThread));
390     if (!pThread) {
391         logerror("not enough memory to create main thread.");
392         return 0;
393     }
394     memset(pThread, 0, sizeof(*pThread));
395
396     strncpy(pThread->szName, "Main", sizeof(pThread->szName) - 1);
397     strncpy(pThread->szDesc, "The main process",
398             sizeof(pThread->szDesc) - 1);
399     pThread->ulThreadID = GetCurrentThreadId();
400     pThread->bMainThread = TRUE;
401
402     TlsSetValue(dwTLSIndex, pThread);
403
404     /* Make this the running thread. */
405     loc_RunThisThread();
406
407     logerror("NT pthreads initialized");
408     return 0;
409 }
410
411
412 /************************
413  * empth_create
414  *
415  * Create a new thread.
416  *
417  * prio  - priority, not particularly useful in our context.
418  * entry - entry point function for thread.
419  * size  - stack size.
420  * flags - debug control.
421  *           LWP_STACKCHECK  - not needed
422  * name  - name of the thread, for debug.
423  * desc  - description of thread, for debug.
424  * ud    - "user data".  The "ctx_ptr" gets this value
425  *         when the thread is active.
426  *         It is also passed to the entry function...
427  */
428 empth_t *
429 empth_create(int prio, void (*entry)(void *), int size, int flags,
430              char *name, char *desc, void *ud)
431 {
432     empth_t *pThread = NULL;
433
434     loc_debug("creating new thread %s:%s", name, desc);
435
436     pThread = malloc(sizeof(*pThread));
437     if (!pThread) {
438         logerror("not enough memory to create thread: %s (%s)", name,
439                  desc);
440         return NULL;
441     }
442     memset(pThread, 0, sizeof(*pThread));
443
444     strncpy(pThread->szName, name, sizeof(pThread->szName) - 1);
445     strncpy(pThread->szDesc, desc, sizeof(pThread->szDesc) - 1);
446     pThread->pvUserData = ud;
447     pThread->pfnEntry = entry;
448     pThread->bMainThread = FALSE;
449
450     /* Create thread event sem, auto reset. */
451     pThread->hThreadEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
452
453     if (size < loc_MIN_THREAD_STACK)
454         size = loc_MIN_THREAD_STACK;
455
456     pThread->ulThreadID = _beginthread(empth_threadMain, size, pThread);
457     if (pThread->ulThreadID == -1) {
458         logerror("can not create thread: %s (%s): %s", name, desc,
459                  strerror(errno));
460         goto bad;
461     }
462
463     loc_debug("new thread id is %ld", pThread->ulThreadID);
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 }