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

/*
 * Threads.h
 *
 *
 * Basic public type definitions
 *   for Xerox Runtime threads package.
 *
 * Demers, January 25, 1991 4:24:55 pm PST
 */


#ifndef	←XR←THREADS←
#define ←XR←THREADS← 1


#ifndef ←XR←BASIC←TYPES←
#   include "BasicTypes.h"
#endif

#ifndef ←XR←THREADS←INLINES←
#   include "ThreadsInlines.h"
#endif

#ifndef ←XR←THREADS←SHARED←MEM←
#   include "ThreadsSharedMem.h"
#endif

/*
 * Exported to CedarExtra.h
 *
 *   XR←MonitorEntry
 *   XR←MonitorExit
 *   XR←WaitCV
 *   XR←Notify
 *   XR←Broadcast
 *   XR←Fork
 *   XR←JoinCT
 */

/*
 * Exported thread creation interceptor
 *
 *   XR←RegisterThreadStartProc
 */

/*
 * Exported signal/error stuff
 *
 *   XR←RegisterHandler
 *
 *   XR←CallHandler
 *   XR←RestartHandlee
 *
 *   XR←GetNextHandlee
 *   XR←GetHandlerStackOf
 *   XR←GetHandlerStack
 *   XR←SetHandlerStack
 *
 *   XR←CallDebugger
 *
 *   XR←Exit
 */

/*
 * Exported property stuff
 *
 *   XR←GetThreadProperty
 *   XR←GetThreadInheritedProperty
 *   XR←SetThreadProperty
 *   XR←SetThreadInheritedProperty
 *
 *   XR←GetPerThreadDataID
 *   XR←GetPerThreadDataAddress
 */

/*
 * Exported to Process
 *
 *   XR←InitializeMonitor
 *   XR←InitializeCondition
 *
 *   XR←MsecToTicks
 *   XR←TicksToMsec
 *
 *   XR←SetTimeout
 *   XR←DisableTimeout
 *
 *   XR←PauseAbortable
 *   XR←AbortPending
 *   XR←CancelAbort
 *
 *   XR←DetachCT
 *   XR←GetCurrent
 *   XR←SetPriority
 *   XR←GetPriority
 *   XR←AbortCT
 *   XR←DisableAborts
 *   XR←EnableAborts
 *   XR←Yield
 *   XR←ValidateCT
 */

/*
 * Exported for use by the brave:
 *
 *   XR←ExitWorld
 *   XR←KillWorld
 */

/*
 * Exported for use by Michael Plass:
 *
 *   XR←RegisterSwitchCallback
 */


/*
 * IMPORTANT NOTE:
 *
 *   compiler and other runtime support assumes every data structure
 *   can be initialized to zeroes!
 */


#define XR←Thread	struct XR←ThreadRep *	/* breaks recursion in dcls */


/*
 * Time
 *
 * ... for cv timeouts, pausing, etc.
 */

typedef unsigned XR←Ticks;

#define XR←TICKS←PER←SECOND	20
#define XR←MSECS←PER←TICK	(1000/XR←TICKS←PER←SECOND)
#define XR←USECS←PER←TICK	(1000000/XR←TICKS←PER←SECOND)

#define XR←WAIT←FOREVER ((XR←Ticks) 0)
#define XR←END←OF←TIME	((XR←Ticks)(0xffffffff))

extern XR←Ticks		XR←ticksPerSecond;
extern unsigned		XR←msecsPerTick;
extern unsigned		XR←usecPerTick;

extern XR←Ticks		XR←waitForever;
extern XR←Ticks		XR←endOfTime;


extern XR←Ticks
XR←TicksSinceBoot (/* */);
/*
    Return time in ticks since boot.
    Wraparound is ignored.
*/

extern XR←Ticks
XR←MsecToTicks (/* msec */);
/*
    unsigned msec;

    Convert milliseconds to ticks.
    Rounds up.
    Overflow if msec > LAST(unsigned)-1000.
*/
#if XR←MSEC←TO←TICKS
#   define XR←MsecToTicks(msec) \
	( ((msec)+(XR←MSECS←PER←TICK-1)) / XR←MSECS←PER←TICK )
#endif

extern unsigned
XR←TicksToMsec (/* ticks */);
/*
    XR←Ticks ticks;

    Convert ticks to milliseconds.
    Undefined if it would overflow.
*/
#if XR←TICKS←TO←MSEC
#   define XR←TicksToMsec(ticks)	((ticks)*XR←MSECS←PER←TICK)
#endif


