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

/*
 * UIOPrivate.h
 *
 *
 * Private definitions for Unix I/O interface
 *   for Xerox Runtime threads package.
 *
 * Suitable for AT&T streams.
 *
 * Demers, July 30, 1990 9:53:32 am PDT
 * Boehm, May 14, 1990 2:51:23 pm PDT
 */


#ifndef	←XR←UIO←PRIVATE←
#define ←XR←UIO←PRIVATE← 1

#ifndef ←XR←THREADS←BACKDOOR←
#   include "ThreadsBackdoor.h"
#endif

#ifndef ←XR←UIO←
#   include "UIO.h"
#endif


/*
 * The following controls whether STREAMS support is compiled in.
 * On some systems we may have to fudge the streams header files
 *   if STREAMS is false.
 */
#define STREAMS		1

/*
 * The following controls a hack in ThreadsUIO.c/USysCalls.c to allow strings
 * declared in data or bss segments to be passed to IOPs in a packaged
 * threads configuration.  It doesn't hurt anything in other configurations.
 *
 * NOTE: in 2←12 and later versions this is NEVER needed.
 */

#define XR←COPY←FOR←PACKAGED←THREADS	0

/*
 * Queue of events (Unix signals) passed back to PCR threads ...
 */

#define XR←UIO←EVENT←QUEUE←SIZE		8


/*
 * Max descriptors per IOP.
 * Each kind of UIO IOP may implement fewer than this ...
 */

#define XR←MAX←FDS←PER←IOP		256




#if !defined(POLLIN)
#   include <poll.h>
#endif

#if STREAMS
#   if !defined(I←STR)
#	include <stropts.h>
#   endif
#endif



#if XR←COPY←FOR←PACKAGED←THREADS
    extern char * XR←UIOFixStringArg();
#else
#   define XR←UIOFixStringArg(x)	(x)
#endif


/*
 * Wait Kinds -- set of descriptor events to wait for
 *
 * Taken from System V <poll.h>
 */

typedef unsigned XR←UIOWaitKinds;

/*
    POLLIN		-- normal input
    POLLPRI		-- priority input
    POLLOUT		-- output

    POLLERR		-- error return
    POLLHUP		-- stream disappeared
    POLLNVAL		-- descriptor not a stream
*/


/*
 * State of FDE
 *
 * It's okay to `OR' XR←FDE←STATE←BUSY into any other state.
 */

typedef unsigned XR←FDEState;

#   define XR←FDE←STATE←FREE		((XR←FDEState)(0))
#   define XR←FDE←STATE←BUSY		((XR←FDEState)(1))
#   define XR←FDE←STATE←OPEN		((XR←FDEState)(2))


/*
 * Data structures for virtual file descriptor entries (FDE's)
 *   and physical file descriptor cache entries (FDC's)
 */

#define XR←FDC	struct XR←FDCRep *
#define XR←FDE	struct XR←FDERep *

typedef struct XR←FDERep {
    int fde←index;
    unsigned fde←gen;			/* generation number */
    int fde←opCnt;			/* # ops in progress */
    XR←FDEState fde←state;
    XR←IOPE fde←owner;			/* IOP that owns descriptor */
    int fde←ownerIndex;			/* owner's descriptor index */
    XR←FDKind fde←fdKind;		/* kind of associated descriptors */
    					/*   (same as kind of IOP) */
    XR←UIOBlocking fde←getBlocking;	/* input blocking behavior */
    XR←UIOBlocking fde←putBlocking;	/* output blocking behavior */
    XR←Ticks fde←getTimeout;		/* input timeout */
    XR←Ticks fde←putTimeout;		/* output timeout */
    bool fde←inputPri;			/* poll input => pri */
    XR←FDC fde←fdc;			/* -> cached fdc or NIL */
    struct XR←CVRep fde←avail;		/* wait here e.g. for FDC */
#undef XR←FDE
} * XR←FDE;



typedef struct XR←FDCRep {
    int fdc←index;		/* index in cache */
    unsigned fdc←whenUsed;	/* for LRU */
    bool fdc←valid;		/* Unix descriptor exists */
    XR←FDE fdc←fde;		/* associated fde or NIL */
#undef XR←FDC
} * XR←FDC;

/*
 * UIO IOP working directory maintenance
 */

