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