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