extern bool
XR←TicksLater (/* a, b */);
/*
    XR←Ticks a, b;

    Return TRUE iff a is strictly later than b.
*/
#if XR←TICKS←LATER
#   define XR←TicksLater(a,b) ( ((int)((a)-(b))) > 0 )
#endif


/*
 * Spin Locks
 *
 * Spin locks come in two flavors:
 *
 *   Primitive spin locks (PSL's) are acquired by vprocessors.
 *   General spin locks (GSL's) are acquired by threads.
 */

typedef unsigned XR←LockBit;


/*
 * PSL's
 */


typedef struct XR←PSLRep {
    XR←LockBit psl←locked;		/* lock is held */
} * XR←PSL;


/*
 * GSL's
 */


typedef struct XR←GSLRep {
    XR←LockBit gsl←locked;		/* lock is held */
} * XR←GSL;

#define XR←NULL←GSL←REP	{ ((XR←LockBit)(0)) }


/*
 * Thread Queues
 *
 * Conceptually there's a thread queue for each <sstat,pri>.
 * Must hold metalock to manipulate these.
 */

typedef struct XR←TQRep {
    XR←Thread tq←tail;			/* -> tail of circ list by t←next */
    unsigned long tq←schedCtlInfo;	/* scheduler control per pri */
} * XR←TQ;

#define XR←NULL←TQ←REP	{ NIL, 0 }

/*
 * Wait Queues
 *
 * Every wait queue is protected by a GSL.
 * This GSL is overloaded to protect ml, cv structures, so it is
 *   acquired/released separately from wait queue operations.
 * Caller of wait queue operations must hold the GSL.
 */

typedef struct XR←WQRep {
    struct XR←GSLRep wq←gsl;
    XR←Thread wq←tail;		/* -> tail of circular list using t←wNext */
} * XR←WQ;

#define XR←NULL←WQ←REP	{ XR←NULL←GSL←REP, NIL }

/*
 * Monitor Locks
 */

typedef struct XR←MLRep {
    struct XR←WQRep ml←wq;		/* (:0) waiters queue */
    XR←Thread ml←holder;		/* holds mon←lock */
} *XR←ML;

#define ml←gsl	ml←wq.wq←gsl

#define XR←NULL←ML←REP	{ XR←NULL←WQ←REP, NIL }

/* for assembly language */
#if( defined(sparc) )
#else
#   undef XR←MONITOR←ENTRY
#   define XR←MONITOR←ENTRY	0
#   undef XR←MONITOR←EXIT
#   define XR←MONITOR←EXIT	0
#endif

extern void
XR←InitializeMonitor (/* ml */);
/*
    XR←ML ml;
    
    Initialize `ml'.
    Call only once, before handing out the object.
*/


extern void
XR←MonitorEntry (/* XR←ML ml */);

extern void
XR←MonitorEntryOutOfLine (/* XR←ML ml */);
/*
    Acquire `ml'.

    Defining XR←MONITOR←ENTRY replaces the C version of XR←MonitorEntry
      by an assembly language one, which may call XR←MonitorEntryOutOfLine.
*/


extern void
XR←MonitorExit (/* XR←ML ml */);

extern void
XR←MonitorExitOutOfLine (/* XR←ML ml */);
/*
    Release `ml', waking up next thread waiting on it.

    Defining XR←MONITOR←EXIT replaces the C version of XR←MonitorEntry
      by an assembly language one, which may call XR←MonitorExitOutOfLine.
*/


/*
 * Condition Variables (CV's)
 */

typedef struct XR←CVRep {
    struct XR←WQRep cv←wq;	/* (:0) waiting threads */
    XR←Ticks cv←timeout;	/* timeout for waiting threads */
    bool cv←abortable;		/* aborts enabled? */
    bool cv←wakeupWaiting;	/* a wakeup is waiting */
} *XR←CV;

#define cv←gsl	cv←wq.wq←gsl

#define XR←NULL←CV←REP { XR←NULL←WQ←REP, XR←WAIT←FOREVER, FALSE, FALSE }

extern void
XR←InitializeCondition (/* cv, timeout */);
/*
    XR←CV cv;
    XR←Ticks timeout;
    
    Initialize `cv'.  
    Call only once per variable.
*/


