/* begincopyright Copyright (c) 1988 Xerox Corporation. All rights reserved. Use and copying of this software and preparation of derivative works based upon this software are permitted. Any distribution of this software or derivative works must comply with all applicable United States export control laws. This software is made available AS IS, and Xerox Corporation makes no warranty about the software, its performance or its conformity to any specification. Any person obtaining a copy of this software is requested to send their name and post office or electronic mail address to: PCR Coordinator Xerox PARC 3333 Coyote Hill Rd. Palo Alto, CA 94304 endcopyright */ /* * ThreadsPrivate.h * * Demers, February 18, 1992 2:09:28 pm PST */ #ifndef ←XR←THREADS←PRIVATE← #define ←XR←THREADS←PRIVATE← 1 #ifndef ←XR←BASIC←TYPES← #include "BasicTypes.h" #endif #ifndef ←XR←THREADS←INLINES← # include "ThreadsInlines.h" #endif #ifndef ←XR←THREADS← #include "Threads.h" #endif #ifndef ←XR←THREADS←BACKDOOR← #include "ThreadsBackdoor.h" #endif #define XR←DEBUG←METALOCK 0 /* * Virtual Processor primitives * * Implementations are processor and O/S dependent. */ /* Spinning */ extern void XR←SpinStep (/* usec */); /* One step of spin loop. Spin for specified number of usec. Doesn't attempt a process switch. */ /* Lock Bits */ #define XR←LOCK←BIT←UNLOCKED ((XR←LockBit)(0)) extern bool XR←TestAndSet (/* lockPtr */); /* XR←LockBit *lockPtr; */ extern bool XR←Test (/* lockPtr */); /* XR←LockBit *lockPtr; XR←LockBit is OPAQUE! */ #if XR←TEST # define XR←Test(lp) (*(lp) != XR←LOCK←BIT←UNLOCKED) #endif extern void XR←Unset (/* lockPtr */); /* LockBit *lockPtr; XR←LockBit is OPAQUE! */ #if XR←UNSET # define XR←Unset(lp) *(lp) = XR←LOCK←BIT←UNLOCKED #endif /* * PSL's */ extern void XR←InitPSL (/* psl */); /* XR←PSL psl; Initialize `psl'. Call only once per variable. */ #if XR←INIT←PSL # define XR←InitPSL(psl) \ XR←Unset(&((psl)->psl←locked)) #endif extern void XR←AcquirePSLInner (/* psl */); /* XR←PSL psl; Acquire `psl', assuming initial TestAndSet failed. */ extern void XR←AcquirePSL (/* psl */); /* XR←PSL psl; Acquire `psl'. */ #if XR←ACQUIRE←PSL # define XR←AcquirePSL(psl) \ if(XR←TestAndSet(&((psl)->psl←locked))) XR←AcquirePSLInner (psl); #endif extern void XR←ReleasePSL (/* psl */); /* XR←PSL psl; Release `psl'. */ #if XR←RELEASE←PSL # define XR←ReleasePSL(psl) \ XR←Unset(&((psl)->psl←locked)) #endif /* * GSL's */ extern void XR←InitGSL (/* gsl */); /* XR←GSL gsl; Initialize `gsl'. Call only once per variable. */ #if XR←INIT←GSL # define XR←InitGSL(gsl) \ { XR←Unset(&((gsl)->gsl←locked)); } #endif extern void XR←AcquireGSL (/* gsl */); /* XR←GSL gsl; Acquire `gsl'. */ extern void XR←AcquireGSLInner (/* gsl */); /* XR←GSL gsl; Inner routine to acquire `gsl'. */ #if XR←ACQUIRE←GSL # define XR←AcquireGSL(gsl) { \ if ( XR←TestAndSet(&((gsl)->gsl←locked)) ) \ XR←AcquireGSLInner(gsl); \ } #endif extern bool XR←TryGSL (/* gsl */); /* XR←GSL gsl; Try to acquire `gsl' without spinning. Return TRUE if successful, FALSE if unsuccessful. */ #if XR←TRY←GSL # define XR←TryGSL(gsl) (! XR←TestAndSet(&((gsl)->gsl←locked)) ) #endif extern void XR←ReleaseGSL (/* gsl */); /* XR←GSL gsl; Release `gsl'. */ #if XR←RELEASE←GSL # define XR←ReleaseGSL(gsl) { \ XR←Unset(&((gsl)->gsl←locked)); \ } #endif /* * Thread Queues */ extern void XR←InitTQ (/* tq */); /* XR←TQ tq; Initialize `tq'. Call only once per variable. */ #if XR←INIT←TQ # define XR←InitTQ(tq) { (tq)->tq←tail = NIL; (tq)->tq←schedCtlInfo = 0; } #endif extern void XR←AddHdTQ (/* tq, t */); /* XR←TQ tq; XR←Thread t; Insert thread `t' at head (wrong end) of thread queue `tq'. */ extern void XR←AddTlTQ (/* tq, t */); /* XR←TQ tq; XR←Thread t; Insert thread `t' at tail of thread queue `tq'. */ extern bool XR←NonemptyTQ (/* tq */); /* XR←TQ tq; Test whether thread queue `tq' is nonempty. */ #if XR←NONEMPTY←TQ # define XR←NonemptyTQ(tq) ((tq)->tq←tail != NIL) #endif extern XR←Thread XR←RemHdTQ (/* tq */); /* XR←TQ tq; Remove and return element at head of thread queue `tq'. The queue must be nonempty. */ extern XR←Thread XR←GetHdTQ(/* XR←TQ tq */); /* Return the element at head of queue. The queue must be nonempty. */ #if XR←GET←HD←TQ # define XR←GetHdTQ(tq) ((tq)->tq←tail->t←next) #endif extern void XR←RemTQ (/* t */); /* XR←Thread t; Remove thread `t' from any thread queue containing it. */ /* * Wait Queues */ extern void XR←InitWQ (/* wq */); /* XR←WQ wq; Initialize `wq'. Call only once per variable. */ #if XR←INIT←WQ # define XR←InitWQ(wq) \ { XR←InitGSL(&((wq)->wq←gsl)); (wq)->wq←tail = NIL; } #endif extern void XR←AddHdWQ (/* wq, p */); /* XR←WQ wq; XR←Proc p; Insert process `p' at head (wrong end) of queue `wq'. */ extern void XR←AddTlWQ (/* wq, p */); /* XR←WQ wq; XR←Proc p; Insert process `p' at tail of queue `wq'. */ extern bool XR←NonemptyWQ (/* wq */); /* XR←WQ wq; Test whether process `wq' is nonempty. */ #if XR←NONEMPTY←WQ # define XR←NonemptyWQ(wq) ((wq)->wq←tail != NIL) #endif extern XR←Thread XR←RemHdWQ (/* wq */); /* XR←WQ wq; Remove and return element at head of queue `wq'. The queue must be nonempty. */ extern void XR←RemWQ (/* p */); /* XR←Proc p; Remove process `p' from its wait queue, if it's on one. */ /* * Thread Switching */ extern XR←Thread XR←AllocThread (/* doWait */); /* bool doWait; Return an XR←Thread with allocated stack segment. May return NIL if none available and `doWait' is false. Caller must NOT hold metalock. */ extern void XR←Jump3 (/* arg, proc, sp */); /* unsigned arg; void (*proc)(); XR←Pointer sp; Call `proc(arg)' after setting stack pointer to `sp'. Return from `proc' is not allowed. */ extern void XR←Jump5 (/* arg, proc, sp, opc, osp */); /* unsigned arg; void (*proc)(); XR←Pointer sp, opc, osp; Call `proc(arg)' after setting stack pointer to `sp'. Fake up frame to look like caller was (opc, osp) context (this is for the benefit of Unix(tm) dbx). Return from `proc' is not allowed. */ extern void XR←JmpToHandler(/* swStat, opc, osp */); /* XR←SwStat swStat; XR←Pointer opc, osp; Invoke handler of current thread, If opc is non-NIL, fake up frame to look like caller was (opc, osp) context (for the benefit of Unix(tm) dbx). */ extern void XR←ForkJumpProc(/* forkProc */); /* XR←MesaProc forkProc; The jumpProc for doing a FORK. */ extern void XR←Exit (); /* Terminate the current thread. May be called from a handler. */ extern void XR←PauseNotAbortable(/* ticks */); /* XR←Ticks ticks; Pause, with aborts disabled. */ extern XR←VPE XR←NakedNotifyInner(/* XR←CV cv */); /* Internals of NakedNotify. Result, if non-NIL, is a vpe that ought to resched because of a thread made ready by the notify; the caller may invoke RequestResched to make this happen. */ extern XR←VPE XR←SetThreadReady (/* t */); /* XR←Thread t; Set t ready. Caller has already removed t from t->t←wq. ... but t may still be on t->t←tq (i.e. timer queue). Caller must NOT hold metalock. Guaranteed not to call XR←Switch. Result is hint about which VProcessor should switch to the newly-runnable thread, or NIL if no switch is indicated. Caller may invoke XR←Switch or RequestResched to force the switch to take place. */ #ifdef XR←DBX←KLUDGE /* bit OR'd into swStat to force examining a thread */ # define XR←EXAMINE←BIT 0x40000000 #endif extern void XR←Switch (/* t, nextState, swStat */); /* XR←Thread t; (* switch to p if possible *) XR←SStat nextstate; (* next state of currThread *) XR←SwStat swStat; (* called from handler? *) Thread switch. Switch to t if "possible" -- t != NIL and t-> is in READY state. Otherwise choose "best" (highest priority READY) t to switch to. Precondition: If next←state is RUN this is a client error. If next←state is READY ... fine. If next←state is WAIT←ML then XR←currThread must already be on XR←currThread->t←wq, and caller must hold its GSL. If next←state is WAIT←CV then XR←currThread must already be on XR←currThread->t←wq, and caller must hold its GSL. A check for abort is done before putting the thread to sleep. The `swStat' parameter indicates whether the caller is a thread or a (UNIX) interrupt handler, and which stack it runs on. Caller does NOT hold metalock. */ extern void XR←DisposeOfCurrentThread (/* nextState, swStat */); /* XR←SStat nextState; (* next state of currThread *) XR←SwStat swStat; (* from handler? *) Dispose of current thread by putting it on queue corresponding to `nextState'. Acquires the metalock: caller must not already hold it, and will hold it on return. */ extern void XR←DispatchNewThread (/* t, swStat */); /* XR←Thread t; (* whom to yield to (hint) *) XR←SwStat swStat; (* from handler? *) Dispatch a thread. Run `t' if possible, otherwise choose best available. Okay if `t' is NIL. Caller must hold metalock. No return. */ extern void XR←FinishSwitch (/* swStat, jumping */); /* XR←SwStat swStat; (* handler? on handler stack? *) bool jumping; (* not returning thru t←resume *) Final cleanup part of thread switch. Called with metalock held, releases it. Does not check XR←vpe->vpe←doSwitch. */ extern void XR←ForceStackToMemory(/* XR←JumBuf jb */); /* Force stack to memory and save context in *jb by XR←setjmp/XR←longjmp pair. */ extern void XR←Jumpee(/* swStat */); /* XR←SwStat swStat; (* handler? on handler stack? *) This procedure is entered by XR←Jump with the metalock held. It invokes the t←jumpProc. */ extern void XR←RequestResched (/* vpe */); /* XR←VPE vpe; Ask the specified vprocessor to reschedule. If vpe == NIL, request reschedule of all vprocessors. */ extern void XR←CheckReschedRequest (); /* Check if there's a deferred reschedule request and if so honor it. Caller must NOT be int handler. */ #if XR←CHECK←RESCHED←REQUEST # define XR←CheckReschedRequest() \ while( XR←vpe->vpe←doSwitch ) { \ XR←Switch(NIL, XR←SSTAT←READY, XR←SWSTAT←THREAD); } #endif extern void XR←AddTimeout (/* t */); /* XR←Thread t; Add t to global timeout queue for time `t'->t←when. Caller must hold metalock. (Assume t is already on queue of the condition variable). */ extern void XR←RemTimeout (/* t */); /* XR←Thread t; Remove t from timeout queue, if it's there. Caller must hold metalock. */ extern bool XR←DoTimeouts (); /* Processing after alarm has been received. Called by alarm handler, or by first invocation of switch after the alarm, (the switcher notices XR←sysArea->sa←doTimeouts has been set). May fail to set all timed out threads ready because of failure to acquire a GSL. Returns TRUE iff there are some remaining timed-out threads that weren't set ready. Caller must hold metalock. */ /* * Acquire / Release system locks */ extern void XR←LockMeta (/* char * whence */); #if XR←LOCK←META && (!XR←DEBUG←METALOCK) # define XR←LockMeta(whence) XR←AcquirePSL(&(XR←sysArea->sa←metaLock)); #endif extern bool XR←TryMeta (/* char * whence */); #if XR←TRY←META && (!XR←DEBUG←METALOCK) # define XR←TryMeta(whence) \ (!(XR←TestAndSet(&(XR←sysArea->sa←metaLock.psl←locked)))) #endif extern void XR←UnlockMeta (/* */); #if XR←UNLOCK←META && (!XR←DEBUG←METALOCK) # define XR←UnlockMeta() XR←ReleasePSL(&(XR←sysArea->sa←metaLock)); #endif #if XR←DEBUG←METALOCK /* metaLock tracing -- significant only if the above are not inlined */ # define sa←metaLockerPID sa←clientData[XR←MAX←SA←CLIENT←DATA-1] # define sa←metaLockerName sa←clientData[XR←MAX←SA←CLIENT←DATA-2] # define sa←metaLockerThread sa←clientData[XR←MAX←SA←CLIENT←DATA-3] #endif /* XR←DEBUG←METALOCK */ #endif ←XR←THREADS←PRIVATE←