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