typedef struct XR←UIOWDBufRep {
    int wdb←maxLen;
    int wdb←len;
    char wdb←data[ /*maxLen+1*/ 4];
} * XR←UIOWDBuf;

#define XR←UIOWDBufBytes(len)	( sizeof(struct XR←UIOWDBufRep) + (len) - 3 )

extern char *
XR←UIOFixPathForIOP(/* char *path, XR←IOPE iope */);
/*
    (to be called only from a thread)
    Return (possibly new, heap-allocated) full pathname for path.
    As side-effect, if iope is not NIL, ensure that the IOP's wdBuf
      is big enough to hold (the directory prefix of) the full path name.
*/

extern int
XR←UIOIOPChDirForPaths(/* char *fullPath, char *fullPath2 */);
/*
    (fullPath2 may be NIL)
    (to be called only from IOP)
    Change IOP's unix working directory to appropriate (common) prefix of
      fullPath (and fullPath2 if it's not NIL).
    Store working directory in IOP's WD slot, to be accessed by
      XR←GetUIOIOPEWD(iope).
    Return length of common prefix (which is now IOP's working directory)
      on success, or (-errno) on failure.
*/


/*
 *
 * UIO system data structure
 *
 */

typedef struct XR←UIOAreaRep {

    /* monitor locks */
	struct XR←MLRep uioa←ml;
	struct XR←MLRep uioa←wdML;

    /* PCR working directory cache */
        XR←UIOWDBuf uioa←pcrWD;

    /* IOPs */
	XR←IOPE uioa←iope;
	int uioa←numIOPE;
#	define XR←UIOIOPELimit \
	(&(XR←uioArea->uioa←iope[XR←uioArea->uioa←numIOPE]))

    /* FDEs */
	XR←FDE uioa←fde;
	int uioa←numFDE;
#	define XR←UIOFDELimit \
	(&(XR←uioArea->uioa←fde[XR←uioArea->uioa←numFDE]))

    /* FDCs */
	XR←FDC uioa←fdc;
	int uioa←numFDC;
#	define XR←UIOFDCLimit \
	(&(XR←uioArea->uioa←fdc[XR←uioArea->uioa←numFDC]))

    /* cv for spinning to wait for free fdc (rare!) */
	struct XR←CVRep uioa←spinCV;

    /* op count (for LRU) */
	unsigned uioa←opCnt;

    /* misc data for sys calls */
	XR←IOPOrder uioa←spawnWaitList;
	bool uioa←spawnChildDied;
	struct XR←VPOrderRep uioa←profVPOrderBuf;

} * XR←UIOArea;


extern XR←UIOArea XR←uioArea;		/* readonly, shared */

/*
 * IOPE client data fields
 *
 *   (uioFreeSlots is protected by uioArea monitor lock uioa←ml)
 *   (IOPEWDBuf is protected by uioArea monitor lock uioa←wdML)
 */

#define iope←uioFDKind		iope←clientData[0]
#define iope←uioFDEBase		iope←clientData[1]
#define iope←uioFreeSlots	iope←clientData[2]


#define XR←GetUIOIOPEWD(iope) \
	((XR←UIOWDBuf)((iope)->iope←clientData[3]))
#define XR←SetUIOIOPEWD(iope, wdb) \
	(iope)->iope←clientData[3] = ((unsigned)(wdb))

#define XR←GetUIOIOPEWDBuf(iope) \
	((XR←UIOWDBuf)((iope)->iope←clientData[4]))
#define XR←SetUIOIOPEWDBuf(iope, wdb) \
	(iope)->iope←clientData[4] = ((unsigned)(wdb))


/*
 *
 * Per-IOP data structure for wait-until-ready requests
 *
 */

typedef struct XR←WaitReadyDataRep {
    struct pollfd wrd←pollfd;
} * XR←WaitReadyData;

#   define wrd←fd	wrd←pollfd.fd
#   define wrd←events	wrd←pollfd.events
#   define wrd←revents	wrd←pollfd.revents

#define XR←waitReadyData ((XR←WaitReadyData)(XR←iope->iope←clientData[5]))
#define XR←GetWaitReadyData(iope) \
	((XR←WaitReadyData)((iope)->iope←clientData[5]))
#define XR←SetWaitReadyData(iope, d) \
	(iope)->iope←clientData[5] = ((unsigned)(d))
#define XR←numWaitReadyData (XR←iope->iope←clientData[6])
#define XR←GetNumWaitReadyData(iope) \
	((iope)->iope←clientData[6])
