//*static char *SCCSID = "@(#)semlocal.h 6.8 92/03/11";*/
/*      static char *SCCSID = "@(#)semlocal.h   6.8 92/03/11" */

/*
 *      Semaphores - Local Defines, Macros, and Data Structures
 *
 *      This is an OS/2 2.x specific file
 *      IBM/Microsoft Confidential
 *
 *      Copyright (c) IBM Corporation 1987, 1989
 *      Copyright (c) Microsoft Corporation 1987, 1989
 *
 *      All Rights Reserved
 *
 *      Created: June 1989 - Julie Bennett
 *      10 July 1991 added performance counters for 32 bit sem. b724626
 *      22 Jan  1992 decrease perfview overhead by check to see if perfview
 *                   is up (aa04504)
 *      11 Mar  1992 b734380 remove perfview counters
 *      31 Jan  1994 xxxxxxx Add definition for a PM Semiphore. RAC
 *      June    1994 xxxxxxx Mofidfy initialize size of SHared Semaphore table
 *               and add values that define how many entries too grow the
 *               table by when it becomes full. Look for RAC
 */

/*
 *  The following is to make structure offsets constant in a SEMSTRICT
 *  kernel. The extra PMDEBUG fields aren't used if PMDEBUG isn't globally
 *  defined.
 *  The problem this solves is the following:
 *    The PMDEBUG flag is turned on only for *some*, not *all* of the
 *    files which use the semaphore structures PEVENT, SEVENT, SMUTEX,
 *    PMUTEX, SMUX, and PMUX. Thus the offset of the ulSig field will
 *    vary among various .obj modules of a debugging kernel. C can't
 *    handle this and the build kernel quickly panics because it is
 *    not looking in the right place for the semaphore signatures.
 *
 *    The following kludge tricks the compiler into generating the same
 *    offset for ulSig whether PMDEBUG is defined, or not, by always
 *    adding the debugging fields if in a SEMSTRICT kernel.
 */

#ifdef PMDEBUG
#  define SEMDEBUG
#endif
#ifdef SEMSTRICT
#  ifndef SEMDEBUG
#    define SEMDEBUG
#  endif
#endif


//b734380extern BOOL fPerfview;                                      //aa04504


/*
 *  Constants
 */

/* Table Sizes and Increments */
/********************************* RAC *********************************/
/*       Allocate table to initializ value of 112                      */
/********************************* RAC *********************************/

#define  SEM_SHTBLSIZE           112   // Initial size of shared semaphore
                                       //   table in dwords
#define  SEM_PRTBLSIZE            16   // Initial size of private semaphore
                                       //   table in dwords
#define  SEM_SHBMPSIZE             1   // Initial size of shared bitmap in
                                       //   dwords
#define  SEM_STRTBLSIZE            1   // Initial size of string table in
                                       //   entries
#define  SEM_TBLINCR               2   // Increment amount for all tables

/********************************* RAC *********************************/
/*       Set table grow size to 8 entries                              */
/********************************* RAC *********************************/

#define  SEM_TBLGROW               8   // Grow amount for Share Sem tables

/* String Values */
#define  SEM_MAXNAME             256   // Maximum length of a semaphore name
#define  SEM_PREFIXLEN             7   // Offset for name prefix
#define  SEM_PREFIX      "\\SEM32\\"   // Actual prefix

/* Bit Values */
#define  SEM_WORD_BYTES            2   // Symbol for a  word = 2 bytes
#define  SEM_DWORD_BYTES           4   // Symbol for a dword = 4 bytes
#define  SEM_BYTE_BITS             8   // Symbol for a byte = 8 bits
#define  SEM_DWORD_BITS           32   // Symbol for a dword = 32 bits

/* Timeout Values */
#define  SEM_INDEFINITE_WAIT      -1   // Wait indefinitely on a block
#define  SEM_IMMEDIATE_RETURN      0   // Timeout of zero (return w/out blocking)

/* Limit Values */
#define  SEM_MAXTBLSIZE   0x00010000   // Max number of table entries (all tables)
#define  SEM_MAXOPENS         0xffff   // Max number of opens per semaphore
#define  SEM_MAXPOSTS         0xffff   // Max number of posts per event
#define  SEM_MAXREQUESTS      0xffff   // Max number of requests per mutex
#define  SEM_MAXWAITS         0xffff   // Max number of waits per mux
#define  SEM_MAXSEMS              64   // Max number of semaphores in mux

/* Mask Values */
#define  SEM_SHAREDMASK   0x80000000   // Mask to get shared flag in sem handle
#define  SEM_ZEROMSB      0x7fffffff   // Mask to zero out the msb
#define  SEM_RESET_EV     ~DE_POSTED   // Mask to zero out DE_POSTED flag
#define  SEM_EVT_MUX    ~DMW_MTX_MUX   // Mask to zero out DMW_MTX_MUX flag

/* Flags */
#define  SEM_ALLPOSTED    0xffffffff   // Initialize event bitmap to all posted
#define  SEM_MUTEX_UNOWNED         0   // Value of slot # when mutex unowned
#define  SEM_EMPTY_SEMREC 0xffffffff   // Denotes an empty SemRecord in Mux

#define  SEM_SRFLAG       0xfffffffe   // Flag to see if allocated semrec is used
#define  SEM_OPENQFLAG             0   // Flag to see if allocated openq is used
#define  SEM_MUXQFLAG              0   // Flag to see if allocated muxq is used

#define  SEM_MUXFLAGS  (DC_SEM_SHARED | DCMW_WAIT_ANY | DCMW_WAIT_ALL)
                                       // Zeros all but orig. bits set in Create
#define  SEM_WAKE_ALL  (WAKEUP_SINGLE | WAKEUP_SINGLE_ALL)
                                       // Parameter for multiple single wakeup
#define  DC_ALL_SEMS   (DCE_EVENT_SEM | DCM_MUTEX_SEM | DCMW_MUX_SEM)
                                       // All semaphore types

#define  SEM_MTXTYPE               1   // The type of semaphore is a mutex
#define  SEM_EVTTYPE               0   // The type of semaphore is an event

#define  FAILURE                   1   // Return code used by internal routines
                                       //   to specify failure

#define  SEM_INVALID_PID           0   // Invalid process id used for dev help
                                       //   interface

/*
 *  Semaphore Attributes - usFlags field of semaphore structures
 */
#define  DE_POSTED            0x0040   // The event sem APIs set this flag
                                       //   if the event is in the posted state
#define  DM_OWNER_DIED        0x0080   // The process died while owning the
                                       //   mutex semaphore
#define  DMW_MTX_MUX          0x0100   // The muxwait semaphore APIs set this
                                       //   flag if the mux contains mutex sems
#define  DHO_SEM_OPEN         0x0200   // dh_OpenEventSem sets this flag to
                                       //   indicate that device drivers have
                                       //   opened the given semaphore
#define  DE_16BIT_MW          0x0400   // Part of a 16-bit MuxWait if this
                                       //   flag is set


/*
 *  Type Declarations
 */
typedef  PSEM   SEMTBL;          // table of pointers to semaphores
typedef  SEMTBL *PSEMTBL;        // pointer to a semaphore table

/* Open Queue Node Structure */
typedef struct openq_s {
    PID          pidOpener;      // process id of opening process
    USHORT       usOpenCt;       // number of Opens for this process
    struct openq_s *pNextOpen;   // pointer to next node in list
#ifdef SEMSTRICT
    ULONG        ulSig;
#endif
} OPENQ;
typedef  OPENQ   *POPENQ;        // pointer to an OPENQ


/* MuxWait Semaphore Structures and Union */
typedef struct smux_s {

//b734380 PVOID        pPvwBlk;        // pointer to perfview data block

    USHORT       usFlags;        // attributes
    USHORT       cSemRec;        // count of semaphore records
    PSEMRECORD   pSemRec;        // array of semaphore record entries
    USHORT       usWaitCt;       // number of threads waiting on the mux
    POPENQ       pOpenQ;         // pointer to the open queue
    PSZ          pszName;        // name of semaphore, null if anonymous
#ifdef SEMDEBUG
    PULONG       pulCreatAddr;   // Address passed in by app during create
#endif
#ifdef SEMSTRICT
    ULONG        ulSig;
#endif
} SMUX;
typedef  SMUX    *PSMUX;         // pointer to a MUX

typedef struct pmux_s {

//b734380  PVOID        pPvwBlk;        // pointer to perfview data block

    USHORT       usFlags;        // attributes
    USHORT       cSemRec;        // count of semaphore records
    PSEMRECORD   pSemRec;        // array of semaphore record entries
    USHORT       usWaitCt;       // number of threads waiting on the mux
    USHORT       usOpenCt;       // number of opens
    PPTDA        pPTDA;          // pointer to PTDA of creator
#ifdef SEMDEBUG
    PULONG       pulCreatAddr;   // Address passed in by app during create
#endif
#ifdef SEMSTRICT
    ULONG        ulSig;
#endif
} PMUX;
typedef  PMUX    *PPMUX;         // pointer to a MUX

typedef union muxun_s {
    SMUX         ShMux;          // a shared mux structure
    PMUX         PrMux;          // a private mux structure
} MUXUN;
typedef  MUXUN   *PMUXUN;        // pointer to a MUXUN


/* Mux Queue Node Structure */
typedef struct muxq_s {
    PMUXUN       pMux;           // pointer to a mux (shared or private)
    struct muxq_s *pNextMux;     // pointer to next mux waiter in list
#ifdef SEMSTRICT
    ULONG        ulSig;
#endif
} MUXQ;
typedef  MUXQ    *PMUXQ;         // pointer to a MUXQ


/* Event Semaphore Structures and Union */
typedef struct sevent_s {

//b734380 PVOID        pPvwBlk;        // pointer to perfview data block

    USHORT       usFlags;        // attributes
    PMUXQ        pMuxQ;          // pointer to the mux queue
    USHORT       usPostCt;       // number of posts
    POPENQ       pOpenQ;         // pointer to the open queue
    PSZ          pszName;        // name of semaphore, null if anonymous
#ifdef SEMDEBUG
    PULONG       pulCreatAddr;   // Address passed in by app during create
#endif
#ifdef SEMSTRICT
    ULONG        ulSig;
#endif
    PTCB         ptcb;           // ptcb of caller
} SEVENT;
typedef  SEVENT  *PSEVENT;       // pointer to an EVENT

typedef struct pevent_s {

//b734380 PVOID        pPvwBlk;        // pointer to perfview data block

    USHORT       usFlags;        // attributes
    PMUXQ        pMuxQ;          // pointer to the mux queue
    USHORT       usPostCt;       // number of posts
    USHORT       usOpenCt;       // number of opens
#ifdef SEMDEBUG
    PULONG       pulCreatAddr;   // Address passed in by app during create
#endif
#ifdef SEMSTRICT
    ULONG        ulSig;
#endif
} PEVENT;
typedef  PEVENT  *PPEVENT;       // pointer to an EVENT

typedef union evtun_s {
    SEVENT       ShEvt;          // a shared event structure
    PEVENT       PrEvt;          // a private event structure
} EVTUN;
typedef  EVTUN   *PEVTUN;        // pointer to a EVTUN


