]> git.pond.sub.org Git - empserver/blob - src/lib/empthread/pthread.c
Indented with src/scripts/indent-emp.
[empserver] / src / lib / empthread / pthread.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  *  pthread.c: Interface from Empire threads to POSIX threads
29  * 
30  *  Known contributors to this file:
31  *     Sasha Mikheev
32  *     Steve McClure, 1998
33  */
34
35 #include <stdio.h>
36 #if !defined(_WIN32)
37 #include <sys/time.h>
38 #include <unistd.h>
39 #endif
40 #include <sys/types.h>
41 #include <signal.h>
42 #include <errno.h>
43 #include <string.h>
44
45 #include "misc.h"
46 #include "empthread.h"
47 #include "prototypes.h"
48
49 #include <stdarg.h>
50
51 #ifdef _EMPTH_POSIX
52 static pthread_key_t ctx_key;
53 static int empth_flags;
54 static char **udata;            /* pointer to out global context */
55
56 static pthread_mutex_t mtx_ctxsw;       /* thread in critical section */
57
58
59 #if 0
60 static void empth_setctx _PROTO((void *));
61 #endif
62 static void empth_restorectx _PROTO(());
63
64 void *
65 empth_start(void *ctx)
66 {
67     struct sigaction act;
68     extern emp_sig_t panic();
69     extern emp_sig_t shutdwn();
70
71
72     /* actually it should inherit all this from main but... */
73 #ifdef SA_SIGINFO
74     act.sa_flags = SA_SIGINFO;
75 #endif
76     sigemptyset(&act.sa_mask);
77     act.sa_handler = shutdwn;
78     /* pthreads on Linux use SIGUSR1 (*shrug*) so only catch it if not on
79        a Linux box running POSIX threads -- STM */
80 #if !(defined(__linux__) && defined(_EMPTH_POSIX))
81     sigaction(SIGUSR1, &act, NULL);
82 #endif
83     sigaction(SIGTERM, &act, NULL);
84     sigaction(SIGINT, &act, NULL);
85     act.sa_handler = panic;
86     sigaction(SIGBUS, &act, NULL);
87     sigaction(SIGSEGV, &act, NULL);
88     sigaction(SIGILL, &act, NULL);
89     sigaction(SIGFPE, &act, NULL);
90     act.sa_handler = SIG_IGN;
91     sigaction(SIGPIPE, &act, NULL);
92
93     act.sa_handler = empth_alarm;
94     sigaction(SIGALRM, &act, NULL);
95
96     ((empth_t *)ctx)->id = pthread_self();
97     pthread_setspecific(ctx_key, ctx);
98     pthread_mutex_lock(&mtx_ctxsw);
99     *udata = ((empth_t *)ctx)->ud;
100     ((empth_t *)ctx)->ep(((empth_t *)ctx)->ud);
101     empth_exit();
102     return NULL;
103 }
104
105 static void
106 empth_status(char *format, ...)
107 {
108     va_list ap;
109     static struct timeval startTime;
110     struct timeval tv;
111     char buf[1024];
112     int sec, msec;
113     empth_t *a;
114
115     va_start(ap, format);
116     if (empth_flags & EMPTH_PRINT) {
117         if (startTime.tv_sec == 0)
118             gettimeofday(&startTime, 0);
119         gettimeofday(&tv, 0);
120         sec = tv.tv_sec - startTime.tv_sec;
121         msec = (tv.tv_usec - startTime.tv_usec) / 1000;
122         if (msec < 0) {
123             sec++;
124             msec += 1000;
125         }
126         vsprintf(buf, format, ap);
127         a = empth_self();
128         printf("%d:%02d.%03d %17s: %s\n", sec / 60, sec % 60, msec / 10,
129                a->name, buf);
130
131     }
132     va_end(ap);
133 }
134
135
136 int
137 empth_init(char **ctx_ptr, int flags)
138 {
139     empth_t *ctx;
140     struct sigaction act;
141
142
143     pthread_key_create(&ctx_key, 0);
144 #ifdef _DECTHREADS_
145     pthread_mutex_init(&mtx_ctxsw, pthread_mutexattr_default);
146 #else
147     pthread_mutex_init(&mtx_ctxsw, 0);
148 #endif
149
150     act.sa_flags = 0;
151     sigemptyset(&act.sa_mask);
152     act.sa_handler = empth_alarm;
153     sigaction(SIGALRM, &act, NULL);
154
155     udata = ctx_ptr;
156     ctx = (empth_t *)malloc(sizeof(empth_t));
157     if (!ctx) {
158         logerror("pthread init failed: not enough memory");
159         exit(1);
160     }
161     ctx->name = "Main";
162     ctx->desc = "empire main";
163     ctx->ep = 0;
164     ctx->ud = 0;
165     ctx->id = pthread_self();
166     ctx->state = 0;
167     pthread_setspecific(ctx_key, ctx);
168     pthread_mutex_lock(&mtx_ctxsw);
169     empth_flags = flags;
170     logerror("pthreads initialized");
171     return 0;
172 }
173
174
175 /*
176  * prio can be used for setting scheeduling policy but...
177  * it seems to be optional in POSIX threads and Solaris
178  * for example just ignores it.
179  * More then that priority is not needed even in lwp threads.
180  */
181 empth_t *
182 empth_create(int prio, void (*entry) (), int size, int flags,
183              char *name, char *desc, void *ud)
184 {
185     pthread_t t;
186     pthread_attr_t attr;
187     empth_t *ctx;
188     int eno;
189
190     empth_status("creating new thread %s:%s", name, desc);
191
192     ctx = (empth_t *)malloc(sizeof(empth_t));
193     if (!ctx) {
194         logerror("not enough memoty to create thread: %s (%s)", name,
195                  desc);
196         return NULL;
197     }
198     ctx->name = strdup(name);
199     ctx->desc = strdup(desc);
200     ctx->ud = ud;
201     ctx->state = 0;
202     ctx->ep = entry;
203
204 #ifdef _DECTHREADS_
205     eno = pthread_attr_init(&attr) ? errno : 0;
206 #else
207     eno = pthread_attr_init(&attr);
208 #endif
209     if (eno) {
210         logerror("can not create thread attribute %s (%s): %s", name, desc,
211                  strerror(eno));
212         goto bad;
213     }
214 #if defined(__linux__)
215     /* Linux doesn't let you adjust the stack */
216 #elif defined(_DECTHREADS_)
217     /* DEC does not have PTHREAD_STACK_MIN constant */
218     /* Do not go below default size                 */
219     if (size > pthread_attr_getstacksize(attr))
220         pthread_attr_setstacksize(&attr, size);
221 #else
222     if (size < PTHREAD_STACK_MIN)
223         size = PTHREAD_STACK_MIN + 1;
224
225     pthread_attr_setstacksize(&attr, size);
226 #endif
227
228     pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
229
230 #ifdef _DECTHREADS_
231     eno = pthread_create(&t, attr, empth_start, (void *)ctx) ? errno : 0;
232 #else
233     eno = pthread_create(&t, &attr, empth_start, (void *)ctx);
234 #endif
235     if (eno) {
236         logerror("can not create thread: %s (%s): %s", name, desc,
237                  strerror(eno));
238         goto bad;
239     }
240     empth_status("new thread id is %d", t);
241     return ctx;
242     pthread_attr_destroy(&attr);
243   bad:
244     pthread_attr_destroy(&attr);
245     free(ctx);
246     return NULL;
247 }
248
249
250 #if 0
251 static void
252 empth_setctx(void *ct)
253 {
254     empth_t *ctx_ptr;
255
256 #ifdef _DECTHREADS_
257     pthread_getspecific(ctx_key, (pthread_addr_t *) & ctx_ptr);
258 #else
259     ctx_ptr = (empth_t *)pthread_getspecific(ctx_key);
260 #endif
261     ctx_ptr->ud = ct;
262     *udata = ((empth_t *)ctx_ptr)->ud;
263     pthread_setspecific(ctx_key, (void *)ctx_ptr);
264     empth_status("context saved");
265 }
266 #endif
267
268 static void
269 empth_restorectx(void)
270 {
271     empth_t *ctx_ptr;
272
273 #ifdef _DECTHREADS_
274     pthread_getspecific(ctx_key, (pthread_addr_t *) & ctx_ptr);
275 #else
276     ctx_ptr = (empth_t *)pthread_getspecific(ctx_key);
277 #endif
278     *udata = (char *)ctx_ptr->ud;
279     if (ctx_ptr->state == EMPTH_KILLED) {
280         empth_status("i am dead");
281         empth_exit();
282     }
283     empth_status("context restored");
284 }
285
286 empth_t *
287 empth_self(void)
288 {
289 #ifdef _DECTHREADS_
290     empth_t *ctx_ptr;
291
292     pthread_getspecific(ctx_key, (pthread_addr_t *) & ctx_ptr);
293     return ctx_ptr;
294 #else
295     return (empth_t *)pthread_getspecific(ctx_key);
296 #endif
297 }
298
299 void
300 empth_exit(void)
301 {
302     empth_t *ctx_ptr;
303
304     pthread_mutex_unlock(&mtx_ctxsw);
305     empth_status("empth_exit");
306 #ifdef _DECTHREADS_
307     pthread_getspecific(ctx_key, (pthread_addr_t *) & ctx_ptr);
308 #else
309     ctx_ptr = (empth_t *)pthread_getspecific(ctx_key);
310 #endif
311     /* We want to leave the main thread around forever, until it's time
312        for it to die for real (in a shutdown) */
313     if (!strcmp(ctx_ptr->name, "Main")) {
314         while (1) {
315 #ifdef _DECTHREADS_
316             pthread_yield();
317 #endif
318             sleep(60);
319         }
320     }
321
322     free(ctx_ptr);
323     pthread_exit(0);
324 }
325
326 void
327 empth_yield(void)
328 {
329     pthread_mutex_unlock(&mtx_ctxsw);
330     sleep(10);                  /* take a nap  pthread_yield(); */
331     pthread_mutex_lock(&mtx_ctxsw);
332     empth_restorectx();
333 }
334
335 void
336 empth_terminate(empth_t *a)
337 {
338     /* logerror("calling non supported function empth_terminate: %s:%d",
339        __FILE__, __LINE__); */
340     empth_status("killing thread %s", a->name);
341     a->state = EMPTH_KILLED;
342 #ifndef _DECTHREADS_
343     /* DEC and OSX do not have pthread_kill. Not sure that cancel is correct. */
344 #if (!defined __ppc__)
345     pthread_kill(a->id, SIGALRM);
346 #endif
347 #endif
348     return;
349 }
350
351 void
352 empth_select(int fd, int flags)
353 {
354
355     fd_set readmask;
356     fd_set writemask;
357     struct timeval tv;
358     int n;
359
360     pthread_mutex_unlock(&mtx_ctxsw);
361     empth_status("%s select on %d",
362                  flags == EMPTH_FD_READ ? "read" : "write", fd);
363     while (1) {
364         tv.tv_sec = 1000000;
365         tv.tv_usec = 0;
366
367         FD_ZERO(&readmask);
368         FD_ZERO(&writemask);
369
370         switch (flags) {
371         case EMPTH_FD_READ:
372             FD_SET(fd, &readmask);
373             break;
374         case EMPTH_FD_WRITE:
375             FD_SET(fd, &writemask);
376             break;
377         default:
378             logerror("bad flag %d passed to empth_select", flags);
379             empth_exit();
380         }
381
382         n = select(fd + 1, &readmask, &writemask, (fd_set *) 0, &tv);
383
384         if (n < 0) {
385             if (errno == EINTR) {
386                 /* go handle the signal */
387                 empth_status("select broken by signal");
388                 goto done;
389                 return;
390             }
391             /* strange but we dont get EINTR on select broken by signal */
392             empth_status("select failed (%s)", strerror(errno));
393             goto done;
394             return;
395         }
396
397         if (flags == EMPTH_FD_READ && FD_ISSET(fd, &readmask)) {
398             empth_status("input ready");
399             break;
400         }
401         if (flags == EMPTH_FD_WRITE && FD_ISSET(fd, &writemask)) {
402             empth_status("output ready");
403             break;
404         }
405     }
406
407   done:
408     pthread_mutex_lock(&mtx_ctxsw);
409     empth_restorectx();
410
411 }
412
413
414 emp_sig_t
415 empth_alarm(int sig)
416 {
417     struct sigaction act;
418     empth_status("got alarm signal");
419 #ifdef SA_RESTART
420     act.sa_flags &= ~SA_RESTART;
421 #endif
422     sigemptyset(&act.sa_mask);
423     act.sa_handler = empth_alarm;
424     sigaction(SIGALRM, &act, NULL);
425 }
426
427 void
428 empth_wakeup(empth_t *a)
429 {
430     empth_status("waking up thread %s", a->name);
431 #ifndef _DECTHREADS_
432 #if (!defined __ppc__)
433     pthread_kill(a->id, SIGALRM);
434 #endif
435 #endif
436     empth_status("waiting for it to run");
437     /* empth_yield(); */
438 }
439
440 void
441 empth_sleep(long until)
442 {
443     struct timeval tv;
444
445     empth_status("going to sleep %ld sec", until - time(0));
446     pthread_mutex_unlock(&mtx_ctxsw);
447     tv.tv_sec = until - time(NULL);
448     tv.tv_usec = 0;
449     do {
450         select(0, NULL, NULL, NULL, &tv);
451     } while ((tv.tv_sec = until - time(NULL)) > 0);
452     empth_status("sleep done. Waiting for lock");
453     pthread_mutex_lock(&mtx_ctxsw);
454     empth_restorectx();
455 }
456
457
458 empth_sem_t *
459 empth_sem_create(char *name, int cnt)
460 {
461     empth_sem_t *sm;
462
463     sm = (empth_sem_t *)malloc(sizeof(empth_sem_t));
464     if (!sm) {
465         logerror("out of memory at %s:%d", __FILE__, __LINE__);
466         return NULL;
467     }
468     strncpy(sm->name, name, sizeof(sm->name) - 1);
469     sm->count = cnt;
470 #ifdef _DECTHREADS_
471     pthread_mutex_init(&sm->mtx_update, pthread_mutexattr_default);
472     pthread_mutex_init(&sm->mtx_sem, pthread_mutexattr_default);
473     pthread_cond_init(&sm->cnd_sem, pthread_condattr_default);
474 #else
475     pthread_mutex_init(&sm->mtx_update, 0);
476     pthread_mutex_init(&sm->mtx_sem, 0);
477     pthread_cond_init(&sm->cnd_sem, 0);
478 #endif
479     return sm;
480 }
481
482 void
483 empth_sem_signal(empth_sem_t *sm)
484 {
485     empth_status("signal on semaphore %s:%d", sm->name, sm->count);
486     pthread_mutex_lock(&sm->mtx_update);
487     if (sm->count++ < 0) {
488         pthread_mutex_unlock(&sm->mtx_update);
489         pthread_mutex_lock(&sm->mtx_sem);
490         pthread_cond_signal(&sm->cnd_sem);
491         pthread_mutex_unlock(&sm->mtx_sem);
492     } else
493         pthread_mutex_unlock(&sm->mtx_update);
494 }
495
496 void
497 empth_sem_wait(empth_sem_t *sm)
498 {
499     empth_status("wait on semaphore %s:%d", sm->name, sm->count);
500     pthread_mutex_lock(&sm->mtx_update);
501     if (--sm->count < 0) {
502         pthread_mutex_unlock(&sm->mtx_update);
503         empth_status("blocking");
504         pthread_mutex_unlock(&mtx_ctxsw);
505         pthread_mutex_lock(&sm->mtx_sem);
506         pthread_cond_wait(&sm->cnd_sem, &sm->mtx_sem);
507         empth_status("waking up");
508         pthread_mutex_unlock(&sm->mtx_sem);
509         pthread_mutex_lock(&mtx_ctxsw);
510         empth_restorectx();
511     } else
512         pthread_mutex_unlock(&sm->mtx_update);
513 }
514
515 #endif