]> git.pond.sub.org Git - empserver/blob - src/lib/empthread/ntthread.c
(main,close_files,loc_NT_Term,empth_exit):
[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         /* The main line.  Wait forever. */
472         while (1) {
473             if (!debug) {
474                 if (service_stopped())
475                     shutdwn(0);
476                 Sleep(3);
477             } else {
478                 char buf[20];
479                 printf("\nEmpire Server>");
480                 fgets(buf, sizeof(buf), stdin);
481                 if (!strnicmp(buf, "quit", 4))
482                     shutdwn(0);
483             }
484         }
485     } else {
486         TlsSetValue(loc_GVAR.dwTLSIndex, NULL);
487         loc_FreeThreadInfo(pThread);
488         _endthread();
489     }
490 }
491
492 /************************
493  * empth_yield
494  *
495  * Yield processing to another thread.
496  */
497 void
498 empth_yield(void)
499 {
500     loc_BlockThisThread();
501     loc_RunThisThread();
502 }
503
504 /************************
505  * empth_terminate
506  *
507  * Kill off the thread.
508  */
509 void
510 empth_terminate(empth_t *a)
511 {
512     loc_Thread_t *pThread = (loc_Thread_t *)a;
513
514     loc_debug("killing thread %s", pThread->szName);
515     pThread->bKilled = TRUE;
516
517     SetEvent(pThread->hThreadEvent);
518 }
519
520 /************************
521  * empth_select
522  *
523  * Do a select on the given file.
524  * Wait for IO on it.
525  *
526  * This would be one of the main functions used within
527  * gen\io.c
528  */
529 void
530 empth_select(int fd, int flags)
531 {
532     loc_Thread_t *pThread =
533         (loc_Thread_t *)TlsGetValue(loc_GVAR.dwTLSIndex);
534     fd_set readmask;
535     fd_set writemask;
536     struct lwpProc *proc;
537     struct timeval tv;
538     int n;
539
540     loc_debug("%s select on %d",
541               flags == EMPTH_FD_READ ? "read" : "write", fd);
542     loc_BlockThisThread();
543
544     while (1) {
545         tv.tv_sec = 1000000;
546         tv.tv_usec = 0;
547
548         FD_ZERO(&readmask);
549         FD_ZERO(&writemask);
550
551         switch (flags) {
552         case EMPTH_FD_READ:
553             FD_SET(fd, &readmask);
554             break;
555         case EMPTH_FD_WRITE:
556             FD_SET(fd, &writemask);
557             break;
558         default:
559             logerror("bad flag %d passed to empth_select", flags);
560             empth_exit();
561         }
562
563         n = select(fd + 1, &readmask, &writemask, (fd_set *) 0, &tv);
564
565         if (n < 0) {
566             if (errno == EINTR) {
567                 /* go handle the signal */
568                 loc_debug("select broken by signal");
569                 goto done;
570                 return;
571             }
572             /* strange but we dont get EINTR on select broken by signal */
573             loc_debug("select failed (%s)", strerror(errno));
574             goto done;
575             return;
576         }
577
578         if (flags == EMPTH_FD_READ && FD_ISSET(fd, &readmask)) {
579             loc_debug("input ready");
580             break;
581         }
582         if (flags == EMPTH_FD_WRITE && FD_ISSET(fd, &writemask)) {
583             loc_debug("output ready");
584             break;
585         }
586     }
587
588   done:
589     loc_RunThisThread();
590 }
591
592 /************************
593  * empth_alarm
594  */
595 void
596 empth_alarm(int sig)
597 {
598     loc_Thread_t *pThread =
599         (loc_Thread_t *)TlsGetValue(loc_GVAR.dwTLSIndex);
600
601     loc_debug("got alarm signal  %d", sig);
602
603     /* Let it run if it is blocked like... */
604     SetEvent(pThread->hThreadEvent);
605 }
606
607 /************************
608  * empth_wakeup
609  *
610  * Wake up the specified thread.
611  */
612 void
613 empth_wakeup(empth_t *a)
614 {
615     loc_Thread_t *pThread = (loc_Thread_t *)a;
616
617     loc_debug("waking up thread %s", pThread->szName);
618
619     /* Let it run if it is blocked... */
620     SetEvent(pThread->hThreadEvent);
621 }
622
623 /************************
624  * empth_sleep
625  *
626  * Put the given thread to sleep...
627  */
628 void
629 empth_sleep(long until)
630 {
631     loc_Thread_t *pThread =
632         (loc_Thread_t *)TlsGetValue(loc_GVAR.dwTLSIndex);
633     unsigned long ulSec;
634
635     ulSec = until - time(0);
636
637     loc_debug("going to sleep %ld sec", ulSec);
638
639     loc_BlockThisThread();
640
641     WaitForSingleObject(pThread->hThreadEvent, (ulSec * 1000));
642
643     loc_debug("sleep done. Waiting to run.");
644
645     loc_RunThisThread();
646 }
647
648
649 /************************
650  * empth_sem_create
651  *
652  * Create a signalling semaphore.
653  */
654 empth_sem_t *
655 empth_sem_create(char *name, int cnt)
656 {
657     loc_Sem_t *pSem;
658
659     pSem = (loc_Sem_t *)malloc(sizeof(*pSem));
660     if (!pSem) {
661         logerror("out of memory at %s:%d", __FILE__, __LINE__);
662         return NULL;
663     }
664
665     memset(pSem, 0, sizeof(pSem));
666     strncpy(pSem->szName, name, sizeof(pSem->szName) - 1);
667
668     pSem->hMutex = CreateMutex(NULL, FALSE, NULL);
669     pSem->hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
670     pSem->count = cnt;
671
672     return pSem;
673 }
674
675 /************************
676  * empth_sem_signal
677  *
678  * Hit/signal the specified semaphore.
679  */
680 void
681 empth_sem_signal(empth_sem_t *sm)
682 {
683     loc_Sem_t *pSem = (loc_Sem_t *)sm;
684
685     loc_debug("signal on semaphore %s:%d", pSem->szName, pSem->count);
686
687     /* Wait for the Semaphore */
688     WaitForSingleObject(pSem->hMutex, INFINITE);
689
690     if (pSem->count++ < 0) {
691         SetEvent(pSem->hEvent);
692     }
693
694     ReleaseMutex(pSem->hMutex);
695 }
696
697 /************************
698  * empth_sem_wait
699  *
700  * Wait for the specified signal semaphore
701  * to be signaled.
702  */
703 void
704 empth_sem_wait(empth_sem_t *sm)
705 {
706     loc_Thread_t *pThread =
707         (loc_Thread_t *)TlsGetValue(loc_GVAR.dwTLSIndex);
708     loc_Sem_t *pSem = (loc_Sem_t *)sm;
709
710     loc_debug("wait on semaphore %s:%d", pSem->szName, pSem->count);
711
712     /* Remove the thread from the running state. */
713     loc_BlockThisThread();
714
715     /* Wait for the Semaphore */
716     WaitForSingleObject(pSem->hMutex, INFINITE);
717     if (--pSem->count < 0) {
718         loc_debug("blocking");
719         ReleaseMutex(pSem->hMutex);
720
721         WaitForSingleObject(pSem->hEvent, INFINITE);
722
723         loc_debug("waking up");
724     } else
725         ReleaseMutex(pSem->hMutex);
726
727     loc_RunThisThread();
728 }
729
730 #endif /* _WIN32 */