#define XR←SetNumWaitReadyData(iope,x) \
	(iope)->iope←clientData[6] = ((unsigned)(x))


typedef struct XR←WaitReadyHeaderRep {
    unsigned wrh←index;
    XR←WaitReadyData wrh←wrd;	/* ref to WaitReadyDataRep or NIL */
    XR←IOPOrder wrh←orders;	/* list of wait ready orders for this FDE */
} * XR←WaitReadyHeader;

#define XR←waitReadyHeaders ((XR←WaitReadyHeader)(XR←iope->iope←clientData[7]))
#define XR←GetWaitReadyHeaders(iope) \
	((XR←WaitReadyHeader)((iope)->iope←clientData[7]))
#define XR←SetWaitReadyHeaders(iope,h) \
	(iope)->iope←clientData[7] = ((unsigned)(h))


/*
 * Asynchronous events
 */

typedef struct XR←UIOEventQueueRep {
    unsigned uioeq←wNum;
    unsigned uioeq←rNum;
    int uioeq←eventID[XR←UIO←EVENT←QUEUE←SIZE]
} *XR←UIOEventQueue;

#define XR←uioEventQueue ((XR←UIOEventQueue)(XR←iope->iope←clientData[8]))
#define XR←GetUIOEventQueue(iope) \
	((XR←UIOEventQueue)((iope)->iope←clientData[8]))
#define XR←SetUIOEventQueue(iope,q) \
	(iope)->iope←clientData[8] = ((unsigned)(q))

extern void
XR←UIONotifyEventAvail();
/*
    Notify any waiting thread of an asynchronous event in IOP.
*/


/*
 * 
 * Descriptor Utilities
 *
 */

#define XR←IOP←POLL←TIMEOUT←MSEC 2000	/* internal timeout */

extern int
XR←UIOPollStd(
    /* XR←waitReadyData fds, int nfds, unsigned timeout, unsigned *flagp */
);
/*
    Like a System V poll call, but on standard descriptors.
    The timeout is specified in msec.
    As late as possible, test *flagp and if it is nonzero, return (-1)
      with errno set to EINTR.  Ideally this should be atomic!
    Normal result is number of descriptors that tested ready.
*/

extern bool
XR←UIOPollStream(
    /* XR←waitReadyData fds, int nfds, unsigned timeout, unsigned *flagp */
    /* Assumed to be called only from IOP, or with stack arguments.      */
);
/*
    Like XR←UIOPollStd.
*/

/*
 * IOP order proc utilities
 */

extern int
XR←UIOIOPFixDescriptor (/* fde, d */);
/*
    XR←FDE fde;
    int d;
    XR←FDKind kind;

    Assume d is the result of a Unix system call to create a descriptor --
      open, socket, ... (thus d could be -1).
    Insist that d be of right kind for fde, else return (-ENOSTR).
    Set `d' to be non-blocking, move it to the descriptor slot corresponding to
      `fde'.
    Return the new descriptor (guaranteed to be the slot corresponding to `fde',
      which may be different from the original value of d), or -errno on
      failure.
    On error returns, the descriptor `d' has been closed.
*/


extern int
XR←UIOIOPSetNonBlockingStd(/*int d*/);
/*
    Set Unix (standard) descriptor d to non-blocking.
    Return d on success, or (-errno) on failure.
*/


extern int
XR←UIOIOPSetNonBlockingStream(/*int d*/);
/*
    Set Unix (streams) descriptor d to non-blocking.
    Return d on success, or (-errno) on failure.
*/


extern void
XR←UIONotifyIOPODone(/* iopo */);
/*
    XR←IOPOrder iopo;

    Set iopo←done and do a naked notify of iopo←cv,
	with some error checking.
    This is the approved way to notify the waiting thread that an
	IOP order has been completed.
*/

extern unsigned
XR←UIOComputeLengthForSingleIOSysCall(/* char *b, unsigned maxlen */);
/*
    Compute length for a single I/O call, to limit amount of time spent
      uninterruptably blocked in system call. 
*/

/*
 *
 * VP Orders
 *
 */

/* --- */
#define vpo←fdc vpo←args[0]

extern void
XR←RecvDescriptorVPOrderProc (/* vpo */);
/*
    XR←VPOrder vpo;

    Receive descriptor.
    Argument in vpo←fdc tells where it belongs.
*/