/* Mutex Semaphore Structures and Union */
typedef struct smutex_s {

//b734380 PVOID        pPvwBlk;        // pointer to perfview data block

    USHORT       usFlags;        // attributes
    PMUXQ        pMuxQ;          // pointer to the mux queue
    USHORT       usRequestCt;    // number of requests
    USHORT       usSlotNum;      // slot number of the owning thread
    USHORT       usRequesterCt;  // number of requesters
    POPENQ       pOpenQ;         // pointer to the open queue
    PSZ          pszName;        // name of semaphore, null if anonymous
#ifdef SEMDEBUG
    PULONG       pulCreatAddr;   // Address passed in by app during create
#endif
#ifdef SEMSTRICT
    ULONG        ulSig;
#endif
} SMUTEX;
typedef  SMUTEX  *PSMUTEX;       // pointer to a MUTEX

typedef struct pmutex_s {

//b734380 PVOID        pPvwBlk;        // pointer to perfview data block

    USHORT       usFlags;        // attributes
    PMUXQ        pMuxQ;          // pointer to the mux queue
    USHORT       usRequestCt;    // number of requests
    USHORT       usSlotNum;      // slot number of owning thread
    USHORT       usRequesterCt;  // number of requesters
    USHORT       usOpenCt;       // number of opens
#ifdef SEMDEBUG
    PULONG       pulCreatAddr;   // Address passed in by app during create
#endif
#ifdef SEMSTRICT
    ULONG        ulSig;
#endif
} PMUTEX;
typedef  PMUTEX  *PPMUTEX;       // pointer to a MUTEX

typedef union mtxun_s {
    SMUTEX       ShMtx;          // a shared mutex structure
    PMUTEX       PrMtx;          // a private mutex structure
} MTXUN;
typedef  MTXUN   *PMTXUN;        // pointer to a MTXUN


/* Shared and Private Unions */
typedef union shun_s {
    SEVENT       SEvt;           // shared event
    SMUTEX       SMtx;           // shared mutex
    SMUX         SMux;           // shared mux
} SHUN;
typedef SHUN *PSHUN;             // pointer to a SHUN

typedef union prun_s {
    PEVENT       PEvt;           // private event
    PMUTEX       PMtx;           // private mutex
    PMUX         PMux;           // private mux
} PRUN;
typedef PRUN *PPRUN;             // pointer to a PRUN


/* String Structures */
typedef struct semstrnode_s {
    ULONG        hsem;           // semaphore handle
    PSZ          psz;            // pointer to the string
    struct semstrnode_s *pNext;  // pointer to next string node
#ifdef SEMSTRICT
    ULONG        ulSig;
#endif
} SEMSTRNODE;
typedef SEMSTRNODE *PSEMSTRNODE; // ptr to a SEMSTRNODE

typedef struct semtreenode_s {
    ULONG        ulKey;          // hash key
    PSEMSTRNODE  pStrNode;       // pointer to string node
} SEMTBLNODE;
typedef SEMTBLNODE *PSEMTBLNODE;



/*
 *  Global Variables
 */
extern  PSEMTBL  pShSemTbl;          // pointer to shared semaphore table
extern  ULONG    ulShTblSize;        // size of shared semaphore table
extern  ULONG    ulShTotUsed;        // number of shared semaphore entries used
extern  ULONG    ulShNextFree;       // next free slot in pShSemTbl
extern  HKSEM    hksShTbl;           // kernel sem handle for shared sem table

extern  PSEMTBLNODE  pShSemStrTbl;   // pointer to shared string table
extern  ULONG    ulShSemStrTblSize;  // size of shared string table
extern  ULONG    ulShSemStrTblUsed;  // number of string table entries used
extern  HKSEM    hksStrTbl;          // kernel sem handle for string table

extern  PMUXQ    pFreeMux;           // pool of free MuxQ nodes
extern  ULONG    ulFreeMuxCount;     // # nodes in pFreeMux
extern  HKSEM    hksSemRec;          // kernel sem handle for mux semrecord

/*
 *  Extern Declarations
 */

/*  32-bit Semaphore Routines
 *
 *  These routines are the PRIVATE routines for the 32-bit semaphore
 *  API routines.  They should NOT be called by any other component.
 */

/* 32-bit Semaphore Allocation/Validation Layer Routines */

extern  ULONG    semSharedAlloc(PVOID *,ULONG,POPENQ *,PHSEM);
extern  ULONG    semPrivateAlloc(PVOID *,ULONG,PHSEM);
extern  ULONG    semOpenSharedAlloc(PSZ,PHSEM,POPENQ *,PPTDA);
extern  ULONG    semAllocWaitMuxQs(ULONG,PSEMTBL *,PMUXQ *);


/* 32-bit Semaphore Worker Layer Routines */

extern  void     semSharedClose(PSEM,ULONG,PSZ);
extern  void     semPrivateClose(PSEM,ULONG);
extern  ULONG    semOpenSem(PHSEM,POPENQ,POPENQ *,PUSHORT,PPTDA);
extern  ULONG    semCreateEventSem(PSZ,PHEV,ULONG,BOOL,PEVTUN,POPENQ);
extern  ULONG    semCreateMutexSem(PSZ,PHMTX,ULONG,BOOL,PMTXUN,POPENQ);
extern  ULONG    semCreateMuxWaitSem(PSZ,PHMUX,ULONG,PSEMRECORD,ULONG,
                                     PMUXUN,POPENQ);
extern  ULONG    semWaitMuxWaitSem(PSEM,ULONG,PULONG,PMUXQ *);
extern  ULONG    semAddMuxWaitSem(HMUX,PSEMRECORD,PMUXQ);

//b734380extern  void     PvwStartBlk(PULONG);                              /*b724626*/
//b734380extern  void     PvwEndBlk(PULONG);                                /*b724626*/

/* 32-bit Semaphore Exit Routines */

extern  void     semWakeAllMtxNMux(PSEM);


/* 32-bit Semaphore Tools Routines */

extern  ULONG    semChkSharedHdl(ULONG,ULONG,PPTDA);
extern  ULONG    semChkPrivateHdl(ULONG,ULONG);
extern  ULONG    semChkHdl(ULONG,ULONG,PPTDA);
extern  ULONG    semGetTblHdl(PSEMTBL,ULONG,PULONG,PULONG);
extern  ULONG    semGetType(ULONG);
extern  void     semSlotToPidTid(USHORT,PPID,PTID);
extern  void     semGetOpenQNode(POPENQ,POPENQ *,POPENQ *,PID);
extern  ULONG    semSrchSnglForPid(PSEM);
extern  ULONG    semSrchMuxForPid(PSEM);
extern  ULONG    semChkSigInMux(PSEMRECORD,USHORT);
extern  ULONG    semChkSRInMux(PSEMRECORD,USHORT,USHORT);
extern  ULONG    semChkDupHandle(PSEMRECORD,USHORT);
extern  ULONG    semIfHdlInSR(PSEM,ULONG,PULONG);
extern  ULONG    semIfAllEvtsPosted(PSEMRECORD,USHORT);
extern  ULONG    semIfAllMtxFree(PSEMRECORD,USHORT);
extern  void     semOwnMtxInSR(PSEMRECORD,PSEMRECORD,PTCB);
extern  void     semAddToMuxQ(ULONG,PMUXQ);
extern  void     semDelFromMuxQ(ULONG,PMUXUN);
extern  void     semUpdateAllMuxQ(PMUXUN);
extern  void     semDelAllMuxQ(PMUXUN);
extern  void     semUpdateFreeMuxQ(ULONG);
extern  void     semFreeMuxQList(PMUXQ);
extern  ULONG    semHash(INT,PSZ);
extern  ULONG    semGetStr(PSZ,PULONG);
extern  ULONG    semStoreStr(PSZ,PULONG *);
extern  ULONG    semStrTblSrch(ULONG,PSEMTBLNODE *);
extern  ULONG    semStrTblInsert(ULONG,PSEMSTRNODE);
extern  ULONG    semStrTblDelete(PSZ);
extern  ULONG    semValidateName(PSZ, PSZ *);

/* For 16-bit muxwaits - found in muxsem.asm */
extern  ULONG    semEventWakeMuxWait(HEV);



/*
 *  Validation Macros
 */
#ifdef SEMSTRICT               /* All-Strict Kernel */

#define SEM_OPENQSIG    0x514E504F
#define SEM_MUXQSIG     0x5158554D
#define SEM_SMUXSIG     0x58554D53
#define SEM_PMUXSIG     0x58554D50
#define SEM_SMTXSIG     0x58544D53
#define SEM_PMTXSIG     0x58544D50
#define SEM_SEVTSIG     0x54564553
#define SEM_PEVTSIG     0x54564550
#define SEM_SNODSIG     0x444F4E53

#define semInitOpenQSig(pOpenQ) (pOpenQ)->ulSig = SEM_OPENQSIG
#define semInitMuxQSig(pMQ) (pMQ)->ulSig = SEM_MUXQSIG
#define semInitSMuxSig(pMux) (pMux)->ulSig = SEM_SMUXSIG
#define semInitPMuxSig(pMux) (pMux)->ulSig = SEM_PMUXSIG
#define semInitSMtxSig(pMtx) (pMtx)->ulSig = SEM_SMTXSIG
#define semInitPMtxSig(pMtx) (pMtx)->ulSig = SEM_PMTXSIG
#define semInitSEvtSig(pEvt) (pEvt)->ulSig = SEM_SEVTSIG
#define semInitPEvtSig(pEvt) (pEvt)->ulSig = SEM_PEVTSIG
#define semInitSNodeSig(pStrNode) (pStrNode)->ulSig = SEM_SNODSIG

#else                          /* Non-Strict Kernel */

#define semInitOpenQSig(pOpenQ)
#define semInitMuxQSig(pMQ)
#define semInitSMuxSig(pMux)
#define semInitPMuxSig(pMux)
#define semInitSMtxSig(pMtx)
#define semInitPMtxSig(pMtx)
#define semInitSEvtSig(pEvt)
#define semInitPEvtSig(pEvt)
#define semInitSNodeSig(pStrNode)

#endif


#define semAssertOpenQSig(pOpenQ) \
   tkAssert(((POPENQ)pOpenQ)->ulSig == SEM_OPENQSIG)
#define semAssertMuxQSig(pMQ)  tkAssert(((PMUXQ)pMQ)->ulSig == SEM_MUXQSIG)
#define semAssertSMuxSig(pMux) tkAssert(((PSMUX)pMux)->ulSig == SEM_SMUXSIG)
#define semAssertPMuxSig(pMux) tkAssert(((PPMUX)pMux)->ulSig == SEM_PMUXSIG)
#define semAssertSMtxSig(pMtx) tkAssert(((PSMUTEX)pMtx)->ulSig == SEM_SMTXSIG)
#define semAssertPMtxSig(pMtx) tkAssert(((PPMUTEX)pMtx)->ulSig == SEM_PMTXSIG)
#define semAssertSEvtSig(pEvt) tkAssert(((PSEVENT)pEvt)->ulSig == SEM_SEVTSIG)
#define semAssertPEvtSig(pEvt) tkAssert(((PPEVENT)pEvt)->ulSig == SEM_PEVTSIG)
#define semAssertSNodeSig(pStrNode) \
   tkAssert(((PSEMSTRNODE)pStrNode)->ulSig == SEM_SNODSIG)



/*
 *  Tool Macros
 */

