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