extern void
XR←SetTimeout (/* cv, timeout */);
/*
    XR←CV cv;
    XR←Ticks timeout;

    Set the timeout associated with `cv'.
    Has no effect on currently waiting threads.
*/

extern void
XR←DisableTimeout (/* cv */);
/*
    XR←CV cv;

    Disable timeouts for `cv'.
    Has no effect on currently waiting threads.
*/
#if XR←DISABLE←TIMEOUT
#   define XR←DisableTimeout(cv) (cv)->cv←timeout = XR←WAIT←FOREVER
#endif


extern XR←Ticks
XR←GetTimeout (/* cv */);
/*
    XR←CV cv;

    Get the timeout associated with `cv'.
*/
#if XR←GET←TIMEOUT
#   define XR←GetTimeout(cv) ((cv)->cv←timeout)
#endif


extern void
XR←EnableAborts (/* cv */);
/*
    XR←CV cv;

    Enable aborts on `cv'.
    No effect on threads currently waiting.
*/
#if XR←ENABLE←ABORTS
#   define XR←EnableAborts(cv) (cv)->cv←abortable = TRUE
#endif


extern void
XR←DisableAborts (/* cv */);
/*
    XR←CV cv;

    Disable aborts on `cv'.
    No effect on threads currently waiting.
*/
#if XR←DISABLE←ABORTS
#   define XR←DisableAborts(cv) (cv)->cv←abortable = FALSE
#endif


extern int
XR←WaitCV (/* cv, ml */);
/*
    XR←CV cv;
    XR←ML ml;
    
    Wait for `cv'.
    Assumes `ml' is held; releases it while waiting.
    If `ml' is NIL, that's okay - this is a wait on an icv.
    Return 0 if okay, -1 if ABORTED.
*/


extern void
XR←Notify (/* cv */);
/*
    XR←CV cv;

    Notify `cv'.
*/


extern void
XR←Broadcast (/* cv */);
/*
    XR←CV cv;

    Broadcast `cv'.
*/


extern void
XR←NakedNotify (/* cv */);
/*
    XR←CV cv;

    Naked notify of `cv'.
    This is intended to be used by an ioprocessor rather than a thread.
*/


/*
 * context save/restore
 *
 * The following stuff replaces Unix ←setjmp/←longjmp.
 * It's faster, and XR←longjmp works for switching away from a stack
 *   that you're subsequently going to switch back to.
 * The XR and Unix versions CANNOT BE USED INTERCHANGEABLY:
 *   ←setjmp/XR←longjmp or XR←setjmp/←longjmp pairs will blow up!
 *
 * Also here is some processor-dependent information about the format
 *   of a procedure frame (arguably this belongs somewhere else).
 *
 * We assume a frame is described by a <fp,sp> pair, and contains enough
 *   information to get the caller's resume pc and frame.
 *
 * On some processors (e.g. sparc) we don't need the fp, and it disappears
 *   from the macro-generated code.
 */

#if defined(sparc)

#   define JMPBUFSIZE	3	/* could be 2 ... */

#   define XR←PC←FROM←JMP←BUF(b) (((XR←Pointer *)(b->jb←data))[0])
#   define XR←FP←FROM←JMP←BUF(b) --> not used on sparc <--
#   define XR←SP←FROM←JMP←BUF(b) (((XR←Pointer *)(b->jb←data))[2])

#   define XR←SAVED←PC(t) ((XR←Pointer)(((XR←Thread)(t))->t←resume.jb←data[0]))
#   define XR←SAVED←FP(t) --> not used on sparc <--
#   define XR←SAVED←SP(t) ((XR←Pointer)(((XR←Thread)(t))->t←resume.jb←data[2]))

#   define XR←GET←SAVED←FRAME(t,fp,sp) \
	(sp) = XR←SAVED←SP(t)

#   define XR←OLD←PC←FROM←FRAME(fp,sp)	((XR←Pointer *)(sp))[15]
#   define XR←OLD←FP←FROM←FRAME(fp,sp)	--> not used on sparc <--
#   define XR←OLD←SP←FROM←FRAME(fp,sp)	((XR←Pointer *)(sp))[14]

/* following must work for aliased fp=ofp and for aliased sp=osp */
#   define XR←GET←OLD←FRAME←FROM←FRAME(fp,sp,ofp,osp) \
	(osp) = ((XR←Pointer *)(sp))[14]

#   define XR←RSA←BYTES		(16*4)	/* size of register save area */

