]> git.pond.sub.org Git - empserver/blob - src/lib/empthread/ntthread.c
Merge pre-4-2-20-invasive. Summary of changes:
[empserver] / src / lib / empthread / ntthread.c
1 /*
2  *  Empire - A multi-player, client/server Internet based war game.
3  *  Copyright (C) 1986-2004, 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 the "LEGAL", "LICENSE", "CREDITS" and "README" files for all the
23  *  related information and legal notices. It is expected that any future
24  *  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  */
34
35 /*
36  * EMPTHREADs for Windows NT.
37  *
38  * Actually, threads for any Win32 platform,
39  * like Win95, Win98, WinCE, and whatever other
40  * toy OSs are in our future from Microsoft.
41  *
42  * WIN32 has a full pre-emptive threading environment.
43  * But, Empire can not handle pre-emptive threading.
44  * Thus, we will use the threads, but limit the preemption
45  * using a Mutex semaphore.
46  *
47  */
48
49 #include <stdio.h>
50 #include <sys/types.h>
51 #include <signal.h>
52 #include <errno.h>
53 #include "misc.h"
54 #include "empthread.h"
55 #include "prototypes.h"
56
57 #if defined(_WIN32) && defined(_EMPTH_WIN32)
58 #include <winsock2.h>
59 #undef NS_ALL
60 #include <windows.h>
61 #include <process.h>
62
63
64 #define loc_MIN_THREAD_STACK  16384
65
66 /************************
67  * loc_Thread_t
68  *
69  * The REAL empth_t thread structure.
70  * The external world only gets
71  * a void pointer to this.
72  */
73 typedef struct loc_Thread_t {
74
75     /* The thread name, passed in at create time. */
76     char szName[17];
77     /* The thread description, passed in at create time. */
78     char szDesc[80];
79
80     /* True if this is the main line, and not a real thread. */
81     BOOL bMainThread;
82
83     /* The user data passed in at create time. */
84     void *pvUserData;
85
86     /* True if this thread has been killed. */
87     BOOL bKilled;
88
89     /* The entry function for the thread. */
90     void (*pfnEntry) (void *);
91
92     /* The system thread ID. */
93     unsigned long ulThreadID;
94
95     /* An Event sem that the thread will wait/sleep on. */
96     HANDLE hThreadEvent;
97 } loc_Thread_t;
98
99
100 /************************
101  * loc_Sem_t
102  *
103  * The REAL empth_sem_t structure.
104  * The external world only gets
105  * a void pointer to this.
106  */
107 typedef struct empth_sem_t {
108
109     char szName[17];
110
111     /* An exclusion semaphore for this sem. */
112     HANDLE hMutex;
113     /* An Event sem that the thread(s) will sleep on. */
114
115     HANDLE hEvent;
116     /* The count variable */
117     int count;
118 } loc_Sem_t;
119
120 static struct {
121     /* This is the thread exclusion/non-premption mutex. */
122     /* The running thread has this MUTEX, and all others are */
123     /* either blocked on it, or waiting for some OS response. */
124     HANDLE hThreadMutex;
125
126     /* This is the thread startup event sem. */
127     /* We use this to lockstep when we are starting up threads. */
128     HANDLE hThreadStartEvent;
129
130     /* The Thread Local Storage index.  We store the pThread pointer */
131     /* for each thread at this index. */
132     DWORD dwTLSIndex;
133
134     /* The current running thread. */
135     loc_Thread_t *pCurThread;
136
137     /* Ticks at start */
138     unsigned long ulTickAtStart;
139
140     /* Pointer out to global context.  "player". */
141     /* From empth_init parameter. */
142     char **ppvUserData;
143
144     /* Global flags.  From empth_init parameter. */
145     int flags;
146 } loc_GVAR;
147
148
149 /************************
150  * loc_debug
151  *
152  * Print out the current thread's status??
153  */
154 static void
155 loc_debug(const char *pszFmt, ...)
156 {
157     va_list vaList;
158     unsigned long ulCurTick;
159     unsigned long ulRunTick;
160     unsigned long ulMs, ulSec, ulMin, ulHr;
161     loc_Thread_t *pThread =
162         (loc_Thread_t *)TlsGetValue(loc_GVAR.dwTLSIndex);
163     char buf[1024];
164
165     if ((loc_GVAR.flags & EMPTH_PRINT) != 0) {
166
167         /* Ticks are in milliseconds */
168         ulCurTick = GetTickCount();
169
170         ulRunTick = ulCurTick - loc_GVAR.ulTickAtStart;
171         ulMs = ulRunTick % 1000L;
172         ulSec = (ulRunTick / 1000L) % 60L;
173         ulMin = (ulRunTick / (60L * 1000L)) % 60L;
174         ulHr = (ulRunTick / (60L * 60L * 1000L));
175
176         va_start(vaList, pszFmt);
177         vsprintf(buf, pszFmt, vaList);
178         va_end(vaList);
179
180         if (pThread) {
181             printf("%ld:%02ld:%02ld.%03ld %17s: %s\n",
182                    ulHr, ulMin, ulSec, ulMs, pThread->szName, buf);
183         } else {
184             printf("%ld:%02ld:%02ld.%03ld %17s: %s\n",
185                    ulHr, ulMin, ulSec, ulMs, "UNKNOWN", buf);
186         }
187
188     }
189 }
190
191 /************************
192  * loc_FreeThreadInfo
193  *
194  */
195 static void
196 loc_FreeThreadInfo(loc_Thread_t *pThread)
197 {
198     if (pThread) {
199         if (pThread->hThreadEvent)
200             CloseHandle(pThread->hThreadEvent);
201         memset(pThread, 0, sizeof(*pThread));
202         free(pThread);
203     }
204 }
205
206 /************************
207  * loc_RunThisThread
208  *
209  * This thread wants to run.
210  * When this function returns, the
211  * globals are set to this thread info,
212  * and the thread owns the MUTEX sem.
213  */
214 static void
215 loc_RunThisThread()
216 {
217     loc_Thread_t *pThread =
218         (loc_Thread_t *)TlsGetValue(loc_GVAR.dwTLSIndex);
219
220     if (pThread->bKilled) {
221         if (!pThread->bMainThread) {
222             TlsSetValue(loc_GVAR.dwTLSIndex, NULL);
223             loc_FreeThreadInfo(pThread);
224             _endthread();
225         }
226     }
227
228     /* Get the MUTEX semaphore, wait forever. */
229     WaitForSingleObject(loc_GVAR.hThreadMutex, INFINITE);
230
231     if (!loc_GVAR.pCurThread) {
232         /* Set the globals to this thread. */
233         *loc_GVAR.ppvUserData = pThread->pvUserData;
234
235         loc_GVAR.pCurThread = pThread;
236     } else {
237         /* Hmm, a problem, eh? */
238         logerror("RunThisThread, someone already running.");
239     }
240 }
241
242 /************************
243  * loc_BlockThisThread
244  *
245  * This thread was running.  It no longer wants to.
246  */
247 static void
248 loc_BlockThisThread()
249 {
250     loc_Thread_t *pThread =
251         (loc_Thread_t *)TlsGetValue(loc_GVAR.dwTLSIndex);
252
253     if (loc_GVAR.pCurThread == pThread) {
254         /* Reset the globals back to original */
255
256         loc_GVAR.pCurThread = NULL;
257         *loc_GVAR.ppvUserData = NULL;
258
259         /* Release the MUTEX */
260         ReleaseMutex(loc_GVAR.hThreadMutex);
261     } else {
262         /* Hmm, this thread was not the running one. */
263         logerror("BlockThisThread, not running.");
264     }
265 }
266
267
268 /************************
269  * loc_SleepThisThread
270  */
271 static void
272 loc_SleepThisThread(unsigned long ulMillisecs)
273 {
274     loc_Thread_t *pThread =
275         (loc_Thread_t *)TlsGetValue(loc_GVAR.dwTLSIndex);
276
277     /* Make sure the event thread is clean. */
278     ResetEvent(pThread->hThreadEvent);
279
280     /* Get the MUTEX semaphore, wait the number of MS */
281     WaitForSingleObject(pThread->hThreadEvent, ulMillisecs);
282 }
283
284
285 /************************
286  * empth_threadMain
287  *
288  * This is the main line of each thread.
289  * This is really a static local func....
290  */
291 void
292 empth_threadMain(void *pvData)
293 {
294     time_t now;
295
296     loc_Thread_t *pThread = (loc_Thread_t *)pvData;
297
298     /* Out of here... */
299     if (!pvData)
300         return;
301
302     /* Store pThread on this thread. */
303     TlsSetValue(loc_GVAR.dwTLSIndex, pvData);
304
305     /* Get the ID of the thread. */
306     pThread->ulThreadID = GetCurrentThreadId();
307
308     /* Signal that the thread has started. */
309     SetEvent(loc_GVAR.hThreadStartEvent);
310
311     /* seed the rand() function */
312     time(&now);
313     srand(now ^ (unsigned int)pThread);
314
315     /* Switch to this thread context */
316     loc_RunThisThread();
317
318     /* Run the thread. */
319     if (pThread->pfnEntry)
320         pThread->pfnEntry(pThread->pvUserData);
321
322     /* Kill the thread. */
323     empth_exit();
324 }
325
326 /************************
327  * empth_init
328  *
329  * Initialize the thread environment.
330  *
331  * This is called from the program
332  * main line.
333  */
334 int
335 empth_init(char **ctx_ptr, int flags)
336 {
337     loc_Thread_t *pThread = NULL;
338
339     loc_GVAR.ulTickAtStart = GetTickCount();
340     loc_GVAR.ppvUserData = ctx_ptr;
341     loc_GVAR.flags = flags;
342     loc_GVAR.dwTLSIndex = TlsAlloc();
343
344     /* Create the thread mutex sem. */
345     /* Initally unowned. */
346     loc_GVAR.hThreadMutex = CreateMutex(NULL, FALSE, NULL);
347     if (!loc_GVAR.hThreadMutex) {
348         logerror("Failed to create mutex");
349         return 0;
350     }
351
352     /* Create the thread start event sem. */
353     /* Automatic state reset. */
354     loc_GVAR.hThreadStartEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
355     if (!loc_GVAR.hThreadStartEvent) {
356         logerror("Failed to create mutex");
357         return 0;
358     }
359
360     /* Create the global Thread context. */
361     pThread = (loc_Thread_t *)malloc(sizeof(*pThread));
362     if (!pThread) {
363         logerror("not enough memory to create main thread.");
364         return 0;
365     }
366     memset(pThread, 0, sizeof(*pThread));
367
368     strncpy(pThread->szName, "Main", sizeof(pThread->szName) - 1);
369     strncpy(pThread->szDesc, "The main process",
370             sizeof(pThread->szDesc) - 1);
371     pThread->ulThreadID = GetCurrentThreadId();
372     pThread->bMainThread = TRUE;
373
374     TlsSetValue(loc_GVAR.dwTLSIndex, pThread);
375
376     /* Make this the running thread. */
377     loc_RunThisThread();
378
379     logerror("NT pthreads initialized");
380     return 0;
381 }
382
383
384 /************************
385  * empth_create
386  *
387  * Create a new thread.
388  *
389  * prio  - priority, not particularly useful in our context.
390  * entry - entry point function for thread.
391  * size  - stack size.
392  * flags - debug control.
393  *           LWP_STACKCHECK  - not needed
394  * name  - name of the thread, for debug.
395  * desc  - description of thread, for debug.
396  * ud    - "user data".  The "ctx_ptr" gets this value
397  *         when the thread is active.
398  *         It is also passed to the entry function...
399  */
400 empth_t *
401 empth_create(int prio, void (*entry)(void *), int size, int flags,
402              char *name, char *desc, void *ud)
403 {
404     loc_Thread_t *pThread = NULL;
405
406     loc_debug("creating new thread %s:%s", name, desc);
407
408     pThread = (loc_Thread_t *)malloc(sizeof(*pThread));
409     if (!pThread) {
410         logerror("not enough memory to create thread: %s (%s)", name,
411                  desc);
412         return NULL;
413     }
414     memset(pThread, 0, sizeof(*pThread));
415
416     strncpy(pThread->szName, name, sizeof(pThread->szName) - 1);
417     strncpy(pThread->szDesc, desc, sizeof(pThread->szDesc) - 1);
418     pThread->pvUserData = ud;
419     pThread->pfnEntry = entry;
420     pThread->bMainThread = FALSE;
421
422     /* Create thread event sem, auto reset. */
423     pThread->hThreadEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
424
425     if (size < loc_MIN_THREAD_STACK)
426         size = loc_MIN_THREAD_STACK;
427
428     pThread->ulThreadID = _beginthread(empth_threadMain, size,
429                                        (void *)pThread);
430     if (pThread->ulThreadID == -1) {
431         logerror("can not create thread: %s (%s): %s", name, desc,
432                  strerror(errno));
433         goto bad;
434     }
435
436     loc_debug("new thread id is %ld", pThread->ulThreadID);
437     return pThread;
438
439   bad:
440     if (pThread) {
441         loc_FreeThreadInfo(pThread);
442     }
443     return NULL;
444 }
445
446
447 /************************
448  * empth_self
449  */
450 empth_t *
451 empth_self(void)
452 {
453     loc_Thread_t *pThread =
454         (loc_Thread_t *)TlsGetValue(loc_GVAR.dwTLSIndex);
455
456     return pThread;
457 }
458
459 /************************
460  * empth_exit
461  */
462 void
463 empth_exit(void)
464 {
465     loc_Thread_t *pThread =
466         (loc_Thread_t *)TlsGetValue(loc_GVAR.dwTLSIndex);
467
468     loc_BlockThisThread();
469
470     loc_debug("empth_exit");
471
472     if (pThread->bMainThread) {
473         /* The main line.  Wait forever. */
474         while (1) {
475             if (daemonize) {
476                 if (service_stopped())
477                     shutdwn(0);
478                 Sleep(3);
479             } else {
480                 char buf[20];
481                 printf("\nEmpire Server>");
482                 fgets(buf, sizeof(buf), stdin);
483                 if (!strnicmp(buf, "quit", 4))
484                     shutdwn(0);
485             }
486         }
487     } else {
488         TlsSetValue(loc_GVAR.dwTLSIndex, NULL);
489         loc_FreeThreadInfo(pThread);
490         _endthread();
491     }
492 }
493
494 /************************
495  * empth_yield
496  *
497  * Yield processing to another thread.
498  */
499 void
500 empth_yield(void)
501 {
502     loc_BlockThisThread();
503     loc_RunThisThread();
504 }
505
506 /************************
507  * empth_terminate
508  *
509  * Kill off the thread.
510  */
511 void
512 empth_terminate(empth_t *a)
513 {
514     loc_Thread_t *pThread = (loc_Thread_t *)a;
515
516     loc_debug("killing thread %s", pThread->szName);
517     pThread->bKilled = TRUE;
518
519     SetEvent(pThread->hThreadEvent);
520 }
521
522 /************************
523  * empth_select
524  *
525  * Do a select on the given file.
526  * Wait for IO on it.
527  *
528  * This would be one of the main functions used within
529  * gen\io.c
530  */
531 void
532 empth_select(int fd, int flags)
533 {
534     loc_Thread_t *pThread =
535         (loc_Thread_t *)TlsGetValue(loc_GVAR.dwTLSIndex);
536     fd_set readmask;
537     fd_set writemask;
538     struct lwpProc *proc;
539     struct timeval tv;
540     int n;
541
542     loc_debug("%s select on %d",
543               flags == EMPTH_FD_READ ? "read" : "write", fd);
544     loc_BlockThisThread();
545
546     while (1) {
547         tv.tv_sec = 1000000;
548         tv.tv_usec = 0;
549
550         FD_ZERO(&readmask);
551         FD_ZERO(&writemask);
552
553         switch (flags) {
554         case EMPTH_FD_READ:
555             FD_SET(fd, &readmask);
556             break;
557         case EMPTH_FD_WRITE:
558             FD_SET(fd, &writemask);
559             break;
560         default:
561             logerror("bad flag %d passed to empth_select", flags);
562             empth_exit();
563         }
564
565         n = select(fd + 1, &readmask, &writemask, (fd_set *) 0, &tv);
566
567         if (n < 0) {
568             if (errno == EINTR) {
569                 /* go handle the signal */
570                 loc_debug("select broken by signal");
571                 goto done;
572                 return;
573             }
574             /* strange but we dont get EINTR on select broken by signal */
575             loc_debug("select failed (%s)", strerror(errno));
576             goto done;
577             return;
578         }
579
580         if (flags == EMPTH_FD_READ && FD_ISSET(fd, &readmask)) {
581             loc_debug("input ready");
582             break;
583         }
584         if (flags == EMPTH_FD_WRITE && FD_ISSET(fd, &writemask)) {
585             loc_debug("output ready");
586             break;
587         }
588     }
589
590   done:
591     loc_RunThisThread();
592 }
593
594 /************************
595  * empth_alarm
596  */
597 void
598 empth_alarm(int sig)
599 {
600     loc_Thread_t *pThread =
601         (loc_Thread_t *)TlsGetValue(loc_GVAR.dwTLSIndex);
602
603     loc_debug("got alarm signal  %d", sig);
604
605     /* Let it run if it is blocked like... */
606     SetEvent(pThread->hThreadEvent);
607 }
608
609 /************************
610  * empth_wakeup
611  *
612  * Wake up the specified thread.
613  */
614 void
615 empth_wakeup(empth_t *a)
616 {
617     loc_Thread_t *pThread = (loc_Thread_t *)a;
618
619     loc_debug("waking up thread %s", pThread->szName);
620
621     /* Let it run if it is blocked... */
622     SetEvent(pThread->hThreadEvent);
623 }
624
625 /************************
626  * empth_sleep
627  *
628  * Put the given thread to sleep...
629  */
630 void
631 empth_sleep(long until)
632 {
633     loc_Thread_t *pThread =
634         (loc_Thread_t *)TlsGetValue(loc_GVAR.dwTLSIndex);
635     unsigned long ulSec;
636
637     ulSec = until - time(0);
638
639     loc_debug("going to sleep %ld sec", ulSec);
640
641     loc_BlockThisThread();
642
643     WaitForSingleObject(pThread->hThreadEvent, (ulSec * 1000));
644
645     loc_debug("sleep done. Waiting to run.");
646
647     loc_RunThisThread();
648 }
649
650
651 /************************
652  * empth_sem_create
653  *
654  * Create a signalling semaphore.
655  */
656 empth_sem_t *
657 empth_sem_create(char *name, int cnt)
658 {
659     loc_Sem_t *pSem;
660
661     pSem = (loc_Sem_t *)malloc(sizeof(*pSem));
662     if (!pSem) {
663         logerror("out of memory at %s:%d", __FILE__, __LINE__);
664         return NULL;
665     }
666
667     memset(pSem, 0, sizeof(pSem));
668     strncpy(pSem->szName, name, sizeof(pSem->szName) - 1);
669
670     pSem->hMutex = CreateMutex(NULL, FALSE, NULL);
671     pSem->hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
672     pSem->count = cnt;
673
674     return pSem;
675 }
676
677 /************************
678  * empth_sem_signal
679  *
680  * Hit/signal the specified semaphore.
681  */
682 void
683 empth_sem_signal(empth_sem_t *sm)
684 {
685     loc_Sem_t *pSem = (loc_Sem_t *)sm;
686
687     loc_debug("signal on semaphore %s:%d", pSem->szName, pSem->count);
688
689     /* Wait for the Semaphore */
690     WaitForSingleObject(pSem->hMutex, INFINITE);
691
692     if (pSem->count++ < 0) {
693         SetEvent(pSem->hEvent);
694     }
695
696     ReleaseMutex(pSem->hMutex);
697 }
698
699 /************************
700  * empth_sem_wait
701  *
702  * Wait for the specified signal semaphore
703  * to be signaled.
704  */
705 void
706 empth_sem_wait(empth_sem_t *sm)
707 {
708     loc_Thread_t *pThread =
709         (loc_Thread_t *)TlsGetValue(loc_GVAR.dwTLSIndex);
710     loc_Sem_t *pSem = (loc_Sem_t *)sm;
711
712     loc_debug("wait on semaphore %s:%d", pSem->szName, pSem->count);
713
714     /* Remove the thread from the running state. */
715     loc_BlockThisThread();
716
717     /* Wait for the Semaphore */
718     WaitForSingleObject(pSem->hMutex, INFINITE);
719     if (--pSem->count < 0) {
720         loc_debug("blocking");
721         ReleaseMutex(pSem->hMutex);
722
723         WaitForSingleObject(pSem->hEvent, INFINITE);
724
725         loc_debug("waking up");
726     } else
727         ReleaseMutex(pSem->hMutex);
728
729     loc_RunThisThread();
730 }
731
732 #endif /* _WIN32 */