]> git.pond.sub.org Git - empserver/blob - src/lib/lwp/lwp.c
Import of Empire 4.2.12
[empserver] / src / lib / lwp / lwp.c
1 /*
2  * lwp.c -- lightweight process creation, destruction and manipulation.
3  * Copyright (C) 1991-3 Stephen Crane.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  * 
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  * 
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the Free
17  * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18  *
19  * author: Stephen Crane, (jsc@doc.ic.ac.uk), Department of Computing,
20  * Imperial College of Science, Technology and Medicine, 180 Queen's
21  * Gate, London SW7 2BZ, England.
22  */
23
24 #include <stdio.h>
25 #include <signal.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include "lwp.h"
29 #include "lwpint.h"
30 #include "prototypes.h"
31
32 #if defined(_EMPTH_LWP)
33
34 #ifdef BOUNDS_CHECK
35 #include <bounds/fix-args.h>
36 #include <bounds/unchecked.h>
37 #endif
38
39 #ifdef hpc
40 extern struct lwpProc *initcontext;
41 extern int startpoint;
42 #endif
43
44 struct lwpQueue LwpSchedQ[LWP_MAX_PRIO], LwpDeadQ;
45
46 struct lwpProc  *LwpCurrent = NULL;
47 char            **LwpContextPtr;
48 int             LwpMaxpri=0;            /* maximum priority so far */
49
50 #ifdef POSIXSIGNALS
51 static sigset_t oldmask;
52 #else /* POSIXSIGNALS */
53 static int      oldmask;
54 #endif /* POSIXSIGNALS */
55
56 /* for systems without strdup  */
57 #ifdef NOSTRDUP
58 extern char *strdup();
59 #endif /* NOSTRDUP */
60
61 static void     lwpStackCheckInit();
62 static int      lwpStackCheck();
63 static void     lwpStackCheckUsed();
64
65 /* check stack direction */
66 static int growsdown (x)
67         void    *x;
68 {
69         int y;
70         
71 #ifdef BOUNDS_CHECK
72         BOUNDS_CHECKING_OFF;
73 #endif
74         y = (x > (void *)&y);
75
76 #ifdef BOUNDS_CHECK
77         BOUNDS_CHECKING_ON;
78 #endif
79         
80         return y;
81 }
82
83 /*
84  * lwpReschedule -- schedule another process.  we also check for dead
85  * processes here and free them.
86  */
87 void lwpReschedule()
88 {
89         extern struct lwpQueue LwpSchedQ[];
90         static int lcount = LCOUNT;
91         static struct lwpProc *nextp;
92         static int i;
93 #ifdef POSIXSIGNALS
94         static sigset_t tmask;
95 #endif /* POSIXSIGNALS */
96
97         if (LwpCurrent && (LwpCurrent->flags & LWP_STACKCHECK)) {
98                 lwpStackCheck(LwpCurrent);
99         }
100         if (!--lcount) {
101                 int p = lwpSetPriority(LWP_MAX_PRIO-1);
102                 lcount = LCOUNT;
103 #ifdef POSIXSIGNALS
104                 sigprocmask (SIG_SETMASK, &oldmask, &tmask);
105                 sigprocmask (SIG_SETMASK, &tmask, &oldmask);
106 #else /* POSIXSIGNALS */
107                 sigsetmask(sigsetmask(oldmask));
108 #endif /* POSIXSIGNALS */
109                 LwpCurrent->pri = p;
110         }
111         
112         /* destroy dead threads */
113         lwpStatus(LwpCurrent,  "Cleaning dead queue");
114         while (NULL != (nextp = lwpGetFirst(&LwpDeadQ))) {
115                 if (nextp == LwpCurrent) {
116                         lwpStatus(nextp,
117                                   "OOOPS, we are running already dead thread");
118                         exit(1);
119                 }
120                 lwpDestroy(nextp);
121                 lwpStatus(LwpCurrent,  "Destroying done");
122         }
123                 
124         for (i=LwpMaxpri+1; i--; ) {
125                 while (NULL != (nextp = lwpGetFirst(&LwpSchedQ[i]))) {
126                         if (!nextp->dead)
127                                 break;
128                         /* clean up after dead bodies */
129                         lwpStatus(nextp, "got a dead body");
130                         if (nextp == LwpCurrent) {
131                                 lwpStatus(nextp,
132                                           "we are in it -- will bury later");
133                                 lwpAddTail(&LwpDeadQ, nextp);
134                         }
135                         else{
136                                 lwpDestroy(nextp);
137 /*                              fprintf(stderr,  "Destroying done\n"); */
138                         }
139                         nextp = 0;
140                 }
141                 if (nextp)
142                         break;
143         }
144         if (LwpCurrent == 0 && nextp == 0) {
145                 fprintf(stderr, "No processes to run!\n");
146                 exit(1);
147         }
148         if (LwpCurrent)
149                 lwpStatus(LwpCurrent, "switch out");
150         /* do context switch */
151 #ifdef BOUNDS_CHECK
152         BOUNDS_CHECKING_OFF;
153 #endif
154
155 #if defined(hpc)
156         {
157                 int endpoint;
158
159                 endpoint = &endpoint;
160                 if (initcontext == NULL || endpoint < startpoint) {
161                         i = lwpSave(LwpCurrent->context);
162                 } else {
163                         LwpCurrent->size = endpoint - startpoint;
164                         LwpCurrent->sbtm = realloc(LwpCurrent->sbtm, LwpCurrent->size);
165                         memcpy(LwpCurrent->sbtm, startpoint, LwpCurrent->size);
166                         if (i = lwpSave(LwpCurrent->context)) {
167                                 memcpy(startpoint, LwpCurrent->sbtm, LwpCurrent->size);
168                                 i = 1;
169                         }
170                 }
171         }
172 #else
173         i = lwpSave(LwpCurrent->context);
174 #endif
175 #ifdef BOUNDS_CHECK
176         BOUNDS_CHECKING_ON;
177 #endif
178         
179         if (LwpCurrent != nextp &&
180             !(LwpCurrent && i)) {
181                 /* restore previous context */
182                 lwpStatus(nextp, "switch in", nextp->pri);
183                 LwpCurrent = nextp;
184                 *LwpContextPtr = LwpCurrent->ud;
185 #ifdef BOUNDS_CHECK
186                 BOUNDS_CHECKING_OFF;
187 #endif
188                 lwpRestore(LwpCurrent->context);
189
190 #ifdef BOUNDS_CHECK
191                 BOUNDS_CHECKING_ON;
192 #endif
193         }
194 }
195
196 /*
197  * lwpEntryPoint -- process entry point.
198  */
199 void lwpEntryPoint()
200 {
201         extern struct lwpProc *LwpCurrent;
202 #ifdef POSIXSIGNALS
203         sigset_t set;
204 #endif /* POSIXSIGNALS */
205
206 #ifdef BOUNDS_CHECK
207         BOUNDS_CHECKING_OFF;
208 #endif
209 #ifdef POSIXSIGNALS
210         sigemptyset (&set);
211         sigaddset (&set, SIGALRM);
212         sigprocmask (SIG_SETMASK, &set, &oldmask);
213 #else /*  POSIXSIGNALS */
214         sigsetmask(SIGNALS);
215 #endif /* POSIXSIGNALS */
216         *LwpContextPtr = LwpCurrent->ud;
217
218         lwpStatus(LwpCurrent, "starting at entry point");
219         (*LwpCurrent->entry)(LwpCurrent->argc, LwpCurrent->argv,
220                 LwpCurrent->ud);
221         lwpExit();
222 #ifdef BOUNDS_CHECK
223         BOUNDS_CHECKING_ON;
224 #endif
225
226
227 }
228
229 /*
230  * lwpCreate -- create a process.
231  */
232 struct lwpProc *
233 lwpCreate(priority, entry, size, flags, name, desc, argc, argv, ud)
234         int     priority;
235         void    (*entry)();
236         int     size;
237         int     flags;
238         char    *name;
239         char    *desc;
240         int     argc;
241         char    *argv[];
242         void    *ud;
243 {
244         extern struct lwpProc *LwpCurrent;
245         struct lwpProc *newp;
246         int     *s, x;
247 #ifdef UCONTEXT
248         stack_t sp;
249 #else /* UCONTEXT */
250         void    *sp;
251 #endif /* UCONTEXT */
252         unsigned long stackp;
253
254         if (!(newp = (struct lwpProc *)malloc(sizeof(struct lwpProc))))
255                 return (0);
256         if (flags & LWP_STACKCHECK) {
257                 /* Add a 1K buffer on each side of the stack */
258                 size += 2 * LWP_REDZONE;
259         }
260         size += LWP_EXTRASTACK;
261         size += sizeof(stkalign_t);
262         if (!(s = (int *)malloc(size)))
263                 return (0);
264         newp->flags = flags;
265         newp->name = strdup(name);
266         newp->desc = strdup(desc);
267         newp->entry = entry;
268         newp->argc = argc;
269         newp->argv = argv;
270         newp->ud = ud;
271         if ((newp->flags & LWP_STACKCHECK) == 0) {
272                 stackp = growsdown((void *)&x) ? 
273                     (((long)s) + size - sizeof(stkalign_t) - LWP_EXTRASTACK) :
274                     (long) s + LWP_EXTRASTACK;
275 #ifdef UCONTEXT
276                 sp.ss_sp = (void *)(stackp & -sizeof(stkalign_t));
277                 sp.ss_size = size;
278                 sp.ss_flags = 0;
279 #else /* UCONTEXT */
280                 sp = (void *)(stackp & -sizeof(stkalign_t));
281 #endif /* UCONTEXT */
282         } else {
283                 if (growsdown(&x)) {
284                         /* round address off to stkalign_t */
285                         stackp = ((long)s) + size - LWP_REDZONE -
286                                 LWP_EXTRASTACK - sizeof(stkalign_t);
287 #ifdef UCONTEXT  
288                         sp.ss_sp = (void *)(stackp & -sizeof(stkalign_t));
289                         sp.ss_size = size;
290                         sp.ss_flags = 0;
291                         newp->lowmark = (void *)(((long) sp.ss_sp) + LWP_EXTRASTACK);
292 #else /* UCONTEXT */
293                         sp = (void *)(stackp & -sizeof(stkalign_t));
294                         newp->lowmark = (void *)(((long) sp) + LWP_EXTRASTACK);
295 #endif /* UCONTEXT */
296                         newp->himark = s;
297                 } else {
298                         stackp = ((long)s) + LWP_REDZONE + LWP_EXTRASTACK;
299 #ifdef UCONTEXT  
300                         sp.ss_sp = (void *)(((long)stackp) &
301                                 -sizeof(stkalign_t));
302                         sp.ss_size = size;
303                         sp.ss_flags = 0;
304 #else /* UCONTEXT */
305                         sp = (void *)(((long)stackp) & -sizeof(stkalign_t));
306 #endif /* UCONTEXT */
307                         newp->lowmark = (void *)s;
308                         newp->himark = (void *)(((long)s) + size - LWP_REDZONE);
309                 }
310         }
311         if (LWP_MAX_PRIO <= priority)
312                 priority = LWP_MAX_PRIO-1;
313         if (LwpMaxpri < (newp->pri = priority))
314                 LwpMaxpri = priority;
315         newp->sbtm = (void *)s;
316         newp->size = size;
317         newp->dead = 0;
318         if (flags & LWP_STACKCHECK)
319                 lwpStackCheckInit(newp);
320         lwpStatus(newp, "creating process structure sbtm: %d",
321                   (int)newp->sbtm);
322         lwpReady(newp);
323         lwpReady(LwpCurrent);
324 #ifdef UCONTEXT
325         lwpInitContext(newp, &sp); /* architecture-dependent: from arch.c */
326 #else /* UCONTEXT */
327         lwpInitContext(newp, sp);  /* architecture-dependent: from arch.c */
328 #endif /* UCONTEXT */
329         lwpReschedule();
330         return (newp);
331 }
332
333 void lwpDestroy(proc)
334         struct lwpProc *proc;
335 {
336         if (proc->flags & LWP_STACKCHECK){
337                 lwpStackCheckUsed(proc);
338                 lwpStackCheck(proc);
339         }
340         lwpStatus(proc, "destroying sbtm: %d", (int)proc->sbtm);
341         proc->entry = 0;
342         proc->ud = 0;
343         proc->argv = 0;
344         free((char *)proc->sbtm); 
345         free(proc->name); 
346         free(proc->desc); 
347         proc->name = 0;
348         proc->desc = 0;
349         proc->sbtm = 0;
350         proc->lowmark = 0;
351         proc->himark = 0;
352         free((char *)proc); 
353 }
354
355 /*
356  * lwpReady -- put process on ready queue.  if null, assume current.
357  */
358 void lwpReady(p)
359         struct lwpProc *p;
360 {
361         extern struct lwpProc *LwpCurrent;
362         extern struct lwpQueue LwpSchedQ[];
363
364         if (!p)
365                 p = LwpCurrent;
366         lwpStatus(p, "added to run queue");
367         lwpAddTail(&LwpSchedQ[p->pri], p);
368 }
369
370 /*
371  * return user's data
372  */
373 void *lwpGetUD(p)
374         struct lwpProc *p;
375 {
376         if (!p)
377                 p = LwpCurrent;
378         return (p->ud);
379 }
380
381 /*
382  * set user's data
383  */
384 void lwpSetUD(p, ud)
385         struct lwpProc *p;
386         char    *ud;
387 {
388         if (!p)
389                 p = LwpCurrent;
390         p->ud = ud;
391 }
392
393 /*
394  * set name & desc
395  */
396 void lwpSetDesc(p, name, desc)
397         struct lwpProc *p;
398         char    *name;
399         char    *desc;
400 {
401         if (!p)
402                 p = LwpCurrent;
403         free(p->name);
404         free(p->desc);
405         p->name = strdup(name);
406         p->desc = strdup(desc);
407 }
408
409 /*
410  * lwpYield -- yield the processor to another thread.
411  */
412 void lwpYield()
413 {
414         lwpStatus(LwpCurrent, "yielding control");
415         lwpReady(LwpCurrent);
416         lwpReschedule();
417 }
418
419 /*
420  * cause the current process to be scheduled for deletion.
421  */
422 void lwpExit()
423 {
424         lwpStatus(LwpCurrent, "marking self as dead");
425         LwpCurrent->dead = 1;
426         lwpYield();
427 }
428
429 /*
430  * mark another process as dead, so it will never be rescheduled.
431  * remove any lingering FD action
432  */
433 void lwpTerminate(p)
434         struct lwpProc *p;
435 {
436         lwpStatus(p, "terminating process");
437         p->dead = 1;
438         if (p->fd >= 0)
439                 lwpWakeupFd(p);
440 }
441
442 /*
443  * set the thread's priority, returning the old.
444  * if the new priority is lower than the old, we reschedule.
445  */
446 int lwpSetPriority(new)
447         int new;
448 {
449         int old = LwpCurrent->pri;
450
451         if (LWP_MAX_PRIO <= new)
452                 new = LWP_MAX_PRIO-1;
453         if (LwpMaxpri < new)
454                 LwpMaxpri = new;
455         LwpCurrent->pri = new;
456         lwpStatus(LwpCurrent, "resetting priority (%d -> %d)", old, new);
457         if (new < old)
458                 lwpYield();
459         return (old);
460 }
461
462 /*
463  * initialise the coroutine structures
464  */
465 struct lwpProc *lwpInitSystem(pri, ctxptr, flags)
466         int     pri;
467         char    **ctxptr;
468         int     flags;
469 {
470         extern struct lwpQueue LwpSchedQ[];
471         extern struct lwpProc *LwpCurrent;
472         struct lwpQueue *q;
473         int i, *stack;
474         struct lwpProc *sel;
475
476         LwpContextPtr = ctxptr;
477         if (pri < 1)
478                 pri = 1;
479         /* *LwpContextPtr = 0; */
480         if (!(LwpCurrent = (struct lwpProc *)calloc (1, sizeof(struct lwpProc))))
481                 return (0);
482         if (!(stack = (int *)malloc(64)))
483                 return (0);
484         if (LWP_MAX_PRIO <= pri)
485                 pri = LWP_MAX_PRIO-1;
486         if (LwpMaxpri < pri)
487                 LwpMaxpri = pri;
488         LwpCurrent->next = 0;
489         LwpCurrent->sbtm = stack;       /* dummy stack for "main" */
490         LwpCurrent->pri = pri;
491         LwpCurrent->dead = 0;
492         LwpCurrent->flags = flags;
493         LwpCurrent->name = "Main";
494         for (i=LWP_MAX_PRIO, q=LwpSchedQ; i--; q++)
495                 q->head = q->tail = 0;
496         LwpDeadQ.head = LwpDeadQ.tail = 0;
497         /* must be lower in priority than us for this to work right */
498         sel = lwpCreate(0, lwpSelect, 16384, flags, "EventHandler",
499                 "Select (main loop) Event Handler", 0, 0, 0);
500         lwpInitSelect(sel);
501         return (LwpCurrent);
502 }
503
504 /* lwpStackCheckInit
505  *
506  * Initialize the entire stack (including both redzones) with the stack
507  * check mark.  Thus, we can get some indication of how much stack was
508  * used.
509  */
510 static void lwpStackCheckInit(newp)
511         struct lwpProc *newp;
512 {
513         register int i;
514         register long *lp;
515
516         int lim = newp->size/sizeof(long);
517         if (!newp || !newp->sbtm)
518                 return;
519         for (lp=newp->sbtm,i=0; i < lim; i++,lp++) {
520                 *lp = LWP_CHECKMARK;
521         }
522 }
523
524 /* lwpStackCheck
525  *
526  * Check if the thread has overflowed/underflowed its stack.
527  * NOTE:
528  *   If an problem occurs, it is not corrected.
529  *   The buffer is not cleaned up, nor is the thread terminated.
530  *   Cleaning up the buffer would be a mistake, and terminating
531  *   the thread, well, could be done.   Should more like take
532  *   down the entire process.
533  */
534 static int lwpStackCheck(newp)
535         struct lwpProc *newp;
536 {
537         register int end, amt;
538         register unsigned int i;
539         register long *lp;
540         register int growsDown;
541         int     marker;
542
543         if (!newp || !newp->himark || !newp->lowmark)
544                 return(1);
545         growsDown = growsdown(&marker);
546         for (lp=newp->himark,i=0; i < LWP_REDZONE/sizeof(long); i++,lp++) {
547                 if (*lp == LWP_CHECKMARK)
548                         continue;
549                 /* Stack overflow. */
550                 if (growsDown) {
551                         end = i;
552                         while (i < LWP_REDZONE/sizeof(long)) {
553                                 if (*lp++ != LWP_CHECKMARK)
554                                         end = i;
555                                 i++;
556                         }
557                         amt = (end+1) * sizeof(long);
558                 } else {
559                         amt = (i+1) * sizeof(long);
560                 }
561                 lwpStatus(newp, "Thread stack overflowed %d bytes (of %u)",
562                         amt, newp->size - 2*LWP_REDZONE - sizeof(stkalign_t));
563                 return(0);
564         }
565         for (lp=newp->lowmark,i=0; i < LWP_REDZONE/sizeof(long); i++,lp++) {
566                 if (*lp == LWP_CHECKMARK)
567                         continue;
568                 /* Stack underflow. */
569                 if (growsDown) {
570                         end = i;
571                         while (i < LWP_REDZONE/sizeof(long)) {
572                                 if (*lp++ != LWP_CHECKMARK)
573                                         end = i;
574                                 i++;
575                         }
576                         amt = (end+1) * sizeof(long);
577                 } else {
578                         amt = (LWP_REDZONE - i+1) * sizeof(long);
579                 }
580                 lwpStatus(newp, "Thread stack underflow %d bytes (of %u)",
581                         amt, newp->size - 2*LWP_REDZONE - sizeof(stkalign_t));
582                 return(0);
583         }
584         return(1);
585 }
586
587 /* lwpStackCheckUsed
588  *
589  * Figure out how much stack was used by this thread.
590  */
591 static void lwpStackCheckUsed(newp)
592         struct lwpProc *newp;
593 {
594         register int i;
595         register long *lp;
596         register int lim;
597         int     marker;
598
599         if (!newp || !newp->sbtm)
600                 return;
601         lim = newp->size/sizeof(long);
602         if (growsdown(&marker)) {
603                 /* Start at the bottom and find first non checkmark. */
604                 for (lp=newp->sbtm,i=0; i < lim; i++,lp++) {
605                         if (*lp != LWP_CHECKMARK) {
606                                 break;
607                         }
608                 }
609         } else {
610                 /* Start at the top and find first non checkmark. */
611                 lp = newp->sbtm;
612                 lp += newp->size/sizeof(long);
613                 lp--;
614                 for (i=0; i < lim; i++, lp--) {
615                         if (*lp != LWP_CHECKMARK) {
616                                 break;
617                         }
618                 }
619         }
620         lwpStatus(newp, "stack use: %u bytes (of %u total)",
621                 (i * sizeof(long)) - LWP_REDZONE,
622                 newp->size - 2*LWP_REDZONE - sizeof(stkalign_t));
623 }
624
625 #endif