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