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