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