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