]> git.pond.sub.org Git - empserver/blob - src/lib/empthread/ntthread.c
Update copyright notice.
[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 #include <winsock2.h>
57 #undef NS_ALL
58 #include <windows.h>
59 #include <process.h>
60
61 #define loc_MIN_THREAD_STACK  16384
62
63 /************************
64  * loc_Thread_t
65  *
66  * The REAL empth_t thread structure.
67  * The external world only gets a void pointer to this.
68  */
69 typedef struct loc_Thread_t {
70
71     /* The thread name, passed in at create time. */
72     char szName[17];
73     /* The thread description, passed in at create time. */
74     char szDesc[80];
75
76     /* True if this is the main line, and not a real thread. */
77     BOOL bMainThread;
78
79     /* The user data passed in at create time. */
80     void *pvUserData;
81
82     /* True if this thread has been killed. */
83     BOOL bKilled;
84
85     /* The entry function for the thread. */
86     void (*pfnEntry) (void *);
87
88     /* The system thread ID. */
89     unsigned long ulThreadID;
90
91     /* An Event sem that the thread will wait/sleep on. */
92     HANDLE hThreadEvent;
93 } loc_Thread_t;
94
95
96 /************************
97  * loc_Sem_t
98  *
99  * The REAL empth_sem_t structure.
100  * The external world only gets a void pointer to this.
101  */
102 typedef struct empth_sem_t {
103
104     char szName[17];
105
106     /* An exclusion semaphore for this sem. */
107     HANDLE hMutex;
108     /* An Event sem that the thread(s) will sleep on. */
109
110     HANDLE hEvent;
111     /* The count variable */
112     int count;
113 } loc_Sem_t;
114
115 static struct {
116     /* This is the thread exclusion/non-premption mutex. */
117     /* The running thread has this MUTEX, and all others are */
118     /* either blocked on it, or waiting for some OS response. */
119     HANDLE hThreadMutex;
120
121     /* This is the thread startup event sem. */
122     /* We use this to lockstep when we are starting up threads. */
123     HANDLE hThreadStartEvent;
124
125     /* This is an event used to wakeup the main thread */
126     /* to start the shutdown sequence. */
127     HANDLE hShutdownEvent;
128
129     /* The Thread Local Storage index.  We store the pThread pointer */
130     /* for each thread at this index. */
131     DWORD dwTLSIndex;
132
133     /* The current running thread. */
134     loc_Thread_t *pCurThread;
135
136     /* Ticks at start */
137     unsigned long ulTickAtStart;
138
139     /* Pointer out to global context.  "player". */
140     /* From empth_init parameter. */
141     char **ppvUserData;
142
143     /* Global flags.  From empth_init parameter. */
144     int flags;
145 } loc_GVAR;
146
147
148 /************************
149  * loc_debug
150  *
151  * Print out the current thread's status??
152  */
153 static void
154 loc_debug(const char *pszFmt, ...)
155 {
156     va_list vaList;
157     unsigned long ulCurTick;
158     unsigned long ulRunTick;
159     unsigned long ulMs, ulSec, ulMin, ulHr;
160     loc_Thread_t *pThread =
161         (loc_Thread_t *)TlsGetValue(loc_GVAR.dwTLSIndex);
162     char buf[1024];
163
164     if ((loc_GVAR.flags & EMPTH_PRINT) != 0) {
165
166         /* Ticks are in milliseconds */
167         ulCurTick = GetTickCount();
168
169         ulRunTick = ulCurTick - loc_GVAR.ulTickAtStart;
170         ulMs = ulRunTick % 1000L;
171         ulSec = (ulRunTick / 1000L) % 60L;
172         ulMin = (ulRunTick / (60L * 1000L)) % 60L;
173         ulHr = (ulRunTick / (60L * 60L * 1000L));
174
175         va_start(vaList, pszFmt);
176         vsprintf(buf, pszFmt, vaList);
177         va_end(vaList);
178
179         if (pThread) {
180             printf("%ld:%02ld:%02ld.%03ld %17s: %s\n",
181                    ulHr, ulMin, ulSec, ulMs, pThread->szName, buf);
182         } else {
183             printf("%ld:%02ld:%02ld.%03ld %17s: %s\n",
184                    ulHr, ulMin, ulSec, ulMs, "UNKNOWN", buf);
185         }
186
187     }
188 }
189
190 /************************
191  * loc_FreeThreadInfo
192  *
193  */
194 static void
195 loc_FreeThreadInfo(loc_Thread_t *pThread)
196 {
197     if (pThread) {
198         if (pThread->hThreadEvent)
199             CloseHandle(pThread->hThreadEvent);
200         memset(pThread, 0, sizeof(*pThread));
201         free(pThread);
202     }
203 }
204
205 /************************
206  * loc_RunThisThread
207  *
208  * This thread wants to run.
209  * When this function returns, the globals are set to this thread
210  * info, and the thread owns the MUTEX sem.
211  */
212 static void
213 loc_RunThisThread()
214 {
215     loc_Thread_t *pThread =
216         (loc_Thread_t *)TlsGetValue(loc_GVAR.dwTLSIndex);
217
218     if (pThread->bKilled) {
219         if (!pThread->bMainThread) {
220             TlsSetValue(loc_GVAR.dwTLSIndex, NULL);
221             loc_FreeThreadInfo(pThread);
222             _endthread();
223         }
224     }
225
226     /* Get the MUTEX semaphore, wait forever. */
227     WaitForSingleObject(loc_GVAR.hThreadMutex, INFINITE);
228
229     if (!loc_GVAR.pCurThread) {
230         /* Set the globals to this thread. */
231         *loc_GVAR.ppvUserData = pThread->pvUserData;
232
233         loc_GVAR.pCurThread = pThread;
234     } else {
235         /* Hmm, a problem, eh? */
236         logerror("RunThisThread, someone already running.");
237     }
238 }
239
240 /************************
241  * loc_BlockThisThread
242  *
243  * This thread was running.  It no longer wants to.
244  */
245 static void
246 loc_BlockThisThread()
247 {
248     loc_Thread_t *pThread =
249         (loc_Thread_t *)TlsGetValue(loc_GVAR.dwTLSIndex);
250
251     if (loc_GVAR.pCurThread == pThread) {
252         /* Reset the globals back to original */
253
254         loc_GVAR.pCurThread = NULL;
255         *loc_GVAR.ppvUserData = NULL;
256
257         /* Release the MUTEX */
258         ReleaseMutex(loc_GVAR.hThreadMutex);
259     } else {
260         /* Hmm, this thread was not the running one. */
261         logerror("BlockThisThread, not running.");
262     }
263 }
264
265
266 /************************
267  * loc_SleepThisThread
268  */
269 static void
270 loc_SleepThisThread(unsigned long ulMillisecs)
271 {
272     loc_Thread_t *pThread =
273         (loc_Thread_t *)TlsGetValue(loc_GVAR.dwTLSIndex);
274
275     /* Make sure the event thread is clean. */
276     ResetEvent(pThread->hThreadEvent);
277
278     /* Get the MUTEX semaphore, wait the number of MS */
279     WaitForSingleObject(pThread->hThreadEvent, ulMillisecs);
280 }
281
282
283 /************************
284  * loc_Exit_Handler
285  *
286  * Ctrl-C, Ctrl-Break, Window-Closure, User-Logging-Off or
287  * System-Shutdown will initiate a shutdown.
288  * This is done by calling empth_request_shutdown()
289  */
290 static BOOL
291 loc_Exit_Handler(DWORD fdwCtrlType)
292 {
293     switch (fdwCtrlType) 
294     { 
295         case CTRL_C_EVENT:
296         case CTRL_CLOSE_EVENT:
297         case CTRL_BREAK_EVENT: 
298         case CTRL_LOGOFF_EVENT: 
299         case CTRL_SHUTDOWN_EVENT: 
300             empth_request_shutdown();
301             return TRUE;
302         default:
303             return FALSE;
304     }
305 }
306
307 /************************
308  * empth_request_shutdown
309  *
310  * This wakes up the main thread so shutdown can proceed.
311  * This is done by signalling hShutdownEvent.
312  */
313 void
314 empth_request_shutdown(void)
315 {
316     SetEvent(loc_GVAR.hShutdownEvent);
317 }
318
319 /************************
320  * loc_BlockMainThread
321  *
322  * This blocks up the main thread.  loc_WakeupMainThread() is used
323  * wakeup the main so shutdown can proceed.
324  */
325 static void
326 loc_BlockMainThread(void)
327 {
328     /* Get the MUTEX semaphore, wait the number of MS */
329     WaitForSingleObject(loc_GVAR.hShutdownEvent, INFINITE);
330 }
331
332 /************************
333  * empth_threadMain
334  *
335  * This is the main line of each thread.
336  * This is really a static local func....
337  */
338 void
339 empth_threadMain(void *pvData)
340 {
341     time_t now;
342
343     loc_Thread_t *pThread = (loc_Thread_t *)pvData;
344
345     /* Out of here... */
346     if (!pvData)
347         return;
348
349     /* Store pThread on this thread. */
350     TlsSetValue(loc_GVAR.dwTLSIndex, pvData);
351
352     /* Get the ID of the thread. */
353     pThread->ulThreadID = GetCurrentThreadId();
354
355     /* Signal that the thread has started. */
356     SetEvent(loc_GVAR.hThreadStartEvent);
357
358     /* seed the rand() function */
359     time(&now);
360     srand(now ^ (unsigned int)pThread);
361
362     /* Switch to this thread context */
363     loc_RunThisThread();
364
365     /* Run the thread. */
366     if (pThread->pfnEntry)
367         pThread->pfnEntry(pThread->pvUserData);
368
369     /* Kill the thread. */
370     empth_exit();
371 }
372
373 /************************
374  * empth_init
375  *
376  * Initialize the thread environment.
377  *
378  * This is called from the program main line.
379  */
380 int
381 empth_init(char **ctx_ptr, int flags)
382 {
383     loc_Thread_t *pThread = NULL;
384
385     loc_GVAR.ulTickAtStart = GetTickCount();
386     loc_GVAR.ppvUserData = ctx_ptr;
387     loc_GVAR.flags = flags;
388     loc_GVAR.dwTLSIndex = TlsAlloc();
389
390     /* Create the thread mutex sem. */
391     /* Initally unowned. */
392     loc_GVAR.hThreadMutex = CreateMutex(NULL, FALSE, NULL);
393     if (!loc_GVAR.hThreadMutex) {
394         logerror("Failed to create mutex %d", GetLastError());
395         return 0;
396     }
397
398     /* Create the thread start event sem. */
399     /* Automatic state reset. */
400     loc_GVAR.hThreadStartEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
401     if (!loc_GVAR.hThreadStartEvent) {
402         logerror("Failed to create start event %d", GetLastError());
403         return 0;
404     }
405
406     /* Create the shutdown event for the main thread. */
407     /* Manual reset */
408     loc_GVAR.hShutdownEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
409     if (!loc_GVAR.hShutdownEvent) {
410         logerror("Failed to create shutdown event %d", GetLastError());
411         return 0;
412     }
413     SetConsoleCtrlHandler((PHANDLER_ROUTINE)loc_Exit_Handler, TRUE);
414
415     /* Create the global Thread context. */
416     pThread = (loc_Thread_t *)malloc(sizeof(*pThread));
417     if (!pThread) {
418         logerror("not enough memory to create main thread.");
419         return 0;
420     }
421     memset(pThread, 0, sizeof(*pThread));
422
423     strncpy(pThread->szName, "Main", sizeof(pThread->szName) - 1);
424     strncpy(pThread->szDesc, "The main process",
425             sizeof(pThread->szDesc) - 1);
426     pThread->ulThreadID = GetCurrentThreadId();
427     pThread->bMainThread = TRUE;
428
429     TlsSetValue(loc_GVAR.dwTLSIndex, pThread);
430
431     /* Make this the running thread. */
432     loc_RunThisThread();
433
434     logerror("NT pthreads initialized");
435     return 0;
436 }
437
438
439 /************************
440  * empth_create
441  *
442  * Create a new thread.
443  *
444  * prio  - priority, not particularly useful in our context.
445  * entry - entry point function for thread.
446  * size  - stack size.
447  * flags - debug control.
448  *           LWP_STACKCHECK  - not needed
449  * name  - name of the thread, for debug.
450  * desc  - description of thread, for debug.
451  * ud    - "user data".  The "ctx_ptr" gets this value
452  *         when the thread is active.
453  *         It is also passed to the entry function...
454  */
455 empth_t *
456 empth_create(int prio, void (*entry)(void *), int size, int flags,
457              char *name, char *desc, void *ud)
458 {
459     loc_Thread_t *pThread = NULL;
460
461     loc_debug("creating new thread %s:%s", name, desc);
462
463     pThread = (loc_Thread_t *)malloc(sizeof(*pThread));
464     if (!pThread) {
465         logerror("not enough memory to create thread: %s (%s)", name,
466                  desc);
467         return NULL;
468     }
469     memset(pThread, 0, sizeof(*pThread));
470
471     strncpy(pThread->szName, name, sizeof(pThread->szName) - 1);
472     strncpy(pThread->szDesc, desc, sizeof(pThread->szDesc) - 1);
473     pThread->pvUserData = ud;
474     pThread->pfnEntry = entry;
475     pThread->bMainThread = FALSE;
476
477     /* Create thread event sem, auto reset. */
478     pThread->hThreadEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
479
480     if (size < loc_MIN_THREAD_STACK)
481         size = loc_MIN_THREAD_STACK;
482
483     pThread->ulThreadID = _beginthread(empth_threadMain, size,
484                                        (void *)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(long 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 = (loc_Sem_t *)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 */