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