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