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