#   define XR←CHECK←FRAME(fp,sp,t) XR←WITHIN←STACK(sp,t,XR←RSA←BYTES)

#else
    --> fix me for this cputype <--
#endif

typedef struct XR←JmpBufRep {
    unsigned jb←data[JMPBUFSIZE];
} *XR←JmpBuf;


extern int
XR←setjmp(/* jb */);
/*
    XR←JmpBuf jb;

    Save context in *jb.
    On a sparc this is very cheap.
*/
#pragma unknown←control←flow(XR←setjmp)


extern void
XR←longjmp(/* jb, rv */);
/*
    XR←jmpbuf jb;
    int rv;

    Return value rv to the context saved in *jb.
*/


/*
 * signal/error interface
 *
 *
 * A handler proc is invoked
 *   - as a result of a trap (in Unix, one of a collection of signals)
 *   - explicitly by XR←CallHandler(...).
 *
 * When it is invoked:
 *   It starts with an empty stack.
 *   It can call XR←RestartHandlee(...) instead of returning.
 *   It can terminate the thread by calling XR←Exit.
 *   IF the handlee is prepared to resume, the handler can resume it
 *     by just returning.
 *
 * A handler proc is registered with each thread, and is inherited across
 *   XR←Fork calls.
 *
 * It is okay to register NIL; a NIL handler just calls the debugger.
 */


/* Args to Unix signal handler -- see <sys/signal.h> */

typedef struct XR←TrapArgsRep {
    int ta←sig;
    int ta←code;
    struct sigcontext *ta←scp;
    XR←Pointer ta←addr;
} *XR←TrapArgs;

/* type code for handler */

typedef unsigned XR←HandlerWhich;
    /* 0 => not a handler call */
    /* low values are reserved for machine- or OS- traps */
    /* low-order bit => not resumable */

#   define XR←HANDLER←WHICH←NULL		0

#   define XR←HANDLER←WHICH←MK←FATAL(which)	((which) | 0x1)
#   define XR←HANDLER←WHICH←IS←FATAL(which)	((which) & 0x1)

#   define XR←HANDLER←WHICH←TRAP←UNIX		(0x2)
#   define XR←HANDLER←WHICH←TRAP←UNIX←FATAL \
		XR←HANDLER←WHICH←MK←FATAL(XR←HANDLER←WHICH←TRAP←UNIX)

#   define XR←HANDLER←WHICH←STACK←OVERFLOW \
		XR←HANDLER←WHICH←MK←FATAL(1022)

#define XR←HANDLER←WHICH←TRAP←LIM	((XR←HandlerWhich)(1024))



/* registered handler proc */

typedef void (*XR←HandlerProc)(/*
    XR←Thread handlee,		-- obsolete, always XR←currThread
    XR←HandlerWhich which,	-- exception type code
    XR←Pointer arg		-- XR←TrapArgs for trap, else arbitrary
    XR←Pointer clientData	-- client data registered with proc
*/);


/* XR←HandlerData is private */

typedef struct XR←HandlerDataRep {
    XR←HandlerProc hd←proc;	/* registered handler proc */
    XR←Pointer hd←clientData;	/* passed to proc when invoked */
    XR←HandlerWhich hd←which;	/* saved XR←HandlerProc `which' argument */
    XR←Pointer hd←arg;		/* saved XR←HandlerProc `arg' argument */
    struct XR←JmpBufRep
	hd←resume;		/* resume after trap or CallDebugger ... */
    unsigned hd←result;		/* ... by XR←longjmp(&hd←resume, hd←result) */
} * XR←HandlerData;


extern void
XR←InitHandlerData (/* XR←HandlerData handler, XR←Thread next */);


extern void
XR←RegisterHandler (/* XR←HandlerProc proc, XR←Pointer data */);
/*
    Register a handler `proc' for this thread and its descendants.
*/


/* Debugger request/response messages */

typedef int XR←DBMsg;

#   define XR←DB←MSG←NONE		0

#   define XR←DB←MSG←IS←REQUEST(msg)	((msg) > 0)

#   define XR←DB←MSG←NO←HANDLER←REQUEST		1
#   define XR←DB←MSG←HANDLER←REQUEST		2
#   define XR←DB←MSG←BAD←PROCEED←REQUEST	3
#   define XR←DB←MSG←BREAK←REQUEST		4
#   define XR←DB←MSG←CLIENT←REQUEST		5
    /* etc */

