]> git.pond.sub.org Git - empserver/blob - src/lib/empthread/ntthread.c
Update copyright notice.
[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 <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 loc_Thread_t {
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 empth_sem_t {
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     time_t now;
293
294     loc_Thread_t *pThread = (loc_Thread_t *)pvData;
295
296     /* Out of here... */
297     if (!pvData)
298         return;
299
300     /* Store pThread on this thread. */
301     TlsSetValue(loc_GVAR.dwTLSIndex, pvData);
302
303     /* Get the ID of the thread. */
304     pThread->ulThreadID = GetCurrentThreadId();
305
306     /* Signal that the thread has started. */
307     SetEvent(loc_GVAR.hThreadStartEvent);
308
309     /* seed the rand() function */
310     time(&now);
311     srand(now ^ (unsigned int)pThread);
312
313     /* Switch to this thread context */
314     loc_RunThisThread();
315
316     /* Run the thread. */
317     if (pThread->pfnEntry)
318         pThread->pfnEntry(pThread->pvUserData);
319
320     /* Kill the thread. */
321     empth_exit();
322 }
323
324 /************************
325  * empth_init
326  *
327  * Initialize the thread environment.
328  *
329  * This is called from the program
330  * main line.
331  */
332 int
333 empth_init(char **ctx_ptr, int flags)
334 {
335     loc_Thread_t *pThread = NULL;
336
337     loc_GVAR.ulTickAtStart = GetTickCount();
338     loc_GVAR.ppvUserData = ctx_ptr;
339     loc_GVAR.flags = flags;
340     loc_GVAR.dwTLSIndex = TlsAlloc();
341
342     /* Create the thread mutex sem. */
343     /* Initally unowned. */
344     loc_GVAR.hThreadMutex = CreateMutex(NULL, FALSE, NULL);
345     if (!loc_GVAR.hThreadMutex) {
346         logerror("Failed to create mutex");
347         return 0;
348     }
349
350     /* Create the thread start event sem. */
351     /* Automatic state reset. */
352     loc_GVAR.hThreadStartEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
353     if (!loc_GVAR.hThreadStartEvent) {
354         logerror("Failed to create mutex");
355         return 0;
356     }
357
358     /* Create the global Thread context. */
359     pThread = (loc_Thread_t *)malloc(sizeof(*pThread));
360     if (!pThread) {
361         logerror("not enough memory to create main thread.");
362         return 0;
363     }
364     memset(pThread, 0, sizeof(*pThread));
365
366     strncpy(pThread->szName, "Main", sizeof(pThread->szName) - 1);
367     strncpy(pThread->szDesc, "The main process",
368             sizeof(pThread->szDesc) - 1);
369     pThread->ulThreadID = GetCurrentThreadId();
370     pThread->bMainThread = TRUE;
371
372     TlsSetValue(loc_GVAR.dwTLSIndex, pThread);
373
374     /* Make this the running thread. */
375     loc_RunThisThread();
376
377     logerror("NT pthreads initialized");
378     return 0;
379 }
380
381
382 /************************
383  * empth_create
384  *
385  * Create a new thread.
386  *
387  * prio  - priority, not particularly useful in our context.
388  * entry - entry point function for thread.
389  * size  - stack size.
390  * flags - debug control.
391  *           LWP_STACKCHECK  - not needed
392  * name  - name of the thread, for debug.
393  * desc  - description of thread, for debug.
394  * ud    - "user data".  The "ctx_ptr" gets this value
395  *         when the thread is active.
396  *         It is also passed to the entry function...
397  */
398 empth_t *
399 empth_create(int prio, void (*entry)(void *), int size, int flags,
400              char *name, char *desc, void *ud)
401 {
402     loc_Thread_t *pThread = NULL;
403
404     loc_debug("creating new thread %s:%s", name, desc);
405
406     pThread = (loc_Thread_t *)malloc(sizeof(*pThread));
407     if (!pThread) {
408         logerror("not enough memory to create thread: %s (%s)", name,
409                  desc);
410         return NULL;
411     }
412     memset(pThread, 0, sizeof(*pThread));
413
414     strncpy(pThread->szName, name, sizeof(pThread->szName) - 1);
415     strncpy(pThread->szDesc, desc, sizeof(pThread->szDesc) - 1);
416     pThread->pvUserData = ud;
417     pThread->pfnEntry = entry;
418     pThread->bMainThread = FALSE;
419
420     /* Create thread event sem, auto reset. */
421     pThread->hThreadEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
422
423     if (size < loc_MIN_THREAD_STACK)
424         size = loc_MIN_THREAD_STACK;
425
426     pThread->ulThreadID = _beginthread(empth_threadMain, size,
427                                        (void *)pThread);
428     if (pThread->ulThreadID == -1) {
429         logerror("can not create thread: %s (%s): %s", name, desc,
430                  strerror(errno));
431         goto bad;
432     }
433
434     loc_debug("new thread id is %ld", pThread->ulThreadID);
435     return pThread;
436
437   bad:
438     if (pThread) {
439         loc_FreeThreadInfo(pThread);
440     }
441     return NULL;
442 }
443
444
445 /************************
446  * empth_self
447  */
448 empth_t *
449 empth_self(void)
450 {
451     loc_Thread_t *pThread =
452         (loc_Thread_t *)TlsGetValue(loc_GVAR.dwTLSIndex);
453
454     return pThread;
455 }
456
457 /************************
458  * empth_exit
459  */
460 void
461 empth_exit(void)
462 {
463     loc_Thread_t *pThread =
464         (loc_Thread_t *)TlsGetValue(loc_GVAR.dwTLSIndex);
465
466     loc_BlockThisThread();
467
468     loc_debug("empth_exit");
469
470     if (pThread->bMainThread) {
471         char buf[20];
472         /* The main line.  Wait forever. */
473         while (1) {
474             printf("\nEmpire Server>");
475             fgets(buf, sizeof(buf), stdin);
476             if (!strnicmp(buf, "quit", 4))
477                 shutdwn(0);
478         }
479     } else {
480         TlsSetValue(loc_GVAR.dwTLSIndex, NULL);
481         loc_FreeThreadInfo(pThread);
482         _endthread();
483     }
484 }
485
486 /************************
487  * empth_yield
488  *
489  * Yield processing to another thread.
490  */
491 void
492 empth_yield(void)
493 {
494     loc_BlockThisThread();
495     loc_RunThisThread();
496 }
497
498 /************************
499  * empth_terminate
500  *
501  * Kill off the thread.
502  */
503 void
504 empth_terminate(empth_t *a)
505 {
506     loc_Thread_t *pThread = (loc_Thread_t *)a;
507
508     loc_debug("killing thread %s", pThread->szName);
509     pThread->bKilled = TRUE;
510
511     SetEvent(pThread->hThreadEvent);
512 }
513
514 /************************
515  * empth_select
516  *
517  * Do a select on the given file.
518  * Wait for IO on it.
519  *
520  * This would be one of the main functions used within
521  * gen\io.c
522  */
523 void
524 empth_select(int fd, int flags)
525 {
526     loc_Thread_t *pThread =
527         (loc_Thread_t *)TlsGetValue(loc_GVAR.dwTLSIndex);
528     fd_set readmask;
529     fd_set writemask;
530     struct lwpProc *proc;
531     struct timeval tv;
532     int n;
533
534     loc_debug("%s select on %d",
535               flags == EMPTH_FD_READ ? "read" : "write", fd);
536     loc_BlockThisThread();
537
538     while (1) {
539         tv.tv_sec = 1000000;
540         tv.tv_usec = 0;
541
542         FD_ZERO(&readmask);
543         FD_ZERO(&writemask);
544
545         switch (flags) {
546         case EMPTH_FD_READ:
547             FD_SET(fd, &readmask);
548             break;
549         case EMPTH_FD_WRITE:
550             FD_SET(fd, &writemask);
551             break;
552         default:
553             logerror("bad flag %d passed to empth_select", flags);
554             empth_exit();
555         }
556
557         n = select(fd + 1, &readmask, &writemask, (fd_set *) 0, &tv);
558
559         if (n < 0) {
560             if (errno == EINTR) {
561                 /* go handle the signal */
562                 loc_debug("select broken by signal");
563                 goto done;
564                 return;
565             }
566             /* strange but we dont get EINTR on select broken by signal */
567             loc_debug("select failed (%s)", strerror(errno));
568             goto done;
569             return;
570         }
571
572         if (flags == EMPTH_FD_READ && FD_ISSET(fd, &readmask)) {
573             loc_debug("input ready");
574             break;
575         }
576         if (flags == EMPTH_FD_WRITE && FD_ISSET(fd, &writemask)) {
577             loc_debug("output ready");
578             break;
579         }
580     }
581
582   done:
583     loc_RunThisThread();
584 }
585
586 /************************
587  * empth_alarm
588  */
589 void
590 empth_alarm(int sig)
591 {
592     loc_Thread_t *pThread =
593         (loc_Thread_t *)TlsGetValue(loc_GVAR.dwTLSIndex);
594
595     loc_debug("got alarm signal  %d", sig);
596
597     /* Let it run if it is blocked like... */
598     SetEvent(pThread->hThreadEvent);
599 }
600
601 /************************
602  * empth_wakeup
603  *
604  * Wake up the specified thread.
605  */
606 void
607 empth_wakeup(empth_t *a)
608 {
609     loc_Thread_t *pThread = (loc_Thread_t *)a;
610
611     loc_debug("waking up thread %s", pThread->szName);
612
613     /* Let it run if it is blocked... */
614     SetEvent(pThread->hThreadEvent);
615 }
616
617 /************************
618  * empth_sleep
619  *
620  * Put the given thread to sleep...
621  */
622 void
623 empth_sleep(long until)
624 {
625     loc_Thread_t *pThread =
626         (loc_Thread_t *)TlsGetValue(loc_GVAR.dwTLSIndex);
627     unsigned long ulSec;
628
629     ulSec = until - time(0);
630
631     loc_debug("going to sleep %ld sec", ulSec);
632
633     loc_BlockThisThread();
634
635     WaitForSingleObject(pThread->hThreadEvent, (ulSec * 1000));
636
637     loc_debug("sleep done. Waiting to run.");
638
639     loc_RunThisThread();
640 }
641
642
643 /************************
644  * empth_sem_create
645  *
646  * Create a signalling semaphore.
647  */
648 empth_sem_t *
649 empth_sem_create(char *name, int cnt)
650 {
651     loc_Sem_t *pSem;
652
653     pSem = (loc_Sem_t *)malloc(sizeof(*pSem));
654     if (!pSem) {
655         logerror("out of memory at %s:%d", __FILE__, __LINE__);
656         return NULL;
657     }
658
659     memset(pSem, 0, sizeof(pSem));
660     strncpy(pSem->szName, name, sizeof(pSem->szName) - 1);
661
662     pSem->hMutex = CreateMutex(NULL, FALSE, NULL);
663     pSem->hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
664     pSem->count = cnt;
665
666     return pSem;
667 }
668
669 /************************
670  * empth_sem_signal
671  *
672  * Hit/signal the specified semaphore.
673  */
674 void
675 empth_sem_signal(empth_sem_t *sm)
676 {
677     loc_Sem_t *pSem = (loc_Sem_t *)sm;
678
679     loc_debug("signal on semaphore %s:%d", pSem->szName, pSem->count);
680
681     /* Wait for the Semaphore */
682     WaitForSingleObject(pSem->hMutex, INFINITE);
683
684     if (pSem->count++ < 0) {
685         SetEvent(pSem->hEvent);
686     }
687
688     ReleaseMutex(pSem->hMutex);
689 }
690
691 /************************
692  * empth_sem_wait
693  *
694  * Wait for the specified signal semaphore
695  * to be signaled.
696  */
697 void
698 empth_sem_wait(empth_sem_t *sm)
699 {
700     loc_Thread_t *pThread =
701         (loc_Thread_t *)TlsGetValue(loc_GVAR.dwTLSIndex);
702     loc_Sem_t *pSem = (loc_Sem_t *)sm;
703
704     loc_debug("wait on semaphore %s:%d", pSem->szName, pSem->count);
705
706     /* Remove the thread from the running state. */
707     loc_BlockThisThread();
708
709     /* Wait for the Semaphore */
710     WaitForSingleObject(pSem->hMutex, INFINITE);
711     if (--pSem->count < 0) {
712         loc_debug("blocking");
713         ReleaseMutex(pSem->hMutex);
714
715         WaitForSingleObject(pSem->hEvent, INFINITE);
716
717         loc_debug("waking up");
718     } else
719         ReleaseMutex(pSem->hMutex);
720
721     loc_RunThisThread();
722 }
723
724 #endif /* _WIN32 */