/***LP  semGetBit - Gets a Bit in a given Bitmap
 *
 *      semGetBit gets the value of a particular bit in a
 *      specified bitmap.  The handle value is the bit position in
 *      the bitmap.
 *
 *      ENTRY   _ulHandle  - handle value
 *              _pulBitmap - pointer to the bitmap
 *
 *      EXIT    bit value in given location
 *
 *      DEFINED AS A MACRO
 *
 *      PSEUDO CODE
 *
 *      _ulHandle &= SEM_ZEROMSB
 *      ulIndex = _ulHandle / SEM_DWORD_BITS
 *      ulBitVal = _ulHandle % SEM_DWORD_BITS
 *
 *      return (_pulBitmap[ulIndex] & (1 << ulBitVal))
 */

#define semGetBit(_ulHandle, _pulBitmap)                                      \
    (_pulBitmap[((_ulHandle) & SEM_ZEROMSB) / SEM_DWORD_BITS] &               \
    (1 << ((_ulHandle) % SEM_DWORD_BITS)))


/***LP  semSetBit - Sets a Bit in a given Bitmap
 *
 *      semSetBit sets the value of a particular bit in a
 *      specified bitmap.  The handle value is the bit position in
 *      the bitmap.
 *
 *      ENTRY   _ulHandle  - handle value
 *              _pulBitmap - pointer to the bitmap
 *
 *      EXIT    none
 *
 *      DEFINED AS A MACRO
 *
 *      PSEUDO CODE
 *
 *      _ulHandle &= SEM_ZEROMSB
 *      ulIndex = _ulHandle / SEM_DWORD_BITS
 *      ulBitVal = _ulHandle % SEM_DWORD_BITS
 *
 *      _pulBitmap[ulIndex] |= (1 << ulBitVal)
 */

#define semSetBit(_ulHandle, _pulBitmap)                                      \
    (_pulBitmap[((_ulHandle) & SEM_ZEROMSB) / SEM_DWORD_BITS ] |=             \
    (1 << ((_ulHandle) % SEM_DWORD_BITS)))


/***LP  semZeroBit - Zero Out Bit in Bitmap
 *
 *      semZeroBit zeros out the value of a particular bit in a
 *      specified bitmap.  The handle value is the bit position in
 *      the bitmap.
 *
 *      ENTRY   _ulHandle  - handle value
 *              _pulBitmap - pointer to the bitmap
 *
 *      EXIT    none
 *
 *      DEFINED AS A MACRO
 *
 *      PSEUDO CODE
 *
 *      _ulHandle &= SEM_ZEROMSB
 *      _ulIndex = _ulHandle / SEM_DWORD_BITS
 *      _ulBitVal = _ulHandle % SEM_DWORD_BITS
 *
 *      _pulBitmap[ulIndex] &= ~(1 << ulBitVal)       // one's complement
 */

#define semZeroBit(_ulHandle, _pulBitmap)                                     \
    (_pulBitmap[((_ulHandle) & SEM_ZEROMSB) / SEM_DWORD_BITS ] &=             \
    ~(1 << ((_ulHandle) % SEM_DWORD_BITS)))


/***LP  semChkNotKrnlSpace - Check If User Pointer In Kernel Space
 *
 *      semChkNotKrnlSpace checks to see if a given user pointer is
 *      in kernel space.  If it is, then an error is returned.  This
 *      step is necessary since the TKFu... and TKSu... routines may
 *      be called from within the kernel.  Therefore, if a user happens
 *      to pass in a valid kernel address, then the kernel memory will
 *      be either read from or overwritten.  This check ensures that the
 *      user space will not be in the kernel.  Also, there is no need to
 *      check if the buffer is partly in user space and partly in kernel
 *      space.  This will be checked and flagged when the memory manager
 *      tries to lock the region.
 *
 *      ENTRY   _pChk - pointer to verify
 *
 *      EXIT    Success - continue execution
 *              Failure - ERROR_INVALID_PARAMETER
 *
 *      DEFINED AS A MACRO
 *
 *      PSEUDO CODE
 *
 *      If (_pChk >= SEL_512MEG)           // start of kernel space
 *        generate page fault
 *        return (ERROR_INVALID_PARAMETER)
 *      Endif
 */

#define semChkNotKrnlSpace(_pChk) {                                           \
    if (_pChk >= (PVOID)SEL_512MEG) {                                         \
      TKUsrMemPageFault((ULONG)_pChk);                                        \
      return (ERROR_INVALID_PARAMETER);                                       \
    }                                                                         \
}


/***LP  semAddSharedBits - Adds The Shared Bit To The Handle
 *
 *      semAddSharedBits returns a handle value with the appropriate
 *      bit set to denote that the handle is shared.  This routine
 *      only modifies the upper word of the handle.  The lower word
 *      of the handle is left alone.
 *
 *      ENTRY   _hsem - handle to a semaphore
 *
 *      EXIT    _hsem with shared bit added
 *
 *      DEFINED AS A MACRO
 *
 *      PSEUDO CODE
 *
 *      add "shared bit" to _hsem
 */

#define semAddSharedBits(_hsem)                                               \
    _hsem | SEM_SHAREDMASK


/***LP  semIfShared - Checks If Handle Is a Shared Semaphore
 *
 *      semIfShared checks to see if a handle is a shared semaphore.
 *      It does so by checking the appropriate bit in the handle.
 *
 *      ENTRY   _hsem - handle to a semaphore
 *
 *      EXIT    Success - TRUE  - if shared semaphore
 *                        FALSE - if private semaphore
 *
 *      DEFINED AS A MACRO
 *
 *      PSEUDO CODE
 *
 *      check "shared bit" in _hsem
 */

#define semIfShared(_hsem)                                                    \
    _hsem & SEM_SHAREDMASK


/***LP  semGetIndex - Compute The Index For A Given Handle
 *
 *      semGetIndex returns the index for the given handle.  It does
 *      so by stripping off the appropriate bits in the upper word
 *      of the handle, leaving just the lower word as the index into
 *      the semaphore tables.
 *
 *      NOTE  Since the index value is currently ONLY the lower
 *            word of the handle, the upper word is simply
 *            zero'd out by the use of casts.  This saves 1 clock
 *            and approximately 4 bytes per call.  If the offset
 *            should ever grow into the high word, this macro
 *            will have to change to take out the appropriate bits
 *            individually.
 *
 *      ENTRY   _hsem - handle to a semaphore
 *
 *      EXIT    index value for semaphore handle
 *
 *      DEFINED AS A MACRO
 *
 *      PSEUDO CODE
 *
 *      strip off "shared bit" and "signature bits" from _hsem
 *          (_hsem & SEM_ZEROMSB & ~SEM_SIGBITS)
 */

#define semGetIndex(_hsem)                                                    \
    (ULONG)((USHORT)_hsem)


/***LP  semGetShHdl - Compute The Shared Handle From The Given Index
 *
 *      semGetShHdl returns the shared handle for a given index value.
 *      It does so by adding the appropriate bits in the upper word
 *      of the handle.
 *
 *      ENTRY   _ulIndex - index into shared semaphore table
 *
 *      EXIT    shared handle from index value for user
 *
 *      DEFINED AS A MACRO
 *
 *      PSEUDO CODE
 *
 *      add "shared bit" and "signature bits" to index
 */

#define semGetShHdl(_ulIndex)                                                 \
    _ulIndex | SEM_SHAREDMASK | SEM_HANDLESIG


/***LP  semStripSigBits - Strip Off Signature Bits From The Handle
 *
 *      semStripSigBits strips off all of the signature bits from the
 *      given handle.  This only affects the upper word of the handle.
 *      The lower word of the handle remains the same.
 *
 *      ENTRY   _hsem - handle to a semaphore
 *
 *      EXIT    handle without signature bits
 *
 *      DEFINED AS A MACRO
 *
 *      PSEUDO CODE
 *
 *      strip off "signature bits" from _hsem
 */

#define semStripSigBits(_hsem)                                                \
    _hsem & ~SEM_SIGBITS


/***LP  semAddSigBits - Adds The Signature Bits To The Handle
 *
 *      semAddSigBits adds all of the signature bits to the given
 *      handle.  This only affects the upper word of the handle.
 *      The lower word of the handle remains the same.
 *
 *      ENTRY   _hsem - handle to a semaphore
 *
 *      EXIT    handle with signature bits
 *
 *      DEFINED AS A MACRO
 *
 *      PSEUDO CODE
 *
 *      add "signature bits" to _hsem
 */

#define semAddSigBits(_hsem)                                                  \
    _hsem | SEM_HANDLESIG


/***LP  semVerifySigBits - Verifies the Signature Bits Are Correct
 *
 *      semVerifySigBits verifies that the signature bits are correct
 *      for a 32-bit semaphore handle.  A return value of "TRUE" means
 *      that the handle contains the correct signature bits.  A return
 *      value of false means that the handle has the incorrect signature
 *      bits set.
 *
 *      ENTRY   _hsem - handle to a semaphore
 *
 *      EXIT    TRUE  - if handle has signature bits
 *              FALSE - if handle does NOT have signature bits
 *
 *      DEFINED AS A MACRO
 *
 *      PSEUDO CODE
 *
 *      verify "signature bits" in _hsem are correct
 *
 *         Defect 98809 Changed to verify SEM_HANDLESIG bit only
 */

#define semVerifySigBits(_hsem)                                               \
    (_hsem & SEM_HANDLESIG) == SEM_HANDLESIG


/***LP  semAssertNoSigBits - Asserts No Signature Bits In Handle
 *
 *      semAssertNoSigBits checks to be sure that there are no
 *      signature bits in the given handle.  If any such bits exist,
 *      the kernel will internal error.
 *
 *      ENTRY   _hsem - handle to a semaphore
 *
 *      EXIT    none
 *
 *      DEFINED AS A MACRO
 *
 *      PSEUDO CODE
 *
 *      assert "signature bits" do not exist in _hsem
 */

#define semAssertNoSigBits(_hsem)                                             \
    tkAssert((_hsem & SEM_SIGBITS) == 0)


/***LP  semPostEvtInMux - Wakeup Threads Waiting For Mux On Post Event
 *
 *      semPostEvtInMux wakes up all threads waiting for a mux
 *      containing the posted event that are ready to run.
 *
 *      ENTRY   _pEvt - pointer to the event
 *              _hev  - handle of the event
 *
 *      EXIT    Success - a thread may have been awakened
 *
 *      DEFINED AS A MACRO
 *
 *      PSEUDO CODE
 *
 *      call TKWakeup on ALL single waiters
 *      For (each MuxQ node of posted event)          // muxwait waiters
 *        pSemRec[0].hsemCur in mux = semrecord index of posted event
 *        If (DCMW_WAIT_ANY)
 *          remove all MuxQ nodes associated with this mux
 *          call TKWakeup on ALL threads waiting on mux
 *        Else                                        // DCMW_WAIT_ALL
 *          If (ALL events are posted)
 *            remove all MuxQ nodes associated with this mux
 *            call TKWakeup on ALL threads waiting on mux
 *          Endif
 *        Endif
 *      Endfor
 */