#   define XR←DB←MSG←IS←REPLY(msg)	((msg) < 0)

#   define XR←DB←MSG←PROCEED←REPLY	(-1)
#   define XR←DB←MSG←EXIT←REPLY		(-2)
#   define XR←DB←MSG←ABORT←REPLY	(-3)
    /* etc */

extern XR←DBMsg
XR←CallDebugger2(/* XR←Pointer arg, XR←DBMsg msg */);
/*
    Call the debugger, making arg and msg available to it.

    The supplied msg should be of kind XR←DB←MSG←xxx←REQUEST.
    Result is of kind XR←DB←MSG←xxx←REPLY.
*/

extern XR←DBMsg
XR←CallDebugger(/* XR←Pointer arg */);
/*
    Equivalent to XR←CallDebugger2(arg, XR←DB←MSG←CLIENT←REQUEST).
*/



extern void
XR←Exit();
/*
    Simulate return from top level procedure of the current thread.
    Do not release monitor locks, ...
*/



/*
 *  Storage management -- imports
 */

extern XR←Pointer
XR←ExtensionAlloc (/* nWords */);
/*
    int nWords;

    Allocate a frame extension of given size (in words).
*/

XR←Pointer
XR←ExtensionFree (/* x */);
/*
    XR←Pointer x;

    Assuming x is the address of a frame extension allocated with
    XR←ExtensionAlloc, free it and return NIL.
*/

/*
 * Stack description
 */

typedef struct XR←StackRep {
    struct XR←SegRep stack←seg;	/* shared memory segment containing stack */
    XR←Pointer stack←handlerInitial;	/* initial value for handler stack */
    XR←Pointer stack←clientInitial;	/* initial value for client stack */
    XR←Pointer stack←warmLimit;	/* warm stack limit -- checked by e.g. Switch */
} * XR←Stack;

/* NOTE: the following work for machines on which the stack "grows"	*/
/*   toward smaller addresses -- e.g. sparc, 68K, vax, ...		*/

#define stack←physInitial stack←handlerInitial
#define stack←physLimit stack←seg.seg←addr

#define XR←WITHIN←STACK(sp,t,bytes)	\
	( (((sp)+(bytes)) <= (t)->t←stack.stack←physInitial) \
		&& ((sp) >= (t)->t←stack.stack←physLimit) )

#define XR←WITHIN←WARM←STACK(sp,t,bytes)	\
	( (((sp)+(bytes)) <= (t)->t←stack.stack←physInitial) \
		&& ((sp) >= (t)->t←stack.stack←warmLimit) )

#define XR←WARM←STACK←EXCEEDED(sp,t) \
	( (sp) < (t)->t←stack.stack←warmLimit )

#ifdef sparc
# define XR←STACK←ALIGNMENT←OK(sp) (((sp) & 0x7) == 0)
# define XR←STACK←ALIGN(sp) ( ((XR←Pointer)(sp)) & ~0x7 )
#else
--> fix me <--
#endif

/*
 * Thread descriptors
 */

/* Scheduling State */

typedef unsigned XR←SStat;
#   define XR←SSTAT←NONE	((XR←SStat)(0))
#   define XR←SSTAT←FREE	((XR←SStat)(1))	/* free, with stack  */
#   define XR←SSTAT←READY	((XR←SStat)(2))	/* ready */
#   define XR←SSTAT←RUN		((XR←SStat)(3))	/* running */
#   define XR←SSTAT←WAIT←ML	((XR←SStat)(4))	/* waiting ml */
#   define XR←SSTAT←WAIT←CV	((XR←SStat)(5))	/* waiting cv */
#   define XR←SSTAT←LAST 	((XR←SStat)(5))


/* Priority */

typedef unsigned XR←Pri;
#   define XR←PRI←IDLE			((XR←Pri)(0))
#   define XR←PRI←USER←BACKGROUND	((XR←Pri)(1))
#   define XR←PRI←SYS←BACKGROUND	((XR←Pri)(2))
#   define XR←PRI←USER←NORMAL		((XR←Pri)(3))
#   define XR←PRI←USER←FOREGROUND	((XR←Pri)(4))
#   define XR←PRI←SYS←NORMAL		((XR←Pri)(5))
#   define XR←PRI←SYS←FOREGROUND	((XR←Pri)(6))
#   define XR←PRI←SYS←EXCLUSIVE		((XR←Pri)(7))
#   define XR←PRI←LAST 7

