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