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