/* Switch State */

typedef unsigned XR←SwStat;

#   define XR←SWSTAT←NONE		((XR←SwStat)(0))
#   define XR←SWSTAT←DONT←PREEMPT	((XR←SwStat)(1))
#   define XR←SWSTAT←THREAD		((XR←SwStat)(2))
#   define XR←SWSTAT←HANDLER		((XR←SwStat)(3))
#   define XR←SWSTAT←HSTACK		((XR←SwStat)(4))
#   define XR←SWSTAT←LAST 4

/* Join State */

typedef unsigned XR←JStat;
#   define XR←JSTAT←NONE		((XR←JStat)(0))
#   define XR←JSTAT←EXITING		((XR←JStat)(1))
#   define XR←JSTAT←JOINING		((XR←JStat)(2))
#   define XR←JSTAT←JOINED		((XR←JStat)(3))
#   define XR←JSTAT←DETACHED		((XR←JStat)(4))
#   define XR←JSTAT←DONE		((XR←JStat)(5))
#   define XR←JSTAT←LAST 5

/* Debugger request/response messages */

    /* see XR←DBMsg above */


#   define XR←NUM←PER←THREAD←DATA	8

/* Thread Data Structure */

typedef struct XR←ThreadRep {

    /* result of last UNIX system call */
    /* ThreadsMachDep.s depends on these fields being at offset 0 and 4 */

    int t←errno;		/* result of last system call ... */
    int t←errnoLock;		/* ... that was invoked with lock false */

    /* rescheduling hints for monitor entry/exit */
    /*  ThreadsMachDep.s depends on these fields being at offset 8 and 12 */

    void * t←vpeToReschedOnMonitorExit;
    XR←ML t←mlNeeded;

    /* index values */

    int t←index;		/* index in thread table */
    int t←timeoutIndex;		/* >0 ==> waiting for timeout */
    unsigned t←gen;		/* generation number (won't repeat) */

    /* scheduling state, thread queue -- must hold metaLock to change */

    XR←SStat t←sStat;		/* scheduling state */
    XR←Pri t←pri;		/* scheduling priority */
    XR←Ticks t←when;		/* ticks-since-boot when timeout wanted */

    XR←Thread t←next;		/* next in thread queue */
    XR←TQ t←tq;			/* queue containing this thread */

    unsigned long
        t←schedCtlInfo;		/* see ThreadsSchedCtl.h */
    unsigned long
        t←disabled;		/* see ThreadsSchedCtl.h */

    /* wait queue */

    XR←Thread t←wNext;		/* next pointer in wait queue */
    XR←WQ t←wq;			/* wq containing this thread */

    /* free queue */

    XR←TQ t←fq;			/* free queue for this thread */

    /* slow timeout */

    struct XR←CVRep t←timerCV;

    /* context */

    struct XR←JmpBufRep t←resume;
    XR←SwStat t←swStat;

    /* handler data */

    struct XR←HandlerDataRep t←handlerData; /* ??? */

    /* memory map info */

    struct XR←StackRep t←stack;	/* thread stack description */

    /* ABORT state */

    bool t←abortable;		/* significant when waiting on cv/icv	*/
    bool t←abortRequest;	/* abort requested but not yet acted on	*/

    /* thread (re)starting */

    XR←Pointer t←jumpSP;		/* non-NIL ==> call XR←Jumpee with */
					/*   sp set to this */
    void (*t←jumpProc)(/*arg*/);	/* called with t←jumpArg by XR←Jumpee */
    unsigned t←jumpArg;

    /* FORK/JOIN/Detach MONITORED RECORD */

    struct XR←MLRep t←ml;	/* lock on remaining fields */
    XR←MesaProc t←startProc;	/* start proc for this thread */ /* ??? */
    XR←Pointer t←result;	/* result data for joiner */
    XR←JStat t←jStat;		/* JOIN/Detach state */
    struct XR←CVRep t←jCVc;	/* for join-detach, child waits */
    struct XR←CVRep t←jCVp;	/* for join-detach, parent waits */

    /* debugger */

    XR←DBMsg t←dbMsg;		/* for communication with debugger */
    bool t←dbFreeze;		/* freeze request from debugger */
    bool t←dbFrozen;		/* thread is frozen */

    /* per-thread client data */

    XR←Pointer t←dynEnv;	/* dynamic environment */
    XR←Pointer t←perThreadData[XR←NUM←PER←THREAD←DATA];

#undef XR←Thread
} * XR←Thread;

