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