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

/*
 * XR←Threads.h
 *
 * Demers, July 13, 1992 4:43:12 pm PDT
 */


#ifndef	←←XR←Threads←h
#define ←←XR←Threads←h 1

#include <xr/XR←Basics.h>
#include <th/PCR←Th.h>

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


typedef	struct XR←ThreadRep * XR←Thread;


/*
 * 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←WAIT←FOREVER ((XR←Ticks) 0)



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

extern XR←Ticks
XR←MsecToTicks (unsigned msec);
/*
    Convert milliseconds to ticks.
    Rounds up.
    Overflow if msec > LAST(unsigned)-1000.
*/

extern unsigned
XR←TicksToMsec (XR←Ticks ticks);
/*
    Convert ticks to milliseconds.
    Undefined if it would overflow.
*/


extern bool
XR←TicksLater (XR←Ticks a, XR←Ticks b);
/*
    Return PCR←Bool←true iff a is strictly later than b.
*/

/*
 * BEWARE: the following is private to implementation.
 * It depends on XR←WAIT←FOREVER being 0.
 */

#define XR←TimeoutDataFromTimeout(timeout) ((PCR←Msec)(\
	((long)(timeout)) * XR←MSECS←PER←TICK )) 

#define XR←TimeoutFromTimeoutData(td) ((XR←Ticks)(\
	((long)(td)) / XR←MSECS←PER←TICK ))

#define XR←WakeupFromTimeoutData(td) (\
	PCR←Th←WakeMsec( (PCR←Msec)((long)(td)+(long)(PCR←waitForever))  ))





/*
 * Monitor Locks
 */

typedef struct XR←MLRep {
    PCR←Th←ML ml←ml;
    long ml←pad[1];
} *XR←ML;

struct XR←MLRep XR←MLRep←null;


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


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


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



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

typedef struct XR←CVRep {
    PCR←Th←CV cv←cv;
    long cv←timeoutData;		/* coded -- see impl */
    const PCR←sigset←t *cv←sigMask;	/* either NIL or alertable */
    long cv←pad[1];
} *XR←CV;

struct XR←CVRep XR←CVRep←null;

#define XR←sigmaskForAbortable (&(PCR←sigset←t←readonlyEmpty))
	/* implementation detail */
#define XR←sigmaskForNotAbortable (NIL)
	/* implementation detail */

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


extern void
XR←SetTimeout (XR←CV cv, XR←Ticks timeout);
/*
    Set the timeout associated with `cv'.
    Has no effect on currently waiting threads.
*/

extern void
XR←DisableTimeout (XR←CV cv);
/*
    Disable timeouts for `cv'.
    Has no effect on currently waiting threads.
*/


extern XR←Ticks
XR←GetTimeout (XR←CV cv);
/*
    Get the timeout associated with `cv'.
*/


extern void
XR←EnableAborts (XR←CV cv);
/*
    Enable aborts on `cv'.
    No effect on threads currently waiting.
*/


extern void
XR←DisableAborts (XR←CV cv);
/*
    Disable aborts on `cv'.
    No effect on threads currently waiting.
*/


extern int
XR←WaitCV (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 (XR←CV cv);
/*
    Notify `cv'.
*/


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


/*
 * The following is very delicate ...
 *
 *   It depends on JMPBUFSIZE being at least as big as
 *   PCR←setjmp←JBLEN from posix/setjmp.h (it is)
 *   and probably on other things I'm not aware of.
 *   The implementation must be assembly code that jumps
 *   to setjmp ...
 */

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


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


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



/*
 * 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 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 globally.
 *
 * It is okay to register NIL; a NIL handler just calls the debugger.
 */


/* Args to Unix signal handler -- these are inadequately dummied up */

typedef struct XR←TrapArgsRep {
    int ta←sig;
    int ta←code;
    void *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
*/);



extern void
XR←RegisterHandler (XR←HandlerProc proc, XR←Pointer data);
/*
    Register (globally) a handler `proc'.
*/

extern void
XR←RestartHandlee(XR←Thread t, XR←JmpBuf where, unsigned result);
/*
    t is ignored.
    where must not be NIL.
    This just does a longjmp(where, result)
*/

XR←Thread
XR←GetNextHandlee (XR←Thread t);
/*
    return( (t == NIL) ? current thread : NIL );
*/


/* Debugger request/response messages */

/* This stuff is NYI */

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



/*
 * Thread (NOT world) exit ...
 */

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



/*
 *  Storage management -- imports
 */

extern XR←Pointer
XR←ExtensionAlloc (int nWords);
/*
    Allocate a frame extension of given size (in words).
*/

extern XR←Pointer
XR←ExtensionFree (XR←Pointer x);
/*
    Assuming x is the address of a frame extension allocated with
    XR←ExtensionAlloc, free it and return NIL.
*/

/* Thread Data Structure */

struct XR←ThreadRep {
    long youCantLookInHere;
};

extern XR←Thread XR←currThread;	/* current thread, OOPS! */




/*
 * Thread Priorities
 */

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


extern XR←Pri
XR←GetPriority (void);
/*
    Return priority of current thread.
*/


extern void
XR←SetPriority (XR←Pri pri);
/*
    Set priority of current thread.
*/



extern void
XR←Yield (void);
/*
    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 (struct XR←CTRep *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
);
/*

      `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 (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.
    */



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 (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 (void);
/*
    Check whether an abort has been requested.
    This does not clear the p←abortRequest flag.
*/

extern int
XR←CancelAbort (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 (XR←CT ct);
/*
    Detach `ct'.
    Return 0 if okay, or -1 if `ct' is invalid.
*/


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


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


/*
 * Per-Thread data
 */


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


extern void
XR←SetThreadProperty (XR←Pointer p);
/*
    Set 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, ...).
*/


    

#endif /* ←←XR←Threads←h */