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