extern void
XR←FlushDescriptorVPOrderProc (/* vpo */);
/*
    XR←VPOrder vpo;

    Flush cached descriptor.
    Argument in vpo←fdc tells which one.
*/

/* --- */

#define vpo←profBuf	vpo←args[0]
#define vpo←profBufSize	vpo←args[1]
#define vpo←profOffset	vpo←args[2]
#define vpo←profScale	vpo←args[3]

extern void
XR←ProfilVPOrderProc (/* vpo */);
/*
    XR←VPOrder vpo;

    Execute profil system call.
*/

/* --- */
#define vpo←mma	vpo←args[0]

extern void
XR←MMapVPOrderProc (/* vpo */);
/*
    XR←VPOrder vpo;

    Execute mmap system call in vp.
*/

extern void
XR←MUnmapVPOrderProc (/* vpo */);
/*
    XR←VPOrder vpo;

    Execute munmap system call in vp.
*/

extern void
XR←MProtectVPOrderProc (/* vpo */);
/*
    XR←VPOrder vpo;

    Execute mprotect system call in vp.
*/


/* --- */
#define vpo←sha	vpo←args[0]

extern void
XR←ShmAtVPOrderProc (/* vpo */);
/*
    XR←VPOrder vpo;

    Execute shmat system call.
*/

extern void
XR←ShmDtVPOrderProc (/* vpo */);
/*
    XR←VPOrder vpo;

    Execute shmdt system call.
*/


/*
 *
 * IOPOrders
 *
 */


/*
 * args shared by many procs
 */

#define iopo←fdc 	iopo←args[0]
#define iopo←fde 	iopo←args[1]

/* --- */

#define iopo←oName	iopo←args[2]
#define iopo←oFlags	iopo←args[3]
#define iopo←oMode	iopo←args[4]

extern void
XR←OpenIOPOrderProc (/* iopo */);
/*
    XR←IOPOrder iopo;

    Open a named file.
    Args in iopo←fde, iopo←oName, iopo←oFlags, iopo←oMode.
    Result in iopo←results[0] is (fde←ownerIndex) or (-errno).
*/

/* --- */

extern void
XR←CloseIOPOrderProc (/* iopo */);
/*
    XR←IOPOrder iopo;

    Close a descriptor.
    Arg in iopo←fde
      and fde←fdc (cache entry to flush by issuing VPOrder, may be NIL).
    Result in iopo←results[0] is 0 or (-errno).
*/

/* --- */

extern void
XR←SendDescriptorIOPOrderProc (/* iopo */);
/*
    XR←IOPOrder iopo;

    Send a descriptor to the VP caches.
    Arguments in iopo←fde (=> which descriptor)
      and iopo←fdc (which cache entry to store into).

    Assumed never to fail (panic if it does).
*/

/* --- */

#define iopo←wrHow	iopo←args[2]

extern void
XR←WaitReadyIOPOrderProc (/* iopo */);
/*
    XR←IOPOrder iopo;

    Args in iopo←fde and iopo←wrHow.
    Wait for descriptor to be ready as specified by wrHow.

    Result in iopo←results[0] is 0 or (-errno).
*/


extern void
XR←WaitReadyIOPCancelProc (/* iopo */);
/*
    XR←IOPOrder iopo;

    Cancel previously issued WaitReady order in iopo.
*/

/* --- */

#define iopo←sAf	iopo←args[2]
#define iopo←sType	iopo←args[3]
#define iopo←sProtocol	iopo←args[4]

extern void
XR←SocketIOPOrderProc (/* iopo */);
/*
    XR←IOPOrder iopo;

    Execute socket call.
    Result in iopo←results[0] is (fde←ownerIndex) or (-errno)
*/

/* --- */

#define iopo←accNewFDE		iopo←args[2]
#define iopo←accAddr		iopo←args[3]
#define iopo←accAddrLen		iopo←args[4]

extern void
XR←AcceptIOPOrderProc (/* iopo */);
/*
    XR←IOPOrder iopo;

    Execute accept call.
    Result in iopo←results[0] is (new fde←ownerIndex) or (-errno)
*/

/* --- */

#define iopo←profBuf		iopo←args[0]
#define iopo←profBufSize	iopo←args[1]
#define iopo←profOffset		iopo←args[2]
#define iopo←profScale		iopo←args[3]

