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