death to register
[openafs.git] / src / lwp / lwp_nt.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 /* For NT, LWP is implemented on top of fibers. The design attempts to
11  * follow the current LWP implementation so that we are not using 2 LWP
12  * models. The parts of the Unix LWP model that are not actually used in
13  * AFS have not been implemented at all. With the exception of preemption,
14  * adding those parts to this NT base would be a matter of adding routines.
15  *
16  * required lib is kernel32.lib, header is winbase.h
17  */
18
19 #include <afsconfig.h>
20 #include <afs/param.h>
21
22
23 #ifdef AFS_NT40_ENV
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <afs/afsutil.h>
27 #include "lwp.h"
28
29
30 static struct lwp_ctl *lwp_init;
31 #define LWPANCHOR (*lwp_init)
32
33 PROCESS lwp_cpptr;
34
35 char lwp_debug = 0;
36
37 #define  READY          2
38 #define  WAITING                3
39 #define  DESTROYED      4
40 #define QWAITING                5
41
42 /* Debugging macro */
43 #ifdef DEBUG
44 #define Debug(level, msg)\
45          if (lwp_debug && lwp_debug >= level) {\
46              printf("***LWP (0x%p): ", lwp_cpptr);\
47              printf msg;\
48              putchar('\n');\
49          }
50
51 #else
52 #define Debug(level, msg)
53 #endif
54
55
56 /* Forward declarations */
57 static void Dispatcher(void);
58 static void Exit_LWP(void);
59 static void Abort_LWP(char *msg);
60 static VOID WINAPI Enter_LWP(PVOID fiberData);
61 static void Initialize_PCB(PROCESS pcb, int priority, int stacksize,
62                            int (*funP)(), void *argP, char *name);
63
64 static void Dispose_of_Dead_PCB();
65 static void Free_PCB();
66 static int Internal_Signal();
67 static void purge_dead_pcbs(void);
68
69 static void lwp_remove(PROCESS p, struct QUEUE *q);
70 static void insert(PROCESS p, struct QUEUE *q);
71 static void move(PROCESS p, struct QUEUE *from, struct QUEUE *to);
72
73 /* biggest LWP stack created so far - used by IOMGR */
74 int lwp_MaxStackSeen = 0;
75
76 #ifdef DEBUG
77 static void Dump_One_Process(PROCESS pid);
78 static void Dump_Processes(void);
79 #endif
80
81 int lwp_nextindex;
82
83 #define MAX_PRIORITIES  (LWP_MAX_PRIORITY+1)
84
85 /* Invariant for runnable queues: The head of each queue points to the
86  * currently running process if it is in that queue, or it points to the
87  * next process in that queue that should run.
88  */
89 struct QUEUE {
90     PROCESS     head;
91     int         count;
92 } runnable[MAX_PRIORITIES], blocked;
93
94 /* Iterator macro */
95 #define for_all_elts(var, q, body)\
96         {\
97             PROCESS var, _NEXT_;\
98             int _I_;\
99             for (_I_=q.count, var = q.head; _I_>0; _I_--, var=_NEXT_) {\
100                 _NEXT_ = var -> next;\
101                 body\
102             }\
103         }
104
105 #ifdef AFS_WIN95_ENV
106
107 LPVOID ConvertThreadToFiber(PROCESS x)
108 {
109         return NULL;
110 }
111 LPVOID CreateFiber(DWORD x ,LPVOID y,PROCESS z)
112 {
113         return NULL;
114 }
115
116 VOID SwitchToFiber(LPVOID p)
117 {
118 }
119
120 VOID DeleteFiber(LPVOID p)
121 {
122 }
123 #endif
124
125
126 int lwp_MinStackSize = 0;
127
128 /* LWP_InitializeProcessSupport - setup base support for fibers.
129  *
130  * Arguments:
131  *      priority - priority of main thread.
132  *
133  * Returns:
134  *      pid - return this process
135  *      value:
136  *      LWP_SUCCESS (else aborts)
137  *      
138  */
139
140 int LWP_InitializeProcessSupport(int priority, PROCESS *pid)
141 {
142     PROCESS pcb;
143     int i;
144     char* value;
145
146     Debug(0, ("Entered LWP_InitializeProcessSupport"))
147     if (lwp_init != NULL) return LWP_SUCCESS;
148
149     if (priority >= MAX_PRIORITIES) return LWP_EBADPRI;
150
151     pcb = (PROCESS)malloc(sizeof(*pcb));
152     if (pcb == NULL)
153         Abort_LWP("Insufficient Storage to Initialize LWP PCB");
154     (void) memset((void*)pcb, 0, sizeof(*pcb));
155     pcb->fiber = ConvertThreadToFiber(pcb);
156     if (pcb == NULL) 
157         Abort_LWP("Cannot convert main thread to LWP fiber");
158
159     lwp_init = (struct lwp_ctl *) malloc(sizeof(struct lwp_ctl));
160     if (lwp_init == NULL)
161         Abort_LWP("Insufficient Storage to Initialize LWP CTL");
162     (void) memset((void*)lwp_init, 0, sizeof(struct lwp_ctl));
163
164     for (i=0; i<MAX_PRIORITIES; i++) {
165         runnable[i].head = NULL;
166         runnable[i].count = 0;
167     }
168     blocked.head = NULL;
169     blocked.count = 0;
170
171     LWPANCHOR.processcnt = 1;
172     LWPANCHOR.outerpid = pcb;
173     LWPANCHOR.outersp = NULL;
174
175
176     Initialize_PCB(pcb, priority, 0, NULL, NULL,
177                 "Main Process [created by LWP]");   
178
179     lwp_cpptr = pcb;
180     Debug(10, ("Init: Insert 0x%p into runnable at priority %d\n", pcb, priority))
181     insert(pcb, &runnable[priority]);
182
183     if ( ( value = getenv("AFS_LWP_STACK_SIZE")) == NULL )
184         lwp_MinStackSize = AFS_LWP_MINSTACKSIZE;        
185     else
186         lwp_MinStackSize = (AFS_LWP_MINSTACKSIZE>atoi(value)?
187                                 AFS_LWP_MINSTACKSIZE : atoi(value));
188    
189     *pid = pcb;
190
191     return LWP_SUCCESS;
192 }
193
194 /* LWP_CreateProcess - create a new fiber and start executing it.
195  *
196  * Arguments:
197  *      funP - start function
198  *      stacksize - size of 
199  *      priority - LWP priority
200  *      argP - initial parameter for start function
201  *      name - name of LWP
202  *
203  * Return:
204  *      pid - handle of created LWP
205  *      value:
206  *      0 - success
207  *      LWP_EINIT - error in intialization.
208  *
209  */
210 int LWP_CreateProcess(int (*funP)(), int stacksize, int priority, void *argP,
211                       char *name, PROCESS *pid)
212 {
213     PROCESS pcb;
214     
215     purge_dead_pcbs();
216
217     pcb = (PROCESS)malloc(sizeof(*pcb));
218     if (pcb == NULL)
219         return LWP_ENOMEM;
220     (void) memset((void*)pcb, 0, sizeof(*pcb));
221
222     /*
223      * on some systems (e.g. hpux), a minimum usable stack size has
224      * been discovered
225      */
226     if (stacksize < lwp_MinStackSize) {
227       stacksize = lwp_MinStackSize;
228     }
229     /* more stack size computations; keep track of for IOMGR */
230     if (lwp_MaxStackSeen < stacksize)
231         lwp_MaxStackSeen = stacksize;
232
233     pcb->fiber = CreateFiber(stacksize, Enter_LWP, pcb);
234     if (pcb->fiber == NULL) {
235         free((void*)pcb);
236         return LWP_EINIT;
237     }
238     Debug(0, ("Create: pcb=0x%p, funP=0x%p, argP=0x%p\n", pcb, funP, argP))
239     /* Fiber is now created, so fill in PCB */
240     Initialize_PCB(pcb, priority, stacksize, funP, argP, name);
241     Debug(10, ("Create: Insert 0x%p into runnable at priority %d\n", pcb, priority))
242     insert(pcb, &runnable[priority]);
243
244     LWPANCHOR.processcnt++;
245
246     /* And hand off execution. */
247     SwitchToFiber(pcb->fiber);
248
249     *pid = pcb;
250
251     return LWP_SUCCESS;
252 }
253
254 int LWP_DestroyProcess(PROCESS pid)
255 {
256     Debug(0, ("Entered Destroy_Process"))
257     if (!lwp_init)
258         return LWP_EINIT;
259
260     
261     if (lwp_cpptr != pid) {
262         Dispose_of_Dead_PCB(pid);
263     } else {
264         pid->status = DESTROYED;
265         move(pid, &runnable[pid->priority], &blocked);
266     }
267
268     Dispatcher();
269
270     return LWP_SUCCESS;
271 }
272
273 int LWP_QWait(void)
274 {
275     PROCESS tp;
276     (tp=lwp_cpptr) -> status = QWAITING;
277     lwp_remove(tp, &runnable[tp->priority]);
278     Dispatcher();
279     return LWP_SUCCESS;
280 }
281
282 int LWP_QSignal(PROCESS pid)
283 {
284     if (pid->status == QWAITING) {
285         pid->status = READY;
286         Debug(10, ("QSignal: Insert 0x%p into runnable at priority %d\n", pid, pid->priority))
287         insert(pid, &runnable[pid->priority]);
288         return LWP_SUCCESS;
289     }
290     else return LWP_ENOWAIT;
291 }
292
293 int LWP_CurrentProcess(PROCESS *pid)
294 {
295     Debug(0, ("Entered Current_Process"))
296     if (lwp_init) {
297             *pid = lwp_cpptr;
298             return LWP_SUCCESS;
299     } else
300         return LWP_EINIT;
301 }
302
303 PROCESS LWP_ThreadId()
304 {
305     Debug(0, ("Entered ThreadId"))
306     if (lwp_init)
307         return lwp_cpptr;
308     else
309         return (PROCESS) 0;
310 }
311
312 int LWP_DispatchProcess(void)           /* explicit voluntary preemption */
313 {
314     Debug(2, ("Entered Dispatch_Process"))
315     if (lwp_init) {
316         Dispatcher();
317         return LWP_SUCCESS;
318     } else
319         return LWP_EINIT;
320 }
321
322
323 #ifdef DEBUG
324 static void Dump_Processes(void)
325 {
326     if (lwp_init) {
327         int i;
328         for (i=0; i<MAX_PRIORITIES; i++)
329             for_all_elts(x, runnable[i], {
330                 printf("[Priority %d]\n", i);
331                 Dump_One_Process(x);
332             })
333         for_all_elts(x, blocked, { Dump_One_Process(x); })
334     } else
335         printf("***LWP: LWP support not initialized\n");
336 }
337 #endif
338
339
340 int LWP_GetProcessPriority(pid, priority)       /* returns process priority */
341     PROCESS pid;
342     int *priority;
343 {
344     Debug(0, ("Entered Get_Process_Priority"))
345     if (lwp_init) {
346         *priority = pid->priority;
347         return 0;
348     } else
349         return LWP_EINIT;
350 }
351
352 static int Internal_Signal(char *event)
353 {
354     int rc = LWP_ENOWAIT;
355     int i;
356
357     Debug(0, ("Entered Internal_Signal [event id 0x%p]", event))
358     if (!lwp_init) return LWP_EINIT;
359     if (event == NULL) return LWP_EBADEVENT;
360     for_all_elts(temp, blocked, {
361         if (temp->status == WAITING)
362             for (i=0; i < temp->eventcnt; i++) {
363                 if (temp -> eventlist[i] == event) {
364                     temp -> eventlist[i] = NULL;
365                     rc = LWP_SUCCESS;
366                     Debug(0, ("Signal satisfied for PCB 0x%p", temp))
367                     if (--temp->waitcnt == 0) {
368                         temp -> status = READY;
369                         temp -> wakevent = i+1;
370                         move(temp, &blocked, &runnable[temp->priority]);
371                         break;
372                     }
373                 }
374             }
375     })
376     return rc;
377 }
378
379 /* signal the occurence of an event */
380 int LWP_INTERNALSIGNAL(void *event, int yield)
381 {
382     Debug(2, ("Entered LWP_SignalProcess"))
383     if (lwp_init) {
384         int rc;
385         rc = Internal_Signal(event);
386         if (yield)
387             Dispatcher();
388         return rc;
389     } else
390         return LWP_EINIT;
391 }
392
393 int LWP_TerminateProcessSupport()       /* terminate all LWP support */
394 {
395     int i;
396
397     Debug(0, ("Entered Terminate_Process_Support"))
398     if (lwp_init == NULL) return LWP_EINIT;
399     if (lwp_cpptr != LWPANCHOR.outerpid)
400         Abort_LWP("Terminate_Process_Support invoked from wrong process!");
401     /* Is this safe??? @@@ */
402     for (i=0; i<MAX_PRIORITIES; i++)
403         for_all_elts(cur, runnable[i], { Free_PCB(cur); })
404     for_all_elts(cur, blocked, { Free_PCB(cur); })
405     free(lwp_init);
406     lwp_init = NULL;
407     return LWP_SUCCESS;
408 }
409
410 int LWP_MwaitProcess(int wcount, void **evlist)
411 {
412     int ecount, i;
413
414     Debug(0, ("Entered Mwait_Process [waitcnt = %d]", wcount))
415     if (evlist == NULL) {
416         Dispatcher();
417         return LWP_EBADCOUNT;
418     }
419
420     for (ecount = 0; evlist[ecount] != NULL; ecount++) ;
421
422     if (ecount == 0) {
423         Dispatcher();
424         return LWP_EBADCOUNT;
425     }
426
427     if (!lwp_init)
428         return LWP_EINIT;
429
430     if (wcount>ecount || wcount<0) {
431         Dispatcher();
432         return LWP_EBADCOUNT;
433     }
434     if (ecount > lwp_cpptr->eventlistsize) {
435
436         void **save_eventlist = lwp_cpptr->eventlist;
437         lwp_cpptr->eventlist = (char **)realloc(lwp_cpptr->eventlist,
438                                                 ecount*sizeof(char *));
439         if (lwp_cpptr->eventlist == NULL) {
440             lwp_cpptr->eventlist = save_eventlist;
441             Dispatcher();
442             return LWP_ENOMEM;
443         }
444         lwp_cpptr->eventlistsize = ecount;
445     }
446     for (i=0; i<ecount; i++) lwp_cpptr -> eventlist[i] = evlist[i];
447     if (wcount > 0) {
448         lwp_cpptr->status = WAITING;
449
450         move(lwp_cpptr, &runnable[lwp_cpptr->priority], &blocked);
451
452     }
453     lwp_cpptr->wakevent = 0;
454     lwp_cpptr->waitcnt = wcount;
455     lwp_cpptr->eventcnt = ecount;
456
457     Dispatcher();
458
459     return LWP_SUCCESS;
460 }
461
462 int LWP_WaitProcess(void *event)                /* wait on a single event */
463 {
464     void *tempev[2];
465
466     Debug(2, ("Entered Wait_Process"))
467     if (event == NULL) return LWP_EBADEVENT;
468     tempev[0] = event;
469     tempev[1] = NULL;
470     return LWP_MwaitProcess(1, tempev);
471 }
472
473
474 /* Internal Support Routines */
475 static void Initialize_PCB(PROCESS pcb, int priority, int stacksize,
476                            int (*funP)(), void *argP, char *name)
477 {
478     int i = 0;
479
480     Debug(4, ("Entered Initialize_PCB"))
481     if (name != NULL)
482         while (((pcb->name[i] = name[i]) != '\0') && (i < 31)) i++;
483     pcb->name[31] = '\0';
484     pcb->priority = priority;
485     pcb->stacksize = stacksize;
486     pcb->funP = funP;
487     pcb->argP = argP;
488     pcb->status = READY;
489     pcb->eventlist = (void**)malloc(EVINITSIZE*sizeof(void*));
490     pcb->eventlistsize =  pcb->eventlist ? EVINITSIZE : 0;
491     pcb->eventcnt = 0;
492     pcb->wakevent = 0;
493     pcb->waitcnt = 0;
494     pcb->next = pcb->prev = (PROCESS)NULL;
495     pcb->iomgrRequest = (struct IoRequest*)NULL;
496     pcb->index = lwp_nextindex ++;
497 }
498
499
500 static VOID WINAPI Enter_LWP(PVOID fiberData)
501 {
502     PROCESS pcb = (PROCESS)fiberData;
503
504 /* next lines are new..... */
505     lwp_cpptr = pcb;
506
507     Debug(2, ("Enter_LWP: pcb=0x%p, funP=0x%p, argP=0x%p\n", pcb, pcb->funP, pcb->argP))
508
509     (*pcb->funP)(pcb->argP);
510
511     LWP_DestroyProcess(pcb);
512     Dispatcher();
513 }
514
515 static void Abort_LWP(char *msg)
516 {
517     Debug(0, ("Entered Abort_LWP"))
518     printf("***LWP: %s\n",msg);
519 #ifdef DEBUG
520     printf("***LWP: Abort --- dumping PCBs ...\n");
521     Dump_Processes();
522 #endif
523     Exit_LWP();
524 }
525
526 #ifdef DEBUG
527 static void Dump_One_Process(PROCESS pid)
528 {
529     int i;
530
531     printf("***LWP: Process Control Block at 0x%p\n", pid);
532     printf("***LWP: Name: %s\n", pid->name);
533     if (pid->funP != NULL)
534         printf("***LWP: Initial entry point: 0x%p\n", pid->funP);
535     switch (pid->status) {
536         case READY:     printf("READY");     break;
537         case WAITING:   printf("WAITING");   break;
538         case DESTROYED: printf("DESTROYED"); break;
539         default:        printf("unknown");
540     }
541     putchar('\n');
542     printf("***LWP: Priority: %d \tInitial parameter: 0x%p\n",
543             pid->priority, pid->argP);
544     if (pid->stacksize != 0) {
545         printf("***LWP:  Stacksize: %d\n", pid->stacksize);
546     }
547     if (pid->eventcnt > 0) {
548         printf("***LWP: Number of events outstanding: %d\n", pid->waitcnt);
549         printf("***LWP: Event id list:");
550         for (i=0;i<pid->eventcnt;i++)
551             printf(" 0x%p", pid->eventlist[i]);
552         putchar('\n');
553     }
554     if (pid->wakevent>0)
555         printf("***LWP: Number of last wakeup event: %d\n", pid->wakevent);
556 }
557 #endif
558
559
560 static void purge_dead_pcbs(void)
561 {
562     for_all_elts(cur, blocked, { if (cur->status == DESTROYED) Dispose_of_Dead_PCB(cur); })
563 }
564
565 int LWP_TraceProcesses = 0;
566
567 static void Dispatcher(void)
568 {
569     int i;
570 #ifdef DEBUG
571     static int dispatch_count = 0;
572
573     if (LWP_TraceProcesses > 0) {
574         for (i=0; i<MAX_PRIORITIES; i++) {
575             printf("[Priority %d, runnable (%d):", i, runnable[i].count);
576             for_all_elts(p, runnable[i], {
577                 printf(" \"%s\"", p->name);
578             })
579             puts("]");
580         }
581         printf("[Blocked (%d):", blocked.count);
582         for_all_elts(p, blocked, {
583             printf(" \"%s\"", p->name);
584         })
585         puts("]");
586     }
587 #endif
588     /* Move head of current runnable queue forward if current LWP is still
589      *in it.
590      */
591     if (lwp_cpptr != NULL && lwp_cpptr == runnable[lwp_cpptr->priority].head)
592         runnable[lwp_cpptr->priority].head =
593             runnable[lwp_cpptr->priority].head -> next;
594     /* Find highest priority with runnable processes. */
595     for (i=MAX_PRIORITIES-1; i>=0; i--)
596         if (runnable[i].head != NULL) break;
597
598     if (i < 0) Abort_LWP("No READY processes");
599
600 #ifdef DEBUG
601     if (LWP_TraceProcesses > 0)
602         printf("Dispatch %d [PCB at 0x%p] \"%s\"\n", ++dispatch_count,
603                runnable[i].head, runnable[i].head->name);
604 #endif
605
606     lwp_cpptr = runnable[i].head;
607     SwitchToFiber(lwp_cpptr->fiber);
608 }
609
610 void lwp_abort(void)
611 {
612     afs_NTAbort();
613 }
614
615 static void Exit_LWP(void)
616 {
617
618     lwp_abort();
619 }
620
621 static void Delete_PCB(PROCESS pid)
622 {
623     Debug(4, ("Entered Delete_PCB"))
624     lwp_remove(pid, (pid->status==WAITING || pid->status==DESTROYED
625                      ? &blocked
626                      : &runnable[pid->priority]));
627     LWPANCHOR.processcnt--;
628 }
629
630
631 static void Free_PCB(PROCESS pid)
632 {
633     Debug(4, ("Entered Free_PCB"))
634     if (pid->fiber != NULL) {
635         DeleteFiber(pid->fiber);
636     }
637     if (pid->eventlist != NULL)  free((void*)pid->eventlist);
638     free((void*)pid);
639 }
640
641 static void Dispose_of_Dead_PCB(PROCESS cur)
642 {
643     Debug(4, ("Entered Dispose_of_Dead_PCB"))
644     Delete_PCB(cur);
645     Free_PCB(cur);
646 }
647
648 /* Queue manipulation. */
649 static void lwp_remove(PROCESS p, struct QUEUE *q)
650 {
651     /* Special test for only element on queue */
652     if (q->count == 1)
653         q -> head = NULL;
654     else {
655         /* Not only element, do normal remove */
656         p -> next -> prev = p -> prev;
657         p -> prev -> next = p -> next;
658     }
659     /* See if head pointing to this element */
660     if (q->head == p) q -> head = p -> next;
661     q->count--;
662     p -> next = p -> prev = NULL;
663 }
664
665 static void insert(PROCESS p, struct QUEUE *q)
666 {
667     if (q->head == NULL) {      /* Queue is empty */
668         q -> head = p;
669         p -> next = p -> prev = p;
670     } else {                    /* Regular insert */
671         p -> prev = q -> head -> prev;
672         q -> head -> prev -> next = p;
673         q -> head -> prev = p;
674         p -> next = q -> head;
675     }
676     q->count++;
677 }
678
679 static void move(PROCESS p, struct QUEUE *from, struct QUEUE *to)
680 {
681     lwp_remove(p, from);
682     insert(p, to);
683 }
684 #endif /* AFS_NT40_ENV */