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