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