extern XR←Thread XR←currThread;	/* current thread, PER PROCESSOR */

/*
 * Thread Priorities
 */

extern XR←Pri
XR←GetPriority ();
/*
    Return priority of current thread.
*/
#if XR←GET←PRIORITY
#   define XR←GetPriority()	(XR←currThread->t←pri)
#endif


extern void
XR←SetPriority (/* pri */);
/*
    XR←Pri pri;

    Set priority of current thread.
*/

extern void
XR←Yield (/* */);
/*
    Yield processor.
*/


/*
 * Checked Thread Data Structure -- includes ref and generation number
 */

typedef struct XR←CTRep {
    XR←Thread ct←thread;	/* -> thread */
    unsigned ct←gen;		/* generation number */
} *XR←CT;


extern void
XR←GetCurrent (/* result */);
/*
    struct XR←CTRep *result;

    Return the current thread in *result.
*/


/*
 * Thread Create/Destroy runtime support
 */

extern void
XR←Fork (/*
    struct XR←CTRep *result,
    XR←MesaProc mp
*/);

extern int
XR←TryFork (/*
    struct XR←CTRep *result,
    XR←MesaProc mp
*/);

extern int
XR←TryFork3 (/*
    struct XR←CTRep *result,
    XR←MesaProc mp,
    unsigned minStackBytes
*/);
/*

      `mp' takes no arguments and returns a result record (possibly NIL?).
      It is the responsibility of the joiner to free this record
        using XR←ExtensionFree.

    Fork a new thread running `mp'.
    Return the checked thread in *result.
    XR←TryFork returns 0 on success or (-1) if thread slot is unavailable.
    XR←TryFork3 returns 0 on success or (-1) if no thread slot with at least
      the requested number of stack bytes is available.
    XR←Fork waits (not abortable) if no thread slot is available.
*/



/* OBSOLESCENT */
    extern void
    XR←RegisterThreadStartProc (/* startProc */);
    /*
        XR←MesaProc startProc;

        Register startProc as the "start proc" for this thread and its
          descendants.
        Ordinarily, the effect of
          XR←Fork(result, forkProc);
        is
          result = (*forkProc->mp←proc)(forkProc);
        After RegisterThreadStartProc(startProc) the effect will be
          result = (*startProc->mp←proc)(forkProc, startProc);
        allowing the startProc to catch uncaught signals, etc. 
    
        It is okay to register NIL.
    */
#   if XR←REGISTER←THREAD←START←PROC
#      define XR←RegisterThreadStartProc(startProc) \
	   XR←currThread->t←startProc = (startProc)
#   endif


extern XR←Pointer
XR←JoinCT (/* ct */);
/*
    XR←CT ct;
    
    JOIN with `ct' returning (pointer to) its results.
    The caller is presumed to know how many results to expect.
    If cp is an invalid thread, return ((XR←Pointer)(-1)), which is
      guaranteed not to be a valid pointer.
*/


extern int
XR←TryJoinCT(/*
    XR←CT ct,
    bool abortable,
    XR←Pointer *resultp
*/);
/*
    Try to join thread ct, storing (pointer to) its results in *resultp.
    Return 0 on success, -EINVAL, -ESRCH or -EABORTED on failure.
*/


extern int
XR←PauseAbortable (/* ticks */);
/*
    XR←Ticks ticks;

    Pause the specified number of ticks, with aborts enabled.
    Ticks may be XR←WAIT←FOREVER.
    Return 0 ordinarily, or -1 if aborted.
*/

extern bool
XR←AbortPending ();
/*
    Check whether an abort has been requested.
    This does not clear the p←abortRequest flag.
*/
#if XR←ABORT←PENDING
#   define XR←AbortPending() (XR←currThread->t←abortRequest)
#endif

extern int
XR←CancelAbort (/* ct */);
/*
    XR←CT ct;
    
    Cancel any abort request for `ct' (i.e. clear the t←abortRequest flag).
    If `ct' == NIL it means the current thread.
    Return 0 if okay, or -1 if `ct' is invalid.
*/


extern int
XR←DetachCT (/* ct */);
/*
    XR←CT ct;
    
    Detach `ct'.
    Return 0 if okay, or -1 if `ct' is invalid.
*/


