76476cb895937297c77455036858a22979e3c694
[openafs.git] / src / lwp / test / testlwp.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  * 
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 /*******************************************************************\
11 *                                                                   *
12 *       Information Technology Center                               *
13 *       Carnegie-Mellon University                                  *
14 *                                                                   *
15 *                                                                   *
16 \*******************************************************************/
17
18
19 /* allocate externs here */
20 #include <afsconfig.h>
21 #include <afs/param.h>
22
23
24 #define LWP_KERNEL
25 #include "lwp.h"
26
27 #define  NULL       0
28
29 #define  ON         1
30 #define  OFF        0
31 #define  TRUE       1
32 #define  FALSE      0
33 #define  READY      (1<<1)
34 #define  WAITING    (1<<2)
35 #define  DESTROYED  (1<<3)
36 #define  MAXINT     (~(1<<((sizeof(int)*8)-1)))
37 #define  MINSTACK   44
38
39 /* Debugging macro */
40 #ifdef DEBUG
41 #define Debug(level, msg)\
42          if (lwp_debug && lwp_debug >= level) {\
43              printf("***LWP (0x%x): ", lwp_cpptr);\
44              printf msg;\
45              putchar('\n');\
46          }
47
48 #else
49 #define Debug(level, msg)
50
51 #endif
52 \f
53 int      Dispatcher();
54 int      Create_Process_Part2();
55 int      Exit_LWP();
56 int Initialize_Stack(), Stack_Used();
57 char   (*RC_to_ASCII());
58
59 #define MAX_PRIORITIES  (LWP_MAX_PRIORITY+1)
60
61 struct QUEUE {
62     PROCESS     head;
63     int         count;
64 } runnable[MAX_PRIORITIES], blocked;
65
66 /* Offset of stack field within pcb -- used by stack checking stuff */
67 int stack_offset;
68
69 static remove(p, q)
70     PROCESS p;
71     struct QUEUE *q;
72 {
73     /* Special test for only element on queue */
74     if (q->count == 1)
75         q -> head = NULL;
76     else {
77         /* Not only element, do normal remove */
78         p -> next -> prev = p -> prev;
79         p -> prev -> next = p -> next;
80     }
81     /* See if head pointing to this element */
82     if (q->head == p) q -> head = p -> next;
83     q->count--;
84     p -> next = p -> prev = NULL;
85 }
86
87 static insert(p, q)
88     PROCESS p;
89     struct QUEUE *q;
90 {
91     if (q->head == NULL) {      /* Queue is empty */
92         q -> head = p;
93         p -> next = p -> prev = p;
94     } else {                    /* Regular insert */
95         p -> prev = q -> head -> prev;
96         q -> head -> prev -> next = p;
97         q -> head -> prev = p;
98         p -> next = q -> head;
99     }
100     q->count++;
101 }
102
103 static move(p, from, to)
104     PROCESS p;
105     struct QUEUE *from, *to;
106 {
107     remove(p, from);
108     insert(p, to);
109 }
110
111 /* Iterator macro */
112 #define for_all_elts(var, q, body)\
113         {\
114             PROCESS var, _NEXT_;\
115             int _I_;\
116             for (_I_=q.count, var = q.head; _I_>0; _I_--, var=_NEXT_) {\
117                 _NEXT_ = var -> next;\
118                 body\
119             }\
120         }
121 \f
122 /*                                                                          */
123 /*****************************************************************************\
124 *                                                                             *
125 *  Following section documents the Assembler interfaces used by LWP code      *
126 *                                                                             *
127 \*****************************************************************************/
128
129 /*
130         savecontext(int (*ep)(), struct lwp_context *savearea, char *sp);
131
132 Stub for Assembler routine that will
133 save the current SP value in the passed
134 context savearea and call the function
135 whose entry point is in ep.  If the sp
136 parameter is NULL, the current stack is
137 used, otherwise sp becomes the new stack
138 pointer.
139
140         returnto(struct lwp_context *savearea);
141
142 Stub for Assembler routine that will
143 restore context from a passed savearea
144 and return to the restored C frame.
145
146 */
147
148 /* Macro to force a re-schedule.  Strange name is historical */
149
150 #define Set_LWP_RC(dummy) savecontext(Dispatcher, &lwp_cpptr->context, NULL)
151
152 static struct lwp_ctl *lwp_init;
153
154 int LWP_CreateProcess(ep, stacksize, priority, parm, name, pid)
155    int   (*ep)();
156    int   stacksize, priority;
157    char  *parm;
158    char  *name;
159    PROCESS *pid;
160 {
161     PROCESS temp, temp2;
162     char *stackptr;
163
164     Debug(0, ("Entered LWP_CreateProcess"))
165     /* Throw away all dead process control blocks */
166     purge_dead_pcbs();
167     if (lwp_init) {
168         temp = (PROCESS) malloc (sizeof (struct lwp_pcb));
169         if (temp == NULL) {
170             Set_LWP_RC();
171             return LWP_ENOMEM;
172         }
173         if (stacksize < MINSTACK)
174             stacksize = 1000;
175         else
176             stacksize = 4 * ((stacksize+3) / 4);
177         if ((stackptr = (char *) malloc(stacksize)) == NULL) {
178             Set_LWP_RC();
179             return LWP_ENOMEM;
180         }
181         if (priority < 0 || priority >= MAX_PRIORITIES) {
182             Set_LWP_RC();
183             return LWP_EBADPRI;
184         }
185 #ifdef DEBUG
186 #ifdef STACK_USED
187         Initialize_Stack(stackptr, stacksize);
188 #endif
189 #endif
190         Initialize_PCB(temp, priority, stackptr, stacksize, ep, parm, name);
191         insert(temp, &runnable[priority]);
192         temp2 = lwp_cpptr;
193         lwp_cpptr = temp;
194         savecontext(Create_Process_Part2, &temp2->context, stackptr+stacksize-4);
195         Set_LWP_RC();
196         *pid = temp;
197         return 0;
198     } else
199         return LWP_EINIT;
200 }
201
202 int LWP_CurrentProcess(pid)     /* returns pid of current process */
203     PROCESS *pid;
204 {
205     Debug(0, ("Entered Current_Process"))
206     if (lwp_init) {
207             *pid = lwp_cpptr;
208             return LWP_SUCCESS;
209     } else
210         return LWP_EINIT;
211 }
212
213 #define LWPANCHOR (*lwp_init)
214
215 int LWP_DestroyProcess(pid)             /* destroy a lightweight process */
216     PROCESS pid;
217 {
218     PROCESS temp;
219
220     Debug(0, ("Entered Destroy_Process"))
221     if (lwp_init) {
222         if (lwp_cpptr != pid) {
223             Dispose_of_Dead_PCB(pid);
224             Set_LWP_RC();
225         } else {
226             pid -> status = DESTROYED;
227             move(pid, &runnable[pid->priority], &blocked);
228             temp = lwp_cpptr;
229             savecontext(Dispatcher, &(temp -> context),
230                         &(LWPANCHOR.dsptchstack[(sizeof LWPANCHOR.dsptchstack)-4]));
231         }
232         return LWP_SUCCESS;
233     } else
234         return LWP_EINIT;
235 }
236
237 int LWP_DispatchProcess()               /* explicit voluntary preemption */
238 {
239     Debug(2, ("Entered Dispatch_Process"))
240     if (lwp_init) {
241         Set_LWP_RC();
242         return LWP_SUCCESS;
243     } else
244         return LWP_EINIT;
245 }
246
247 #ifdef DEBUG
248 Dump_Processes()
249 {
250     if (lwp_init) {
251         int i;
252         for (i=0; i<MAX_PRIORITIES; i++)
253             for_all_elts(x, runnable[i], {
254                 printf("[Priority %d]\n", i);
255                 Dump_One_Process(x);
256             })
257         for_all_elts(x, blocked, { Dump_One_Process(x); })
258     } else
259         printf("***LWP: LWP support not initialized\n");
260 }
261 #endif
262
263 int LWP_GetProcessPriority(pid, priority)       /* returns process priority */
264     PROCESS pid;
265     int *priority;
266 {
267     Debug(0, ("Entered Get_Process_Priority"))
268     if (lwp_init) {
269         *priority = pid -> priority;
270         return 0;
271     } else
272         return LWP_EINIT;
273 }
274
275 int LWP_InitializeProcessSupport(priority, pid)
276     int priority;
277     PROCESS *pid;
278 {
279     PROCESS temp;
280     struct lwp_pcb dummy;
281     int i;
282
283     Debug(0, ("Entered LWP_InitializeProcessSupport"))
284     if (lwp_init != NULL) return LWP_EINIT;
285
286     /* Set up offset for stack checking -- do this as soon as possible */
287     stack_offset = (char *) &dummy.stack - (char *) &dummy;
288
289     if (priority >= MAX_PRIORITIES) return LWP_EBADPRI;
290     for (i=0; i<MAX_PRIORITIES; i++) {
291         runnable[i].head = NULL;
292         runnable[i].count = 0;
293     }
294     blocked.head = NULL;
295     blocked.count = 0;
296     lwp_init = (struct lwp_ctl *) malloc(sizeof(struct lwp_ctl));
297     temp = (PROCESS) malloc(sizeof(struct lwp_pcb));
298     if (lwp_init == NULL || temp == NULL)
299         Abort_LWP("Insufficient Storage to Initialize LWP Support");
300     LWPANCHOR.processcnt = 1;
301     LWPANCHOR.outerpid = temp;
302     LWPANCHOR.outersp = NULL;
303     Initialize_PCB(temp, priority, NULL, 0, NULL, NULL, "Main Process [created by LWP]");
304     insert(temp, &runnable[priority]);
305     savecontext(Dispatcher, &temp->context, NULL);
306     LWPANCHOR.outersp = temp -> context.topstack;
307     Set_LWP_RC();
308     *pid = temp;
309     return LWP_SUCCESS;
310 }
311
312 int LWP_INTERNALSIGNAL(event, yield)            /* signal the occurence of an event */
313     char *event;
314     int yield;
315 {
316     Debug(2, ("Entered LWP_SignalProcess"))
317     if (lwp_init) {
318         int rc;
319         rc = Internal_Signal(event);
320         if (yield) Set_LWP_RC();
321         return rc;
322     } else
323         return LWP_EINIT;
324 }
325
326 int LWP_TerminateProcessSupport()       /* terminate all LWP support */
327 {
328     int pc;
329     int i;
330
331     Debug(0, ("Entered Terminate_Process_Support"))
332     if (lwp_init == NULL) return LWP_EINIT;
333     if (lwp_cpptr != LWPANCHOR.outerpid)
334         Abort_LWP("Terminate_Process_Support invoked from wrong process!");
335     pc = LWPANCHOR.processcnt-1;
336     for (i=0; i<MAX_PRIORITIES; i++)
337         for_all_elts(cur, runnable[i], { Free_PCB(cur); })
338     for_all_elts(cur, blocked, { Free_PCB(cur); })
339     free(lwp_init);
340     lwp_init = NULL;
341     return LWP_SUCCESS;
342 }
343
344 int LWP_WaitProcess(event)              /* wait on a single event */
345     char *event;
346 {
347     char *tempev[2];
348
349     Debug(2, ("Entered Wait_Process"))
350     if (event == NULL) return LWP_EBADEVENT;
351     tempev[0] = event;
352     tempev[1] = NULL;
353     return LWP_MwaitProcess(1, tempev);
354 }
355
356 int LWP_MwaitProcess(wcount, evlist, ecount)    /* wait on m of n events */
357     int wcount, ecount;
358     char *evlist[];
359 {
360     Debug(0, ("Entered Mwait_Process [waitcnt = %d]", wcount))
361
362     if (ecount == 0) {
363         Set_LWP_RC();
364         return LWP_EBADCOUNT;
365     }
366     if (lwp_init) {
367         if (wcount>ecount || wcount<0) {
368             Set_LWP_RC();
369             return LWP_EBADCOUNT;
370         }
371         if (ecount > LWP_MAX_EVENTS) {
372             Set_LWP_RC();
373             return LWP_EBADCOUNT;
374         }
375         if (ecount == 1)
376             lwp_cpptr->eventlist[0] = evlist[0];
377         else
378             memcpy(lwp_cpptr->eventlist, evlist, ecount*sizeof(char *));
379         if (wcount > 0) {
380             lwp_cpptr -> status = WAITING;
381             move(lwp_cpptr, &runnable[lwp_cpptr->priority], &blocked);
382         }
383         lwp_cpptr -> wakevent = 0;
384         lwp_cpptr -> waitcnt = wcount;
385         lwp_cpptr -> eventcnt = ecount;
386         Set_LWP_RC();
387         return LWP_SUCCESS;
388     }
389     return LWP_EINIT;
390 }
391
392 int LWP_StackUsed(pid, max, used)
393     PROCESS pid;
394     int *max, *used;
395 {
396 #define NO_STACK_STUFF
397 #ifdef DEBUG
398 #ifdef STACK_USED
399 #undef NO_STACK_STUFF
400 #endif
401 #endif
402
403 #ifdef NO_STACK_STUFF
404     return LWP_NO_STACK;
405 #else
406     *max = pid -> stacksize;
407     *used = Stack_Used(pid->stack, *max);
408     return LWP_SUCCESS;
409 #endif
410 }
411 \f
412 /*
413  *  The following functions are strictly
414  *  INTERNAL to the LWP support package.
415  */
416
417 static Abort_LWP(msg)
418     char *msg;
419 {
420     struct lwp_context tempcontext;
421
422     Debug(0, ("Entered Abort_LWP"))
423     printf("***LWP: %s\n",msg);
424     printf("***LWP: Abort --- dumping PCBs ...\n");
425 #ifdef DEBUG
426     Dump_Processes();
427 #endif
428     if (LWPANCHOR.outersp == NULL)
429         Exit_LWP();
430     else
431         savecontext(Exit_LWP, &tempcontext, LWPANCHOR.outersp);
432 }
433
434 static Create_Process_Part2 ()  /* creates a context for the new process */
435 {
436     PROCESS temp;
437
438     Debug(2, ("Entered Create_Process_Part2"))
439     temp = lwp_cpptr;           /* Get current process id */
440     savecontext(Dispatcher, &temp->context, NULL);
441     (*temp->ep)(temp->parm);
442     LWP_DestroyProcess(temp);
443 }
444
445 static Delete_PCB(pid)  /* remove a PCB from the process list */
446     PROCESS pid;
447 {
448     Debug(4, ("Entered Delete_PCB"))
449     remove(pid, (pid->blockflag || pid->status==WAITING || pid->status==DESTROYED
450                  ? &blocked
451                  : &runnable[pid->priority]));
452     LWPANCHOR.processcnt--;
453 }
454
455 #ifdef DEBUG
456 static Dump_One_Process(pid)
457    PROCESS pid;
458 {
459     int i;
460
461     printf("***LWP: Process Control Block at 0x%x\n", pid);
462     printf("***LWP: Name: %s\n", pid->name);
463     if (pid->ep != NULL)
464         printf("***LWP: Initial entry point: 0x%x\n", pid->ep);
465     if (pid->blockflag) printf("BLOCKED and ");
466     switch (pid->status) {
467         case READY:     printf("READY");     break;
468         case WAITING:   printf("WAITING");   break;
469         case DESTROYED: printf("DESTROYED"); break;
470         default:        printf("unknown");
471     }
472     putchar('\n');
473     printf("***LWP: Priority: %d \tInitial parameter: 0x%x\n",
474             pid->priority, pid->parm);
475     if (pid->stacksize != 0) {
476         printf("***LWP:  Stacksize: %d \tStack base address: 0x%x\n",
477                 pid->stacksize, pid->stack);
478         printf("***LWP: HWM stack usage: ");
479         printf("%d\n", Stack_Used(pid->stack,pid->stacksize));
480         free (pid->stack);
481     }
482     printf("***LWP: Current Stack Pointer: 0x%x\n", pid->context.topstack);
483     if (pid->eventcnt > 0) {
484         printf("***LWP: Number of events outstanding: %d\n", pid->waitcnt);
485         printf("***LWP: Event id list:");
486         for (i=0;i<pid->eventcnt;i++)
487             printf(" 0x%x", pid->eventlist[i]);
488         putchar('\n');
489     }
490     if (pid->wakevent>0)
491         printf("***LWP: Number of last wakeup event: %d\n", pid->wakevent);
492 }
493 #endif
494
495 static purge_dead_pcbs()
496 {
497     for_all_elts(cur, blocked, { if (cur->status == DESTROYED) Dispose_of_Dead_PCB(cur); })
498 }
499
500 int LWP_TraceProcesses = 0;
501
502 static Dispatcher()             /* Lightweight process dispatcher */
503 {
504     int i;
505 #ifdef DEBUG
506     static int dispatch_count = 0;
507
508     if (LWP_TraceProcesses > 0) {
509         for (i=0; i<MAX_PRIORITIES; i++) {
510             printf("[Priority %d, runnable (%d):", i, runnable[i].count);
511             for_all_elts(p, runnable[i], {
512                 printf(" \"%s\"", p->name);
513             })
514             puts("]");
515         }
516         printf("[Blocked (%d):", blocked.count);
517         for_all_elts(p, blocked, {
518             printf(" \"%s\"", p->name);
519         })
520         puts("]");
521     }
522 #endif
523
524     for (i=MAX_PRIORITIES-1; i>=0; i--)
525         if (runnable[i].head != NULL) break;
526
527     if (i < 0) Abort_LWP("No READY processes");
528
529 #ifdef DEBUG
530     if (LWP_TraceProcesses > 0)
531         printf("Dispatch %d [PCB at 0x%x] \"%s\"\n", ++dispatch_count, runnable[i].head, runnable[i].head->name);
532 #endif
533     lwp_cpptr = runnable[i].head;
534     runnable[i].head = runnable[i].head -> next;
535     returnto(&lwp_cpptr->context);
536 }
537
538 static Dispose_of_Dead_PCB (cur)
539     PROCESS cur;
540 {
541     Debug(4, ("Entered Dispose_of_Dead_PCB"))
542     Delete_PCB(cur);
543     Free_PCB(cur);
544 /*
545     Internal_Signal(cur);
546 */
547 }
548
549 static Exit_LWP()
550 {
551     abort();
552 }
553
554 static Free_PCB(pid)
555     PROCESS pid;
556 {
557     Debug(4, ("Entered Free_PCB"))
558     if (pid -> stack != NULL) {
559         Debug(0, ("HWM stack usage: %d, [PCB at 0x%x]",
560                    Stack_Used(pid->stack,pid->stacksize), pid))
561         free(pid -> stack);
562     }
563     free(pid);
564 }
565
566 static Initialize_PCB(temp, priority, stack, stacksize, ep, parm, name)
567     PROCESS temp;
568     int (*ep)();
569     int stacksize, priority;
570     char *parm;
571     char *name,*stack;
572 {
573     int i = 0;
574
575     Debug(4, ("Entered Initialize_PCB"))
576     if (name != NULL)
577         while (((temp -> name[i] = name[i]) != '\0') && (i < 31)) i++;
578     temp -> name[31] = '\0';
579     temp -> status = READY;
580     temp -> eventcnt = 0;
581     temp -> wakevent = 0;
582     temp -> waitcnt = 0;
583     temp -> blockflag = 0;
584     temp -> priority = priority;
585     temp -> stack = stack;
586     temp -> stacksize = stacksize;
587     temp -> ep = ep;
588     temp -> parm = parm;
589     temp -> misc = NULL;        /* currently unused */
590     temp -> next = NULL;
591     temp -> prev = NULL;
592 }
593
594 static int Internal_Signal(event)
595     char *event;
596 {
597     int rc = LWP_ENOWAIT;
598     int i;
599
600     Debug(0, ("Entered Internal_Signal [event id 0x%x]", event))
601     if (!lwp_init) return LWP_EINIT;
602     if (event == NULL) return LWP_EBADEVENT;
603     for_all_elts(temp, blocked, {
604         if (temp->status == WAITING)
605             for (i=0; i < temp->eventcnt; i++) {
606                 if (temp -> eventlist[i] == event) {
607                     temp -> eventlist[i] = NULL;
608                     rc = LWP_SUCCESS;
609                     Debug(0, ("Signal satisfied for PCB 0x%x", temp))
610                     if (--temp->waitcnt == 0) {
611                         temp -> status = READY;
612                         temp -> wakevent = i+1;
613                         move(temp, &blocked, &runnable[temp->priority]);
614                         break;
615                     }
616                 }
617             }
618     })
619     return rc;
620 }
621
622 #ifdef DEBUG
623 #ifdef STACK_USED
624 static Initialize_Stack(stackptr, stacksize)
625     char *stackptr;
626     int stacksize;
627 {
628     int i;
629
630     Debug(4, ("Entered Initialize_Stack"))
631     for (i=0; i<stacksize; i++) stackptr[i] = i & 0xff;
632 }
633
634 static int Stack_Used(stackptr, stacksize)
635    char *stackptr;
636    int stacksize;
637 {
638     int i;
639
640     for (i=0;i<stacksize;i++)
641     if ((unsigned char) stackptr[i] != (i & 0xff))
642         return (stacksize-i);
643     return 0;
644 }
645 #endif
646 #endif