text: /*
   * Copyright 2000, International Business Machines Corporation and others.
   * All Rights Reserved.
   *
   * This software has been released under the terms of the IBM Public
   * License.  For details, see the LICENSE file in the top-level source
   * directory or online at http://www.openafs.org/dl/license10.html
   */
  
  /* For NT, LWP is implemented on top of fibers. The design attempts to
   * follow the current LWP implementation so that we are not using 2 LWP
   * models. The parts of the Unix LWP model that are not actually used in
   * AFS have not been implemented at all. With the exception of preemption,
   * adding those parts to this NT base would be a matter of adding routines.
   *
   * required lib is kernel32.lib, header is winbase.h
   */
  
  #include <afsconfig.h>
  #include <afs/param.h>
  
  #include <roken.h>
  
  #ifdef AFS_NT40_ENV
  #include <afs/opr.h>
  #include <afs/afsutil.h>
  #include "lwp.h"
  
  
  static struct lwp_ctl *lwp_init;
  #define LWPANCHOR (*lwp_init)
  
  PROCESS lwp_cpptr;
  
  char lwp_debug = 0;
  
  #define  READY		2
  #define  WAITING		3
  #define  DESTROYED	4
  #define QWAITING		5
  
  /* Debugging macro */
  #ifdef DEBUG
  #define Debug(level, msg)\
  	 if (lwp_debug && lwp_debug >= level) {\
  	     printf("***LWP (0x%p): ", lwp_cpptr);\
  	     printf msg;\
  	     putchar('\n');\
  	 }
  
  #else
  #define Debug(level, msg)
  #endif
  
  
  /* Forward declarations */
  static void Dispatcher(void);
  static void Abort_LWP(char *msg);
  static VOID WINAPI Enter_LWP(PVOID fiberData);
  static void Initialize_PCB(PROCESS pcb, int priority, int stacksize,
  			   int (*funP)(), void *argP, char *name);
  
  static void Dispose_of_Dead_PCB();
  static void Free_PCB();
  static int Internal_Signal();
  static void purge_dead_pcbs(void);
  
  static void lwp_remove(PROCESS p, struct QUEUE *q);
  static void insert(PROCESS p, struct QUEUE *q);
  static void move(PROCESS p, struct QUEUE *from, struct QUEUE *to);
  
  /* biggest LWP stack created so far - used by IOMGR */
  int lwp_MaxStackSeen = 0;
  
  #ifdef DEBUG
  static void Dump_One_Process(PROCESS pid);
  static void Dump_Processes(void);
  #endif
  
  int lwp_nextindex;
  
  #define MAX_PRIORITIES	(LWP_MAX_PRIORITY+1)
  
  /* Invariant for runnable queues: The head of each queue points to the
   * currently running process if it is in that queue, or it points to the
   * next process in that queue that should run.
   */
  struct QUEUE {
      PROCESS	head;
      int		count;
  } runnable[MAX_PRIORITIES], blocked;
  
  /* Iterator macro */
  #define for_all_elts(var, q, body)\
  	{\
  	    PROCESS var, _NEXT_;\
  	    int _I_;\
  	    for (_I_=q.count, var = q.head; _I_>0; _I_--, var=_NEXT_) {\
  		_NEXT_ = var -> next;\
  		body\
  	    }\
  	}
  
  #ifdef AFS_WIN95_ENV
  
  LPVOID ConvertThreadToFiber(PROCESS x)
  {
  	return NULL;
  }
  LPVOID CreateFiber(DWORD x ,LPVOID y,PROCESS z)
  {
  	return NULL;
  }
  
  VOID SwitchToFiber(LPVOID p)
  {
  }
  
  VOID DeleteFiber(LPVOID p)
  {
  }
  #endif
  
  
  int lwp_MinStackSize = 0;
  
  /* LWP_InitializeProcessSupport - setup base support for fibers.
   *
   * Arguments:
   *	priority - priority of main thread.
   *
   * Returns:
   *	pid - return this process
   *	value:
   *	LWP_SUCCESS (else aborts)
   *
   */
  
  int LWP_InitializeProcessSupport(int priority, PROCESS *pid)
  {
      PROCESS pcb;
      int i;
      char* value;
  
      Debug(0, ("Entered LWP_InitializeProcessSupport"))
      if (lwp_init != NULL) return LWP_SUCCESS;
  
      if (priority >= MAX_PRIORITIES) return LWP_EBADPRI;
  
      pcb = malloc(sizeof(*pcb));
      if (pcb == NULL)
  	Abort_LWP("Insufficient Storage to Initialize LWP PCB");
      (void) memset((void*)pcb, 0, sizeof(*pcb));
      pcb->fiber = ConvertThreadToFiber(pcb);
      if (pcb == NULL)
  	Abort_LWP("Cannot convert main thread to LWP fiber");
  
      lwp_init = malloc(sizeof(struct lwp_ctl));
      if (lwp_init == NULL)
  	Abort_LWP("Insufficient Storage to Initialize LWP CTL");
      (void) memset((void*)lwp_init, 0, sizeof(struct lwp_ctl));
  
      for (i=0; i<MAX_PRIORITIES; i++) {
  	runnable[i].head = NULL;
  	runnable[i].count = 0;
      }
      blocked.head = NULL;
      blocked.count = 0;
  
      LWPANCHOR.processcnt = 1;
      LWPANCHOR.outerpid = pcb;
      LWPANCHOR.outersp = NULL;
  
  
      Initialize_PCB(pcb, priority, 0, NULL, NULL,
  		"Main Process [created by LWP]");
  
      lwp_cpptr = pcb;
      Debug(10, ("Init: Insert 0x%p into runnable at priority %d\n", pcb, priority))
      insert(pcb, &runnable[priority]);
  
      if ( ( value = getenv("AFS_LWP_STACK_SIZE")) == NULL )
  	lwp_MinStackSize = AFS_LWP_MINSTACKSIZE;
      else
  	lwp_MinStackSize = (AFS_LWP_MINSTACKSIZE>atoi(value)?
  				AFS_LWP_MINSTACKSIZE : atoi(value));
  
      *pid = pcb;
  
      return LWP_SUCCESS;
  }
  
  /* LWP_CreateProcess - create a new fiber and start executing it.
   *
   * Arguments:
   *	funP - start function
   *	stacksize - size of
   *	priority - LWP priority
   *	argP - initial parameter for start function
   *	name - name of LWP
   *
   * Return:
   *	pid - handle of created LWP
   *	value:
   *	0 - success
   *	LWP_EINIT - error in intialization.
   *
   */
  int LWP_CreateProcess(int (*funP)(), int stacksize, int priority, void *argP,
  		      char *name, PROCESS *pid)
  {
      PROCESS pcb;
  
      purge_dead_pcbs();
  
      pcb = malloc(sizeof(*pcb));
      if (pcb == NULL)
  	return LWP_ENOMEM;
      (void) memset((void*)pcb, 0, sizeof(*pcb));
  
      /*
       * on some systems (e.g. hpux), a minimum usable stack size has
       * been discovered
       */
      if (stacksize < lwp_MinStackSize) {
        stacksize = lwp_MinStackSize;
      }
      /* more stack size computations; keep track of for IOMGR */
      if (lwp_MaxStackSeen < stacksize)
  	lwp_MaxStackSeen = stacksize;
  
      pcb->fiber = CreateFiber(stacksize, Enter_LWP, pcb);
      if (pcb->fiber == NULL) {
  	free(pcb);
  	return LWP_EINIT;
      }
      Debug(0, ("Create: pcb=0x%p, funP=0x%p, argP=0x%p\n", pcb, funP, argP))
      /* Fiber is now created, so fill in PCB */
      Initialize_PCB(pcb, priority, stacksize, funP, argP, name);
      Debug(10, ("Create: Insert 0x%p into runnable at priority %d\n", pcb, priority))
      insert(pcb, &runnable[priority]);
  
      LWPANCHOR.processcnt++;
  
      /* And hand off execution. */
      SwitchToFiber(pcb->fiber);
  
      *pid = pcb;
  
      return LWP_SUCCESS;
  }
  
  int LWP_DestroyProcess(PROCESS pid)
  {
      Debug(0, ("Entered Destroy_Process"))
      if (!lwp_init)
  	return LWP_EINIT;
  
  
      if (lwp_cpptr != pid) {
  	Dispose_of_Dead_PCB(pid);
      } else {
  	pid->status = DESTROYED;
  	move(pid, &runnable[pid->priority], &blocked);
      }
  
      Dispatcher();
  
      return LWP_SUCCESS;
  }
  
  int LWP_QWait(void)
  {
      PROCESS tp;
      (tp=lwp_cpptr) -> status = QWAITING;
      lwp_remove(tp, &runnable[tp->priority]);
      Dispatcher();
      return LWP_SUCCESS;
  }
  
  int LWP_QSignal(PROCESS pid)
  {
      if (pid->status == QWAITING) {
  	pid->status = READY;
  	Debug(10, ("QSignal: Insert 0x%p into runnable at priority %d\n", pid, pid->priority))
  	insert(pid, &runnable[pid->priority]);
  	return LWP_SUCCESS;
      }
      else return LWP_ENOWAIT;
  }
  
  int LWP_CurrentProcess(PROCESS *pid)
  {
      Debug(0, ("Entered Current_Process"))
      if (lwp_init) {
  	    *pid = lwp_cpptr;
  	    return LWP_SUCCESS;
      } else
  	return LWP_EINIT;
  }
  
  PROCESS LWP_ThreadId()
  {
      Debug(0, ("Entered ThreadId"))
      if (lwp_init)
          return lwp_cpptr;
      else
          return (PROCESS) 0;
  }
  
  int LWP_DispatchProcess(void)		/* explicit voluntary preemption */
  {
      Debug(2, ("Entered Dispatch_Process"))
      if (lwp_init) {
  	Dispatcher();
  	return LWP_SUCCESS;
      } else
  	return LWP_EINIT;
  }
  
  
  #ifdef DEBUG
  static void Dump_Processes(void)
  {
      if (lwp_init) {
  	int i;
  	for (i=0; i<MAX_PRIORITIES; i++)
  	    for_all_elts(x, runnable[i], {
  		printf("[Priority %d]\n", i);
  		Dump_One_Process(x);
  	    })
  	for_all_elts(x, blocked, { Dump_One_Process(x); })
      } else
  	printf("***LWP: LWP support not initialized\n");
  }
  #endif
  
  
  int LWP_GetProcessPriority(pid, priority)	/* returns process priority */
      PROCESS pid;
      int *priority;
  {
      Debug(0, ("Entered Get_Process_Priority"))
      if (lwp_init) {
  	*priority = pid->priority;
  	return 0;
      } else
  	return LWP_EINIT;
  }
  
  static int Internal_Signal(char *event)
  {
      int rc = LWP_ENOWAIT;
      int i;
  
      Debug(0, ("Entered Internal_Signal [event id 0x%p]", event))
      if (!lwp_init) return LWP_EINIT;
      if (event == NULL) return LWP_EBADEVENT;
      for_all_elts(temp, blocked, {
  	if (temp->status == WAITING)
  	    for (i=0; i < temp->eventcnt; i++) {
  		if (temp -> eventlist[i] == event) {
  		    temp -> eventlist[i] = NULL;
  		    rc = LWP_SUCCESS;
  		    Debug(0, ("Signal satisfied for PCB 0x%p", temp))
  		    if (--temp->waitcnt == 0) {
  			temp -> status = READY;
  			temp -> wakevent = i+1;
  			move(temp, &blocked, &runnable[temp->priority]);
  			break;
  		    }
  		}
  	    }
      })
      return rc;
  }
  
  /* signal the occurence of an event */
  int LWP_INTERNALSIGNAL(void *event, int yield)
  {
      Debug(2, ("Entered LWP_SignalProcess"))
      if (lwp_init) {
  	int rc;
  	rc = Internal_Signal(event);
  	if (yield)
  	    Dispatcher();
  	return rc;
      } else
  	return LWP_EINIT;
  }
  
  int LWP_TerminateProcessSupport()	/* terminate all LWP support */
  {
      int i;
  
      Debug(0, ("Entered Terminate_Process_Support"))
      if (lwp_init == NULL) return LWP_EINIT;
      if (lwp_cpptr != LWPANCHOR.outerpid)
  	Abort_LWP("Terminate_Process_Support invoked from wrong process!");
      /* Is this safe??? @@@ */
      for (i=0; i<MAX_PRIORITIES; i++)
  	for_all_elts(cur, runnable[i], { Free_PCB(cur); })
      for_all_elts(cur, blocked, { Free_PCB(cur); })
      free(lwp_init);
      lwp_init = NULL;
      return LWP_SUCCESS;
  }
  
  int LWP_MwaitProcess(int wcount, void **evlist)
  {
      int ecount, i;
  
      Debug(0, ("Entered Mwait_Process [waitcnt = %d]", wcount))
      if (evlist == NULL) {
  	Dispatcher();
  	return LWP_EBADCOUNT;
      }
  
      for (ecount = 0; evlist[ecount] != NULL; ecount++) ;
  
      if (ecount == 0) {
  	Dispatcher();
  	return LWP_EBADCOUNT;
      }
  
      if (!lwp_init)
  	return LWP_EINIT;
  
      if (wcount>ecount || wcount<0) {
  	Dispatcher();
  	return LWP_EBADCOUNT;
      }
      if (ecount > lwp_cpptr->eventlistsize) {
  
  	void **save_eventlist = lwp_cpptr->eventlist;
  	lwp_cpptr->eventlist = realloc(lwp_cpptr->eventlist,
  				       ecount*sizeof(char *));
  	if (lwp_cpptr->eventlist == NULL) {
  	    lwp_cpptr->eventlist = save_eventlist;
  	    Dispatcher();
  	    return LWP_ENOMEM;
  	}
  	lwp_cpptr->eventlistsize = ecount;
      }
      for (i=0; i<ecount; i++) lwp_cpptr -> eventlist[i] = evlist[i];
      if (wcount > 0) {
  	lwp_cpptr->status = WAITING;
  
  	move(lwp_cpptr, &runnable[lwp_cpptr->priority], &blocked);
  
      }
      lwp_cpptr->wakevent = 0;
      lwp_cpptr->waitcnt = wcount;
      lwp_cpptr->eventcnt = ecount;
  
      Dispatcher();
  
      return LWP_SUCCESS;
  }
  
  int LWP_WaitProcess(void *event)		/* wait on a single event */
  {
      void *tempev[2];
  
      Debug(2, ("Entered Wait_Process"))
      if (event == NULL) return LWP_EBADEVENT;
      tempev[0] = event;
      tempev[1] = NULL;
      return LWP_MwaitProcess(1, tempev);
  }
  
  
  /* Internal Support Routines */
  static void Initialize_PCB(PROCESS pcb, int priority, int stacksize,
  			   int (*funP)(), void *argP, char *name)
  {
      int i = 0;
  
      Debug(4, ("Entered Initialize_PCB"))
      if (name != NULL)
  	while (((pcb->name[i] = name[i]) != '\0') && (i < 31)) i++;
      pcb->name[31] = '\0';
      pcb->priority = priority;
      pcb->stacksize = stacksize;
      pcb->funP = funP;
      pcb->argP = argP;
      pcb->status = READY;
      pcb->eventlist = malloc(EVINITSIZE*sizeof(void*));
      pcb->eventlistsize =  pcb->eventlist ? EVINITSIZE : 0;
      pcb->eventcnt = 0;
      pcb->wakevent = 0;
      pcb->waitcnt = 0;
      pcb->next = pcb->prev = (PROCESS)NULL;
      pcb->iomgrRequest = NULL;
      pcb->index = lwp_nextindex ++;
  }
  
  
  static VOID WINAPI Enter_LWP(PVOID fiberData)
  {
      PROCESS pcb = (PROCESS)fiberData;
  
  /* next lines are new..... */
      lwp_cpptr = pcb;
  
      Debug(2, ("Enter_LWP: pcb=0x%p, funP=0x%p, argP=0x%p\n", pcb, pcb->funP, pcb->argP))
  
      (*pcb->funP)(pcb->argP);
  
      LWP_DestroyProcess(pcb);
      Dispatcher();
  }
  
  static void Abort_LWP(char *msg)
  {
      Debug(0, ("Entered Abort_LWP"))
      printf("***LWP: %s\n",msg);
  #ifdef DEBUG
      printf("***LWP: Abort --- dumping PCBs ...\n");
      Dump_Processes();
  #endif
      opr_abort();
  }
  
  #ifdef DEBUG
  static void Dump_One_Process(PROCESS pid)
  {
      int i;
  
      printf("***LWP: Process Control Block at 0x%p\n", pid);
      printf("***LWP: Name: %s\n", pid->name);
      if (pid->funP != NULL)
  	printf("***LWP: Initial entry point: 0x%p\n", pid->funP);
      switch (pid->status) {
  	case READY:	printf("READY");     break;
  	case WAITING:	printf("WAITING");   break;
  	case DESTROYED:	printf("DESTROYED"); break;
  	default:	printf("unknown");
      }
      putchar('\n');
      printf("***LWP: Priority: %d \tInitial parameter: 0x%p\n",
  	    pid->priority, pid->argP);
      if (pid->stacksize != 0) {
  	printf("***LWP:  Stacksize: %d\n", pid->stacksize);
      }
      if (pid->eventcnt > 0) {
  	printf("***LWP: Number of events outstanding: %d\n", pid->waitcnt);
  	printf("***LWP: Event id list:");
  	for (i=0;i<pid->eventcnt;i++)
  	    printf(" 0x%p", pid->eventlist[i]);
  	putchar('\n');
      }
      if (pid->wakevent>0)
  	printf("***LWP: Number of last wakeup event: %d\n", pid->wakevent);
  }
  #endif
  
  
  static void purge_dead_pcbs(void)
  {
      for_all_elts(cur, blocked, { if (cur->status == DESTROYED) Dispose_of_Dead_PCB(cur); })
  }
  
  int LWP_TraceProcesses = 0;
  
  static void Dispatcher(void)
  {
      int i;
  #ifdef DEBUG
      static int dispatch_count = 0;
  
      if (LWP_TraceProcesses > 0) {
  	for (i=0; i<MAX_PRIORITIES; i++) {
  	    printf("[Priority %d, runnable (%d):", i, runnable[i].count);
  	    for_all_elts(p, runnable[i], {
  		printf(" \"%s\"", p->name);
  	    })
  	    puts("]");
  	}
  	printf("[Blocked (%d):", blocked.count);
  	for_all_elts(p, blocked, {
  	    printf(" \"%s\"", p->name);
  	})
  	puts("]");
      }
  #endif
      /* Move head of current runnable queue forward if current LWP is still
       *in it.
       */
      if (lwp_cpptr != NULL && lwp_cpptr == runnable[lwp_cpptr->priority].head)
  	runnable[lwp_cpptr->priority].head =
  	    runnable[lwp_cpptr->priority].head -> next;
      /* Find highest priority with runnable processes. */
      for (i=MAX_PRIORITIES-1; i>=0; i--)
  	if (runnable[i].head != NULL) break;
  
      if (i < 0) Abort_LWP("No READY processes");
  
  #ifdef DEBUG
      if (LWP_TraceProcesses > 0)
  	printf("Dispatch %d [PCB at 0x%p] \"%s\"\n", ++dispatch_count,
  	       runnable[i].head, runnable[i].head->name);
  #endif
  
      lwp_cpptr = runnable[i].head;
      SwitchToFiber(lwp_cpptr->fiber);
  }
  
  static void Delete_PCB(PROCESS pid)
  {
      Debug(4, ("Entered Delete_PCB"))
      lwp_remove(pid, (pid->status==WAITING || pid->status==DESTROYED
  		     ? &blocked
  		     : &runnable[pid->priority]));
      LWPANCHOR.processcnt--;
  }
  
  
  static void Free_PCB(PROCESS pid)
  {
      Debug(4, ("Entered Free_PCB"))
      if (pid->fiber != NULL) {
  	DeleteFiber(pid->fiber);
      }
      if (pid->eventlist != NULL)  free(pid->eventlist);
      free(pid);
  }
  
  static void Dispose_of_Dead_PCB(PROCESS cur)
  {
      Debug(4, ("Entered Dispose_of_Dead_PCB"))
      Delete_PCB(cur);
      Free_PCB(cur);
  }
  
  /* Queue manipulation. */
  static void lwp_remove(PROCESS p, struct QUEUE *q)
  {
      /* Special test for only element on queue */
      if (q->count == 1)
  	q -> head = NULL;
      else {
  	/* Not only element, do normal remove */
  	p -> next -> prev = p -> prev;
  	p -> prev -> next = p -> next;
      }
      /* See if head pointing to this element */
      if (q->head == p) q -> head = p -> next;
      q->count--;
      p -> next = p -> prev = NULL;
  }
  
  static void insert(PROCESS p, struct QUEUE *q)
  {
      if (q->head == NULL) {	/* Queue is empty */
  	q -> head = p;
  	p -> next = p -> prev = p;
      } else {			/* Regular insert */
  	p -> prev = q -> head -> prev;
  	q -> head -> prev -> next = p;
  	q -> head -> prev = p;
  	p -> next = q -> head;
      }
      q->count++;
  }
  
  static void move(PROCESS p, struct QUEUE *from, struct QUEUE *to)
  {
      lwp_remove(p, from);
      insert(p, to);
  }
  #endif /* AFS_NT40_ENV */
  