extern int
XR←AbortCT (/* ct */);
/*
    XR←CT ct;
    
    Abort `ct'.
    Return 0 if okay, or -1 if `ct' is invalid.
*/


extern int
XR←ValidateCT (/* ct */);
/*
    XR←CT ct;

    Return 0 if okay, -1 if `ct' is invalid.
*/


/*
 * Per-Thread data
 */

extern int
XR←GetPerThreadDataID(/* XR←Pointer key, bool create, bool inherit */);
/*
    Return an id for per-thread data to be associated with the
      (non-NIL) value of key. 
    <Result, XR←GetErrno()>:
      key == NIL => <-1, EINVAL>
      create == FALSE and no registration exists for key => <-1, EINVAL>
      create == TRUE and a registration already exists => <-1, EINVAL>
      no more slots => <-1, ENOMEM>
      okay => <id, ?>
    If the call is successful with create == TRUE, then the corresponding
      per-thread data is inherited across FORKs iff inherit == TRUE.
*/

extern XR←Pointer *
XR←GetPerThreadDataAddress(/* int id */);
/*
    Return address of per-thread data slot.
*/

#if XR←GET←PER←THREAD←DATA←ADDRESS
#   define XR←GetPerThreadDataAddress(id) ((XR←Pointer *) \
	(((char *)(XR←currThread))+((int)(id))) )
#endif

    /* obsolete versions ... */

    extern int
    XR←GetPerThreadDataOffset(/* XR←Pointer key, bool create */);
    /*
        Return a byte offset from XR←currThread
          to be used for per-thread client data
          associated with the (non-zero) value of key.
        The offset will be word-aligned but might be negative.
        <Result, XR←GetErrno()>:
          key == 0 => <-1, EINVAL>
          create == FALSE and no registration exists for key => <-1, EINVAL>
          create == TRUE and a registration already exists => <-1, EINVAL>
          no more slots => <-1, ENOMEM>
          okay => <offset, ?>
    */

    extern XR←Pointer *
    XR←GetPerThreadDataSlot(/* int byteOffset */);
    /*
        Return address of per-thread data slot at given offset.
    */


    extern XR←Pointer
    XR←GetThreadProperty (/* */);
    /*
        Return property value of current thread.
    */

    extern XR←Pointer
    XR←GetThreadInheritedProperty (/* */);
    /*
        Return inherited property value of current thread.
    */

    extern void
    XR←SetThreadProperty (/* p */);
    /*
        XR←Pointer p;

        Set property value of current thread.
    */

    extern void
    XR←SetThreadInheritedProperty (/* p */);
    /*
        XR←Pointer p;

        Set inherited property value of current thread.
    */



/*
 * Terminate the world ...
 */

extern void
XR←ExitWorld (/* int status */);
/*
    Terminate PCR; in Unix(tm) this looks like

        ←exit(status);

    to the invoker of PCR. 

    Tries to free all allocated resources
	(e.g. system semaphores, swap file, ...).
*/


    extern void
    XR←KillWorld (/* */);
    /*
        Equivalent to XR←ExitWorld(-1).
	Obsolescent.
    */


/*
 * Thread switch recording (for performance monitoring)
 */

extern void
XR←RegisterSwitchCallback(/*
    XR←MesaProc proc,
    XR←MesaProc *oldProcP
*/);
/*
    Register a proc to be called whenever a thread is switched away from.
    The proc is called by
      (*(proc->mp←proc))(
          nextThread,	(* dest thread for handoff scheduling *)
          nextState,	(* state this thread is changing to *)
          swStat,	(* from handler? (see XR←SWStat) *)
          proc
          );
    Return old registration in *oldProcP.
      (This may be NIL, => don't return anything.)
    The proc may not acquire locks, or do anything else that might
      induce a switch (e.g. call XR←Yield() ... ).
*/

extern void
XR←RegisterSetReadyCallback(/*
    XR←MesaProc proc,
    XR←MesaProc *oldProcP
*/);
/*
    Register a proc to be called whenever a thread is made runnable.
    The proc is called by
      (*(proc->mp←proc))(
          runnableThread,	(* thread to be made runnable *)
          proc
          );
    Return old registration in *oldProcP.
      (This may be NIL, => don't return anything.)
    The proc may not acquire locks, or do anything else that might
      induce a switch (e.g. call XR←Yield() ... ).
*/

    

#endif ←XR←THREADS←