#define semPostEvtInMux(_pEvt,_hev)                                           \
                                                                              \
{                                                                             \
    PMUXQ _pMQSrch;                   /* ptr to search through MuxQ */        \
    PSEMRECORD _pSRPtr;               /* ptr to semrecord array in muxwait */ \
    register ULONG _ulSRIndex;        /* index location of event in SemRec */ \
    register PMUXUN _pTmpMux;         /* ptr to a mux semaphore */            \
    PTCB _pTCB;                       /* ptr to a TCB node           72485 */ \
                                                                              \
                                                                              \
    /*                                                                        \
     *  Go through all mux waiters of event.                                  \
     */                                                                       \
    _pMQSrch = ((PSHUN)_pEvt)->SEvt.pMuxQ;                                    \
    while (_pMQSrch != NULL) {                                                \
      _pSRPtr = _pMQSrch->pMux->ShMux.pSemRec;                                \
      /*                                                                      \
       *  Find index in semrec and update the last one posted.                \
       */                                                                     \
      _ulSRIndex = 1;                                                         \
      _pTmpMux = _pMQSrch->pMux;                                              \
      while ((_ulSRIndex <= _pTmpMux->ShMux.cSemRec) &&                       \
             ((++_pSRPtr)->hsemCur != _hev))                                  \
        _ulSRIndex++;                                                         \
      _pTmpMux->ShMux.pSemRec[0].hsemCur = _ulSRIndex;                        \
                                                                              \
      /*                                                                      \
       *  Need to advance pointer before updating mux queues.                 \
       *  _pMQSrch cannot be used below here.                                 \
       */                                                                     \
      _pMQSrch = _pMQSrch->pNextMux;                                          \
                                                                              \
      if ((_pTmpMux->ShMux.usFlags & DCMW_WAIT_ANY) ||                        \
          (semIfAllEvtsPosted(_pTmpMux->ShMux.pSemRec,                        \
                              _pTmpMux->ShMux.cSemRec) == NO_ERROR)) {        \
        /*                                                                    \
         *  Wait Any Mux OR All Events of a Wait All mux are posted.          \
         *  Wakeup all threads waiting on the mux.                            \
         */                                                                   \
        semDelAllMuxQ(_pTmpMux);                                              \
                                                                              \
        /*                                                                    \
         *  Get waiter(s) on mux                                              \
         */                                                                   \
        _pTCB = TKQueryWakeup(_pTmpMux,SEM_WAKE_ALL);             /* 72485 */ \
                                                                              \
        /*                                                                    \
         *  For each waiter on the mux, run down the wakeup chain and put     \
         *  the recently freed semaphore index into each TCB. So no matter    \
         *  which thread wakes up first, the correct semaphore index is       \
         *  always there for semWaitMuxWaitSem to use.                        \
         */                                                                   \
        for (; _pTCB != NULL; _pTCB = _pTCB->TCBpTCBWakeup) {     /* 72485 */ \
           _pTCB->TCB_ulSRIndex = _ulSRIndex;                     /* 72485 */ \
        }                                                         /* 72485 */ \
        TKWakeup(_pTmpMux,SEM_WAKE_ALL,NULL);                                 \
      }                                                                       \
    }                                                                         \
}


/***LP  semWakeOnRelMtx - Wakeup A Thread On The Release Of A Mutex
 *
 *      semWakeOnRelMtx wakes up the appropriate thread
 *      after the release of a mutex.   If the thread waiting on the
 *      single mutex is of highest priority, then the single waiter
 *      thread shall be run.  If a thread waiting on a mux containing the
 *      released mutex is of highest priority, then the thread waiting
 *      on the mux shall be run.  If a single waiter thread and a mux
 *      thread have the same priority, then the thread waiting on the
 *      mux will have first priority.
 *
 *      ENTRY   _pMtx - pointer to the mutex
 *              _hmtx - handle of the mutex
 *
 *      EXIT    Success - a thread may have been awakened
 *
 *      DEFINED AS A MACRO
 *
 *      PSEUDO CODE
 *
 *      call TKQueryWakeup on single waiter (get pTCB)
 *      pMaxTCB = pTCB
 *      If (pMaxTCB != NULL)                        // at least one waiter
 *        usMaxPri = priority of pTCB
 *      Endif
 *
 *      For (each MuxQ node in released mutex)
 *        If ((mux is DCMW_WAIT_ANY) || (all mutexes are free))
 *          call TKQueryWakeup on mux pointed to by MuxQ node
 *          If (priority of pTCB >= usMaxPri)
 *            usMaxPri = priority of pTCB
 *            pMaxTCB = pTCB
 *          Endif
 *        Endif
 *      Endfor
 *      If (pMaxTCB != NULL)
 *        If (single waiter)
 *          usSlotNum of mutex = slot number of pMaxTCB
 *          usRequestCt = 1
 *          increment pPTDACur->ulMtxOwned
 *        Else                                                // muxwait
 *          If (DCMW_WAIT_ANY)
 *            usSlotNum of released mutex = slot number of pMaxTCB
 *            usRequestCt = 1
 *            increment pPTDACur->ulMtxOwned
 *          Else                                              // DCMW_WAIT_ALL
 *            assign ownership to ALL mutex in semrecord of mux
 *          Endif
 *          pSemRec[0].hsemCur in mux = semrecord index of released mutex
 *        Endif
 *        call TKWakeup on pMaxTCB->TCBSleepID
 *      Endif
 */

#define semWakeOnRelMtx(_pMtx,_hmtx)                                          \
                                                                              \
{                                                                             \
    register ULONG _ulSRIndex;        /* index into semrecord */              \
    PTCB _pTCB;                       /* ptr to a TCB node */                 \
    PTCB _pMaxTCB;                    /* ptr to TCB with maximum priority */  \
    USHORT _usMaxPri = 0;             /* value of maximum priority */         \
    PMUXQ _pMQSrch;                   /* ptr to search through MuxQ */        \
    PSEMRECORD _pSRPtr;               /* ptr to semrecord array in muxwait */ \
    register PSEM _pSem;              /* ptr to a semaphore */                \
                                                                              \
                                                                              \
    /*                                                                        \
     *  Get the highest priority waiter from mutex single waiters.            \
     */                                                                       \
    _pTCB = TKQueryWakeup(_pMtx,WAKEUP_SINGLE);                               \
    _pMaxTCB = _pTCB;                                                         \
    if (_pMaxTCB != NULL)                                                     \
      /*                                                                      \
       *  There is at least one waiter.                                       \
       */                                                                     \
      _usMaxPri = _pMaxTCB->TCBPriority;                                      \
                                                                              \
    for (_pMQSrch=((PMTXUN)_pMtx)->ShMtx.pMuxQ; _pMQSrch != NULL;             \
                                           _pMQSrch = _pMQSrch->pNextMux) {   \
      _pSem = (PSEM)(_pMQSrch->pMux);                                         \
                                                                              \
      /*                                                                      \
       *  Get maximum priority thread waiting to run.                         \
       */                                                                     \
      if ((((PSHUN)_pSem)->SMux.usFlags & DCMW_WAIT_ANY) ||                   \
          ((semIfAllMtxFree(((PSHUN)_pSem)->SMux.pSemRec,                     \
                            ((PSHUN)_pSem)->SMux.cSemRec)) == NO_ERROR)) {    \
        _pTCB = TKQueryWakeup(_pSem,WAKEUP_SINGLE);                           \
        tkAssert(_pTCB != NULL);                                              \
        tkAssert(_pTCB->TCBSleepID == _pSem);                                 \
                                                                              \
        /*                                                                    \
         *  Check Mux Blockid is equal to SleepID and if priority is > max.   \
         */                                                                   \
        if (_pTCB->TCBPriority >= _usMaxPri) {                                \
          _usMaxPri = _pTCB->TCBPriority;                                     \
          _pMaxTCB = _pTCB;                                                   \
        }                                                                     \
      }                                                                       \
    }                                                                         \
    if (_pMaxTCB != NULL) {                                                   \
      _pSem = (PSEM)_pMaxTCB->TCBSleepID;                                     \
      tkAssert(((PMTXUN)_pMtx)->ShMtx.usRequestCt == 0);                      \
      if (((PSHUN)_pSem)->SMtx.usFlags & DCM_MUTEX_SEM) {                     \
        /*                                                                    \
         *  Single waiter.                                                    \
         */                                                                   \
        ((PMTXUN)_pMtx)->ShMtx.usSlotNum = _pMaxTCB->TCBNumber;               \
        ((PMTXUN)_pMtx)->ShMtx.usRequestCt = 1;                               \
        _pMaxTCB->TCBpPTDA->ulMtxOwned++;                                     \
      }                                                                       \
      else {                                                                  \
        /*                                                                    \
         *  Muxwait waiter.                                                   \
         */                                                                   \
        tkAssert(((PSHUN)_pSem)->SMux.usFlags & DCMW_MUX_SEM);                \
        if (((PMUXUN)_pSem)->ShMux.usFlags & DCMW_WAIT_ANY) {                 \
          /*                                                                  \
           *  Wait any.                                                       \
           */                                                                 \
          ((PMTXUN)_pMtx)->ShMtx.usSlotNum = _pMaxTCB->TCBNumber;             \
          ((PMTXUN)_pMtx)->ShMtx.usRequestCt = 1;                             \
          _pMaxTCB->TCBpPTDA->ulMtxOwned++;                                   \
        }                                                                     \
        else {                                                                \
          /*                                                                  \
           *  Wait all.                                                       \
           */                                                                 \
          _pSRPtr = ((PMUXUN)_pSem)->ShMux.pSemRec;                           \
          semOwnMtxInSR(_pSRPtr + 1,_pSRPtr + ((PMUXUN)_pSem)->ShMux.cSemRec, \
                        _pMaxTCB);                                            \
        }                                                                     \
                                                                              \
        /*                                                                    \
         *  Update last mutex released in mux.                                \
         */                                                                   \
        _ulSRIndex = 1;                                                       \
        _pSRPtr = ((PMUXUN)_pSem)->ShMux.pSemRec;                             \
        while ((_ulSRIndex <= ((PMUXUN)_pSem)->ShMux.cSemRec) &&              \
               ((++_pSRPtr)->hsemCur != _hmtx))                               \
          _ulSRIndex++;                                                       \
        ((PMUXUN)_pSem)->ShMux.pSemRec[0].hsemCur = _ulSRIndex;               \
        _pMaxTCB->TCB_ulSRIndex = _ulSRIndex;                  /* 72485 */    \
                                                                              \
        semUpdateAllMuxQ(_pSem);                                              \
      }                                                                       \
      TKWakeup(_pSem,WAKEUP_SINGLE,NULL);                                     \
    }                                                                         \
}


/***LP  semAddToOpenQ - Add an OpenQ Node to an Open Queue
 *
 *      semAddToOpenQ adds a given node to the specified
 *      open queue in ascending order of their process id values.
 *
 *      ENTRY   _pOpenN    - pointer to the new OpenQ node to add
 *              _ppOpenQ   - address of pointer to start of OpenQ list
 *              _ulDied    - flag for owner died state
 *
 *      EXIT    Success - node added to open queue
 *
 *      DEFINED AS A MACRO
 *
 *      PSEUDO CODE
 *
 *      If (_ulDied)                           // owner died mutex
 *        insert as second node in list
 *      Else                                   // not owner died mutex
 *        insert as first node in list
 *      Endif
 */

