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