extern void
XR←ProfilIOPOrderProc (/* iopo */);
/*
    XR←IOPOrder iopo;

    Order all VProcessors to execute profile call.
*/

/* --- */

#define iopo←mma 	iopo←args[0]

extern void
XR←MMapIOPOrderProc (/* iopo */);
/*
    XR←IOPOrder iopo;

    Recv descriptor and do mmap call on IOP.
    If this is IOP 0, send mmap order to all the vprocessors.
*/

extern void
XR←MUnmapIOPOrderProc (/* iopo */);
/*
    XR←IOPOrder iopo;

    Do munmap call on IOP.
    If this is IOP 0, send munmap order to all the vprocessors.
*/

/* --- */

#define iopo←sha 	iopo←args[0]


extern void
XR←ShmAtIOPOrderProc (/* iopo */);
/*
    XR←IOPOrder iopo;

    Do a shmat call on the IOP.
    If this is IOP 0, send a shmat order to all the vprocessors.
*/

extern void
XR←ShmDtIOPOrderProc (/* iopo */);
/*
    XR←IOPOrder iopo;

    Do a shmdt call on the IOP.
    If this is IOP 0, send a shmdt order to all the vprocessors.
*/


/* --- */

#define iopo←spawnCmd		iopo←args[0]
#define iopo←spawnWD		iopo←args[1]
#define iopo←spawnStdin		iopo←args[2]
#define iopo←spawnStdout	iopo←args[3]
#define iopo←spawnStderr	iopo←args[4]
#define iopo←spawnPID		iopo←args[5]


extern void
XR←SpawnIOPOrderProc (/* iopo */);
/*
    XR←IOPOrder iopo;

    Spawn child.
    The IOPOrder is linked into a global list, not set done until child
      terminates.
    Result in iopo->iopo←results[0] is termination status of child,
      or (-errno) if fork was unsuccessful.
    N.B. This is abortable, and has no cancel proc; iopo must be dynamically
      allocated.
*/


/* --- */

#define iopo←accessPath		iopo←args[0]
#define iopo←accessMode		iopo←args[1]

extern void
XR←AccessIOPOrderProc (/* XR←IOPOrder iopo */);
/*
    iopo->iopo←results[0] = access(path, mode)
      (or -errno on failure)
*/

/* --- */

#define iopo←chmodPath		iopo←args[0]
#define iopo←chmodMode		iopo←args[1]

extern void
XR←ChModIOPOrderProc (/* XR←IOPOrder iopo */);
/*
    iopo->iopo←results[0] = chmod(path, mode)
      (or -errno on failure)
*/


/* --- */

#define iopo←linkName1		iopo←args[0]
#define iopo←linkName2		iopo←args[1]

extern void
XR←LinkIOPOrderProc (/* XR←IOPOrder iopo */);
/*
    iopo->iopo←results[0] = link(name1, name2)
      (or -errno on failure)
*/


/* --- */

#define iopo←mkdirPath		iopo←args[0]
#define iopo←mkdirMode		iopo←args[1]

extern void
XR←MkDirIOPOrderProc (/* XR←IOPOrder iopo */);
/*
    iopo->iopo←results[0] = mkdir(path, mode)
      (or -errno on failure)
*/


/* --- */

#define iopo←mknodName		iopo←args[0]
#define iopo←mknodMode		iopo←args[1]
#define iopo←mknodDev		iopo←args[2]

extern void
XR←MkNodIOPOrderProc (/* XR←IOPOrder iopo */);
/*
    iopo->iopo←results[0] = mknod(path, mode, dev)
      (or -errno on failure)
*/


/* --- */

#define iopo←readlinkPath	iopo←args[0]
#define iopo←readlinkBuf	iopo←args[1]
#define iopo←readlinkBufsiz	iopo←args[2]

extern void
XR←ReadLinkIOPOrderProc (/* XR←IOPOrder iopo */);
/*
    iopo->iopo←results[0] = readlink(path, mode, dev)
      (or -errno on failure)
*/


/* --- */

#define iopo←renameFrom		iopo←args[0]
#define iopo←renameTo		iopo←args[1]

extern void
XR←RenameIOPOrderProc (/* XR←IOPOrder iopo */);
/*
    iopo->iopo←results[0] = rename(from, to)
      (or -errno on failure)
*/


/* --- */

#define iopo←rmdirName		iopo←args[0]