#define semAddToOpenQ(_pOpenN,_ppOpenQ,_ulDied)                               \
                                                                              \
{                                                                             \
    /*                                                                        \
     *  Node to add will be the first in the list.                            \
     *  If the owner died node needs to be first, then the node to add        \
     *  must be the second node in the list.                                  \
     */                                                                       \
    if (_ulDied) {                                                            \
      /*                                                                      \
       *  Make the new node the second one in the list.                       \
       */                                                                     \
      tkAssert(*_ppOpenQ != NULL);                                            \
      _pOpenN->pNextOpen = (*_ppOpenQ)->pNextOpen;                            \
      (*_ppOpenQ)->pNextOpen = _pOpenN;                                       \
    }                                                                         \
    else {                                                                    \
      /*                                                                      \
       *  Make the new node the first one in the list.                        \
       */                                                                     \
      _pOpenN->pNextOpen = *_ppOpenQ;                                         \
      *_ppOpenQ = _pOpenN;                                                    \
    }                                                                         \
}


/***LP  semDelFromOpenQ - Remove an OpenQ Node from an Open Queue
 *
 *      semDelFromOpenQ removes a specified node from a given
 *      open queue.
 *
 *      NOTE: Node must be freed by the calling routine.  This routine
 *            only removes the node from the linked list.
 *
 *      ENTRY   _pCurOpenN - pointer to the OpenQ node to remove
 *              _pPreOpenN - pointer to the previous OpenQ node
 *              _ppOpenQ   - address of pointer to start of OpenQ list
 *
 *      EXIT    Success - node deleted from open queue
 *
 *      DEFINED AS A MACRO
 *
 *      PSEUDO CODE
 *
 *      If (first one in list)
 *        remove first node
 *      Else                                       // middle of list
 *        remove node from middle of list
 *      Endif
 */

#define semDelFromOpenQ(_pCurOpenN,_pPreOpenN,_ppOpenQ)                       \
                                                                              \
{                                                                             \
    tkAssert(*_ppOpenQ != NULL);                                              \
    tkAssert(_pCurOpenN != NULL);                                             \
                                                                              \
    if (_pPreOpenN == NULL) {                                                 \
      /*                                                                      \
       *  Delete the first node in the list.                                  \
       */                                                                     \
      *_ppOpenQ = _pCurOpenN->pNextOpen;                                      \
    }                                                                         \
    else {                                                                    \
      /*                                                                      \
       *  Delete from the middle or end of the list.                          \
       */                                                                     \
      _pPreOpenN->pNextOpen = _pCurOpenN->pNextOpen;                          \
    }                                                                         \
    semAssertOpenQSig(_pCurOpenN);                                            \
}


/***LP  semShiftSemRec - Remove/Shift Handles in SemRecord of Mux
 *
 *      semShiftSemRec deletes a mutex/event handle from the
 *      given semrecord, and then shifts the semrecord entries.
 *
 *      ENTRY   _pMux      - pointer to the muxwait
 *              _ulSRIndex - index into SemRecord
 *              _hsem      - handle of semaphore to delete
 *
 *      EXIT    Success - semrecord is up to date
 *
 *      DEFINED AS A MACRO
 *
 *      PSEUDO CODE
 *
 *      If (pSemRec of mux not empty)                   // shift handles
 *        _ulSRIndex position in semrecord = SEM_EMPTY_SEMREC
 *        remove MuxQ node from MuxQ of event/mutex removed
 *        For (each array position greater than _ulSRIndex)
 *          copy semrecord at (postion + 1) to position
 *        Endfor
 *      Endif
 *      decrement cSemRec in mux
 */

#define semShiftSemRec(_pMux,_ulSRIndex,_hsem)                                \
                                                                              \
{                                                                             \
    PSEMRECORD _pSRPtr;          /* ptr to SemRecord */                       \
    PSEMRECORD _pEndSRPtr;       /* ptr to last SemRecord */                  \
                                                                              \
                                                                              \
    /*                                                                        \
     *  Remove one of the semrecord entries from the mux list by shifting     \
     *  all other semrecord entries by one.  Also, the mux queue node must    \
     *  be removed from the semaphore being deleted.                          \
     */                                                                       \
    if ((_pSRPtr = ((PMUXUN)_pMux)->ShMux.pSemRec) != NULL) {                 \
      tkAssert(_ulSRIndex <= (((PMUXUN)_pMux)->ShMux.pSemRec[0].ulUser));     \
      semDelFromMuxQ(_hsem,_pMux);                /* delete muxq node */      \
                                                                              \
      _pEndSRPtr = _pSRPtr + ((PMUXUN)_pMux)->ShMux.cSemRec;                  \
      _pSRPtr += _ulSRIndex;                                                  \
      for (; _pSRPtr < _pEndSRPtr; _pSRPtr++) {                               \
         *_pSRPtr = *(_pSRPtr + 1);                                           \
      }                                                                       \
      _pSRPtr->hsemCur = SEM_EMPTY_SEMREC;                                    \
    }                                                                         \
                                                                              \
    /*                                                                        \
     *  Decrement the count of the number of semrecord entries in the mux.    \
     */                                                                       \
    (((PMUXUN)_pMux)->ShMux.cSemRec)--;                                       \
}


/***LP  semAddSigInMux - Add handle signature to SemRecord Entries.
 *
 *      semAddSigInMux reverses semChkSigInMux by ORing the handle
 *      signature back into the semrec entries. It assumes cSemRec is
 *      correct and that pSemRec is a user SemRec (e.g. no special entry 0).
 *
 *      ENTRY   _pSemRec - pointer to the list of semrecords in mux
 *              _cSemRec - number of semaphores in _pSemRec
 *
 *      EXIT    Success - signatures added to semrec entries
 *
 *      DEFINED AS A MACRO
 *
 *      PSUEDO CODE
 *
 *      For (each handle in the semrec)
 *        OR in the signature
 *      Endfor
 */

#define semAddSigInMux(_pSemRec,_cSemRec)                                     \
                                                                              \
{                                                                             \
    register PSEMRECORD _pEndSemRec;        /* ptr to last semrec */          \
                                                                              \
                                                                              \
    _pEndSemRec = _pSemRec + _cSemRec;                                        \
    for (; _pSemRec < _pEndSemRec; _pSemRec++) {                              \
      semAssertNoSigBits(_pSemRec->hsemCur);                                  \
      _pSemRec->hsemCur = semAddSigBits(_pSemRec->hsemCur);                   \
    }                                                                         \
}


/***LP  semGetMuxQWtr - Get Mux Semaphore Waiting On MuxQ
 *
 *      semGetMuxQWtr gets a mux semaphore waiting on a mux
 *      queue.  This routine will return the pointer to the found
 *      MuxQ node.
 *
 *      ENTRY   _pMux     - pointer to the mux semaphore
 *              _ulHandle - handle of the mutex/event
 *              _ppMuxQN  - pointer filled in with the found MuxQ node
 *                          filled in with null if not found
 *
 *      EXIT    Success - mux queue node is returned
 *
 *      DEFINED AS A MACRO
 *
 *      PSEUDO CODE
 *
 *      search through MuxQ list of event/mutex for node pointing to mux
 *      If (found)
 *        pMuxQN = pointer to found MuxQ node
 *      Else
 *        pMuxQN = NULL
 *      Endif
 */

#define semGetMuxQWtr(_pMux,_ulHandle,_ppMuxQN)                               \
                                                                              \
{                                                                             \
    ULONG _ulIndex;                      /* array index */                    \
                                                                              \
                                                                              \
    _ulIndex = semGetIndex(_ulHandle);                                        \
    if (semIfShared(_ulHandle))                         /* shared */          \
      *_ppMuxQN = ((PSHUN)pShSemTbl[_ulIndex])->SMtx.pMuxQ;                   \
    else                                                /* private */         \
      *_ppMuxQN = ((PPRUN)pPTDACur->pPrSemTbl[_ulIndex])->PMtx.pMuxQ;         \
                                                                              \
    /*                                                                        \
     *  Search down the mux queue list until the mux queue that points        \
     *  to _pMux is found.                                                    \
     */                                                                       \
    while ((*_ppMuxQN != NULL) && ((*_ppMuxQN)->pMux != _pMux))               \
      *_ppMuxQN = (*_ppMuxQN)->pNextMux;                                      \
}


/***LP  semIfMuxFree - Check If All Semaphores In Mux Are Free
 *
 *      semIfMuxFree checks to see if all of the semaphores
 *      in a mux are either posted or unowned.
 *
 *      ENTRY   _pMux      - pointer to the mux semaphore
 *              _pusCheck  - entry: 0 if wait any, 1 if wait all
 *                           exit:  filled in with:
 *                                    1 if mux is free
 *                                    0 if mux is not free
 *              _usWaitAll - 0 if wait any mux
 *                           1 if wait all mux
 *              _usMtxMux  - 0 if event mux
 *                           1 if mutex mux
 *
 *      EXIT    Success - NO_ERROR
 *              Failure - ERROR_MUTEX_OWNED
 *                        ERROR_SEM_OWNER_DIED
 *
 *      DEFINED AS A MACRO
 *
 *      PSEUDO CODE
 *
 *      If (event semaphore)
 *        If (wait all mux)
 *          For (each event in semrecord of mux)
 *            If (reset)
 *              *pusCheck = FALSE
 *              break
 *            Endif
 *          Endfor
 *        Else                             // wait any mux
 *          For (each event in semrecord of mux)
 *            If (posted)
 *              pSemRec[0].hsemCur = current event
 *              *pusCheck = TRUE
 *              break
 *            Endif
 *          Endfor
 *        Endif
 *      Else                               // mutex semaphore
 *        If (wait all mux)
 *          For (each mutex in semrecord of mux)
 *            If (owner died)
 *              return (ERROR_SEM_OWNER_DIED)
 *            Endif
 *            If (mutex owned by current thread)
 *              return (ERROR_MUTEX_OWNED)
 *            Endif
 *            If (mutex is owned)
 *              *pusCheck = FALSE
 *              break
 *            Endif
 *          Endfor
 *        Else                             // wait any mux)
 *          For (each mutex in semrecord of mux)
 *            If (owner died)
 *              return (ERROR_SEM_OWNER_DIED)
 *            Endif
 *            If (mutex owned by current thread)
 *              return (ERROR_MUTEX_OWNED)
 *            Endif
 *            If (mutex is not owned)
 *              pSemRec[0].hsemCur = index of current mutex
 *              *pusCheck = TRUE
 *              break
 *            Endif
 *          Endfor
 *        Endif
 *      Endif
 *      If (wait all mux)
 *        set last freed to be last sem in semrecord
 *      Endif
 */

