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