]> git.pond.sub.org Git - empserver/blob - src/lib/empthread/ntthread.c
Break inclusion cycle: prototypes.h and commands.h included each
[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 #include <config.h>
48
49 #include <errno.h>
50 #include <signal.h>
51 #include <stdio.h>
52 #include <sys/types.h>
53 #include <time.h>
54 #define WIN32
55 #include <winsock2.h>
56 #undef NS_ALL
57 #include <windows.h>
58 #include <process.h>
59 #include "misc.h"
60 #include "empthread.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 static void
186 loc_FreeThreadInfo(empth_t *pThread)
187 {
188     if (pThread) {
189         if (pThread->hThreadEvent)
190             CloseHandle(pThread->hThreadEvent);
191         memset(pThread, 0, sizeof(*pThread));
192         free(pThread);
193     }
194 }
195
196 /************************
197  * loc_RunThisThread
198  *
199  * This thread wants to run.
200  * When this function returns, the globals are set to this thread
201  * info, and the thread owns the MUTEX sem.
202  */
203 static void
204 loc_RunThisThread(void)
205 {
206     empth_t *pThread = TlsGetValue(dwTLSIndex);
207
208     if (pThread->bKilled) {
209         if (!pThread->bMainThread) {
210             TlsSetValue(dwTLSIndex, NULL);
211             loc_FreeThreadInfo(pThread);
212             _endthread();
213         }
214     }
215
216     /* Get the MUTEX semaphore, wait forever. */
217     WaitForSingleObject(hThreadMutex, INFINITE);
218
219     if (!pCurThread) {
220         /* Set the globals to this thread. */
221         *ppvUserData = pThread->pvUserData;
222
223         pCurThread = pThread;
224     } else {
225         /* Hmm, a problem, eh? */
226         logerror("RunThisThread, someone already running.");
227     }
228 }
229
230 /************************
231  * loc_BlockThisThread
232  *
233  * This thread was running.  It no longer wants to.
234  */
235 static void
236 loc_BlockThisThread(void)
237 {
238     empth_t *pThread = TlsGetValue(dwTLSIndex);
239
240     if (pCurThread == pThread) {
241         /* Reset the globals back to original */
242
243         pCurThread = NULL;
244         *ppvUserData = NULL;
245
246         /* Release the MUTEX */
247         ReleaseMutex(hThreadMutex);
248     } else {
249         /* Hmm, this thread was not the running one. */
250         logerror("BlockThisThread, not running.");
251     }
252 }
253
254 /************************
255  * loc_Exit_Handler
256  *
257  * Ctrl-C, Ctrl-Break, Window-Closure, User-Logging-Off or
258  * System-Shutdown will initiate a shutdown.
259  * This is done by calling empth_request_shutdown()
260  */
261 static BOOL
262 loc_Exit_Handler(DWORD fdwCtrlType)
263 {
264     switch (fdwCtrlType) { 
265         case CTRL_C_EVENT:
266         case CTRL_CLOSE_EVENT:
267         case CTRL_BREAK_EVENT: 
268         case CTRL_LOGOFF_EVENT: 
269         case CTRL_SHUTDOWN_EVENT: 
270             empth_request_shutdown();
271             return TRUE;
272         default:
273             return FALSE;
274     }
275 }
276
277 /************************
278  * empth_threadMain
279  *
280  * This is the main line of each thread.
281  * This is really a static local func....
282  */
283 static void
284 empth_threadMain(void *pvData)
285 {
286     time_t now;
287
288     empth_t *pThread = pvData;
289
290     /* Out of here... */
291     if (!pvData)
292         return;
293
294     /* Store pThread on this thread. */
295     TlsSetValue(dwTLSIndex, pvData);
296
297     /* Get the ID of the thread. */
298     pThread->ulThreadID = GetCurrentThreadId();
299
300     /* Signal that the thread has started. */
301     SetEvent(hThreadStartEvent);
302
303     /* seed the rand() function */
304     time(&now);
305     srand(now ^ (unsigned)pThread);
306
307     /* Switch to this thread context */
308     loc_RunThisThread();
309
310     /* Run the thread. */
311     if (pThread->pfnEntry)
312         pThread->pfnEntry(pThread->pvUserData);
313
314     /* Kill the thread. */
315     empth_exit();
316 }
317
318 /************************
319  * empth_init
320  *
321  * Initialize the thread environment.
322  *
323  * This is called from the program main line.
324  */
325 int
326 empth_init(void **ctx_ptr, int flags)
327 {
328     empth_t *pThread = NULL;
329
330     ulTickAtStart = GetTickCount();
331     ppvUserData = ctx_ptr;
332     global_flags = flags;
333     dwTLSIndex = TlsAlloc();
334
335     /* Create the thread mutex sem. */
336     /* Initally unowned. */
337     hThreadMutex = CreateMutex(NULL, FALSE, NULL);
338     if (!hThreadMutex) {
339         logerror("Failed to create mutex %lu", GetLastError());
340         return 0;
341     }
342
343     /* Create the thread start event sem. */
344     /* Automatic state reset. */
345     hThreadStartEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
346     if (!hThreadStartEvent) {
347         logerror("Failed to create start event %lu", GetLastError());
348         return 0;
349     }
350
351     /* Create the shutdown event for the main thread. */
352     /* Manual reset */
353     hShutdownEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
354     if (!hShutdownEvent) {
355         logerror("Failed to create shutdown event %lu", GetLastError());
356         return 0;
357     }
358     SetConsoleCtrlHandler((PHANDLER_ROUTINE)loc_Exit_Handler, TRUE);
359
360     /* Create the global Thread context. */
361     pThread = malloc(sizeof(*pThread));
362     if (!pThread) {
363         logerror("not enough memory to create main thread.");
364         return 0;
365     }
366     memset(pThread, 0, sizeof(*pThread));
367
368     strncpy(pThread->szName, "Main", sizeof(pThread->szName) - 1);
369     strncpy(pThread->szDesc, "The main process",
370             sizeof(pThread->szDesc) - 1);
371     pThread->ulThreadID = GetCurrentThreadId();
372     pThread->bMainThread = TRUE;
373
374     TlsSetValue(dwTLSIndex, pThread);
375
376     /* Make this the running thread. */
377     loc_RunThisThread();
378
379     logerror("NT pthreads initialized");
380     return 0;
381 }
382
383
384 /************************
385  * empth_create
386  *
387  * Create a new thread.
388  *
389  * prio  - priority, not particularly useful in our context.
390  * entry - entry point function for thread.
391  * size  - stack size.
392  * flags - debug control.
393  *           LWP_STACKCHECK  - not needed
394  * name  - name of the thread, for debug.
395  * desc  - description of thread, for debug.
396  * ud    - "user data".  The "ctx_ptr" gets this value
397  *         when the thread is active.
398  *         It is also passed to the entry function...
399  */
400 empth_t *
401 empth_create(int prio, void (*entry)(void *), int size, int flags,
402              char *name, char *desc, void *ud)
403 {
404     empth_t *pThread = NULL;
405
406     loc_debug("creating new thread %s:%s", name, desc);
407
408     pThread = malloc(sizeof(*pThread));
409     if (!pThread) {
410         logerror("not enough memory to create thread: %s (%s)",
411                  name, desc);
412         return NULL;
413     }
414     memset(pThread, 0, sizeof(*pThread));
415
416     strncpy(pThread->szName, name, sizeof(pThread->szName) - 1);
417     strncpy(pThread->szDesc, desc, sizeof(pThread->szDesc) - 1);
418     pThread->pvUserData = ud;
419     pThread->pfnEntry = entry;
420     pThread->bMainThread = FALSE;
421
422     /* Create thread event sem, auto reset. */
423     pThread->hThreadEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
424
425     if (size < loc_MIN_THREAD_STACK)
426         size = loc_MIN_THREAD_STACK;
427
428     pThread->ulThreadID = _beginthread(empth_threadMain, size, pThread);
429     if (pThread->ulThreadID == -1) {
430         logerror("can not create thread: %s (%s): %s",
431                  name, desc, strerror(errno));
432         goto bad;
433     }
434
435     loc_debug("new thread id is %ld", pThread->ulThreadID);
436     empth_yield();
437     return pThread;
438
439   bad:
440     if (pThread) {
441         loc_FreeThreadInfo(pThread);
442     }
443     return NULL;
444 }
445
446
447 /************************
448  * empth_self
449  */
450 empth_t *
451 empth_self(void)
452 {
453     empth_t *pThread = TlsGetValue(dwTLSIndex);
454
455     return pThread;
456 }
457
458 /************************
459  * empth_exit
460  */
461 void
462 empth_exit(void)
463 {
464     empth_t *pThread = TlsGetValue(dwTLSIndex);
465
466     loc_debug("empth_exit");
467     loc_BlockThisThread();
468
469     TlsSetValue(dwTLSIndex, NULL);
470     loc_FreeThreadInfo(pThread);
471     _endthread();
472 }
473
474 /************************
475  * empth_yield
476  *
477  * Yield processing to another thread.
478  */
479 void
480 empth_yield(void)
481 {
482     loc_BlockThisThread();
483     loc_RunThisThread();
484 }
485
486 /************************
487  * empth_terminate
488  *
489  * Kill off the thread.
490  */
491 void
492 empth_terminate(empth_t *pThread)
493 {
494     loc_debug("killing thread %s", pThread->szName);
495     pThread->bKilled = TRUE;
496
497     SetEvent(pThread->hThreadEvent);
498 }
499
500 /************************
501  * empth_select
502  *
503  * Do a select on the given file.
504  * Wait for IO on it.
505  *
506  * This would be one of the main functions used within gen\io.c
507  */
508 void
509 empth_select(int fd, int flags)
510 {
511     WSAEVENT hEventObject[2];
512     empth_t *pThread = TlsGetValue(dwTLSIndex);
513
514     loc_debug("%s select on %d",
515               flags == EMPTH_FD_READ ? "read" : "write", fd);
516     loc_BlockThisThread();
517
518     hEventObject[0] = WSACreateEvent();
519     hEventObject[1] = pThread->hThreadEvent;
520
521     if (flags == EMPTH_FD_READ)
522         WSAEventSelect(fd, hEventObject[0], FD_READ | FD_ACCEPT | FD_CLOSE);
523     else if (flags == EMPTH_FD_WRITE)
524         WSAEventSelect(fd, hEventObject[0], FD_WRITE | FD_CLOSE);
525     else {
526         logerror("bad flag %d passed to empth_select", flags);
527         empth_exit();
528     }
529
530     WSAWaitForMultipleEvents(2, hEventObject, FALSE, WSA_INFINITE, FALSE);
531
532     WSAEventSelect(fd, hEventObject[0], 0);
533
534     WSACloseEvent(hEventObject[0]);
535
536     loc_RunThisThread();
537 }
538
539 /************************
540  * empth_wakeup
541  *
542  * Wake up the specified thread.
543  */
544 void
545 empth_wakeup(empth_t *pThread)
546 {
547     loc_debug("waking up thread %s", pThread->szName);
548
549     /* Let it run if it is blocked... */
550     SetEvent(pThread->hThreadEvent);
551 }
552
553 /************************
554  * empth_sleep
555  *
556  * Put the given thread to sleep...
557  */
558 void
559 empth_sleep(time_t until)
560 {
561     long lSec;
562
563     loc_BlockThisThread();
564
565     while ((lSec = until - time(0)) > 0) {
566         loc_debug("going to sleep %ld sec", lSec);
567         Sleep(lSec * 1000L);
568     }
569
570     loc_debug("sleep done. Waiting to run.");
571
572     loc_RunThisThread();
573 }
574
575 /************************
576  * empth_request_shutdown
577  *
578  * This wakes up empth_wait_for_signal() so shutdown can proceed.
579  * This is done by signalling hShutdownEvent.
580  */
581 void
582 empth_request_shutdown(void)
583 {
584     SetEvent(hShutdownEvent);
585 }
586
587 int
588 empth_wait_for_signal(void)
589 {
590     loc_BlockThisThread();
591
592     /* Get the MUTEX semaphore, wait the number of MS */
593     WaitForSingleObject(hShutdownEvent, INFINITE);
594
595     loc_RunThisThread();
596     return 0;
597 }
598
599 /************************
600  * empth_sem_create
601  *
602  * Create a signalling semaphore.
603  */
604 empth_sem_t *
605 empth_sem_create(char *name, int cnt)
606 {
607     empth_sem_t *pSem;
608
609     pSem = malloc(sizeof(*pSem));
610     if (!pSem) {
611         logerror("out of memory at %s:%d", __FILE__, __LINE__);
612         return NULL;
613     }
614
615     memset(pSem, 0, sizeof(pSem));
616     strncpy(pSem->szName, name, sizeof(pSem->szName) - 1);
617
618     pSem->hMutex = CreateMutex(NULL, FALSE, NULL);
619     pSem->hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
620     pSem->count = cnt;
621
622     return pSem;
623 }
624
625 /************************
626  * empth_sem_signal
627  *
628  * Hit/signal the specified semaphore.
629  */
630 void
631 empth_sem_signal(empth_sem_t *pSem)
632 {
633     loc_debug("signal on semaphore %s:%d", pSem->szName, pSem->count);
634
635     /* Wait for the Semaphore */
636     WaitForSingleObject(pSem->hMutex, INFINITE);
637
638     if (pSem->count++ < 0) {
639         SetEvent(pSem->hEvent);
640     }
641
642     ReleaseMutex(pSem->hMutex);
643 }
644
645 /************************
646  * empth_sem_wait
647  *
648  * Wait for the specified signal semaphore to be signaled.
649  */
650 void
651 empth_sem_wait(empth_sem_t *pSem)
652 {
653     empth_t *pThread = TlsGetValue(dwTLSIndex);
654
655     loc_debug("wait on semaphore %s:%d", pSem->szName, pSem->count);
656
657     /* Remove the thread from the running state. */
658     loc_BlockThisThread();
659
660     /* Wait for the Semaphore */
661     WaitForSingleObject(pSem->hMutex, INFINITE);
662     if (--pSem->count < 0) {
663         loc_debug("blocking");
664         ReleaseMutex(pSem->hMutex);
665
666         WaitForSingleObject(pSem->hEvent, INFINITE);
667
668         loc_debug("waking up");
669     } else
670         ReleaseMutex(pSem->hMutex);
671
672     loc_RunThisThread();
673 }