#define semIfMuxFree(_pMux,_pusCheck,_usWaitAll,_usMtxMux)                    \
                                                                              \
{                                                                             \
    ULONG _ulHandle;              /* handle of event or mutex in mux */       \
    ULONG _ulIndex;               /* array index */                           \
    PSEM _pSRSem;                 /* ptr to event or mutex from mux */        \
    register ULONG _ulCt;         /* loop counter */                          \
    register PSEMRECORD _pSemRec; /* ptr to semrecord of mux */               \
    register USHORT _cSemRec;     /* number of semrecords in mux */           \
                                                                              \
                                                                              \
    /*                                                                        \
     *  This macro is called from a NO BLOCK section.  All returns must be    \
     *  preceded by a TKEndNoBlock call.                                      \
     */                                                                       \
    tkAssert(_pMux != NULL);                                                  \
    tkAssert(((PSHUN)_pMux)->SMux.usFlags & DCMW_MUX_SEM);                    \
                                                                              \
    _pSemRec = ((PSHUN)_pMux)->SMux.pSemRec;                                  \
    _cSemRec = ((PSHUN)_pMux)->SMux.cSemRec;                                  \
    tkAssert(_cSemRec > 0);                                                   \
    if (_usMtxMux) {                                                          \
      /*                                                                      \
       *  Mux contains Mutex Semaphores.                                      \
       */                                                                     \
      if (_usWaitAll) {                                                       \
        /*                                                                    \
         *  Wait All Mux.                                                     \
         */                                                                   \
        for (_ulCt=1; _ulCt <= _cSemRec; _ulCt++) {                           \
          /*                                                                  \
           *  Get the pointer to the semaphore.                               \
           */                                                                 \
          _ulIndex = semGetIndex((_ulHandle = (++_pSemRec)->hsemCur));        \
          if (semIfShared(_ulHandle))                                         \
            _pSRSem = pShSemTbl[_ulIndex];                                    \
          else                                                                \
            _pSRSem = pPTDACur->pPrSemTbl[_ulIndex];                          \
                                                                              \
          /*                                                                  \
           *  See if mutex is in the owner died state or if it is already     \
           *  owned by the current thread.  If either is true, return an      \
           *  error.                                                          \
           */                                                                 \
          if (((PSHUN)_pSRSem)->SMtx.usFlags & DM_OWNER_DIED) {               \
            TKEndNoBlock();                                                   \
            return (ERROR_SEM_OWNER_DIED);                                    \
          }                                                                   \
          if (((PSHUN)_pSRSem)->SMtx.usSlotNum == pTCBCur->TCBNumber) {       \
            TKEndNoBlock();                                                   \
            return (ERROR_MUTEX_OWNED);                                       \
          }                                                                   \
                                                                              \
          if (!(((PSHUN)_pSRSem)->SMtx.usSlotNum == SEM_MUTEX_UNOWNED)) {     \
            /*                                                                \
             *  Mutex is owned.  Wait All mux is NOT free, so exit.           \
             */                                                               \
            *_pusCheck = FALSE;                                               \
            break;                                                            \
          }                                                                   \
        }                                                                     \
      }                                                                       \
      else {                                                                  \
        /*                                                                    \
         *  Wait Any Mux.                                                     \
         */                                                                   \
        for (_ulCt=1; _ulCt <= _cSemRec; _ulCt++) {                           \
          /*                                                                  \
           *  Get the pointer to the semaphore.                               \
           */                                                                 \
          _ulIndex = semGetIndex((_ulHandle = (++_pSemRec)->hsemCur));        \
          if (semIfShared(_ulHandle))                                         \
            _pSRSem = pShSemTbl[_ulIndex];                                    \
          else                                                                \
            _pSRSem = pPTDACur->pPrSemTbl[_ulIndex];                          \
                                                                              \
          /*                                                                  \
           *  See if mutex is in the owner died state or if it is already     \
           *  owned by the current thread.  If either is true, return an      \
           *  error.                                                          \
           */                                                                 \
          if (((PSHUN)_pSRSem)->SMtx.usFlags & DM_OWNER_DIED) {               \
            TKEndNoBlock();                                                   \
            return (ERROR_SEM_OWNER_DIED);                                    \
          }                                                                   \
          if (((PSHUN)_pSRSem)->SMtx.usSlotNum == pTCBCur->TCBNumber) {       \
            TKEndNoBlock();                                                   \
            return (ERROR_MUTEX_OWNED);                                       \
          }                                                                   \
                                                                              \
          if (((PSHUN)_pSRSem)->SMtx.usSlotNum == SEM_MUTEX_UNOWNED) {        \
            /*                                                                \
             *  Mutex is not owned.  Wait Any mux IS free.  Set the one to    \
             *  wake up to be the current mutex and exit.                     \
             */                                                               \
            ((PSHUN)_pMux)->SMux.pSemRec[0].hsemCur = _ulCt;                  \
            *_pusCheck = TRUE;                                                \
            break;                                                            \
          }                                                                   \
        }                                                                     \
      }                                                                       \
    }                                                                         \
    else {                                                                    \
      /*                                                                      \
       *  Mux contains Event Semaphores.                                      \
       */                                                                     \
      if (_usWaitAll) {                                                       \
        /*                                                                    \
         *  Wait All Mux.                                                     \
         */                                                                   \
        for (_ulCt=1; _ulCt <= _cSemRec; _ulCt++) {                           \
          /*                                                                  \
           *  Get the pointer to the semaphore.                               \
           */                                                                 \
          _ulIndex = semGetIndex((_ulHandle = (++_pSemRec)->hsemCur));        \
          if (semIfShared(_ulHandle))                                         \
            _pSRSem = pShSemTbl[_ulIndex];                                    \
          else                                                                \
            _pSRSem = pPTDACur->pPrSemTbl[_ulIndex];                          \
                                                                              \
          tkAssert(((PSHUN)_pSRSem)->SEvt.usFlags & DCE_EVENT_SEM);           \
          if (!(((PSHUN)_pSRSem)->SEvt.usFlags & DE_POSTED)) {                \
            /*                                                                \
             *  Event is reset.  Wait All mux is NOT free, so exit.           \
             */                                                               \
            *_pusCheck = FALSE;                                               \
            break;                                                            \
          }                                                                   \
        }                                                                     \
      }                                                                       \
      else {                                                                  \
        /*                                                                    \
         *  Wait Any Mux.                                                     \
         */                                                                   \
        for (_ulCt=1; _ulCt <= _cSemRec; _ulCt++) {                           \
          /*                                                                  \
           *  Get the pointer to the semaphore.                               \
           */                                                                 \
          _ulIndex = semGetIndex((_ulHandle = (++_pSemRec)->hsemCur));        \
          if (semIfShared(_ulHandle))                                         \
            _pSRSem = pShSemTbl[_ulIndex];                                    \
          else                                                                \
            _pSRSem = pPTDACur->pPrSemTbl[_ulIndex];                          \
                                                                              \
          tkAssert(((PSHUN)_pSRSem)->SEvt.usFlags & DCE_EVENT_SEM);           \
          if (((PSHUN)_pSRSem)->SEvt.usFlags & DE_POSTED) {                   \
            /*                                                                \
             *  Event is posted.  Wait Any mux IS free.  Set the one to       \
             *  wake up to be the current event and exit.                     \
             */                                                               \
            ((PSHUN)_pMux)->SMux.pSemRec[0].hsemCur = _ulCt;                  \
            *_pusCheck = TRUE;                                                \
            break;                                                            \
          }                                                                   \
        }                                                                     \
      }                                                                       \
    }                                                                         \
                                                                              \
    /*                                                                        \
     *  Set last freed to be last sem in semrecord.                           \
     */                                                                       \
    if (_usWaitAll)                                                           \
      ((PSHUN)_pMux)->SMux.pSemRec[0].hsemCur = _cSemRec;                     \
}


/***LP  semBlockOnMux - Block Current Thread On MuxWait Semaphore
 *
 *      semBlockOnMux blocks the current thread on a mux semaphore.
 *      If other threads have blocked on this same mux, then it
 *      simply increments the wait count of the mux.  However,
 *      if the mux is being blocked on for the first time, the
 *      mux queue node is added to the MuxQ list and the wait count
 *      of the mux is set to one.  The appropriate error codes are
 *      returned.
 *
 *      ENTRY   _pMux     - pointer to the mux semaphore
 *              _ppMuxQN  - address of pointer to list of allocated MuxQ nodes
 *              _ulTimeout - timeout for block
 *
 *      EXIT    Success - NO_ERROR
 *              Failure - ERROR_INTERRUPT
 *                        ERROR_TIMEOUT
 *
 *      DEFINED AS A MACRO
 *
 *      PSEUDO CODE
 *
 *      increment wait count of mux
 *      For (each semaphore in semrecord of mux)
 *        If (MuxQ node not already on MuxQ of event/mutex)
 *          pCurMQ = first node off of allocated MuxQ list
 *          If (hmux is shared)                      // shared
 *            pCurMQ->pMux.pSharedMux = pointer to mux
 *          Else                                             // private
 *            pCurMQ->pMux.pPrivateMux = pointer to mux
 *          add MuxQ node to MuxQ of event/mutex
 *        Endif
 *      Endfor
 *      call TKSleep on mux
 *      check all semrecord entries
 *      return (NO_ERROR)
 */

#define semBlockOnMux(_pMux,_ppMuxQN,_ulTimeout)                              \
                                                                              \
{                                                                             \
    PMUXQ _pCurMQ;                    /* ptr to current MuxQ node */          \
    register PSEMRECORD _pSemRec;      /* ptr to semrecord of mux */          \
    register PSEMRECORD _pEndSemRec;   /* ptr to last semrecord */            \
    ULONG _rc;                        /* return code */                       \
                                                                              \
    /*                                                                        \
     *  This macro is called from a NO BLOCK section.  All returns must be    \
     *  preceded by a TKEndNoBlock call.                                      \
     */                                                                       \
    (((PMUXUN)_pMux)->ShMux.usWaitCt)++;                                      \
    _pSemRec = ((PMUXUN)_pMux)->ShMux.pSemRec;                                \
    _pEndSemRec = _pSemRec + ((PMUXUN)_pMux)->ShMux.cSemRec;                  \
    _pSemRec++;                                                               \
    for (; _pSemRec <= _pEndSemRec; _pSemRec++) {                             \
      /*                                                                      \
       *  For each semrecord in the mux list, if a mux queue node does not    \
       *  exist on the event/mutex mux queue, then add a new mux queue node.  \
       */                                                                     \
      semGetMuxQWtr(_pMux,_pSemRec->hsemCur,&_pCurMQ);                        \
      if (_pCurMQ == NULL) {                                                  \
        _pCurMQ = *_ppMuxQN;            /* new MuxQ node */                   \
        *_ppMuxQN = (*_ppMuxQN)->pNextMux;                                    \
        _pCurMQ->pMux = _pMux;                                                \
        semAddToMuxQ(_pSemRec->hsemCur, _pCurMQ);                             \
      }                                                                       \
    }                                                                         \
                                                                              \
    /*                                                                        \
     *  Block the current thread.                                             \
     */                                                                       \
    TKEndNoBlock();                             /* allow block */             \
                                                                              \
    /***PV - Perfview measurement hook                                        \
     */                                                                       \
/* b734380         */                                                         \
/*if(fPerfview) */ /* Is perfview up ? */                   /* aa04504 */     \
/*   PvwStartBlk((PULONG)(((PMUXUN)_pMux)->ShMux.pPvwBlk));*//* b724626 */    \
    _rc = TKSleep(_pMux,_ulTimeout,0,WAKEUP_SINGLE);                          \
                                                                              \
    /***PV - Perfview measurement hook                                        \
     */                                                                       \
/* b734380          */                                                        \
/*if(fPerfview)*/  /* Is perfview up ? */                   /* aa04504 */     \
/*  PvwEndBlk((PULONG)(((PMUXUN)_pMux)->ShMux.pPvwBlk));*/ /* b724626 */      \
    semUpdateFreeMuxQ(0);                                                     \
    TKBeginNoBlock();                           /* may not block */           \
                                                                              \
    if (_rc != NO_ERROR) {                                                    \
      if (((PSHUN)_pMux)->SMux.usWaitCt > 0)                                  \
        semUpdateAllMuxQ(_pMux);                                              \
      TKEndNoBlock();                                                         \
      return (_rc);                                                           \
    }                                                                         \
    /*                                                                        \
     *  Make sure all semrecord entries are still valid after the block.      \
     */                                                                       \
    if ((_rc = semChkSRInMux(((PMUXUN)_pMux)->ShMux.pSemRec,                  \
                            ((PMUXUN)_pMux)->ShMux.cSemRec,                   \
                            ((PMUXUN)_pMux)->ShMux.usFlags)) != NO_ERROR) {   \
      TKEndNoBlock();                                                         \
      return (_rc);                                                           \
    }                                                                         \
}


