]> git.pond.sub.org Git - empserver/blob - src/lib/empthread/pthread.c
Line breaks and other formatting issues. No functional changes.
[empserver] / src / lib / empthread / pthread.c
1 /*
2  *  Empire - A multi-player, client/server Internet based war game.
3  *  Copyright (C) 1986-2006, 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 files README, COPYING and CREDITS in the root of the source
23  *  tree for related information and legal notices.  It is expected
24  *  that future 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  *     Markus Armbruster, 2005
34  */
35
36 /* Required for PTHREAD_STACK_MIN on some systems, e.g. Solaris: */
37 #define _XOPEN_SOURCE 500
38
39 #include <config.h>
40
41 #include <stdio.h>
42 #if !defined(_WIN32)
43 #include <sys/time.h>
44 #include <unistd.h>
45 #endif
46 #include <sys/types.h>
47 #include <signal.h>
48 #include <errno.h>
49 #include <string.h>
50 #include <limits.h>
51
52 #include "misc.h"
53 #include "empthread.h"
54 #include "prototypes.h"
55
56 #include <stdarg.h>
57
58 #define EMPTH_KILLED  1
59
60 struct empth_t {
61     char *name;                 /* thread name */
62     char *desc;                 /* description */
63     void *ud;                   /* user data */
64     int state;                  /* my state */
65     void (*ep)(void *);         /* entry point */
66     pthread_t id;               /* thread id */
67 };
68
69 struct empth_sem_t {
70     pthread_mutex_t mtx_update; /* use it to update count */
71     int count;
72     char name[80];
73     pthread_mutex_t mtx_sem;
74     pthread_cond_t cnd_sem;
75 };
76
77 /* Thread-specific data key */
78 static pthread_key_t ctx_key;
79
80 /* Flags that were passed to empth_init() */
81 static int empth_flags;
82
83 /* Pointer to thread context variable */
84 static void **udata;
85
86 /*
87  * Non-preemption mutex.
88  * Empire code outside this file is only executed while holding this
89  * mutex.  This makes sure Empire code is never preempted by Empire
90  * code.
91  */
92 static pthread_mutex_t mtx_ctxsw;
93
94 static void empth_status(char *format, ...)
95     ATTRIBUTE((format (printf, 1, 2)));
96 static void empth_alarm(int sig);
97
98 static void *
99 empth_start(void *arg)
100 {
101     empth_t *ctx = arg;
102     struct sigaction act;
103
104     /* actually it should inherit all this from main but... */
105     act.sa_flags = 0;
106     sigemptyset(&act.sa_mask);
107     act.sa_handler = shutdwn;
108     sigaction(SIGTERM, &act, NULL);
109     sigaction(SIGINT, &act, NULL);
110     act.sa_handler = panic;
111     sigaction(SIGBUS, &act, NULL);
112     sigaction(SIGSEGV, &act, NULL);
113     sigaction(SIGILL, &act, NULL);
114     sigaction(SIGFPE, &act, NULL);
115     act.sa_handler = SIG_IGN;
116     sigaction(SIGPIPE, &act, NULL);
117
118     act.sa_handler = empth_alarm;
119     sigaction(SIGALRM, &act, NULL);
120
121     ctx->id = pthread_self();
122     pthread_setspecific(ctx_key, ctx);
123     pthread_mutex_lock(&mtx_ctxsw);
124     *udata = ctx->ud;
125     ctx->ep(ctx->ud);
126     empth_exit();
127     return NULL;
128 }
129
130 static void
131 empth_status(char *format, ...)
132 {
133     va_list ap;
134     static struct timeval startTime;
135     struct timeval tv;
136     char buf[1024];
137     int sec, msec;
138     empth_t *a;
139
140     va_start(ap, format);
141     if (empth_flags & EMPTH_PRINT) {
142         if (startTime.tv_sec == 0)
143             gettimeofday(&startTime, 0);
144         gettimeofday(&tv, 0);
145         sec = tv.tv_sec - startTime.tv_sec;
146         msec = (tv.tv_usec - startTime.tv_usec) / 1000;
147         if (msec < 0) {
148             sec++;
149             msec += 1000;
150         }
151         vsprintf(buf, format, ap);
152         a = empth_self();
153         printf("%d:%02d.%03d %17s: %s\n", sec / 60, sec % 60, msec / 10,
154                a->name, buf);
155
156     }
157     va_end(ap);
158 }
159
160
161 int
162 empth_init(void **ctx_ptr, int flags)
163 {
164     empth_t *ctx;
165     struct sigaction act;
166
167     pthread_key_create(&ctx_key, NULL);
168     pthread_mutex_init(&mtx_ctxsw, NULL);
169
170     act.sa_flags = 0;
171     sigemptyset(&act.sa_mask);
172     act.sa_handler = empth_alarm;
173     sigaction(SIGALRM, &act, NULL);
174
175     udata = ctx_ptr;
176     ctx = malloc(sizeof(empth_t));
177     if (!ctx) {
178         logerror("pthread init failed: not enough memory");
179         exit(1);
180     }
181     ctx->name = "Main";
182     ctx->desc = "empire main";
183     ctx->ep = 0;
184     ctx->ud = 0;
185     ctx->id = pthread_self();
186     ctx->state = 0;
187     pthread_setspecific(ctx_key, ctx);
188     pthread_mutex_lock(&mtx_ctxsw);
189     empth_flags = flags;
190     logerror("pthreads initialized");
191     return 0;
192 }
193
194
195 /*
196  * prio can be used for setting scheeduling policy but...
197  * it seems to be optional in POSIX threads and Solaris
198  * for example just ignores it.
199  * More then that priority is not needed even in lwp threads.
200  */
201 empth_t *
202 empth_create(int prio, void (*entry)(void *), int size, int flags,
203              char *name, char *desc, void *ud)
204 {
205     pthread_t t;
206     pthread_attr_t attr;
207     empth_t *ctx;
208     int eno;
209
210     empth_status("creating new thread %s:%s", name, desc);
211
212     ctx = malloc(sizeof(empth_t));
213     if (!ctx) {
214         logerror("not enough memory to create thread: %s (%s)",
215                  name, desc);
216         return NULL;
217     }
218     ctx->name = strdup(name);
219     ctx->desc = strdup(desc);
220     ctx->ud = ud;
221     ctx->state = 0;
222     ctx->ep = entry;
223
224     eno = pthread_attr_init(&attr);
225     if (eno) {
226         logerror("can not create thread attribute %s (%s): %s",
227                  name, desc, strerror(eno));
228         goto bad;
229     }
230     if (size < PTHREAD_STACK_MIN)
231         size = PTHREAD_STACK_MIN;
232     pthread_attr_setstacksize(&attr, size);
233     pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
234
235     eno = pthread_create(&t, &attr, empth_start, ctx);
236     if (eno) {
237         logerror("can not create thread: %s (%s): %s",
238                  name, desc, strerror(eno));
239         goto bad;
240     }
241     empth_status("new thread id is %ld", (long)t);
242     empth_yield();
243     return ctx;
244
245   bad:
246     pthread_attr_destroy(&attr);
247     free(ctx);
248     return NULL;
249 }
250
251
252 static void
253 empth_restorectx(void)
254 {
255     empth_t *ctx_ptr;
256
257     ctx_ptr = pthread_getspecific(ctx_key);
258     *udata = ctx_ptr->ud;
259     if (ctx_ptr->state == EMPTH_KILLED) {
260         empth_status("i am dead");
261         empth_exit();
262     }
263     empth_status("context restored");
264 }
265
266 empth_t *
267 empth_self(void)
268 {
269     return pthread_getspecific(ctx_key);
270 }
271
272 void
273 empth_exit(void)
274 {
275     empth_t *ctx_ptr;
276
277     pthread_mutex_unlock(&mtx_ctxsw);
278     empth_status("empth_exit");
279     ctx_ptr = pthread_getspecific(ctx_key);
280     /* We want to leave the main thread around forever, until it's time
281        for it to die for real (in a shutdown) */
282     if (!strcmp(ctx_ptr->name, "Main")) {
283         while (1) {
284             sleep(60);
285         }
286     }
287
288     free(ctx_ptr);
289     pthread_exit(0);
290 }
291
292 void
293 empth_yield(void)
294 {
295     pthread_mutex_unlock(&mtx_ctxsw);
296     pthread_mutex_lock(&mtx_ctxsw);
297     empth_restorectx();
298 }
299
300 void
301 empth_terminate(empth_t *a)
302 {
303     empth_status("killing thread %s", a->name);
304     a->state = EMPTH_KILLED;
305     pthread_kill(a->id, SIGALRM);
306 }
307
308 void
309 empth_select(int fd, int flags)
310 {
311
312     fd_set readmask;
313     fd_set writemask;
314     struct timeval tv;
315     int n;
316
317     pthread_mutex_unlock(&mtx_ctxsw);
318     empth_status("%s select on %d",
319                  flags == EMPTH_FD_READ ? "read" : "write", fd);
320     while (1) {
321         tv.tv_sec = 1000000;
322         tv.tv_usec = 0;
323
324         FD_ZERO(&readmask);
325         FD_ZERO(&writemask);
326
327         switch (flags) {
328         case EMPTH_FD_READ:
329             FD_SET(fd, &readmask);
330             break;
331         case EMPTH_FD_WRITE:
332             FD_SET(fd, &writemask);
333             break;
334         default:
335             logerror("bad flag %d passed to empth_select", flags);
336             empth_exit();
337         }
338
339         n = select(fd + 1, &readmask, &writemask, (fd_set *) 0, &tv);
340
341         if (n < 0) {
342             if (errno == EINTR) {
343                 /* go handle the signal */
344                 empth_status("select broken by signal");
345                 goto done;
346                 return;
347             }
348             /* strange but we dont get EINTR on select broken by signal */
349             empth_status("select failed (%s)", strerror(errno));
350             goto done;
351             return;
352         }
353
354         if (flags == EMPTH_FD_READ && FD_ISSET(fd, &readmask)) {
355             empth_status("input ready");
356             break;
357         }
358         if (flags == EMPTH_FD_WRITE && FD_ISSET(fd, &writemask)) {
359             empth_status("output ready");
360             break;
361         }
362     }
363
364   done:
365     pthread_mutex_lock(&mtx_ctxsw);
366     empth_restorectx();
367
368 }
369
370
371 static void
372 empth_alarm(int sig)
373 {
374     struct sigaction act;
375     empth_status("got alarm signal");
376 #ifdef SA_RESTART
377     act.sa_flags &= ~SA_RESTART;
378 #endif
379     sigemptyset(&act.sa_mask);
380     act.sa_handler = empth_alarm;
381     sigaction(SIGALRM, &act, NULL);
382 }
383
384 void
385 empth_wakeup(empth_t *a)
386 {
387     empth_status("waking up thread %s", a->name);
388     pthread_kill(a->id, SIGALRM);
389     empth_status("waiting for it to run");
390 }
391
392 void
393 empth_sleep(time_t until)
394 {
395     struct timeval tv;
396
397     empth_status("going to sleep %ld sec", until - time(0));
398     pthread_mutex_unlock(&mtx_ctxsw);
399     tv.tv_sec = until - time(NULL);
400     tv.tv_usec = 0;
401     do {
402         select(0, NULL, NULL, NULL, &tv);
403     } while ((tv.tv_sec = until - time(NULL)) > 0);
404     empth_status("sleep done. Waiting for lock");
405     pthread_mutex_lock(&mtx_ctxsw);
406     empth_restorectx();
407 }
408
409
410 empth_sem_t *
411 empth_sem_create(char *name, int cnt)
412 {
413     empth_sem_t *sm;
414
415     sm = malloc(sizeof(empth_sem_t));
416     if (!sm) {
417         logerror("out of memory at %s:%d", __FILE__, __LINE__);
418         return NULL;
419     }
420     strncpy(sm->name, name, sizeof(sm->name) - 1);
421     sm->count = cnt;
422     pthread_mutex_init(&sm->mtx_update, NULL);
423     pthread_mutex_init(&sm->mtx_sem, NULL);
424     pthread_cond_init(&sm->cnd_sem, NULL);
425     return sm;
426 }
427
428 void
429 empth_sem_signal(empth_sem_t *sm)
430 {
431     empth_status("signal on semaphore %s:%d", sm->name, sm->count);
432     pthread_mutex_lock(&sm->mtx_update);
433     if (sm->count++ < 0) {
434         pthread_mutex_unlock(&sm->mtx_update);
435         pthread_mutex_lock(&sm->mtx_sem);
436         pthread_cond_signal(&sm->cnd_sem);
437         pthread_mutex_unlock(&sm->mtx_sem);
438     } else
439         pthread_mutex_unlock(&sm->mtx_update);
440 }
441
442 void
443 empth_sem_wait(empth_sem_t *sm)
444 {
445     empth_status("wait on semaphore %s:%d", sm->name, sm->count);
446     pthread_mutex_lock(&sm->mtx_update);
447     if (--sm->count < 0) {
448         pthread_mutex_unlock(&sm->mtx_update);
449         empth_status("blocking");
450         pthread_mutex_unlock(&mtx_ctxsw);
451         pthread_mutex_lock(&sm->mtx_sem);
452         pthread_cond_wait(&sm->cnd_sem, &sm->mtx_sem);
453         empth_status("waking up");
454         pthread_mutex_unlock(&sm->mtx_sem);
455         pthread_mutex_lock(&mtx_ctxsw);
456         empth_restorectx();
457     } else
458         pthread_mutex_unlock(&sm->mtx_update);
459 }