extern void
XR←RmDirIOPOrderProc (/* XR←IOPOrder iopo */);
/*
    iopo->iopo←results[0] = rmdir(name)
      (or -errno on failure)
*/

/* --- */

#define iopo←statPath	iopo←args[0]
#define iopo←statBuf	iopo←args[1]

extern void
XR←StatIOPOrderProc (/* XR←IOPOrder iopo */);
/*
    iopo->iopo←results[0] = stat(path, buf)
      (or -errno on failure)
*/


/* --- */

#define iopo←lstatPath	iopo←args[0]
#define iopo←lstatBuf	iopo←args[1]

extern void
XR←LStatIOPOrderProc (/* XR←IOPOrder iopo */);
/*
    iopo->iopo←results[0] = lstat(path, buf)
      (or -errno on failure)
*/


/* --- */

#define iopo←statfsPath	iopo←args[0]
#define iopo←statfsBuf	iopo←args[1]

extern void
XR←StatFSIOPOrderProc (/* XR←IOPOrder iopo */);
/*
    iopo->iopo←results[0] = statfs(path, buf)
      (or -errno on failure)
*/


/* --- */

#define iopo←symlinkName1	iopo←args[0]
#define iopo←symlinkName2	iopo←args[1]

extern void
XR←SymLinkIOPOrderProc (/* XR←IOPOrder iopo */);
/*
    iopo->iopo←results[0] = symlink(name1, name2)
      (or -errno on failure)
*/


/* --- */

#define iopo←truncatePath	iopo←args[0]
#define iopo←truncateLength	iopo←args[1]

extern void
XR←TruncateIOPOrderProc (/* XR←IOPOrder iopo */);
/*
    iopo->iopo←results[0] = truncate(path, length)
      (or -errno on failure)
*/


/* --- */

#define iopo←unlinkPath		iopo←args[0]

extern void
XR←UnlinkIOPOrderProc (/* XR←IOPOrder iopo */);
/*
    iopo->iopo←results[0] = truncate(path, length)
      (or -errno on failure)
*/


/* --- */

#define iopo←utimesFile		iopo←args[0]
#define iopo←utimesTVP		iopo←args[1]

extern void
XR←UTimesIOPOrderProc (/* XR←IOPOrder iopo */);
/*
    iopo->iopo←results[0] = stat(file, tvp)
      (or -errno on failure)
*/


/* --- */

#define iopo←eventSigNum	iopo←args[0]
#define iopo←eventInteresting	iopo←args[1]

extern void
XR←RegisterInterestIOPOrderProc (/* XR←IOPOrder iopo */);
/*
    iopo->iopo←results[0] = 0 after doing appropriate sigvec calls
      (or -errno on failure)
*/


/* --- */

#define iopo←fcntlCmd	iopo←args[2]
#define iopo←fcntlArg	iopo←args[3]

extern void
XR←FCntlIOPOrderProc (/* XR←IOPOrder iopo */);
/*
    iopo->iopo←results[0] = 0 after doing appropriate fcntl call
      (or -errno on failure)
*/



/* --- */

#define iopo←ioctlRequest	iopo←args[2]
#define iopo←ioctlArgp		iopo←args[3]

extern void
XR←IOCtlIOPOrderProc (/* XR←IOPOrder iopo */);
/*
    iopo->iopo←results[0] = 0 after doing appropriate ioctl call
      (or -errno on failure)
*/



/*
 * Utilities run by threads
 */
 
extern int
XR←MProtect4(/* addr, len, prot, include←iops*/);
/*  XR←Pointer addr;
    unsigned len;
    int prot;
    bool include←iops;
    
    A version of MProtect that allows heap addresses, does no checking  on
    on addr, and allows caller to specify whether or not IOPs are included.
*/

typedef unsigned long XR←FDEWhichIOP;	/* how choose iop for descriptor */ 

#define XR←FDE←WHICH←IOP←FIRST		0
#define XR←FDE←WHICH←IOP←MOSTFREE	1
#define XR←FDE←WHICH←IOP←LEASTFREE	2
#define XR←FDE←WHICH←IOP←LAST		2

#define XR←FDE←WHICH←IOP←FROM←IOPE(iope) \
	((XR←FDEWhichIOP)(iope))
#define XR←FDE←WHICH←IOP←IS←IOPE(which) \
	((which) > XR←FDE←WHICH←IOP←LAST)