/***LP  semIfErrAddMux - Check for Error in New Sem to be Added to Mux
 *
 *      semIfErrAddMux checks for any errors in the new semaphore
 *      about to be added to a particular mux.  The appropriate error
 *      is returned to the caller.
 *
 *      ENTRY   _pMux   - pointer to the mux semaphore
 *              _hsemAdd - handle of the semaphore to add
 *
 *      EXIT    Success - NO_ERROR
 *              Failure - ERROR_DUPLICATE_HANDLE
 *                        ERROR_INVALID_HANDLE
 *                        ERROR_TOO_MANY_SEMAPHORES
 *                        ERROR_WRONG_TYPE
 *
 *      DEFINED AS A MACRO
 *
 *      PSEUDO CODE
 *
 *      If (number of semaphores in mux >= SEM_MAXSEMS)
 *        return (ERROR_TOO_MANY_SEMAPHORES)
 *      Endif
 *      If (handle already exists in semrecord)
 *        return (ERROR_DUPLICATE_HANDLE)
 *      Endif
 *      If (_hsemAdd is a shared semaphore)       // check if open
 *        If (current pid does not have _hsemAdd open)
 *          return (ERROR_INVALID_HANDLE)
 *        Endif
 *      Else                                      // private semaphore
 *        If (pPTDACur->pPrSemTbl[_hsemAdd] == NULL)
 *          return (ERROR_INVALID_HANDLE)
 *        Endif
 *      Endif
 *      If (_hsemAdd NOT the same type as others in mux)
 *        return (ERROR_WRONG_TYPE)
 *      Endif
 *      return (NO_ERROR)
 */

#define semIfErrAddMux(_pMux,_hsemAdd)                                        \
                                                                              \
{                                                                             \
    ULONG _ulSemType;                  /* type of semaphore */                \
    register ULONG _ulAddInd;          /* index of sem to add */              \
                                                                              \
                                                                              \
    /*                                                                        \
     *  This macro is called from a NO BLOCK section.  All returns must be    \
     *  preceded by a TKEndNoBlock call.                                      \
     */                                                                       \
                                                                              \
    /*                                                                        \
     *  Make sure the limit of semaphores allowed in a mux has not been       \
     *  reached and check that the new semaphore handle is not a duplicate.   \
     */                                                                       \
    _ulAddInd = semGetIndex(_hsemAdd);                                        \
    if (((PSHUN)_pMux)->SMux.cSemRec >= SEM_MAXSEMS) {                        \
      TKEndNoBlock();                                                         \
      return (ERROR_TOO_MANY_SEMAPHORES);                                     \
    }                                                                         \
    if (semIfHdlInSR(_pMux,_hsemAdd,NULL) == NO_ERROR) {                      \
      TKEndNoBlock();                                                         \
      return (ERROR_DUPLICATE_HANDLE);                                        \
    }                                                                         \
                                                                              \
    if (semIfShared(_hsemAdd)) {                                              \
      /*                                                                      \
       *  Shared semaphore is to be added to the mux.                         \
       *  Make sure the new semaphore is open in the current process and is   \
       *  not a muxwait semaphore.                                            \
       */                                                                     \
      if ((_ulAddInd >= pPTDACur->ulShBmpSize) ||                             \
          (_ulAddInd >= ulShTblSize) ||                                       \
          (!semGetBit(_ulAddInd,pPTDACur->pShSemBmp))) {                      \
        TKEndNoBlock();                                                       \
        return (ERROR_INVALID_HANDLE);                                        \
      }                                                                       \
      if (((PSHUN)pShSemTbl[_ulAddInd])->SMux.usFlags & DCMW_MUX_SEM) {       \
        TKEndNoBlock();                                                       \
        return (ERROR_WRONG_TYPE);                                            \
      }                                                                       \
    }                                                                         \
    else {                                                                    \
      /*                                                                      \
       *  Private semaphore is to be added to the mux.                        \
       *  Make sure the new semaphore exists in the current process and is    \
       *  not a muxwait semaphore.                                            \
       */                                                                     \
      if ((_ulAddInd >= pPTDACur->ulPrTblSize) ||                             \
          (pPTDACur->pPrSemTbl[_ulAddInd] == NULL)) {                         \
        TKEndNoBlock();                                                       \
        return (ERROR_INVALID_HANDLE);                                        \
      }                                                                       \
      if (((PPRUN)pPTDACur->pPrSemTbl[_hsemAdd])->PMux.usFlags &              \
           DCMW_MUX_SEM) {                                                    \
        TKEndNoBlock();                                                       \
        return (ERROR_WRONG_TYPE);                                            \
      }                                                                       \
                                                                              \
      /*                                                                      \
       *  Private event/mutex cannot be added to a shared mux.                \
       */                                                                     \
      if (((PSHUN)_pMux)->SMux.usFlags & DC_SEM_SHARED) {                     \
        TKEndNoBlock();                                                       \
        return (ERROR_WRONG_TYPE);                                            \
      }                                                                       \
    }                                                                         \
                                                                              \
    /*                                                                        \
     *  Compare types.  Make sure the new one is of the correct type.         \
     */                                                                       \
    if (((PSHUN)_pMux)->SMux.cSemRec > 0) {                                   \
      _ulSemType = semGetType(((PSHUN)_pMux)->SMux.pSemRec[1].hsemCur);       \
      if (semGetType(_hsemAdd) != _ulSemType) {                               \
        TKEndNoBlock();                                                       \
        return (ERROR_WRONG_TYPE);                                            \
      }                                                                       \
    }                                                                         \
}


/***LP  semIfOpenByAll - Check If Semaphore Open By All Waiting Threads
 *
 *      semIfOpenByAll checks to see if the semaphore is open
 *      by all threads waiting on the semaphore.  If any threads do not have
 *      all semaphores open, then they are awakened.
 *
 *      ENTRY   _pMux    - pointer to the mux
 *              _hsemAdd - handle of semaphore to check
 *
 *      EXIT    Success - some threads may have been awakened
 *
 *      DEFINED AS A MACRO
 *
 *      PSEUDO CODE
 *
 *      call TKQueryWakeup for mux (get pTCB)
 *      For (each pTCB in list)
 *        If (_hsemAdd is shared)
 *          If (current pid does not have sem open)
 *            call TKWakeThread for this pTCB
 *        Else                                       // private
 *          If (pTCB->pPTDACur->pPrSemTbl[_hsemAdd] == NULL)
 *            call TKWakeThread for this pTCB
 *          Endif
 *        Endif
 *      Endfor
 */

#define semIfOpenByAll(_pMux,_hsemAdd)                                        \
                                                                              \
{                                                                             \
    register PTCB _pTCB;                 /* ptr to a TCB */                   \
    ULONG _ulIndex;                      /* array index */                    \
                                                                              \
                                                                              \
    /*                                                                        \
     *  Get waiters on mux.                                                   \
     */                                                                       \
    _pTCB = TKQueryWakeup(_pMux,SEM_WAKE_ALL);                                \
                                                                              \
    _ulIndex = semGetIndex(_hsemAdd);                                         \
    if (semIfShared(_hsemAdd)) {                                              \
      /*                                                                      \
       *  Shared semaphore.  For each waiter on the mux, make sure the        \
       *  semaphore to be added is open in the waiter's process.  If it is    \
       *  not open, then wake up the waiter.                                  \
       */                                                                     \
      for (; _pTCB != NULL; _pTCB = _pTCB->TCBpTCBWakeup) {                   \
        if ((_ulIndex >= _pTCB->TCBpPTDA->ulShBmpSize) ||                     \
            (!semGetBit(_hsemAdd,_pTCB->TCBpPTDA->pShSemBmp))) {              \
          semUpdateAllMuxQ(_pMux);                                            \
          TKWakeThread(_pTCB);                                                \
        }                                                                     \
      }                                                                       \
    }                                                                         \
    else {                                                                    \
      /*                                                                      \
       *  Private semaphore.  For each waiter on the mux, make sure the       \
       *  semaphore to be added exists in the waiter's process.  If it does   \
       *  not exist, then wake up the waiter.                                 \
       */                                                                     \
      for (; _pTCB != NULL; _pTCB = _pTCB->TCBpTCBWakeup) {                   \
        if (_pTCB->TCBpPTDA->pPrSemTbl[_ulIndex] == NULL) {                   \
          semUpdateAllMuxQ(_pMux);                                            \
          TKWakeThread(_pTCB);                                                \
        }                                                                     \
      }                                                                       \
    }                                                                         \
}


/***LP  semChkWakeOnAdd - Check If Mux Free After An Add
 *
 *      semChkWakeOnAdd checks to see if a mux is free after an event or
 *      mutex is added to the mux.  If there are any waiters on the mux,
 *      the appropriate waiting thread(s) will be awakened if the mux
 *      is free.
 *
 *      ENTRY   _pMux   - pointer to the mux semaphore
 *              _hsemAdd - handle of the added semaphore
 *
 *      EXIT    Success - wakes up waiter(s) if mux is free
 *              Failure - none
 *
 *      DEFINED AS A MACRO
 *
 *      PSEUDO CODE
 *
 *      pTCB = call TKQueryWakeup with _pMux
 *      If (mutex mux)                                 // mutex
 *        If (mutex is unowned)                // unowned
 *          update mux queues and wait count of mux
 *          usSlotNum = pTCB->TCBNumber
 *          usRequestCt = 1
 *          pTCB->TCBpPTDA->ulMtxOwned++
 *          pSemRec[0].hsemCur = _cSemRec;
 *          call TKWakeThread for pTCB
 *        Endif
 *      Else                                           // event
 *        If (usFlags & DE_POSTED)                     // posted
 *          pSemRec[0].hsemCur = _cSemRec
 *          delete all mux queue nodes for mux
 *          call TKWakeup for all threads
 *        Endif
 *      Endif
 */

