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