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