#define semChkWakeOnAdd(_pMux,_hsemAdd)                                       \
                                                                              \
{                                                                             \
    register PSEM _pSemAdd;     /* ptr to added semaphore */                  \
    PTCB _pTCB;                /* pointer to a TCB */                         \
    USHORT _cSemRec;           /* number of semaphores in mux */              \
    ULONG _ulIndex;            /* array index */                              \
                                                                              \
                                                                              \
    /*                                                                        \
     *  Get the pointer to the semaphore.  Upon entry to this routine, we     \
     *  know that the muxwait is a "wait any" mux.                            \
     */                                                                       \
    _ulIndex = semGetIndex(_hsemAdd);                                         \
    if (semIfShared(_hsemAdd))                                                \
      _pSemAdd = pShSemTbl[_ulIndex];                                         \
    else                                                                      \
      _pSemAdd = pPTDACur->pPrSemTbl[_ulIndex];                               \
                                                                              \
    tkAssert(((PSHUN)_pMux)->SMux.usWaitCt > 0);                              \
    _cSemRec = ((PSHUN)_pMux)->SMux.cSemRec;                                  \
    if (((PSHUN)_pMux)->SMux.usFlags & DMW_MTX_MUX) {                         \
      /*                                                                      \
       *  Mutex mux.  If the mutex is unowned, then one of the threads        \
       *  waiting on the wait any muxwait may wake up.                        \
       */                                                                     \
      if (((PSHUN)_pSemAdd)->SMtx.usSlotNum == SEM_MUTEX_UNOWNED) {           \
        _pTCB = TKQueryWakeup(_pMux,WAKEUP_SINGLE);                           \
        tkAssert(_pTCB != NULL);                                              \
        semUpdateAllMuxQ(_pMux);                                              \
        tkAssert(((PSHUN)_pSemAdd)->SMtx.usFlags & DCM_MUTEX_SEM);            \
        tkAssert(((PSHUN)_pSemAdd)->SMtx.usRequestCt == 0);                   \
        tkAssert(((PSHUN)_pSemAdd)->SMtx.usSlotNum == SEM_MUTEX_UNOWNED);     \
        ((PSHUN)_pSemAdd)->SMtx.usSlotNum = _pTCB->TCBNumber;                 \
        ((PSHUN)_pSemAdd)->SMtx.usRequestCt = 1;                              \
        _pTCB->TCBpPTDA->ulMtxOwned++;                                        \
        ((PSHUN)_pMux)->SMux.pSemRec[0].hsemCur = _cSemRec;                   \
        TKWakeThread(_pTCB);                                                  \
      }                                                                       \
    }                                                                         \
    else {                                                                    \
      /*                                                                      \
       *  Event mux.                                                          \
       */                                                                     \
      if (((PSHUN)_pSemAdd)->SMtx.usFlags & DE_POSTED) {                      \
        /*                                                                    \
         *  The event is posted, so all of the threads waiting on the         \
         *  wait any muxwait may wake up.                                     \
         */                                                                   \
        tkAssert(((PSHUN)_pMux)->SMux.usWaitCt > 0);                          \
        ((PSHUN)_pMux)->SMux.pSemRec[0].hsemCur = _cSemRec;                   \
        semDelAllMuxQ(_pMux);                                                 \
        TKWakeup(_pMux,SEM_WAKE_ALL,NULL);                                    \
      }                                                                       \
    }                                                                         \
}


/***LP  semChkWakeOnDel - Check If Mux Free After A Delete
 *
 *      semChkWakeOnDel checks to see if a mux is free after an event or
 *      mutex is deleted from a mux.  If there are any waiters on the mux,
 *      the appropriate waiting thread(s) will be awakened if the mux
 *      is free.
 *
 *      ENTRY   _pMux - pointer to the mux semaphore
 *
 *      EXIT    Success - wakes up waiter(s) if mux is free
 *
 *      DEFINED AS A MACRO
 *
 *      PSEUDO CODE
 *
 *      If (mutex mux)                              // mutex
 *        If (all mutex in mux are free)
 *          pTCB = call TKQueryWakeup with _pMux
 *          update all mux queue nodes and wait count
 *          assign ownership of all mutex in mux to pTCB
 *          pSemRec[0].hsemCur = _cSemRec
 *          call TKWakeThread on pTCB
 *        Endif
 *      Else                                        // event
 *        If (ALL events are now posted)
 *          remove all MuxQ nodes associated with this mux
 *          pSemRec[0].hsemCur = _cSemRec
 *          call TKWakeup on ALL threads waiting on mux
 *        Endif
 *      Endif
 */

#define semChkWakeOnDel(_pMux)                                                \
                                                                              \
{                                                                             \
    PTCB _pTCB;                     /* ptr to a TCB */                        \
    register PSEMRECORD _pSemRec;   /* ptr to semaphores in mux */            \
    register USHORT _cSemRec;       /* number of semaphores in mux */         \
                                                                              \
                                                                              \
    /*                                                                        \
     *  Upon entry to this routine, we know that the muxwait is a             \
     *  "wait all" mux.                                                       \
     */                                                                       \
    tkAssert(((PSHUN)_pMux)->SMux.usWaitCt > 0);                              \
    _pSemRec = ((PSHUN)_pMux)->SMux.pSemRec;                                  \
    _cSemRec = ((PSHUN)_pMux)->SMux.cSemRec;                                  \
    if (((PSHUN)_pMux)->SMux.usFlags & DMW_MTX_MUX) {                         \
      /*                                                                      \
       *  Mutex mux.  If all mutex are now unowned, then one of the threads   \
       *  waiting on the wait all muxwait may wake up.                        \
       */                                                                     \
      if (semIfAllMtxFree(_pSemRec,_cSemRec) == NO_ERROR) {                   \
        _pTCB = TKQueryWakeup(_pMux,WAKEUP_SINGLE);                           \
        tkAssert(_pTCB != NULL);                                              \
        semUpdateAllMuxQ(_pMux);                                              \
        semOwnMtxInSR(_pSemRec + 1,_pSemRec + _cSemRec,_pTCB);                \
        _pSemRec[0].hsemCur = _cSemRec;                                       \
        TKWakeThread(_pTCB);                                                  \
      }                                                                       \
    }                                                                         \
    else {                                                                    \
      /*                                                                      \
       *  Event mux.  If all of the events in the mux are now posted, then    \
       *  all threads waiting on the mux may now wake up.                     \
       */                                                                     \
      if (semIfAllEvtsPosted(_pSemRec,_cSemRec) == NO_ERROR) {                \
        semDelAllMuxQ(_pMux);                                                 \
        _pSemRec[0].hsemCur = _cSemRec;                                       \
        TKWakeup(_pMux,SEM_WAKE_ALL,NULL);                                    \
      }                                                                       \
    }                                                                         \
}


/***LP  semUndoWaitMuxMtx - Releases All Mutex Owned By Mux
 *
 *      semUndoMuxMtx releases all mutex acquired by a wait on a mux.
 *      This routine should be called when the WaitMuxWait routine
 *      succeeds in waiting on the mux, but cannot correctly return
 *      information to the caller.
 *
 *      ENTRY   _hmux - handle of muxwait semaphore
 *
 *      EXIT    Success - appropriate mutex are released
 *
 *      DEFINED AS A MACRO
 *
 *      PSEUDO CODE
 *
 *      If (mux contains mutex semaphores)
 *        If (mux is WAIT_ALL)                     // WAIT_ALL
 *          For (each mutex in semrecord of mux)
 *            call SEMReleaseMutexSem on mutex
 *          Endfor
 *        Else                                     // WAIT_ANY
 *          call SEMReleaseMutexSem on ONE mutex acquired
 *        Endif
 *      Endif
 */

#define semUndoWaitMuxMtx(_hmux)                                              \
                                                                              \
{                                                                             \
    register PSEM _pMux;               /* ptr to mux */                       \
    register PSEMRECORD _pSemR;        /* ptr to semrecord of mux */          \
    register PSEMRECORD _pEndSR;       /* ptr to last semrecord of mux */     \
    ULONG _ulIndex;                    /* array index */                      \
    ULONG _rc;                         /* return code */                      \
                                                                              \
                                                                              \
    /*                                                                        \
     *  Get pointer to the mux.                                               \
     */                                                                       \
    _ulIndex = semGetIndex(_hmux);                                            \
    if (semIfShared(_hmux))                                                   \
      _pMux = pShSemTbl[_ulIndex];                                            \
    else                                                                      \
      _pMux = pPTDACur->pPrSemTbl[_ulIndex];                                  \
                                                                              \
    _pSemR = ((PMUXUN)_pMux)->ShMux.pSemRec;                                  \
    if (((PMUXUN)_pMux)->ShMux.usFlags & DMW_MTX_MUX) {                       \
      /*                                                                      \
       *  Mutex mux.                                                          \
       */                                                                     \
      if (((PMUXUN)_pMux)->ShMux.usFlags & DCMW_WAIT_ALL) {                   \
        /*                                                                    \
         *  Wait All mux.  For each mutex in the mux list, release            \
         *  ownership of the mutex.                                           \
         */                                                                   \
        _pEndSR = _pSemR + ((PMUXUN)_pMux)->ShMux.cSemRec;                    \
        _pSemR++;                                                             \
        for (; _pSemR <= _pEndSR; _pSemR++) {                                 \
          _rc = SEMReleaseMutexSem(_pSemR->hsemCur);                          \
          tkAssert(_rc == NO_ERROR);                                          \
        }                                                                     \
      }                                                                       \
      else {                                                                  \
        /*                                                                    \
         *  Wait Any mux.  Release ownership of the one mutex that was        \
         *  obtained by the failed WaitMuxWait call.                          \
         */                                                                   \
        _rc = SEMReleaseMutexSem(_pSemR[_pSemR[0].hsemCur].hsemCur);          \
        tkAssert(_rc == NO_ERROR);                                            \
      }                                                                       \
    }                                                                         \
}


/***LP  semMakeFirstOpenQ - Move an Open Queue Node to Front of Open Queue
 *
 *      semMakeFirstOpenQ moves an existing open queue node to the front
 *      of the open queue.  This is only done when a mutex is placed in
 *      the owner died state.  The open queue at the front of the list
 *      contains the process id of the dead owner.
 *
 *      ENTRY   _pid     - process id of node to move
 *              _ppOpenQ - address of pointer to start of OpenQ list
 *
 *      EXIT    Success - NO_ERROR
 *              Failure - FAILURE
 *
 *      DEFINED AS A MACRO
 *
 *      PSEUDO CODE
 *
 *      search through OpenQ list for _pid
 *      If (not first one in list)
 *        move node to front of list
 *      Endif
 */

#define semMakeFirstOpenQ(_pid,_ppOpenQ)                                      \
                                                                              \
{                                                                             \
    register POPENQ _pCurPtr;          /* ptr to current open queue node */   \
    register POPENQ _pPrePtr = NULL;   /* ptr to previous open queue node */  \
                                                                              \
                                                                              \
    tkAssert(*_ppOpenQ != NULL);                                              \
                                                                              \
    /*                                                                        \
     *  Find correct open queue node.                                         \
     */                                                                       \
    _pCurPtr = *_ppOpenQ;                                                     \
    while ((_pCurPtr != NULL) && (_pCurPtr->pidOpener != _pid)) {             \
      _pPrePtr = _pCurPtr;                                                    \
      _pCurPtr = _pCurPtr->pNextOpen;                                         \
    }                                                                         \
                                                                              \
    tkAssert(_pCurPtr != NULL);                                               \
                                                                              \
    /*                                                                        \
     *  Open queue node is not first in the list.  Must now                   \
     *  make it the first one in the list.                                    \
     */                                                                       \
    if (_pPrePtr != NULL) {                                                   \
      _pPrePtr->pNextOpen = _pCurPtr->pNextOpen;                              \
      _pCurPtr->pNextOpen = *_ppOpenQ;                                        \
      *_ppOpenQ = _pCurPtr;                                                   \
    }                                                                         \
}

