]> git.pond.sub.org Git - empserver/blob - src/lib/empthread/ntthread.c
Remove useless memset() before free()
[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_TIMEOUT:
572         res = 0;
573         break;
574     case WSA_WAIT_FAILED:
575         errno = WSAGetLastError();
576         res = -1;
577         break;
578     default:
579         res = 1;
580     }
581
582     WSAEventSelect(handle, hEventObject[0], 0);
583
584     WSACloseEvent(hEventObject[0]);
585
586     loc_RunThisThread(NULL);
587     return res;
588 }
589
590 /************************
591  * empth_wakeup
592  *
593  * Wake up the specified thread.
594  */
595 void
596 empth_wakeup(empth_t *pThread)
597 {
598     loc_debug("waking up thread %s", pThread->szName);
599
600     /* Let it run if it is blocked... */
601     SetEvent(pThread->hThreadEvent);
602 }
603
604 /************************
605  * empth_sleep
606  *
607  * Put the given thread to sleep...
608  */
609 int
610 empth_sleep(time_t until)
611 {
612     long lSec = until - time(0) > 0 ? until - time(0) : 0;
613     empth_t *pThread = TlsGetValue(dwTLSIndex);
614     int iReturn = 0;
615
616     do {
617         loc_BlockThisThread();
618         loc_debug("going to sleep %ld sec", lSec);
619
620         if (WaitForSingleObject(pThread->hThreadEvent, lSec * 1000L) !=
621             WAIT_TIMEOUT)
622             iReturn = -1;
623
624         loc_debug("sleep done. Waiting to run.");
625         loc_RunThisThread(NULL);
626     } while (!iReturn && ((lSec = until - time(0)) > 0));
627
628     return iReturn;
629 }
630
631 /************************
632  * empth_request_shutdown
633  *
634  * This wakes up empth_wait_for_signal() so shutdown can proceed.
635  * This is done by signalling hShutdownEvent.
636  */
637 void
638 empth_request_shutdown(void)
639 {
640     SetEvent(hShutdownEvent);
641 }
642
643 int
644 empth_wait_for_signal(void)
645 {
646     loc_BlockThisThread();
647     loc_RunThisThread(hShutdownEvent);
648     return SIGTERM;
649 }
650
651 empth_rwlock_t *
652 empth_rwlock_create(char *name)
653 {
654     empth_rwlock_t *rwlock;
655
656     rwlock = malloc(sizeof(*rwlock));
657     if (!rwlock)
658         return NULL;
659
660     memset(rwlock, 0, sizeof(*rwlock));
661     rwlock->name = strdup(name);
662
663     if ((rwlock->can_read = CreateEvent(NULL, TRUE, TRUE, NULL)) == NULL) {
664         logerror("rwlock_create: failed to create reader event %s at %s:%d",
665             name, __FILE__, __LINE__);
666         free(rwlock->name);
667         free(rwlock);
668         return NULL;
669     }
670
671     if ((rwlock->can_write = CreateEvent(NULL, FALSE, TRUE, NULL)) == NULL) {
672         logerror("rwlock_create: failed to create writer event %s at %s:%d",
673             name, __FILE__, __LINE__);
674         free(rwlock->name);
675         CloseHandle(rwlock->can_read);
676         free(rwlock);
677         return NULL;
678     }
679     return rwlock;
680 }
681
682 void
683 empth_rwlock_destroy(empth_rwlock_t *rwlock)
684 {
685     if (CANT_HAPPEN(rwlock->nread || rwlock->nwrite))
686         return;
687     if (rwlock->name != NULL)
688         free(rwlock->name);
689     CloseHandle(rwlock->can_read);
690     CloseHandle(rwlock->can_write);
691     free(rwlock);
692 }
693
694 void
695 empth_rwlock_wrlock(empth_rwlock_t *rwlock)
696 {
697     /* block any new readers */
698     ResetEvent(rwlock->can_read);
699     rwlock->nwrite++;
700     loc_BlockThisThread();
701     loc_RunThisThread(rwlock->can_write);
702     CANT_HAPPEN(rwlock->nread != 0);
703 }
704
705 void
706 empth_rwlock_rdlock(empth_rwlock_t *rwlock)
707 {
708     loc_BlockThisThread();
709     loc_RunThisThread(rwlock->can_read);
710     ResetEvent(rwlock->can_write);
711     rwlock->nread++;
712 }
713
714 void
715 empth_rwlock_unlock(empth_rwlock_t *rwlock)
716 {
717     if (CANT_HAPPEN(!rwlock->nread && !rwlock->nwrite))
718         return;
719    if (rwlock->nread) { /* holding read lock */
720         rwlock->nread--;
721         if (rwlock->nread == 0)
722             SetEvent(rwlock->can_write);
723     } else {
724         rwlock->nwrite--;
725         SetEvent(rwlock->can_write);
726     }
727     if (rwlock->nwrite == 0)
728         SetEvent(rwlock->can_read);
729 }