#define XR←IOPE←FROM←WHICH←IOP(which) \
	((XR←IOPE)(which))

extern XR←FDE
XR←AllocateFDE (/* fdKind, which */);
/*
    XR←FDKind fdKind;
    XR←FDEWhichIOP which;

    Allocate an unused fde according to strategy which.
    Caller is assumed to hold uioArea monitor lock.
    On success, return the fde in BUSY (but not OPEN) state.
    On failure, set errno and return NIL.
*/


extern void
XR←FreeFDE (/* fde */);
/*
    XR←FDE fde;

    Free an fde.
    Caller is assumed to hold the fde exclusively, and to hold
      uioArea monitor lock.
*/


#define XR←IncFDEOpCnt(fde)	\
	(fde)->fde←opCnt += 1

#define XR←DecFDEOpCnt(fde)	\
	if( --((fde)->fde←opCnt) == 0 ) XR←Broadcast(&((fde)->fde←avail))


#define XR←FDCUsed(fdc) \
	(fdc)->fdc←whenUsed = (++(XR←uioArea->uioa←opCnt))
/*
    Mark an FDC as used (for LRU).
*/



extern XR←FDC
XR←AllocateFDC (/* fde */);
/*
    XR←FDE fde;

    Allocate an fdc:
      - one that's not assigned to an fde, if possible ...
      - failing that, "steal" LRU FDC assigned to an unlocked fde ...
      - failing either of those, return NIL.
    Set fde->fde←fdc and fdc->fdc←fde pointers.
    Set fdc->fdc←valid = FALSE.
    Caller is assumed to hold uioArea monitor lock.
*/


extern void
XR←FreeFDC(/* fdc */);
/*
    XR←FDC fdc;

    Free an fdc.
    This updates the associated fde, if any, and sets fdc←valid = FALSE.
    Caller is assumed to hold the uioArea ml.
*/


extern XR←FDE
XR←UIODoWithFDELocked(/* fildes, proc, x1, x2, x3 */);
/*
    XR←Fildes fildes;
    int ( *proc)(* fde, x1, x2, x3 *);
    unsigned x1, x2, x3;

    Generic routine for UIO FDE parameter setting.
    The `proc' gets called with the FDE corresponding to `fildes' and
      arguments x1, x2, x3.
    If successful, proc should return a non-negative value; on error, it
      should set errno and return a negative value.
    If `proc' returns a non-negative value, then the FDE is returned
      from DoWithFDE; otherwise NIL is returned  (with an error code in errno).
    Certain error conditions (EBADF ...) may cause DoWithFDE to set errno
      and return without calling proc.
    All this is done with the monitor lock held, so `proc' shouldn't block
      or do anything else that acquires the lock.
*/

#define XR←FDE←LOCKED←WORKER(name) \
    int name (fde, x1, x2, x3) \
	XR←FDE fde; \
	unsigned x1, x2, x3;


extern XR←FDE
XR←UIODoWithNewFDE(/* fdKind, which, proc, x1, x2, x3 */);
/*
    XR←FDKind fdKind;
    XR←FDEWhichIOP which;
    int ( *proc)(* fde, x1, x2, x3 *);
    unsigned x1, x2, x3;

    Generic routine for FDE creation.
    For fdKind, which interpretation see XR←AllocateFDE.
    The `proc' gets called with (newly-allocated) FDE (in state BUSY)
      and arguments x1, x2, x3.
    If successful, proc should return a non-negative value; on error,
    it should set errno and return a negative value.
    If `proc' returns a non-negative value, then the new FDE is returned
      from DoWithNewFDE in state OPEN; otherwise, the FDE is deallocated
      and DoWithNewFDE returns NIL (with an error code in errno).
    Certain error conditions (EMFILE ...) may cause DoWithNewFDE to set errno
      and return without calling proc.
*/


extern XR←FDE
XR←UIODoWithOldAndNewFDE(/* fildes, proc, x1, x2 */);
/*
    XR←IOPE iope;
    int ( *proc)(* fde, fdeNew, x1, x2 *);
    unsigned x1, x2;

    Generic routine for UIO with both and old and a newly-allocated FDE
      (e.g. accept, ...).
    The newly-allocated FDE comes from the same iop as the old one.
    The `proc' gets called with FDE's and arguments x1, x2.
    If successful, proc should return a non-negative value; on error, it
      should set errno and return a negative value.
    If `proc' returns a non-negative value, then the new FDE is returned
      from DoWithNewFDE; otherwise, the FDE is deallocated and DoWithNewFDE
      returns NIL (with an error code in errno).
    Certain error conditions (EMFILE ...) may cause DoWithNewFDE to set errno
      and return without calling proc.
*/



