/* 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_