typedef struct XR←UIOWaitReadyProcRep {
    int (*wrp←proc)(/* XR←UIOWaitReadyProc self, XR←FDE fde */);
    XR←UIOWaitKinds wrp←how;
    XR←Ticks wrp←timeout;
} * XR←UIOWaitReadyProc;
/*
    Type of `waitReadyProc' parameter to UIODoWithFDEAndFDC, below.

    Called by (waitReadyProc->wrp←proc)(waitReadyProc, fde);
    Waits for fde to be ready (w/ appropriate direction, timeout, ...)
    Returns 0 on success, and (-1) with errno set on failure.
*/

extern int
XR←UIODoWithFDEAndFDC(/* fildes, waitReadyProc, proc, x1, x2 */);
/*
    XR←Fildes fildes;
    XR←UIOWaitReadyProc waitReadyProc;
    int (*proc)(XR←FDE fde, XR←FDC fdc, unsigned x1, unsigned x2);
    unsigned x1, x2;

    Generic routine for UIO with an FDE and an associated FDC.
    How to deal with EWOULDBLOCK is specified by `waitReadyProc'.
    The `proc' gets called with (locked) fde and fdc as arguments,
      together with the two client data values `x1' and `x2'.
    Ordinarily, the result of calling proc is returned from UIODoWithFDEAndFDC.
    If successful, `proc' should return a non-negative value; on error, it
      should set errno and return a negative value.
    Certain error conditions (EBADF, XR←EABORTED, ...) cause UIODoWithFDEAndFDC
      to set errno and return -1 without calling proc.
*/

#define XR←FDE←FDC←WORKER(name) \
    int name (fde, fdc, x1, x2) \
	XR←FDE fde; \
	XR←FDC fdc; \
	unsigned x1, x2;


extern struct XR←UIOWaitReadyProcRep XR←UIOIn0WaitReadyProc; 
	/* waitProc for first try on input (POLLIN) operations */

extern struct XR←UIOWaitReadyProcRep XR←UIOIn1WaitReadyProc;
	/* waitProc for later tries on input (POLLIN) operations */

extern struct XR←UIOWaitReadyProcRep XR←UIOOut0WaitReadyProc;

extern struct XR←UIOWaitReadyProcRep XR←UIOOut1WaitReadyProc;

extern struct XR←UIOWaitReadyProcRep XR←UIOPri0WaitReadyProc;

extern struct XR←UIOWaitReadyProcRep XR←UIOInPri0WaitReadyProc;

extern struct XR←UIOWaitReadyProcRep XR←UIOInOut0WaitReadyProc;

extern struct XR←UIOWaitReadyProcRep XR←UIOPriOut0WaitReadyProc;

extern struct XR←UIOWaitReadyProcRep XR←UIOInPriOut0WaitReadyProc;


extern int
XR←UIODoWithFDERefreshFDC(/* fildes, proc, x1, x2 */);
/*
    XR←Fildes fildes;
    int (*proc)(XR←FDE fde, unsigned x1, unsigned x2);
    unsigned x1, x2;

    Generic routine for UIO with an FDE when the operation performed
      by proc would invalidate a cached FDC.
    How to deal with EWOULDBLOCK is specified by `waitReadyProc'.
    The `proc' gets called with (locked) fde as argument,
      together with the two client data values `x1' and `x2'.
    Ordinarily, the result of calling proc is returned.
    If successful, `proc' should return a non-negative value; on error, it
      should set errno and return a negative value.
    Certain error conditions (EBADF, XR←EABORTED, ...) cause
       UIODoWithFDERefreshFDC to set errno and return -1 without calling
       proc.
*/

#define XR←FDE←REFRESH←WORKER(name) \
    int name (fde, x1, x2) \
	XR←FDE fde; \
	unsigned x1, x2;



/*
 * For spawned (Unix) processes ...
 */

extern void
XR←IOPSpawnChildDied();
/*
    Call on IOP that does spawns to reap children that have exited.
*/

#endif ←XR←UIO←PRIVATE←