/*static char *SCCSID = "@(#)semmux.c 6.6 92/01/12";*/
#define SCCSID  "@(#)semmux.c 6.6 92/01/12"

/*
 *      Semaphores - 32-bit MuxWait Semaphore Routines
 *
 *      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
 *
 *      June, 1994 This module was created by Ron Cadima to merge all the
 *         code associated with Mux Wait semaphores into one module and to
 *         optimize the code path in the MuxWait semaphore routines. The code
 *         in this module was originally moved from SEMVERIF.C, SEMALLOC.C
 *         and SEMWRKR.C and then modified.
 *
 *  2/19/96  Change Team 148148 Verify handle when semaphores
 *                              are opened with a null name.
 */

#define INCL_DOSERRORS
#define INCL_TYPES
#include <basedef.h>
#define FNO_NUM FNO_SEMVERIF_C          /* for assertion macros */
#include <fnotask.h>                    /* MUST BE BEFORE osmac.h */
#include <osmac.h>
#include <osconst.h>
#include <ostypes.h>
#include <infoseg.h>
#include <mi.h>
#include <proc.h>
#include <error.h>
#include <sigdef.h>
#include <except.h>
#include <dossymc.h>
#include <seldesc.h>
#include <pgexport.h>
#include <vmexport.h>
#include <vmowner.h>
#include <selxport.h>
#include <tkexport.h>
#include <sem.h>
#include <screen.h>
#include <curdir.h>
#include <dirent.h>
#include <find.h>
#include <semxport.h>
#include <tkptda.h>
#include <ioexp.h>
#include <bsetib.h>
#include <tklocal.h>
#include <kmode.h>
#include <semlocal.h>
#include <string.h>

#include "..\memory\vmkh.h"         // PERF: defect 98488

/***EP  VR32CreateMuxWaitSem - Create a MuxWait Semaphore
 *
 *      VR32CreateMuxWaitSem creates and opens a muxwait semaphore.  If the
 *      pszName is not null, then the semaphore will be a named shared muxwait
 *      semaphore.  If the pszName is null and the DC_SEM_SHARED flag is set,
 *      then the semaphore will be an anonymous shared semaphore.  Otherwise,
 *      the semaphore will be an anonymous private semaphore.  DC_SEM_SHARED
 *      will be ignored if the pszName is not null.  If the semaphore already
 *      exists, then ERROR_DUPLICATE_NAME will be returned.
 *
 *      The named semaphores will be validated by the file system.
 *      Therefore, any name that makes up a valid file name will be a valid
 *      semaphore name.  There is a limit of 255 characters per semaphore
 *      name.  When specifying the name for a system semaphore, the ASCIIZ
 *      name string must include the prefix \SEM32\.
 *
 *      All muxwait semaphores must contain either ALL mutexes or ALL events.
 *      If the creation of a muxwait is being attempted with a combination of
 *      events and mutexes, the ERROR_WRONG_TYPE return code is returned to
 *      the calling routine.  Both shared and private semaphores may be used
 *      in the same muxwait.  However, the private semaphores will not be
 *      valid between processes, so the muxwait will only be valid in the one
 *      process.
 *
 *      If the DCMW_WAIT_ANY flag is set, then the semaphore is to wait for
 *      any of the event/mutex semaphores in its list to be posted/released.
 *      Once any ONE of the semaphores is posted/released, the thread waiting
 *      on the muxwait can run.  If there is more than one mutex in the list
 *      and one of the mutex semaphores was the one semaphore that cleared,
 *      then the thread will only gain ownership of the one mutex that became
 *      available, NOT all of them.  The semaphores will be checked in the
 *      order in which they are defined in the muxwait's list.  If the
 *      DCMW_WAIT_ALL flag is set, then the semaphore is to wait for ALL of
 *      the event/mutex semaphores in its list to be posted/released.  Once
 *      ALL of the semaphores have been posted/released, the thread waiting
 *      on the muxwait can run.
 *
 *      A muxwait can have a maximum of 64 event/mutex semaphores in its list.
 *      If this maximum is reached, then the ERROR_TOO_MANY_SEMAPHORES
 *      return code is returned to the calling thread.
 *
 *      ENTRY   sef     - system entry frame
 *              pszName - asciiz name of MuxWait, or NULL if
 *                        creating an anonymous MuxWait.
 *              phmux   - pointer to be filled in with handle of new muxwait
 *              cSemRec - count of SEMRECORD entries in pSemRec
 *              pSemRec - array of SEMRECORD entries to put into muxwait
 *              flAttr  - attributes:
 *                          DC_SEM_SHARED
 *                          DCMW_WAIT_ANY
 *                          DCMW_WAIT_ALL
 *
 *      EXIT    Success - NO_ERROR
 *              Failure - ERROR_DUPLICATE_HANDLE
 *                        ERROR_DUPLICATE_NAME
 *                        ERROR_INVALID_HANDLE
 *                        ERROR_INVALID_NAME
 *                        ERROR_INVALID_PARAMETER
 *                        ERROR_SEM_OWNER_DIED
 *                        ERROR_TOO_MANY_HANDLES
 *                        ERROR_TOO_MANY_SEMAPHORES
 *                        ERROR_WRONG_TYPE
 *
 *      PSEUDO CODE
 *
 *      If (flAttr has more than 2 bits set)
 *        return (ERROR_INVALID_PARAMETER)
 *      Endif
 *      If (cSemRec > SEM_MAXSEMS)
 *        return (ERROR_TOO_MANY_SEMAPHORES)
 *      Endif
 *      pSemRecord = NULL
 *      If (cSemRec > 0)                                // semrecord
 *        pSemRecord = allocate array of cSemRec + 1 number of SEMRECORDs
 *        pSemRecord[0].hsemCur = SEM_EMPTY_SEMREC      // copy info into it
 *        pSemRecord[0].ulUser = cSemRec + 1
 *        call TKFuBuff to copy pSemRec into pSemRecord
 *      Endif
 *      If (pszName != NULL)
 *        validate pszName
 *        call semStoreStr to copy pszName into pSemName
 *      Endif
 *      rc = call SEMApiCreateMuxWaitSem with pSemName,pSemRecord,hmuxTmp
 *      If (rc == error)
 *        free pSemName and pSemRecord
 *      Else
 *        call TKSuDWord to copy hmuxTmp to phmux
 *      Endif
 *      return (rc)
 */

ULONG cdecl VR32CreateMuxWaitSem(THUNKSEF sef,       /* system entry frame */
                                 PSZ pszName,        /* semaphore name */
                                 PHMUX phmux,        /* ptr to muxwait handle */
                                 ULONG cSemRec,      /* count of SEMRECORDs */
                                 PSEMRECORD pSemRec, /* array of SEMRECORDs */
                                 ULONG flAttr)       /* attributes */

{
    PSEMRECORD pSemRecord = NULL;  /* ptr to resident semaphore record */
    PSZ pSemName = NULL;           /* ptr to resident buffer for pszName */
    HMUX hmuxTmp;                  /* temporary handle */
    PHMUX pStrMux;                 /* ptr to mux handle in string structure */
    ULONG rc;                      /* return code */


    /*
     *  Check flag attributes.
     *  Check 64 limit of semrecord entries.
     */
    if (((flAttr | SEM_MUXFLAGS) != SEM_MUXFLAGS) ||
        (!(((flAttr & DCMW_WAIT_ANY) << 1) ^ (flAttr & DCMW_WAIT_ALL))))
      return (ERROR_INVALID_PARAMETER);
    if (cSemRec > SEM_MAXSEMS)
      return (ERROR_TOO_MANY_SEMAPHORES);

    /*
     *  Verify that user pointers are not in kernel space.
     */
    semChkNotKrnlSpace((PVOID)pszName);
    semChkNotKrnlSpace((PVOID)phmux);
    semChkNotKrnlSpace((PVOID)pSemRec);

    if (cSemRec > 0) {
      /*
       *  Allocate kernel space for semrecord entries.
       *  Copy entries from user space into newly allocated kernel space.
       */
      if ((rc = allocKRHB(((cSemRec + 1)*sizeof(SEMRECORD)),(VMHOB)SEMSEMRECOWNER,
                          SSToDS(&pSemRecord))) != NO_ERROR)
        return (rc);
      pSemRecord[0].hsemCur = SEM_EMPTY_SEMREC;    /* copy info into it */
      pSemRecord[0].ulUser = cSemRec + 1;
      if ((rc = TKFuBuff((PCHAR)(&(pSemRecord[1])),(PCHAR)pSemRec,
                     (cSemRec * sizeof(SEMRECORD)),TK_UM_FATAL)) != NO_ERROR) {
        if (pSemRecord != NULL)
          freeKRHB(pSemRecord);
        return (rc);
      }
    }

    if (pszName != NULL) {
      /*
       *  Validate name string passed in.
       */
      if ((rc = semValidateName(pszName,SSToDS(&pSemName))) != NO_ERROR) {
        if (pSemRecord != NULL)
          freeKRHB(pSemRecord);
        return (rc);
      }
      /*
       *  Set shared flag and store the string node in the string table.
       */
      flAttr |= DC_SEM_SHARED;
      if ((rc = semStoreStr(pSemName,SSToDS(&pStrMux))) != NO_ERROR) {
        if (pSemRecord != NULL)
           freeKRHB(pSemRecord);
        VMFreeKHB(VM_HKH_PUB_SWAPRW,pSemName);
        return (rc);
      }
    }
    /*
     *  Call the allocation/validation layer.
     */
    rc = SEMApiCreateMuxWaitSem(pSemName,SSToDS(&hmuxTmp),cSemRec,
                                  pSemRecord,flAttr);

    if (rc != NO_ERROR) {
      if (pSemRecord != NULL)
        freeKRHB(pSemRecord);
      if (pSemName != NULL) {
        /*
         *  Error.  Free string node from string table and string name
         *  from semaphore structure.
         */
        semStrTblDelete(pSemName);
        VMFreeKHB(VM_HKH_PUB_SWAPRW,pSemName);
      }
    }
    else {
      if (pSemName)
        *pStrMux = semGetIndex(hmuxTmp);
      /*
       *  Copy handle back to user space.
       */
      if ((rc = TKSuDWord(hmuxTmp,phmux)) != NO_ERROR) {
        SEMCloseMuxWaitSem(hmuxTmp);
        /*
         *  The return code from CloseMuxWait is not checked here.
         *  The process is dying, so if the close returns error_sem_busy,
         *  semCleanProcDying will close the semaphore in the current
         *  process.  This could only happen in a race condition where
         *  a named semaphore was opened and waited on by another
         *  process while this process was blocked on the TKSuDWord
         *  (very unlikely).
         */
        return (rc);
      }
    }
#ifdef PMDEBUG
    if (rc == NO_ERROR) {
      if (semIfShared(hmuxTmp))
        ((PSMUX)pShSemTbl[semGetIndex(hmuxTmp)])->pulCreatAddr = phmux;
      else
        ((PPMUX)((pPTDACur->pPrSemTbl)[semGetIndex(hmuxTmp)]))
                ->pulCreatAddr = phmux;
    }
#endif
    return (rc);
}


/***EP  VR32OpenMuxWaitSem - Open a MuxWait Semaphore
 *
 *      VR32OpenMuxWaitSem opens a muxwait semaphore.  DosCreateMuxWaitSem must
 *      create the semaphore before VR32OpenMuxWaitSem can open it.  If the
 *      pszName is not null, then the semaphore is a named shared muxwait
 *      semaphore.  Otherwise, the semaphore will be referenced by the
 *      semaphore handle passed in phmux.  VR32OpenMuxWaitSem only returns the
 *      handle of the semaphore.  It does not test or change any of the
 *      values of the semaphore.  If phmux is not used, then it should be
 *      zero.
 *
 *      The named semaphores will be validated by the file system.
 *      Therefore, any name that makes up a valid file name will be a valid
 *      semaphore name.  There is a limit of 255 characters per semaphore
 *      name.  When specifying the name for a system semaphore, the ASCIIZ
 *      name string must include the prefix \SEM32\.
 *
 *      The system semaphores reside in a memory buffer rather than on a disk
 *      file.  This means that when the last process which has a semaphore
 *      open exits or closes (DosCloseMuxWaitSem) that semaphore, the
 *      semaphore disappears and must be recreated by its next user with
 *      DosCreateMuxWaitSem.
 *
 *      The open/close operations may be nested.  A maximum of 64K opens per
 *      process is allowed for each semaphore at any one time.  However, for a
 *      process to intentionally destroy a semaphore prior to termination, the
 *      number of DosCloseMuxWaitSem calls must equal the number of
 *      DosOpenMuxWaitSem and DosCreateMuxWaitSem calls.
 *
 *      ENTRY   sef     - system entry frame
 *              pszName - asciiz name, NULL => anonymous
 *              phmux   - if psz is NULL, phmux contains the handle of an
 *                        anonymous muxwait created by another process
 *
 *      EXIT    Success - NO_ERROR
 *              Failure - ERROR_INVALID_HANDLE
 *                        ERROR_INVALID_NAME
 *                        ERROR_INVALID_PARAMETER
 *                        ERROR_SEM_NOT_FOUND
 *                        ERROR_TOO_MANY_OPENS
 *
 *      PSEUDO CODE
 *
 *      If (shared)
 *        validate
 *        call semSaveStr to copy pszName into pSemName
 *      Endif
 *      rc = call SEMApiOpenMuxWaitSem with pSemName and hmuxTmp
 *      If (rc == error)
 *        free pSemName
 *      Else
 *        call TKSuDWord to copy hmuxTmp to phmux
 *      Endif
 *      return (rc)
 */

ULONG cdecl VR32OpenMuxWaitSem(THUNKSEF sef,       /* system entry frame */
                               PSZ pszName,        /* semaphore name */
                               PHMUX phmux)        /* ptr to muxwait handle */

{
    PSZ pSemName = NULL;     /* ptr to resident buffer for pszName */
    HMUX hmuxTmp;            /* temporary handle */
    ULONG rc;                /* return code */


    /*
     *  Verify that user pointers are not in kernel space.
     */
    semChkNotKrnlSpace((PVOID)pszName);
    semChkNotKrnlSpace((PVOID)phmux);

    /*
     *  Copy handle into kernel space.
     */
    if ((rc = TKFuDWord(SSToDS(&hmuxTmp),phmux)) != NO_ERROR)
      return (rc);

    if (pszName != NULL) {
      /*
       *  Validate name string passed in.
       */
      if ((rc = semValidateName(pszName,SSToDS(&pSemName))) != NO_ERROR) {
        return (rc);
      }
    }

    /*
     *  Call the allocation/validation layer.
     */
    rc = SEMApiOpenMuxWaitSem(pSemName,SSToDS(&hmuxTmp));

    if (pSemName != NULL)
      VMFreeKHB(VM_HKH_PUB_SWAPRW,pSemName);
    if (rc == NO_ERROR) {
      /*
       *  Copy handle back to user space.
       */
      if ((rc = TKSuDWord(hmuxTmp,phmux)) != NO_ERROR) {
        SEMCloseMuxWaitSem(hmuxTmp);
        /*
         *  The return code from CloseMuxWait is not checked here.
         *  The process is dying, so if the close returns error_sem_busy,
         *  semCleanProcDying will close the semaphore in the current
         *  process.  This could only happen in a race condition where
         *  a named semaphore was opened and waited on by another
         *  process while this process was blocked on the TKSuDWord
         *  (very unlikely).
         */
        return (rc);
      }
    }
    return (rc);
}


/***EP  VR32CloseMuxWaitSem - Close a MuxWait Semaphore
 *
 *      VR32CloseMuxWaitSem closes a muxwait semaphore that was previously
 *      opened via DosOpenMuxWaitSem or DosCreateMuxWaitSem.  If this is the
 *      last reference to this muxwait, then the muxwait semaphore is
 *      destroyed.
 *
 *      The ERROR_SEM_BUSY return code will be returned if the thread is trying
 *      to make the final close of a semaphore that has another thread in the
 *      same process still waiting on it.
 *
 *      The system semaphores reside in a memory buffer rather than on a disk
 *      file.  This means that when the last process which has a semaphore
 *      open exits or closes that semaphore, the semaphore disappears and
 *      must be recreated by its next user with DosCreateMuxWaitSem.
 *
 *      The open/close operations may be nested.  A maximum of 64K opens per
 *      process is allowed for each semaphore at any one time.  However, for a
 *      process to intentionally destroy a semaphore prior to termination, the
 *      number of DosCloseMuxWaitSem calls must equal the number of
 *      DosOpenMuxWaitSem and DosCreateMuxWaitSem calls.
 *
 *      ENTRY   sef  - system entry frame
 *              hmux - handle of a muxwait to close
 *
 *      EXIT    Success - NO_ERROR
 *              Failure - ERROR_INVALID_HANDLE
 *                        ERROR_SEM_BUSY
 *
 *      PSEUDO CODE
 *
 *      return ( SEMCloseMuxWaitSem(hmux) )
 */

ULONG cdecl VR32CloseMuxWaitSem(THUNKSEF sef,      /* system entry frame */
                                HMUX hmux)         /* muxwait handle */

{
    tkAssertTasktime();        /* call NOT allowed at interrupt time */

    /*
     *  Check validity of mux handle.
     */
    if (semChkHdl(hmux,DCMW_MUX_SEM,pPTDACur) != NO_ERROR)
      return (ERROR_INVALID_HANDLE);
    /*
     *  Call the worker layer.
     */
    return ( SEMCloseMuxWaitSem(semStripSigBits(hmux)) );
}


/***EP  VR32WaitMuxWaitSem - Wait on a MuxWait Semaphore
 *
 *      VR32WaitMuxWaitSem allows a thread to wait on a muxwait semaphore that
 *      was previously created (via DosCreateMuxWaitSem) and opened (via
 *      DosOpenMuxWaitSem).  A muxwait can be waiting on multiple numbers of
 *      either all events or all mutexes.
 *
 *      In order to avoid deadlock in the system, if the DCMW_WAIT_ALL flag is
 *      set, a thread will not claim ownership of any mutex until ALL of the
 *      mutexes in the semaphore list have been released.  If the
 *      DCMW_WAIT_ANY flag is set and a mutex is released, the thread will
 *      only gain ownership of the ONE mutex that was released.  The ownership
 *      of all other mutex semaphores in the muxwait's list will remain
 *      unchanged.  The semaphores will be checked in the order in which they
 *      are defined in the muxwait's list.  Also, if any of the mutexes in the
 *      list are owned by the calling thread, ERROR_MUTEX_OWNED will be
 *      returned immediately.
 *
 *      If two threads have the same priority, then a thread waiting on a
 *      muxwait semaphore has priority over a thread waiting on a single mutex
 *      (if the requirements of the muxwait semaphore have been met).  For
 *      example, suppose the released mutex is part of a muxwait semaphore.
 *      One thread is waiting on the single mutex and another thread is
 *      waiting on the muxwait containing the same mutex.  The mutex is then
 *      released.  Asume also that all other mutexes in the muxwait are
 *      unowned and ready to be given to the muxwait.  Both threads have the
 *      same priority, so either one of them could run.  In this case, the
 *      thread waiting on the muxwait semaphore will run.
 *
 *      The event semaphores in a muxwait are all edge-triggered.  This means
 *      that if an event is posted and then reset while the thread is waiting
 *      on the semaphore, then the event will be considered to be posted for
 *      the rest of the thread's waiting period.  This is important for a
 *      muxwait semaphore with the DCMW_WAIT_ALL flag set.  When the
 *      DCMW_WAIT_ANY flag is set, the semaphores will be checked in the
 *      order in which they are defined in the muxwait's list.
 *
 *      The ulTimeout parameter places an upper boundary on the amount of
 *      time to block before returning, even though the semaphore is still
 *      owned.  If any of the semaphores in the muxwait are still in use at
 *      timeout, the ERROR_TIMEOUT return code will be returned.  If a timeout
 *      of zero is passed in, VR32WaitMuxWaitSem will return immediately.  If
 *      a timeout of -1 is passed in, it will block indefinitely.  When the
 *      ERROR_TIMEOUT error code is returned, it should be assumed that the
 *      thread did NOT get ownership of anything.
 *
 *      The ERROR_SEM_OWNER_DIED return code is returned if any of the mutex
 *      semaphores in the muxwait semaphore have been placed in the owner
 *      died state.  This means that a thread died while it owned at least
 *      one mutex, and that mutex is part of the muxwait.  When this
 *      error code is returned, the calling thread should do a
 *      DosQueryMutexSem on each mutex in the muxwait's list to find out which
 *      ones are corrupt.  Each mutex that gets the ERROR_SEM_OWNER_DIED return
 *      code from the query should be closed via calls to DosCloseMutexSem.
 *      Semaphore handles may be reused, so the mutex semaphores that are
 *      closed MUST be deleted from the muxwait semaphore.      When the
 *      ERROR_SEM_OWNER_DIED return code is returned from this routine, a
 *      subsequent call must be made to DosWaitMuxWaitSem after the
 *      appropriate mutex semaphores have been closed and deleted from the
 *      muxwait in order to successfully wait on the muxwait semaphore.
 *
 *      ENTRY   sef       - system entry frame
 *              hmux      - handle of a muxwait
 *              ulTimeout - timeout in milliseconds
 *              pulUser   - For DCMW_WAIT_ANY muxwait, it is filled in with
 *                            the ULONG of the semaphore that occured.  If
 *                            it was a mutex, then it's owned by the caller.
 *                        - For DCMW_WAIT_ALL muxwait, it is filled in with
 *                            the ULONG of the LAST semaphore that occurred.
 *                            All mutex semaphores are owned by the caller.
 *                            Note that, for events, this means that all of
 *                            them were cleared at some point since the wait
 *                            call.  This does not mean that they are all
 *                            clear now.
 *
 *      EXIT    Success - NO_ERROR
 *              Failure - ERROR_EMPTY_MUXWAIT
 *                        ERROR_INTERRUPT
 *                        ERROR_INVALID_HANDLE
 *                        ERROR_INVALID_PARAMETER
 *                        ERROR_MUTEX_OWNED
 *                        ERROR_SEM_OWNER_DIED
 *                        ERROR_TIMEOUT
 *
 *      PSEUDO CODE
 *
 *      rc = call SEMApiWaitMuxWaitSem with ulTmp
 *      If (rc == NO_ERROR)
 *        call TKSuDWord to copy ulTmp to pulUser
 *      Endif
 *      return (rc)
 */

ULONG cdecl VR32WaitMuxWaitSem(THUNKSEF sef,      /* system entry frame */
                               HMUX hmux,         /* muxwait handle */
                               ULONG ulTimeout,   /* timeout in milliseconds */
                               PULONG pulUser)    /* user ulong */

{
    ULONG ulTmp;                 /* temp storage for user ulong */
    ULONG rc;                    /* return code */


    /*
     *  Verify that user pointer is not in kernel space.
     */
    semChkNotKrnlSpace((PVOID)pulUser);

    /*
     *  Call the allocation/validation layer.
     */
    rc = SEMApiWaitMuxWaitSem(hmux,ulTimeout,SSToDS(&ulTmp));

    if (rc == NO_ERROR) {
      /*
       *  Copy the ulUser value back to user space.
       */
      if ((rc = TKSuDWord(ulTmp,pulUser)) != NO_ERROR) {
        semUndoWaitMuxMtx(hmux);
        return (rc);
      }
    }
    return (rc);
}


/***EP  VR32AddMuxWaitSem - Add an Event/Mutex to a Muxwait Semaphore
 *
 *      VR32AddMuxWaitSem adds a mutex or an event to the existing list of
 *      semaphores in a given muxwait semaphore.  Adding a new semaphore to
 *      this list means that the thread waiting on the muxwait semaphore will
 *      have to wait for an additional semaphore to be cleared before it can
 *      run.  VR32AddMuxWaitSem will allow semaphores to be added to a muxwait
 *      while threads are currently waiting on the semaphore.  If a mutex is
 *      being added, then the other semaphores that are part of the muxwait
 *      must also be mutex semaphores.  If an event is being added, then the
 *      other semaphores in the muxwait must be event semaphores.   If not,
 *      then the ERROR_WRONG_TYPE return code is returned to the calling
 *      routine.
 *
 *      A muxwait can have a maximum of 64 event/mutex semaphores in its list.
 *      If this maximum is reached, then the ERROR_TOO_MANY_SEMAPHORES
 *      return code is returned to the calling thread.
 *
 *      ENTRY   sef     - system entry frame
 *              hmux    - handle of a muxwait
 *              pSemRec - pointer to the new SEMRECORD to add
 *
 *      EXIT    Success - NO_ERROR
 *              Failure - ERROR_DUPLICATE_HANDLE
 *                        ERROR_INVALID_HANDLE
 *                        ERROR_INVALID_PARAMETER
 *                        ERROR_TOO_MANY_SEMAPHORES
 *                        ERROR_WRONG_TYPE
 *
 *      PSEUDO CODE
 *
 *      pNewSR = allocate new SemRecord node
 *      call TKFuBuff with pSemRec and pNewSR
 *      rc = call SEMApiAddMuxWaitSem with pNewSR
 *      free pNewSR
 *      return (rc)
 */

ULONG cdecl VR32AddMuxWaitSem(THUNKSEF sef,        /* system entry frame */
                              HMUX hmux,           /* muxwait handle */
                              PSEMRECORD pSemRec)  /* ptr to new SEMRECORD */

{
    PSEMRECORD pNewSR;           /* ptr to resident semrecord */
    ULONG rc;                    /* return code */


    /*
     *  Verify that user pointer is not in kernel space.
     */
    semChkNotKrnlSpace((PVOID)pSemRec);

    /*
     *  Allocate kernel space for semrecord entry.
     *  Copy the user space semrecord into the kernel space semrecord.
     */
    if ((rc = allocKRHB(2 * sizeof(SEMRECORD),(VMHOB)SEMSEMRECOWNER,
                        SSToDS(&pNewSR))) != NO_ERROR)
      return (rc);
    pNewSR[0].hsemCur = SEM_SRFLAG;
    pNewSR[0].ulUser = 2;
    if ((rc = TKFuBuff((PCHAR)(pNewSR+1),(PCHAR)pSemRec,sizeof(SEMRECORD),
                       TK_UM_FATAL)) != NO_ERROR) {
      freeKRHB(pNewSR);
      return (rc);
    }

    /*
     *  Call the allocation/validation layer.
     */
    rc = SEMApiAddMuxWaitSem(hmux,pNewSR);

    if (pNewSR[0].hsemCur == SEM_SRFLAG)
      /*
       *  Allocated semrecord was not used, so free it.
       */
      freeKRHB(pNewSR);
    return (rc);
}


/***EP  VR32DeleteMuxWaitSem - Delete an Event/Mutex from a MuxWait Semaphore
 *
 *      VR32DeleteMuxWaitSem deletes a mutex or an event from the existing list
 *      of semaphores in a given muxwait semaphore.  Removing a semaphore from
 *      this list means that the thread waiting on the muxwait semaphore will
 *      have to wait for fewer semaphores to be cleared before it can run.
 *      VR32DeleteMuxWaitSem will allow semaphores to be deleted from a muxwait
 *      while threads are currently waiting on the semaphore.  If the
 *      semaphore that was deleted happened to be the last one in the muxwait
 *      that the thread was waiting for, then the thread can automatically
 *      run.
 *
 *      ENTRY   sef  - system entry frame
 *              hmux - handle of a muxwait
 *              hsem - handle of semaphore to be deleted from muxwait
 *
 *      EXIT    Success - NO_ERROR
 *              Failure - ERROR_EMPTY_MUXWAIT
 *                        ERROR_INVALID_HANDLE
 *
 *      PSEUDO CODE
 *
 *      return ( SEMApiDeleteMuxWaitSem(hmux,hsem) )
 */

ULONG cdecl VR32DeleteMuxWaitSem(THUNKSEF sef,     /* system entry frame */
                                 HMUX hmux,        /* muxwait handle */
                                 HSEM hsem)        /* semaphore handle */

{

    ULONG ulSRIndex;               /* index into semrecord for hsem */
    register PSEM pMux;            /* pointer to mux semaphore */
    ULONG ulIndex;                 /* array index */

    tkAssertTasktime();        /* call NOT allowed at interrupt time */

    /*
     *  Check validity of mux handle.
     */
    if (semChkHdl(hmux,DCMW_MUX_SEM,pPTDACur) != NO_ERROR)
      return (ERROR_INVALID_HANDLE);
    /*
     *  Call the worker layer.
     */
    TKBeginNoBlock();           /* Section May NOT Block */

    /*
     *  Get pointer to correct table, shared or private.
     */
    ulIndex = semGetIndex(semStripSigBits(hmux));
    if (semIfShared(semStripSigBits(hmux))) {
      /*
       *  Shared Muxwait Semaphore.
       */
      tkAssert(semChkSharedHdl(ulIndex,DCMW_MUX_SEM,pPTDACur) == NO_ERROR);
      pMux = pShSemTbl[ulIndex];
    }
    else {
      /*
       *  Private Muxwait Semaphore.
       */
      tkAssert(semChkPrivateHdl(ulIndex,DCMW_MUX_SEM) == NO_ERROR);
      pMux = pPTDACur->pPrSemTbl[ulIndex];
    }

    /*
     *  Check for empty muxwait.
     */
    if (((PSHUN)pMux)->SMux.cSemRec == 0) {
      tkAssert(!(((PSHUN)pMux)->SMux.usFlags & DMW_MTX_MUX));
      TKEndNoBlock();
      return (ERROR_EMPTY_MUXWAIT);
    }
    /*
     *  Make sure semaphore to be deleted is in the mux list.
     */
    if ((semIfHdlInSR(pMux,semStripSigBits(hsem),SSToDS(&ulSRIndex))) != NO_ERROR) {
      TKEndNoBlock();
      return (ERROR_INVALID_HANDLE);
    }

    /*
     *  Remove mux list entry from muxwait.
     */
    semShiftSemRec(pMux,ulSRIndex,semStripSigBits(hsem));
    if (((PSHUN)pMux)->SMux.cSemRec == 0) {
      /*
       *  The last entry in the mux list was just deleted.  If there
       *  were any waiters, delete the appropriate mux queue nodes and
       *  wake up all of the waiters.
       */
      semDelAllMuxQ(pMux);
      TKWakeup(pMux,SEM_WAKE_ALL,NULL);
      ((PSHUN)pMux)->SMux.usFlags &= SEM_EVT_MUX;
      TKEndNoBlock();
      return (NO_ERROR);
    }

    if ((((PSHUN)pMux)->SMux.usWaitCt > 0) &&
        (((PSHUN)pMux)->SMux.usFlags & DCMW_WAIT_ALL)) {
      /*
       *  Mux is wait all and there is at least one waiter.  If
       *  the newly deleted semaphore causes any of the current
       *  waiters to be awakened, then wake them up.
       */
      semChkWakeOnDel(pMux);
    }

    TKEndNoBlock();
    return (NO_ERROR);
}


/***EP  VR32QueryMuxWaitSem - Query a MuxWait Semaphore
 *
 *      VR32QueryMuxWaitSem will query information about a given muxwait
 *      semaphore if the semaphore was previously opened by the process
 *      with a DosOpenMuxWaitSem.  If the muxwait semaphore does not exist,
 *      then the ERROR_INVALID_HANDLE return code is returned to the calling
 *      thread.  If the pSemRecord parameter is not large enough to hold all
 *      of the semaphore records in the muxwait, then the pcSemRec parameter
 *      is filled in with the count of semaphore records needed for the given
 *      muxwait and the ERROR_PARAM_TOO_SMALL return code is returned
 *      to the calling thread.  This is done so that the calling thread may
 *      do another DosQueryMuxWaitSem with the correct amount of space for the
 *      list.  If the muxwait semaphore does exist, then the appropriate
 *      information is returned to the calling thread.
 *
 *      The ERROR_SEM_OWNER_DIED return code is returned if any of the mutex
 *      semaphores in the muxwait semaphore have been placed in the owner
 *      died state.  This means that a thread died while it owned at least
 *      one mutex, and at least one mutex is part of the muxwait.  Also, the
 *      semaphore has not yet been destroyed by DosCloseMutexSem.
 *      When this error code is returned, the calling thread should do a
 *      DosQueryMutexSem on each mutex in the muxwait's list to find out
 *      which ones are corrupt.  Each mutex that gets the ERROR_SEM_OWNER_DIED
 *      return code from the query should be closed via a call to
 *      DosCloseMutexSem.  Semaphore handles may be reused, so the mutex
 *      semaphores that are closed MUST be deleted from the muxwait
 *      semaphore.
 *
 *      ENTRY   sef      - system entry frame
 *              hmux     - handle of a muxwait
 *              pcSemRec - number of SEMRECORDs that psemrec can accept
 *                       - filled in with count of SEMRECORDs returned
 *              pSemRec  - filled in with SEMRECORDs
 *              pflAttr  - filled in with flAttr passed on Create call
 *
 *      EXIT    Success - NO_ERROR
 *              Failure - ERROR_INVALID_HANDLE
 *                        ERROR_INVALID_PARAMETER
 *                        ERROR_PARAM_TOO_SMALL
 *                        ERROR_SEM_OWNER_DIED
 *
 *      PSEUDO CODE
 *
 *      validate pcSemRec, pSemrec, and pflAttr
 *      call TKFuWord for pcSemRec
 *      pNEWSemRec = allocate SEMRECORD array of size pcSemRec
 *      rc = call SEMApiQueryMuxWaitSem with usCtTmp, pNEWSemRec, ulAttrTmp
 *      If (rc == NO_ERROR)
 *        call TKSuWord to copy usCtTmp to pcSemRec
 *        call TKSuBuff to copy pNEWSemRec into pSemRec
 *        call TKSuDWord to copy ulAttrTmp to pflAttr
 *      Endif
 *      free pNEWSemRec
 *      return (rc)
 */

ULONG cdecl VR32QueryMuxWaitSem(THUNKSEF sef,        /* system entry frame */
                                HMUX hmux,           /* muxwait handle */
                                PULONG pcSemRec,     /* count of SEMRECORDs */
                                PSEMRECORD pSemRec,  /* array of SEMRECORDs */
                                PULONG pflAttr)      /* attributes */

{
    PSEMRECORD pNewSemRec = NULL;  /* ptr to resident SEMRECORDs */
    ULONG ulCtTmp;                 /* temp storage for count of SEMRECORDs */
    ULONG ulAttrTmp;               /* temp storage for attributes */
    ULONG rc;                      /* return code */
    ULONG rc2;                     /* return code */


    /*
     *  Verify that user pointers are not in kernel space.
     */
    semChkNotKrnlSpace((PVOID)pcSemRec);
    semChkNotKrnlSpace((PVOID)pSemRec);
    semChkNotKrnlSpace((PVOID)pflAttr);

    /*
     *  Copy semrecord count from user space to kernel space.
     */
    if ((rc = TKFuDWord(SSToDS(&ulCtTmp),pcSemRec)) != NO_ERROR)
      return (rc);

    if (ulCtTmp > 0) {
      /*
       *  Allocate kernel space for semrecord of same size as
       *  reported from user space.
       *  Only allocate kernel heap for up to the maximum number of
       *  semaphores in the MUXSEM list.                               *B728370
       */
      if ( ulCtTmp > SEM_MAXSEMS) ulCtTmp = SEM_MAXSEMS;

      if ((rc = allocKRHB((ulCtTmp * sizeof(SEMRECORD)),(VMHOB)SEMSEMRECOWNER,
                          SSToDS(&pNewSemRec))) != NO_ERROR)
        return (rc);
    }

    tkAssertTasktime();        /* call NOT allowed at interrupt time */

    /*
     *  Check validity of mux handle.
     */
    if (semChkHdl(hmux,DCMW_MUX_SEM,pPTDACur) != NO_ERROR) {
      /*
       *  If the handle is not valid, then set the semrec count to 0.
       *  This way, the user can tell that the mux handle is
       *  invalid, rather than one of the event/mutex semaphores
       *  contained in the mux.
       */
      ulCtTmp = 0;
      rc = ERROR_INVALID_HANDLE;
    } else {

      /*
       *  Call the worker layer.
       */
      rc = SEMQueryMuxWaitSem(semStripSigBits(hmux),
                           SSToDS(&ulCtTmp),pNewSemRec,SSToDS(&ulAttrTmp));
    }

    if ((rc != ERROR_PARAM_TOO_SMALL) && (ulCtTmp != 0)) {
      /*
       *  Copy semrecord back to user space.
       */
      if ((rc = TKSuBuff((char*)pSemRec,(char*)pNewSemRec,
                (ulCtTmp * sizeof(SEMRECORD)),TK_UM_FATAL)) != NO_ERROR) {
        if (pNewSemRec != NULL)
          freeKRHB(pNewSemRec);
        return (rc);
      }
    }

    if (pNewSemRec != NULL) {
      /*
       *  Free kernel space semrecord.
       */
      freeKRHB(pNewSemRec);
    }

    /*
     *  Copy count of semrecords and attributes flags back to user space.
     */
    if ( ((rc2 =TKSuDWord(ulCtTmp,pcSemRec) != NO_ERROR)) ||
         ((rc2 = TKSuDWord(ulAttrTmp,pflAttr) != NO_ERROR)) )
       return(rc2);

    return (rc);
}



/***EP  SEMApiCreateMuxWaitSem - Allocation to Create a MuxWait Semaphore
 *
 *      SEMApiCreateMuxWaitSem is the allocation routine for the
 *      DosCreateMuxWaitSem API routine.  All necessary items to complete
 *      a create and an open will be allocated in this routine.  It then
 *      calls the worker function that executes the create.  If any of the
 *      allocated items need to be freed, they will be freed by this
 *      routine before exiting.
 *
 *      ENTRY   pszName - asciiz name of MuxWait, or NULL if
 *                        creating an anonymous MuxWait.
 *              phmux   - pointer to be filled in with handle of new muxwait
 *              cSemRec - count of SEMRECORD entries in pSemRec
 *              pSemRec - array of SEMRECORD entries to put into muxwait
 *              flAttr  - attributes:
 *                           DC_SEM_SHARED
 *                           DCMW_WAIT_ANY
 *                           DCMW_WAIT_ALL
 *
 *      EXIT    Success - NO_ERROR
 *              Failure - ERROR_DUPLICATE_HANDLE
 *                        ERROR_DUPLICATE_NAME
 *                        ERROR_INVALID_HANDLE
 *                        ERROR_SEM_OWNER_DIED
 *                        ERROR_TOO_MANY_HANDLES
 *                        ERROR_WRONG_TYPE
 *
 *      PSEUDO CODE
 *
 *      check all SemRecord entries for errors
 *      If (shared)                                // shared
 *        allocate shared mux semaphore and open queue node
 *        rc = call semCreateMuxWaitSem with shared mux semaphore
 *        If (rc == error)
 *          set shared table entry back to NULL
 *          decrement ulShTotUsed
 *          free shared semaphore and open queue node
 *        Endif
 *      Else                                       // private
 *        allocate private mux semaphore
 *        rc = call semCreateMuxWaitSem with private mux semaphore
 *        If (rc == error)
 *          set private table entry back to NULL
 *          decrement pPTDACur->ulPrTotUsed
 *          free private semaphore
 *        Endif
 *      Endif
 *      return (rc)
 */

ULONG SEMApiCreateMuxWaitSem(PSZ pszName,         /* semaphore name */
                             PHMUX phmux,         /* ptr to muxwait handle */
                             ULONG cSemRec,       /* count of SEMRECORDs */
                             PSEMRECORD pSemRec,  /* array of SEMRECORDs */
                             ULONG flAttr)        /* attributes */

{
    PSMUX pSMuxSem;             /* pointer to resident Shared Mux */
    PPMUX pPMuxSem;             /* pointer to resident Private Mux */
    POPENQ pOpenN = NULL;       /* pointer to resident Open Queue node */
    ULONG ulIndex;              /* array index */
    ULONG rc;                   /* return code */


    tkAssertTasktime();        /* call NOT allowed at interrupt time */

    if (flAttr & DC_SEM_SHARED) {

      // 73736 - validate user bundle before allocating resources...
      //          (moved these tests from below)
      if (cSemRec > 0) {
        /*
         *  Check the semrecord entries in the mux.
         *  Look for invalid handles, incorrect signatures, and
         *  duplicate handles.
         */
        if (((rc = semChkSRInMux(pSemRec,(USHORT)cSemRec,1)) != NO_ERROR) ||
            ((rc = semChkSigInMux(pSemRec,(USHORT)cSemRec)) != NO_ERROR) ||
            ((rc = semChkDupHandle(pSemRec,(USHORT)cSemRec)) != NO_ERROR))
          return (rc);
      }

      /*
       *  Shared Muxwait Semaphore.  Allocate appropriate items for
       *  a shared semaphore.
       */
      if ((rc = semSharedAlloc((PVOID*)(SSToDS(&pSMuxSem)),sizeof(SMUX),
                               SSToDS(&pOpenN),phmux)) != NO_ERROR)
        return (rc);

      semInitSMuxSig(pSMuxSem);
      semInitOpenQSig(pOpenN);

      /*
       *  Call worker layer.
       */
      rc = semCreateMuxWaitSem(pszName,phmux,cSemRec,pSemRec,flAttr,
                               (PMUXUN)pSMuxSem,pOpenN);
      if (rc != NO_ERROR) {
        /*
         *  Error.  Back out the create if necessary.  Free the semaphore
         *  structure and the open queue node.
         */
        ulIndex = semGetIndex(*phmux);
        if ((ulIndex < ulShTblSize) && (pShSemTbl[ulIndex] == pSMuxSem)) {
          pShSemTbl[ulIndex] = NULL;
          ulShTotUsed--;
        }
        freeKRHB(pSMuxSem);
        BMPFree(pBMPOpenQSem, pOpenN);  // PERF: 98488
      }
    }
    else {

      // 73736 - validate user bundle before allocating resources...
      //          (moved these tests from below)
      if (cSemRec > 0) {
        /*
         *  Check the semrecord entries in the mux.
         *  Look for invalid handles, incorrect signatures, and
         *  duplicate handles.
         */
        if (((rc = semChkSRInMux(pSemRec,(USHORT)cSemRec,0)) != NO_ERROR) ||
            ((rc = semChkSigInMux(pSemRec,(USHORT)cSemRec)) != NO_ERROR) ||
            ((rc = semChkDupHandle(pSemRec,(USHORT)cSemRec)) != NO_ERROR))
          return (rc);
      }

      /*
       *  Private Muxwait Semaphore.  Allocate appropriate items for
       *  a private semaphore.
       */
      if ((rc = semPrivateAlloc((PVOID*)(SSToDS(&pPMuxSem)),sizeof(PMUX),
                                phmux)) != NO_ERROR)
        return (rc);

      semInitPMuxSig(pPMuxSem);

      /*
       *  Call worker layer.
       */
      rc = semCreateMuxWaitSem(pszName,phmux,cSemRec,pSemRec,flAttr,
                               (PMUXUN)pPMuxSem,NULL);
      if (rc != NO_ERROR) {
        /*
         *  Error.  Back out the create if necessary.  Free the semaphore
         *  structure.
         */
        ulIndex = semGetIndex(*phmux);
        if ((ulIndex < pPTDACur->ulPrTblSize) &&
            (pPTDACur->pPrSemTbl[ulIndex] == pPMuxSem)) {
          pPTDACur->pPrSemTbl[ulIndex] = NULL;
          (pPTDACur->ulPrTotUsed)--;
        }
        freeKRHB(pPMuxSem);
      }
    }
    return (rc);
}


/***EP  SEMApiOpenMuxWaitSem - Allocation to Open a MuxWait Semaphore
 *
 *      SEMApiOpenMuxWaitSem is the allocation routine for the
 *      DosOpenMuxWaitSem API routine.  All necessary items to complete
 *      an open will be allocated in this routine.  It then calls the
 *      worker function that executes the open.  If any of the
 *      allocated items need to be freed, they will be freed by this
 *      routine before exitting.
 *
 *      ENTRY   pszName - asciiz name, NULL => anonymous shared
 *              phmux   - if psz is NULL, phmux contains the handle of an
 *                        anonymous muxwait created by another process
 *
 *      EXIT    Success - NO_ERROR
 *              Failure - ERROR_INVALID_HANDLE
 *                        ERROR_WRONG_TYPE
 *                        ERROR_SEM_NOT_FOUND
 *                        ERROR_TOO_MANY_OPENS
 *
 *      PSEUDO CODE
 *
 *      If (shared)                              // shared
 *        call semOpenSharedAlloc
 *        If (not mux sem)
 *          return (ERROR_INVALID_HANDLE or ERROR_WRONG_TYPE)
 *        Endif
 *        rc = call semOpenSem with open queue
 *        If ((rc == error) || (pOpenN->usOpenCt == SEM_OPENQFLAG))
 *          free pOpenN
 *        Endif
 *        return (rc)
 *      Else                                     // private
 *        call semChkPrivateHdl to validate handle
 *        return( call semOpenSem with open count )
 *      Endif
 */

ULONG SEMApiOpenMuxWaitSem(PSZ pszName,    /* semaphore name */
                           PHMUX phmux)    /* ptr to muxwait handle */

{
    POPENQ pOpenN;                  /* ptr to an open queue node */
    ULONG ulIndex;                  /* table index of handle */
    register PSEM pSem;             /* ptr to the mux semaphore */
    ULONG rc;                       /* return code */


    tkAssertTasktime();        /* call NOT allowed at interrupt time */

    if ((pszName != NULL) || (semIfShared(*phmux))) {
      /*
       *  Shared Muxwait Semaphore.  Allocate appropriate items for an open
       *  of a shared semaphore.
       */
      if ((rc = semOpenSharedAlloc(pszName,phmux,SSToDS(&pOpenN),
                                   pPTDACur)) != NO_ERROR)
        return (rc);

      semInitOpenQSig(pOpenN);
      ulIndex = semGetIndex(*phmux);
      pSem = pShSemTbl[ulIndex];
      /*
       *  Check to be sure semaphore exists and is a muxwait semaphore.
       */
      if ((pSem == NULL) || (!(((PSHUN)pSem)->SMux.usFlags & DCMW_MUX_SEM))) {
        BMPFree(pBMPOpenQSem, pOpenN);  // PERF: 98488
        return ( pszName == NULL || pSem == NULL ? ERROR_INVALID_HANDLE
                                                 : ERROR_WRONG_TYPE );
      }
      tkAssert((ulIndex < ulShTblSize) && (ulIndex < pPTDACur->ulShBmpSize));

      /*
       *  Call worker layer.
       */
      *phmux = semStripSigBits(*phmux);
      rc = semOpenSem(phmux,pOpenN,&((PSHUN)pSem)->SMux.pOpenQ,NULL,pPTDACur);
      if ((rc != NO_ERROR) || (pOpenN->usOpenCt == SEM_OPENQFLAG))
        BMPFree(pBMPOpenQSem, pOpenN);  // PERF: 98488
      return (rc);
    }
    else {
      /*
       *  Private Muxwait Semaphore.  Validate the handle.
       */
      if (!(semVerifySigBits(*phmux)))                        //148148
        return (ERROR_INVALID_HANDLE);                        //148148
      ulIndex = semGetIndex(*phmux);
      if (semChkPrivateHdl(ulIndex,DCMW_MUX_SEM) != NO_ERROR)
          return (ERROR_INVALID_HANDLE);
      pSem = pPTDACur->pPrSemTbl[ulIndex];
      /*
       *  Call the worker layer.
       */
      *phmux = semStripSigBits(*phmux);
      return ( semOpenSem(phmux,NULL,NULL,&((PPRUN)pSem)->PMux.usOpenCt,pPTDACur) );
    }
}




/***EP  SEMApiWaitMuxWaitSem - Allocation to Wait on a MuxWait Semaphore
 *
 *      SEMApiWaitMuxWaitSem is the allocation routine for the
 *      DosWaitMuxWaitSem API routine.  All necessary items to complete
 *      a wait will be allocated in this routine.  It then calls the
 *      worker function that executes the wait.  If any of the
 *      allocated items need to be freed, they will be freed by this
 *      routine before exitting.
 *
 *      ENTRY   hmux      - handle of a muxwait
 *              ulTimeout - timeout in milliseconds
 *              pulUser   - For DCMW_WAIT_ANY muxwait, it is filled in with
 *                            the ULONG of the semaphore that occured.  If
 *                            it was a mutex, then it's owned by the caller.
 *                        - For DCMW_WAIT_ALL muxwait, it is filled in with
 *                            the ULONG of the LAST semaphore that occurred.
 *                            All mutex semaphores are owned by the caller.
 *                            Note that, for events, this means that all of
 *                            them were cleared at some point since the wait
 *                            call.  This does not mean that they are all
 *                            clear now.
 *
 *      EXIT    Success - NO_ERROR
 *              Failure - ERROR_EMPTY_MUXWAIT
 *                        ERROR_INTERRUPT
 *                        ERROR_INVALID_HANDLE
 *                        ERROR_MUTEX_OWNED
 *                        ERROR_SEM_OWNER_DIED
 *                        ERROR_TIMEOUT
 *                        ERROR_WRONG_TYPE
 *
 *      PSEUDO CODE
 *
 *      If (shared)                              // shared
 *        ppSemTbl = &pShSemTbl
 *      Else                                     // private
 *        ppSemTbl = &(pPTDACur->pPrSemTbl)
 *      Endif
 *      validate handle
 *      If (ulTimeout != SEM_IMMEDIATE_RETURN)
 *        allocate linked list of mux queue nodes
 *        validate handle again
 *      Endif
 *      pSem = (*ppSemTbl)[hmux]
 *      rc = call semWaitMuxWaitSem with mux queue nodes and pSem
 *      If ((rc == error) && (ulTimeout != SEM_IMMEDIATE_RETURN))
 *        free list of mux queue nodes
 *      Endif
 *      return (rc)
 */

ULONG SEMApiWaitMuxWaitSem(HMUX hmux,          /* muxwait handle */
                           ULONG ulTimeout,    /* timeout in milliseconds */
                           PULONG pulUser)     /* user ulong */

{
    PMUXQ pMuxQN = NULL;         /* ptr to list of MuxQ nodes */
    ULONG ulIndex;               /* index of handle */
    register PSEM pSem;          /* ptr to mux */
    ULONG rc;                    /* return code */
    PSEMTBL *ppSemTbl;           /* Ptr to ptr to table */


    tkAssertTasktime();        /* call NOT allowed at interrupt time */

    /*
     *  Get pointer to correct table, shared or private.
     *  Check if mux handle is valid.
     */
    ulIndex = semGetIndex(hmux);
    if (semIfShared(hmux)) {
      if (semChkSharedHdl(ulIndex,DCMW_MUX_SEM,pPTDACur) != NO_ERROR)
        return (ERROR_INVALID_HANDLE);
      ppSemTbl = &pShSemTbl;
    }
    else {
      if (semChkPrivateHdl(ulIndex,DCMW_MUX_SEM) != NO_ERROR)
        return (ERROR_INVALID_HANDLE);
      ppSemTbl = &(pPTDACur->pPrSemTbl);
    }

    if (ulTimeout != SEM_IMMEDIATE_RETURN) {
      /*
       *  Not immediate return, so allocate appropriate mux queue nodes.
       */
      if ((rc = semAllocWaitMuxQs(ulIndex,ppSemTbl,
                                  SSToDS(&pMuxQN))) != NO_ERROR)
        return (rc);
      /*
       *  Recheck validity of mux handle after allocations.
       */
      if (semChkHdl(hmux,DCMW_MUX_SEM,pPTDACur) != NO_ERROR) {
        semFreeMuxQList(pMuxQN);
        return (ERROR_INVALID_HANDLE);
      }
    }
    pSem = (*ppSemTbl)[ulIndex];

#ifdef PMDEBUG
    pTCBCur->TCB_SemInfo = semAddSigBits(hmux);
    if (semIfShared(hmux))
       pTCBCur->TCB_SemDebugAddr = (ULONG)((PSMUX)pSem)->pulCreatAddr;
    else
       pTCBCur->TCB_SemDebugAddr = (ULONG)((PPMUX)pSem)->pulCreatAddr;
#endif

    /*
     *  Call the worker layer.
     */
    rc = semWaitMuxWaitSem(pSem,ulTimeout,pulUser,SSToDS(&pMuxQN));

#ifdef PMDEBUG
    pTCBCur->TCB_SemDebugAddr = -1;     /* Clear debug address */
#endif

    if (ulTimeout != SEM_IMMEDIATE_RETURN)
      /*
       *  Not immediate return, so mux queue list was built.
       *  Must free any mux queue nodes from this list (they wer not unused).
       */
      semFreeMuxQList(pMuxQN);
    return (rc);
}


/***EP  SEMApiAddMuxWaitSem - Allocation to Add an Event/Mutex to a MuxWait
 *
 *      SEMApiAddMuxWaitSem is the allocation routine for the
 *      DosAddMuxWaitSem API routine.   All necessary items to complete
 *      an add will be allocated in this routine.  It then calls the
 *      worker function that executes the add.  If any of the
 *      allocated items need to be freed, they will be freed by this
 *      routine before exiting.
 *
 *      ENTRY   hmux    - handle of a muxwait
 *              pNewArr - pointer to the new SEMRECORD array
 *              pMuxQN  - pointer to a MUXQ node
 *
 *      EXIT    Success - NO_ERROR
 *              Failure - ERROR_DUPLICATE_HANDLE
 *                        ERROR_INVALID_HANDLE
 *                        ERROR_SEM_OWNER_DIED
 *                        ERROR_TOO_MANY_SEMAPHORES
 *                        ERROR_WRONG_TYPE
 *
 *      PSEUDO CODE
 *
 *      pMuxQN = allocate mux queue node
 *      pMuxQN->pMux = SEM_MUXQFLAG              // flag to see if it's used
 *      If (shared)                              // shared
 *        ppSemTbl = &pShSemTbl
 *      Else                                     // private
 *        ppSemTbl = &(pPTDACur->pPrSemTbl)
 *      Endif
 *      validate handle
 *      ArrSize = *ppSemTbl[hmux]->cSemRec + 2
 *      pNewArr = allocate array of size ArrSize
 *      validate handle again
 *      While (ArrSize < (Size = *ppSemTbl[hmux]->cSemRec + 2))
 *        pNewArr = reallocate array of size Size
 *        validate handle again
 *        pNewArr[0].ulUser = Size                   // size of array
 *        ArrSize = Size
 *      Endwhile
 *      rc = call semAddMuxWaitSem with pNewArr and pMuxQN
 *      If ((rc == error) || (pMuxQN->pMux == SEM_MUXQFLAG))
 *        free pMuxQN
 *      Endif
 *      return (rc)
 */

ULONG SEMApiAddMuxWaitSem(HMUX hmux,              /* muxwait handle */
                          PSEMRECORD pSemRec)     /* ptr to new SEMRECORD */

{
    register PSEMTBL *ppSemTbl;    /* address of pointer to sem table */
    PMUXQ pMuxQN;                  /* ptr to MuxQ node if needed */
    ULONG ulIndex;                 /* array index */
    PSEMRECORD *ppSemR;            /* address of ptr to semrecord of mux */
    register ULONG ulArrSize;      /* size of the array to be allocated */
    register ULONG ulSize;         /* size of array needed for add */
    ULONG rc;                      /* return code */


    tkAssertTasktime();        /* call NOT allowed at interrupt time */

    if (ulFreeMuxCount > 0) {
      /*
       *  Take mux queue node from mux queue free list.
       */
      pMuxQN = pFreeMux;
      pFreeMux = pFreeMux->pNextMux;
      ulFreeMuxCount--;
    }
    /*
     *  Otherwise, allocate mux queue node.
     */
    else if ((rc = allocKRHB(sizeof(MUXQ),(VMHOB)SEMMUXQOWNER,
                             SSToDS(&pMuxQN))) != NO_ERROR)
      return (rc);
    semInitMuxQSig(pMuxQN);

    /*
     *  Set mux queue flag - to see if mux queue node is used.
     */
    pMuxQN->pMux = SEM_MUXQFLAG;

    /*
     *  Get pointer to correct table, shared or private.
     *  Check validity of the mux handle.
     */
    ulIndex = semGetIndex(hmux);
    if (semIfShared(hmux)) {
      if (semChkSharedHdl(ulIndex,DCMW_MUX_SEM,pPTDACur) != NO_ERROR) {
        semAssertMuxQSig(pMuxQN);
        freeKRHB(pMuxQN);
        return (ERROR_INVALID_HANDLE);
      }
      ppSemTbl = &pShSemTbl;
    }
    else {
      if (semChkPrivateHdl(ulIndex,DCMW_MUX_SEM) != NO_ERROR) {
        semAssertMuxQSig(pMuxQN);
        freeKRHB(pMuxQN);
        return (ERROR_INVALID_HANDLE);
      }
      ppSemTbl = &(pPTDACur->pPrSemTbl);
    }

    if ((((PSMUX)((*ppSemTbl)[ulIndex]))->pSemRec != NULL) &&
        (((PSMUX)((*ppSemTbl)[ulIndex]))->pSemRec[0].ulUser <
         (ULONG)((PSMUX)((*ppSemTbl)[ulIndex]))->cSemRec + 2)) {
      /*
       *  Need to allocate a larger semrecord in order to add the new
       *  mux list entry.
       */
      KSEMRequestMutex(hksSemRec,SEM_INDEFINITE_WAIT);
      if (semChkHdl(hmux,DCMW_MUX_SEM,pPTDACur) != NO_ERROR) {
        semAssertMuxQSig(pMuxQN);
        freeKRHB(pMuxQN);
        KSEMReleaseMutex(hksSemRec);
        return (ERROR_INVALID_HANDLE);
      }
      ppSemR = &(((PSMUX)((*ppSemTbl)[ulIndex]))->pSemRec);
      ulArrSize = ((PSMUX)((*ppSemTbl)[ulIndex]))->pSemRec[0].ulUser;
      while ((*ppSemR != NULL) && (ulArrSize <
             (ulSize = ((PSMUX)((*ppSemTbl)[ulIndex]))->cSemRec + 2))) {
        /*
         *  Reallocate the new semrecord for the mux list entries.
         */
        if ((rc = VMReallocKHB(VM_HKH_PUB_RESRW,ppSemR,
                               ulSize * sizeof(SEMRECORD),0)) != NO_ERROR) {
          semAssertMuxQSig(pMuxQN);
          freeKRHB(pMuxQN);
          KSEMReleaseMutex(hksSemRec);
          return (rc);
        }
        /*
         *  Reset size of semrecord and recheck the validity of the
         *  mux handle.
         */
        (*ppSemR)[0].ulUser = ulSize;
        if (semChkHdl(hmux,DCMW_MUX_SEM,pPTDACur) != NO_ERROR) {
          semAssertMuxQSig(pMuxQN);
          freeKRHB(pMuxQN);
          KSEMReleaseMutex(hksSemRec);
          return (ERROR_INVALID_HANDLE);
        }
        ulArrSize = ulSize;
        ppSemR = &(((PSMUX)((*ppSemTbl)[ulIndex]))->pSemRec);
      }
      KSEMReleaseMutex(hksSemRec);
    }

    /*
     *  Call the worker layer.
     */
    rc = semAddMuxWaitSem(semStripSigBits(hmux),pSemRec,pMuxQN);
    if ((rc != NO_ERROR) || (pMuxQN->pMux == SEM_MUXQFLAG)) {
      /*
       *  Mux queue node not needed, so free it.
       */
      semAssertMuxQSig(pMuxQN);
      freeKRHB(pMuxQN);
    }
    return (rc);
}





/***LP  semCreateMuxWaitSem - Create a MuxWait Semaphore
 *
 *      semCreateMuxWaitSem is the worker function for the
 *      DosCreateMuxWaitSem API routine.  Upon entry to this function, all
 *      necessary items have been allocated, so there is no time at which
 *      this routine should block.  All allocated items will be freed by
 *      the routines that allocated them.
 *
 *      semCreateMuxWaitSem creates and opens a muxwait semaphore.      If the
 *      pszName is not null, then the semaphore will be a named shared
 *      semaphore.  If the pszName is null and the DC_SEM_SHARED flag is set,
 *      then the semaphore will be an anonymous shared semaphore.  Otherwise,
 *      the semaphore will be a private semaphore.  DC_SEM_SHARED will be
 *      ignored if the pszName is not null.  If the semaphore already exists,
 *      then ERROR_DUPLICATE_NAME will be returned.
 *
 *      All muxwait semaphores must contain either ALL mutexes or ALL events.
 *      If the creation of a muxwait is being attempted with a combination of
 *      events and mutexes, the ERROR_WRONG_TYPE return code is returned to
 *      the calling routine.  Both shared and private semaphores may be used
 *      in the same muxwait.  However, the private semaphores will not be
 *      valid between processes, so the muxwait will only be valid in the one
 *      process.
 *
 *      If the DCMW_WAIT_ANY flag is set, then the semaphore is to wait for
 *      any of the event/mutex semaphores in its list to be posted/released.
 *      Once any ONE of the semaphores is posted/released, the thread waiting
 *      on the muxwait can run.  If there is more than one mutex in the list
 *      and one of the mutex semaphores was the one semaphore that cleared,
 *      then the thread will only gain ownership of the one mutex that became
 *      available, NOT all of them.  The semaphores will be checked in the
 *      order in which they are defined in the muxwait's list.  If the
 *      DCMW_WAIT_ALL flag is set, then the semaphore is to wait for ALL of
 *      the event/mutex semaphores in its list to be posted/released.   Once
 *      ALL of the semaphores have been posted/released, the thread waiting
 *      on the muxwait can run.
 *
 *      A muxwait can have a maximum of 64 event/mutex semaphores in its list.
 *      If this maximum is reached, then the ERROR_TOO_MANY_SEMAPHORES
 *      return code is returned to the calling thread.
 *
 *      ENTRY   pszName - asciiz name of MuxWait, or NULL if
 *                        creating an anonymous MuxWait.
 *              phmux   - pointer to be filled in with handle of new muxwait
 *              cSemRec - count of SEMRECORD entries in pSemRec
 *              pSemRec - array of SEMRECORD entries to put into muxwait
 *              flAttr  - attributes:
 *                          DC_SEM_SHARED
 *                          DCMW_WAIT_ANY
 *                          DCMW_WAIT_ALL
 *
 *      EXIT    Success - NO_ERROR
 *              Failure - none
 *
 *      PSEUDO CODE
 *
 *      If (shared)                                        // shared
 *        pMuxSem->usFlags = flAttr | DCMW_MUX_SEM         // fill in structure
 *        pMuxSem->cSemRec = cSemRec
 *        pMuxSem->pSemRec = pSemRec
 *        pMuxSem->usWaitCt = 0
 *        pMuxSem->pOpenQ = NULL
 *        pMuxSem->pszName = pszName
 *        pShSemTbl[phmux] = pMuxSem
 *        phmux |= SEM_SHAREDMASK                          // shared semaphore
 *        increment ulShTotUsed
 *        return ( call semOpenSem with open queue )       // open mux
 *      Else                                               // private
 *        pMuxSem->usFlags = flAttr | DCMW_MUX_SEM         // fill in structure
 *        pMuxSem->cSemRec = cSemRec
 *        pMuxSem->pSemRec = pSemRec
 *        pMuxSem->usWaitCt = 0
 *        pMuxSem->usOpenCt = 0
 *        pMuxSem->pPTDA = pPTDACur
 *        pPTDACur->pPrSemTbl[phmux] = pMuxSem
 *        increment pPTDACur->ulPrTotUsed
 *        return ( call semOpenSem with open count )       // open mux
 *      Endif
 */

ULONG semCreateMuxWaitSem(PSZ pszName,          /* semaphore name */
                          PHMUX phmux,          /* ptr to muxwait handle */
                          ULONG cSemRec,        /* count of SEMRECORDs */
                          PSEMRECORD pSemRec,   /* array of SEMRECORDs */
                          ULONG flAttr,         /* attributes */
                          PMUXUN pMuxSem,       /* ptr to muxwait sem */
                          POPENQ pOpenN)        /* ptr to Open Queue node */

{
    ULONG rc;                   /* return code */


    TKBeginNoBlock();           /* Section May NOT Block */

    if (flAttr & DC_SEM_SHARED) {
      /*
       *  Shared Muxwait Semaphore.
       */
      tkAssert(pShSemTbl[*phmux] == NULL);
      /*
       * Fill in shared semaphore structure.
       */
      if ((cSemRec > 0) && (semGetType(pSemRec[1].hsemCur) == SEM_MTXTYPE))
        pMuxSem->ShMux.usFlags = (USHORT)(flAttr | DCMW_MUX_SEM | DMW_MTX_MUX);
      else
        pMuxSem->ShMux.usFlags = (USHORT)(flAttr | DCMW_MUX_SEM);
      pMuxSem->ShMux.cSemRec = (USHORT)cSemRec;
      pMuxSem->ShMux.pSemRec = pSemRec;
      pMuxSem->ShMux.usWaitCt = 0;
      pMuxSem->ShMux.pOpenQ = NULL;
      pMuxSem->ShMux.pszName = pszName;

      /*
       *  Fill in new handle with shared bits and increment count of total
       *  number of shared semaphores used.
       */
      pShSemTbl[*phmux] = pMuxSem;
      *phmux = semAddSharedBits(*phmux);
      ulShTotUsed++;

      /*
       *  Open muxwait semaphore.
       */
      rc = semOpenSem(phmux,pOpenN,&((PSHUN)pMuxSem)->SMux.pOpenQ,NULL,pPTDACur);
    }
    else {
      /*
       *  Private Muxwait Semaphore.
       */
      tkAssert(pPTDACur->pPrSemTbl[*phmux] == NULL);
      /*
       *  Fill in private semaphore structure.
       */
      if ((cSemRec > 0) && (semGetType(pSemRec[1].hsemCur) == SEM_MTXTYPE))
        pMuxSem->PrMux.usFlags = (USHORT)(flAttr | DCMW_MUX_SEM | DMW_MTX_MUX);
      else
        pMuxSem->PrMux.usFlags = (USHORT)(flAttr | DCMW_MUX_SEM);
      pMuxSem->PrMux.cSemRec = (USHORT)cSemRec;
      pMuxSem->PrMux.pSemRec = pSemRec;
      pMuxSem->PrMux.usWaitCt = 0;
      pMuxSem->PrMux.usOpenCt = 0;
      pMuxSem->PrMux.pPTDA = pPTDACur;

      /*
       *  Fill in new handle and increment the count of total number of
       *  private semaphores used.
       */
      pPTDACur->pPrSemTbl[*phmux] = pMuxSem;
      (pPTDACur->ulPrTotUsed)++;

      /*
       *  Open muxwait semaphore.
       */
      rc = semOpenSem(phmux,NULL,NULL,&((PPRUN)pMuxSem)->PMux.usOpenCt,pPTDACur);
    }
    TKEndNoBlock();
    return (rc);
}


/***EP  SEMCloseMuxWaitSem - Close a MuxWait Semaphore
 *
 *      SEMCloseMuxWaitSem is the worker function for the DosCloseMuxWaitSem
 *      API routine.  Upon entry to this function, all necessary items have
 *      been allocated, so there is no time at which this routine should
 *      block.  All allocated items will be freed by the routines that
 *      allocated them.
 *
 *      SEMCloseMuxWaitSem closes a muxwait semaphore that was previously
 *      opened.  If this is the last reference to this muxwait, then the
 *      muxwait semaphore is destroyed.
 *
 *      The ERROR_SEM_BUSY return code will be returned if the thread is trying
 *      to make the final close of a semaphore that has another thread in the
 *      same process still waiting on it.
 *
 *      ENTRY   hmux - handle of a muxwait to close
 *
 *      EXIT    Success - NO_ERROR
 *              Failure - ERROR_INVALID_HANDLE
 *                        ERROR_SEM_BUSY
 *                        ERROR_SEM_OWNER_DIED
 *                        ERROR_WRONG_TYPE
 *
 *      PSEUDO CODE
 *
 *      If (shared)                              // shared
 *        search OpenQ for current pid
 *        decrement usOpenCt in OpenQ node
 *        If (usOpenCt == 0)
 *          If (any single waiter (on mux) has pid == current thread's pid)
 *            increment usOpenCt
 *            return (ERROR_SEM_BUSY)
 *          Endif
 *          remove OpenQ node from OpenQ list
 *          put a 0 in appropriate bit in local bitmap
 *          If (OpenQ is empty)
 *            destroy semaphore (free memory)
 *            pShSemTbl[hmux] = NULL
 *          Endif
 *        Endif
 *      Else                                     // private
 *        decrement usOpenCt
 *        If (usOpenCt == 0)
 *          If (any single waiter (on mux) has pid == current thread's pid)
 *            increment usOpenCt
 *            return (ERROR_SEM_BUSY)
 *          Endif
 *          destroy semaphore (free memory)
 *          pPTDACur->pPrSemTbl[hmux] = NULL
 *        Endif
 *      Endif
 *      return (NO_ERROR)
 */

ULONG SEMCloseMuxWaitSem(HMUX hmux)        /* muxwait handle */

{
    register ULONG ulIndex;       /* array index */
    POPENQ pCurOpenN;             /* ptr to OpenQ node for current pid */
    POPENQ pPreOpenN;             /* ptr to previous OpenQ node */
    HMUX   lhmux;                 /* Stripped mux hanlde */
    register PSEM pMux;           /* ptr to muxwait semaphore */
    PSEMRECORD pSR = NULL;        /* ptr to semrecord of mux */


    TKBeginNoBlock();           /* Section May NOT Block */
    tkAssertTasktime();         /* call NOT allowed at interrupt time */

    lhmux = semStripSigBits(hmux);

    ulIndex = semGetIndex(lhmux);
    if (semIfShared(lhmux)) {
      /*
       *  Shared Muxwait Semaphore.
       */
      tkAssert(semChkSharedHdl(ulIndex,DCMW_MUX_SEM,pPTDACur) == NO_ERROR);
      pMux = pShSemTbl[ulIndex];
      /*
       *  Get open queue node for current process.
       */
      semGetOpenQNode(((PSHUN)pMux)->SMux.pOpenQ,SSToDS(&pCurOpenN),
                      SSToDS(&pPreOpenN),pPTDACur->ptda_Pid);
      tkAssert(pCurOpenN != NULL);
      if ((--(pCurOpenN->usOpenCt)) == 0) {
        /*
         *  About to make final close of semaphore for current process.
         *  Must make sure there are no threads in the current process
         *  waiting on this semaphore.
         */
        if (semSrchSnglForPid(pMux) == NO_ERROR) {
          (pCurOpenN->usOpenCt)++;
          TKEndNoBlock();
          return (ERROR_SEM_BUSY);
        }
        /*
         *  Delete open queue from semaphore's open queue list.
         *  Zero out the appropriate bit in the per process bitmap.
         *  The open queue node must be freed at the end of the
         *  routine (frees can block).
         */
        semDelFromOpenQ(pCurOpenN,pPreOpenN,&((PSHUN)pMux)->SMux.pOpenQ);
        semZeroBit(ulIndex,pPTDACur->pShSemBmp);
        if (((PSHUN)pMux)->SMux.pOpenQ == NULL) {
          /*
           *  No more opens left for this semaphore, so remove it
           *  from the semaphore table, decrement the total number of
           *  shared semaphores used, and free the structure.
           */
          semAssertSMuxSig(pMux);
          pSR = ((PSHUN)pMux)->SMux.pSemRec;
          semSharedClose(pMux,ulIndex,((PSHUN)pMux)->SMux.pszName);
        }
        /*
         *  Free the open queue node.
         */
        TKEndNoBlock();
        semAssertOpenQSig(pCurOpenN);
        BMPFree(pBMPOpenQSem, pCurOpenN);  // PERF: 98488
        TKBeginNoBlock();
      }
    }
    else {
      /*
       *  Private Muxwait Semaphore.
       */
      tkAssert(semChkPrivateHdl(ulIndex,DCMW_MUX_SEM) == NO_ERROR);
      pMux = pPTDACur->pPrSemTbl[ulIndex];
      if ((--(((PPRUN)pMux)->PMux.usOpenCt)) == 0) {
        /*
         *  About to make final close of semaphore.
         *  Must make sure there are no threads waiting on this semaphore.
         */
        if (semSrchSnglForPid(pMux) == NO_ERROR) {
          (((PPRUN)pMux)->PMux.usOpenCt)++;
          TKEndNoBlock();
          return (ERROR_SEM_BUSY);
        }
        /*
         *  No more opens left for this semaphore, so remove it
         *  from the semaphore table, decrement the total number of
         *  private semaphores used, and free the structure.
         */
        semAssertPMuxSig(pMux);
        pSR = ((PPRUN)pMux)->PMux.pSemRec;
        semPrivateClose(pMux,ulIndex);
      }
    }
    TKEndNoBlock();
    /*
     *  If the semaphore was deleted from the table and a semrecord
     *  existed, then free the semrecord.
     */
    if (pSR != NULL)
      freeKRHB(pSR);
    return (NO_ERROR);
}


/***LP  semWaitMuxWaitSem - Wait on a MuxWait Semaphore
 *
 *      semWaitMuxWaitSem is the worker function for the DosWaitMuxWaitSem
 *      API routine.  Upon entry to this function, all necessary items have
 *      been allocated, so there is no time at which this routine should
 *      block.  All allocated items will be freed by the routines that
 *      allocated them.
 *
 *      semWaitMuxWaitSem allows a thread to wait on a muxwait semaphore
 *      that was previously created and opened.  A muxwait can be waiting on
 *      multiple numbers of either all events or all mutexes.
 *
 *      In order to avoid deadlock in the system, if the DCMW_WAIT_ALL flag is
 *      set, a thread will not claim ownership of any mutex until ALL of the
 *      mutexes in the semaphore list have been released.  If the
 *      DCMW_WAIT_ANY flag is set and a mutex is released, the thread will
 *      only gain ownership of the ONE mutex that was released.  The ownership
 *      of all other mutex semaphores in the muxwait's list will remain
 *      unchanged.  The semaphores will be checked in the order in which they
 *      are defined in the muxwait's list.  Also, if any of the mutexes in the
 *      list are owned by the calling thread, ERROR_MUTEX_OWNED will be
 *      returned immediately.
 *
 *      If two threads have the same priority, then a thread waiting on a
 *      muxwait semaphore has priority over a thread waiting on a single mutex
 *      (if the requirements of the muxwait semaphore have been met).   For
 *      example, suppose the released mutex is part of a muxwait semaphore.
 *      One thread is waiting on the single mutex and another thread is
 *      waiting on the muxwait containing the same mutex.  The mutex is then
 *      released.  Asume also that all other mutexes in the muxwait are
 *      unowned and ready to be given to the muxwait.   Both threads have the
 *      same priority, so either one of them could run.  In this case, the
 *      thread waiting on the muxwait semaphore will run.
 *
 *      The event semaphores in a muxwait are all edge-triggered.  This means
 *      that if an event is posted and then reset while the thread is waiting
 *      on the semaphore, then the event will be considered to be posted for
 *      the rest of the thread's waiting period.  This is important for a
 *      muxwait semaphore with the DCMW_WAIT_ALL flag set.  When the
 *      DCMW_WAIT_ANY flag is set, the semaphores will be checked in the
 *      order in which they are defined in the muxwait's list.
 *
 *      The ulTimeout parameter places an upper boundary on the amount of
 *      time to block before returning, even though the semaphore is still
 *      owned.  If any of the semaphores in the muxwait are still in use at
 *      timeout, the ERROR_TIMEOUT return code will be returned.  If a timeout
 *      of zero is passed in, semWaitMuxWaitSem will return immediately.  If
 *      a timeout of -1 is passed in, it will block indefinitely.  When the
 *      ERROR_TIMEOUT error code is returned, it should be assumed that the
 *      thread did NOT get ownership of anything.
 *
 *      The ERROR_SEM_OWNER_DIED return code is returned if any of the mutex
 *      semaphores in the muxwait semaphore have been placed in the owner
 *      died state.  This means that a thread died while it owned at least
 *      one mutex, and that mutex is part of the muxwait.  Semaphore
 *      handles may be reused, so the mutex semaphores that are closed MUST
 *      be deleted from the muxwait semaphore.
 *
 *      ENTRY   pMux      - pointer to the mux semaphore
 *              ulTimeout - timeout in milliseconds
 *              pulUser   - For DCMW_WAIT_ANY muxwait, it is filled in with
 *                            the ULONG of the semaphore that occured.  If
 *                            it was a mutex, then it's owned by the caller.
 *                        - For DCMW_WAIT_ALL muxwait, it is filled in with
 *                            the ULONG of the LAST semaphore that occurred.
 *                            All mutex semaphores are owned by the caller.
 *                            Note that, for events, this means that all of
 *                            them were cleared at some point since the wait
 *                            call.  This does not mean that they are all
 *                            clear now.
 *              ppMuxQN   - address of pointer to the allocated MuxQ nodes
 *
 *      EXIT    Success - NO_ERROR
 *              Failure - ERROR_EMPTY_MUXWAIT
 *                        ERROR_INTERRUPT
 *                        ERROR_INVALID_HANDLE
 *                        ERROR_MUTEX_OWNED
 *                        ERROR_SEM_OWNER_DIED
 *                        ERROR_TIMEOUT
 *                        ERROR_WRONG_TYPE
 *
 *      PSEUDO CODE
 *
 *      If (pMux->cSemRec == 0)
 *        return (ERROR_EMPTY_MUXWAIT)
 *      Endif
 *      If (NOT all sems in mux are free)
 *        If (ulTimeout == SEM_IMMEDIATE_RETURN)
 *          return (ERROR_TIMEOUT)
 *        block thread on mux
 *      Endif
 *      If (usFlags & DCMW_WAIT_ANY)
 *        If (mutex semaphores)
 *          pulUser = ulUser of mutex that was cleared
 *          usSlotNum of mutex = current thread's slot number
 *          usRequestCt = 1
 *         Endif
 *        Else                                             // event semaphores
 *          pulUser = ulUser of event that was posted
 *          usRequestCt = 1
 *        Endif
 *        increment pPTDACur->ulMtxOwned
 *      Else                                               // DCMW_WAIT_ALL
 *        If (mutex semaphores)
 *          For (all mutexes in semrecord)
 *            usSlotNum of mutex = current thread's slot number
 *            usRequestCt = 1
 *            increment pPTDACur->ulMtxOwned
 *          Endfor
 *          pulUser = ulUser of last mutex released
 *        Else
 *          pulUser = ulUser of last event posted
 *        Endif
 *      Endif
 *      return (NO_ERROR)
 */

ULONG semWaitMuxWaitSem(register PSEM pMux,    /* ptr to mux */
                        ULONG ulTimeout,       /* timeout in milliseconds */
                        PULONG pulUser,        /* ptr to user ulong */
                        PMUXQ *ppMuxQN)        /* ptr to allocated MuxQ's */

{
    ULONG ulSRIndex;             /* index into semrecord */
    USHORT usCheck = FALSE;      /* flag to denote whether or not to block */
    register PSEMRECORD pSemR;   /* ptr to the semrecord of the mux */
    USHORT usFlags;              /* mux flags */
    USHORT usWaitAll;            /* 0 = wait any, 1 = wait all */
    USHORT usMtxMux;             /* 0 = event mux, 1 = mutex mux */
    ULONG rc;                    /* return code */


    TKBeginNoBlock();           /* Section May NOT Block */

    usFlags = ((PSHUN)pMux)->SMux.usFlags;
    tkAssert(usFlags & DCMW_MUX_SEM);
    pSemR = ((PSHUN)pMux)->SMux.pSemRec;
    usWaitAll = usFlags & (USHORT)DCMW_WAIT_ALL;
//  usMtxMux = usFlags & (USHORT)DMW_MTX_MUX;                           72485
    /*
     *  Make sure all events/mutexes in muxwait are still valid.
     */
    if ((rc = semChkSRInMux(pSemR,((PSHUN)pMux)->SMux.cSemRec,
                            usFlags)) != NO_ERROR) {
      TKEndNoBlock();
      return (rc);
    }
    tkAssert(((PSHUN)pMux)->SMux.usWaitCt != SEM_MAXWAITS);

    /*
     *  Check to see if we need to block.  If the mux is wait all,
     *  then all events must be posted or all mutexes must be
     *  unowned.  If the mux is a wait any, then if any of the
     *  events are posted or any of the mutexes are unowned the
     *  mux is considered free.
     *
     *  This macro is entered during a NoBlock section.  If any errors
     *  are found, then the NoBlock section is ended and the error code
     *  is returned to the caller by semIfMuxFree.
     */
    usMtxMux = usFlags & (USHORT)DMW_MTX_MUX;
    if ((usWaitAll = usFlags & (USHORT)DCMW_WAIT_ALL))
      usCheck = TRUE;
    semIfMuxFree(pMux,&usCheck,usWaitAll,usMtxMux);

    if (usCheck) {
      /*
       *  Muxwait is free.  No need to block.
       */
      ulSRIndex = pSemR[0].hsemCur;
      *pulUser = pSemR[ulSRIndex].ulUser;
      if (usMtxMux) {
        /*
         *  Mutex mux.  Assign ownership based on whether it's a
         *  wait any or a wait all.
         */
        tkAssert(semGetType(pSemR[1].hsemCur) == SEM_MTXTYPE);
        if (usWaitAll) {
          /*
           *  Wait ALL mux.  Assign ownership to ALL of the mutexes.
           *  pSemR = first node, pEndSR = last node.
           */
          semOwnMtxInSR(pSemR + 1,
                        pSemR + ((PMUXUN)pMux)->ShMux.cSemRec,pTCBCur);
        }
        else {
          /*
           *  Wait ANY mux.  Assign ownership to ONE of the mutexes.
           *  pEndSR = pSemR.
           */
          pSemR += ulSRIndex;
          semOwnMtxInSR(pSemR,pSemR,pTCBCur);
        }
      }
      TKEndNoBlock();
      return (NO_ERROR);
    }

    /*
     *  Muxwait is not free.  If the user has requested a 0 timeout,
     *  then this routine will return immediately.  Otherwise,
     *  the current thread will now block.
     */
    if (ulTimeout == SEM_IMMEDIATE_RETURN) {
      TKEndNoBlock();
      return (ERROR_TIMEOUT);
    }

    /*
     *  Block the thread on the muxwait.  We are still in a
     *  TKNoBlock section when we enter semBlockOnMux.
     *  If any errors occur, then this macro will end the NoBlock
     *  section and return the error to the caller.
     */
    semBlockOnMux(pMux,ppMuxQN,ulTimeout);

//  pSemR = ((PSHUN)pMux)->SMux.pSemRec;                             72485
//  *pulUser = pSemR[pSemR[0].hsemCur].ulUser;                       72485

    // 78979 - pSemR is a "registered" variable - it's loaded on entry
    //         to the function and never reset. The user could, however,
    //         call DosAddMuxWaitSem (changing it) while we are blocked
    //         on the mux. (Attachmate)
    pSemR = ((PSHUN)pMux)->SMux.pSemRec;

    *pulUser = pSemR[pTCBCur->TCB_ulSRIndex].ulUser;               //72485

    TKEndNoBlock();
    return (NO_ERROR);
}


/***LP  semAddMuxWaitSem - Add an Event/Mutex to a Muxwait Semaphore
 *
 *      semAddMuxWaitSem is the worker function for the DosAddMuxWaitSem
 *      API routine.  Upon entry to this function, all necessary items have
 *      been allocated, so there is no time at which this routine should
 *      block.  All allocated items will be freed by the routines that
 *      allocated them.
 *
 *      semAddMuxWaitSem adds a mutex or an event to the existing list of
 *      semaphores in a given muxwait semaphore.  Adding a new semaphore to
 *      this list means that the thread waiting on the muxwait semaphore will
 *      have to wait for an additional semaphore to be cleared before it can
 *      run.  semAddMuxWaitSem will allow semaphores to be added to a mux
 *      while threads are currently waiting on the semaphore.   If a mutex is
 *      being added, then the other semaphores that are part of the muxwait
 *      must also be mutex semaphores.  If an event is being added, then the
 *      other semaphores in the muxwait must be event semaphores.   If not,
 *      then the ERROR_WRONG_TYPE return code is returned to the calling
 *      routine.
 *
 *      A muxwait can have a maximum of 64 event/mutex semaphores in its list.
 *      If this maximum is reached, then the ERROR_TOO_MANY_SEMAPHORES
 *      return code is returned to the calling thread.
 *
 *      ENTRY   hmux   - handle of a muxwait
 *              pNewSR - pointer to the new SEMRECORD
 *              pMuxQN - pointer to a MUXQ node
 *
 *      EXIT    Success - NO_ERROR
 *              Failure - ERROR_DUPLICATE_HANDLE
 *                        ERROR_INVALID_HANDLE
 *                        ERROR_SEM_OWNER_DIED
 *                        ERROR_TOO_MANY_SEMAPHORES
 *                        ERROR_WRONG_TYPE
 *
 *      PSEUDO CODE
 *
 *      If (shared)                                        // shared
 *        pMux = pShSemTbl[hmux];
 *      Else                                               // private
 *        pMux = pPTDACur->pPrSemTbl[hmux]
 *      Endif
 *      check for errors in adding pNewSR to muxwait
 *      If (pMux->pSemRec == NULL) {                       // empty mux
 *        pNewSR[0].hsemCur = SEM_EMPTY_SEMREC
 *        pMux->cSemRec = 1
 *        pMux->pSemRec = pNewSR
 *      Else                                               // mux list exists
 *        put the new semaphore in the existing array
 *        increment pMux->cSemRec
 *        If (any threads waiting on this muxwait)
 *          pMuxQ->pMux = ptr to this mux
 *          put MuxQ node on added semaphore's MuxQ
 *        Endif
 *      Endif
 *      For (each thread waiting on this mux)
 *        If (newly added semaphore not open by thread)
 *          wakeup the thread
 *        Endif
 *      Endfor
 *      If ((mux waitct > 0) && (usFlags & DCMW_WAIT_ANY))
 *        check if newly added semaphore causes wakeup of threads
 *      Endif
 *      return (NO_ERROR);
 */

ULONG semAddMuxWaitSem(HMUX hmux,            /* muxwait handle */
                       PSEMRECORD pNewSR,    /* ptr to new SEMRECORD */
                       PMUXQ pMuxQN)         /* ptr to MuxQ node */

{
    register PSEM pMux;               /* ptr to the mux semaphore */
    register HSEM hsemAdd;            /* handle of the added semaphore */
    PSEMRECORD pSemRec;               /* ptr to semrecord of mux */
    USHORT cSemRec;                   /* number of semrecords in mux */
    ULONG ulIndex;                    /* array index */
    ULONG rc;                         /* return code */


    TKBeginNoBlock();           /* Section May NOT Block */

    /*
     *  Get pointer to correct table, shared or private.
     */
    ulIndex = semGetIndex(hmux);
    if (semIfShared(hmux)) {
      /*
       *  Shared Muxwait Semaphore.
       */
      tkAssert(semChkSharedHdl(ulIndex,DCMW_MUX_SEM,pPTDACur) == NO_ERROR);
      pMux = pShSemTbl[ulIndex];
    }
    else {
      /*
       *  Private Muxwait Semaphore.
       */
      tkAssert(semChkPrivateHdl(ulIndex,DCMW_MUX_SEM) == NO_ERROR);
      pMux = pPTDACur->pPrSemTbl[ulIndex];
    }

    hsemAdd = semStripSigBits(pNewSR[1].hsemCur);
    pSemRec = ((PSHUN)pMux)->SMux.pSemRec;
    cSemRec = ((PSHUN)pMux)->SMux.cSemRec;

    if (cSemRec > 0) {
      /*
       *  At least one mux list entry.  Check that all semaphores in
       *  mux list are valid.
       */
      if ((rc = semChkSRInMux(pSemRec,cSemRec,
                              ((PSHUN)pMux)->SMux.usFlags)) != NO_ERROR) {
        TKEndNoBlock();
        return (rc);
      }
    }
    /*
     *  Check signature bit of event/mutex to add to mux list.
     */
    if ((rc = semChkSigInMux(pNewSR,1)) != NO_ERROR) {
      TKEndNoBlock();
      return (rc);
    }
    /*
     *  Check for error in adding new semaphore to list.
     *  Look for wrong type, duplicate handle, invalid handle, and too
     *  many semaphores.
     *
     *  This macro is entered during a NoBlock section.  If any errors
     *  are found, then the NoBlock section is ended and the error code
     *  is returned to the caller by semIfErrAddMux.
     */
    semIfErrAddMux(pMux,hsemAdd);

    if (pSemRec == NULL) {
      /*
       *  Empty muxwait.  Simply add the new semrecord and set the
       *  mux type field of the muxwait.
       */
      tkAssert(cSemRec == 0);
      pNewSR[0].hsemCur = SEM_EMPTY_SEMREC;
      ((PSHUN)pMux)->SMux.cSemRec = 1;
      ((PSHUN)pMux)->SMux.pSemRec = pNewSR;
      tkAssert(!(((PSHUN)pMux)->SMux.usFlags & DMW_MTX_MUX));
      if (semGetType(hsemAdd) == SEM_MTXTYPE)
        ((PSHUN)pMux)->SMux.usFlags |= DMW_MTX_MUX;
      TKEndNoBlock();
      return (NO_ERROR);
    }

    /*
     *  List already exists.  Add the new entry to the end of the
     *  existing list.  If no other entries in the list, then set
     *  the muxwait type flag.
     */
    if ((cSemRec == 0) && (semGetType(hsemAdd) == SEM_MTXTYPE))
      ((PSHUN)pMux)->SMux.usFlags |= DMW_MTX_MUX;
    cSemRec++;
    tkAssert((ULONG)cSemRec < pSemRec[0].ulUser);
    pSemRec[cSemRec].hsemCur = hsemAdd;
    pSemRec[cSemRec].ulUser = pNewSR[1].ulUser;
    (((PSHUN)pMux)->SMux.cSemRec)++;

    if (((PSHUN)pMux)->SMux.usWaitCt > 0) {
      /*
       *  At least one thread waiting on the mux.  Need to add
       *  a mux queue node to the newly added semaphore's mux queue.
       */
      pMuxQN->pMux = pMux;
      semAddToMuxQ(hsemAdd,pMuxQN);

      /*
       *  Check all waiters on mux.  If any of the waiters do not
       *  have the newly added semaphore open in their process, then
       *  wake up the waiter.
       */
      semIfOpenByAll(pMux,hsemAdd);
      if ((((PSHUN)pMux)->SMux.usWaitCt > 0) &&
          (((PSHUN)pMux)->SMux.usFlags & DCMW_WAIT_ANY))
        /*
         *  Mux is wait any and there is at least one waiter.  If
         *  the newly added semaphore causes any of the current
         *  waiters to be awakened, then wake them up.
         */
        semChkWakeOnAdd(pMux,hsemAdd);
    }
    TKEndNoBlock();
    return (NO_ERROR);
}


/***EP  SEMDeleteMuxWaitSem - Delete an Event/Mutex from a MuxWait Semaphore
 *
 *      SEMDeleteMuxWaitSem is the worker function for the
 *      DosDeleteMuxWaitSem API routine.  Upon entry to this function, all
 *      necessary items have been allocated, so there is no time at which
 *      this routine should block.  All allocated items will be freed by
 *      the routines that allocated them.
 *
 *      SEMDeleteMuxWaitSem deletes a mutex or an event from the existing
 *      list of semaphores in a given muxwait semaphore.  Removing a semaphore
 *      from this list means that the thread waiting on the muxwait semaphore
 *      will have to wait for fewer semaphores to be cleared before it can
 *      run.  SEMDeleteMuxWaitSem will allow semaphores to be deleted from a
 *      muxwait while threads are currently waiting on the semaphore.   If the
 *      semaphore that was deleted happened to be the last one in the muxwait
 *      that the thread was waiting for, then the thread can automatically
 *      run.
 *
 *      ENTRY   hmux - handle of a muxwait
 *              hsem - handle of semaphore to be deleted from muxwait
 *
 *      EXIT    Success - NO_ERROR
 *              Failure - ERROR_EMPTY_MUXWAIT
 *                        ERROR_INVALID_HANDLE
 *                        ERROR_SEM_OWNER_DIED
 *                        ERROR_WRONG_TYPE
 *
 *      PSEUDO CODE
 *
 *      If (shared)                                        // shared
 *        pMux = pShSemTbl[hmux]
 *      Else                                               // private
 *        pMux = pPTDACur->pPrSemTbl[hmux]
 *      Endif
 *      If (pMux->cSemRec == 0)
 *        return (ERROR_EMPTY_MUXWAIT)
 *      Endif
 *      If (hsem does not exist in muxwait's semrecord list)
 *        return (ERROR_INVALID_HANDLE)
 *      Endif
 *      remove muxlist entry from muxwait
 *      If (pMux->cSemRec == 0)                 // no more semaphores in mux
 *        delete all mux queue nodes
 *        call TKWakeup on all threads waiting on mux
 *        return (NO_ERROR)
 *      Endif
 *      If ((mux waitct > 0) && (usFlags & DCMW_WAIT_ALL))
 *        check if newly deleted semaphore causes wakeup of threads
 *      Endif
 *      return (NO_ERROR)
 */

ULONG SEMDeleteMuxWaitSem(HMUX hmux,      /* muxwait handle */
                          HSEM hsem)      /* semaphore handle to delete */

{
    ULONG ulSRIndex;               /* index into semrecord for hsem */
    register PSEM pMux;            /* pointer to mux semaphore */
    ULONG ulIndex;                 /* array index */


    TKBeginNoBlock();           /* Section May NOT Block */
    tkAssertTasktime();         /* call NOT allowed at interrupt time */

    /*
     *  Get pointer to correct table, shared or private.
     */
    ulIndex = semGetIndex(hmux);
    if (semIfShared(hmux)) {
      /*
       *  Shared Muxwait Semaphore.
       */
      tkAssert(semChkSharedHdl(ulIndex,DCMW_MUX_SEM,pPTDACur) == NO_ERROR);
      pMux = pShSemTbl[ulIndex];
    }
    else {
      /*
       *  Private Muxwait Semaphore.
       */
      tkAssert(semChkPrivateHdl(ulIndex,DCMW_MUX_SEM) == NO_ERROR);
      pMux = pPTDACur->pPrSemTbl[ulIndex];
    }

    /*
     *  Check for empty muxwait.
     */
    if (((PSHUN)pMux)->SMux.cSemRec == 0) {
      tkAssert(!(((PSHUN)pMux)->SMux.usFlags & DMW_MTX_MUX));
      TKEndNoBlock();
      return (ERROR_EMPTY_MUXWAIT);
    }
    /*
     *  Make sure semaphore to be deleted is in the mux list.
     */
    if ((semIfHdlInSR(pMux,hsem,SSToDS(&ulSRIndex))) != NO_ERROR) {
      TKEndNoBlock();
      return (ERROR_INVALID_HANDLE);
    }

    /*
     *  Remove mux list entry from muxwait.
     */
    semShiftSemRec(pMux,ulSRIndex,hsem);
    if (((PSHUN)pMux)->SMux.cSemRec == 0) {
      /*
       *  The last entry in the mux list was just deleted.  If there
       *  were any waiters, delete the appropriate mux queue nodes and
       *  wake up all of the waiters.
       */
      semDelAllMuxQ(pMux);
      TKWakeup(pMux,SEM_WAKE_ALL,NULL);
      ((PSHUN)pMux)->SMux.usFlags &= SEM_EVT_MUX;
      TKEndNoBlock();
      return (NO_ERROR);
    }

    if ((((PSHUN)pMux)->SMux.usWaitCt > 0) &&
        (((PSHUN)pMux)->SMux.usFlags & DCMW_WAIT_ALL)) {
      /*
       *  Mux is wait all and there is at least one waiter.  If
       *  the newly deleted semaphore causes any of the current
       *  waiters to be awakened, then wake them up.
       */
      semChkWakeOnDel(pMux);
    }

    TKEndNoBlock();
    return (NO_ERROR);
}


/***EP  SEMQueryMuxWaitSem - Query a MuxWait Semaphore
 *
 *      SEMQueryMuxWaitSem is the worker function for the DosQueryMuxWaitSem
 *      API routine.  Upon entry to this function, all necessary items have
 *      been allocated, so there is no time at which this routine should
 *      block.  All allocated items will be freed by the routines that
 *      allocated them.
 *
 *      SEMQueryMuxWaitSem will query information about a given muxwait
 *      semaphore if the semaphore was previously opened by the process.
 *      If the pSemRecord parameter is not large enough to hold all of
 *      the semaphore records in the muxwait, then the pcSemRec parameter
 *      is filled in with the count of semaphore records needed for the
 *      given muxwait and the ERROR_PARAM_TOO_SMALL return code is returned
 *      to the calling thread.  This is done so that the calling thread may
 *      do another "query" with the correct amount of space for the list.
 *
 *      The ERROR_SEM_OWNER_DIED return code is returned if any of the mutex
 *      semaphores in the muxwait semaphore have been placed in the owner
 *      died state.  This means that a thread died while it owned at least
 *      one mutex, and at least one mutex is part of the muxwait.  Also, the
 *      semaphore has not yet been destroyed by a "close" of the mutex.
 *
 *      ENTRY   hmux     - handle of a muxwait
 *              pcSemRec - number of SEMRECORDs that psemrec can accept
 *                       - filled in with count of SEMRECORDs returned
 *              pSemRec  - filled in with SEMRECORDs
 *              pflAttr  - filled in with flAttr passed on Create call
 *
 *      EXIT    Success - NO_ERROR
 *              Failure - ERROR_INVALID_HANDLE
 *                        ERROR_PARAM_TOO_SMALL
 *                        ERROR_SEM_OWNER_DIED
 *                        ERROR_WRONG_TYPE
 *
 *      PSEUDO CODE
 *
 *      If (shared)                                        // shared
 *        pMux = pShSemTbl[hmux]
 *      Else                                               // private
 *        pMux = pPTDACur->pPrSemTbl[hmux]
 *      Endif
 *      If (pMux->cSemRec == 0)            // empty mux
 *        pcSemRec = 0
 *        pflAttr = pMux->usFlags & SEM_MUXFLAGS
 *      Else
 *        If (mutex semaphores)
 *          If (any mutexes in mux have owner died flag set)
 *            return (ERROR_SEM_OWNER_DIED)
 *          Endif
 *        Endif
 *        If (pcSemRec < (pcSemRec = pMux->cSemRec))
 *          return (ERROR_PARAM_TOO_SMALL)
 *        Endif
 *        pflAttr = pMux->usFlags & SEM_MUXFLAGS
 *        copy contents of pMux->pSemRec into pSemRec
 *      Endif
 *      return (NO_ERROR)
 */

ULONG SEMQueryMuxWaitSem(HMUX hmux,           /* muxwait handle */
                         PULONG pcSemRec,     /* count of SEMRECORDs */
                         PSEMRECORD pSemRec,  /* array of SEMRECORDs */
                         PULONG pflAttr)      /* attributes */

{
    register PSEM pMux;               /* ptr to mux semaphore */
    ULONG ulIndex;                    /* array index */
    register USHORT cSemR;            /* count of semrecords in pSemR */
    PSEMRECORD pSemR;                 /* ptr to semrecord of mux */
    USHORT usFlags;                   /* flags for mux semaphore */
    ULONG rc;                         /* return code */


    TKBeginNoBlock();           /* Section May NOT Block */

    /*
     *  Get pointer to correct table, shared or private.
     */
    ulIndex = semGetIndex(hmux);
    if (semIfShared(hmux)) {
      /*
       *  Shared Muxwait Semaphore.
       */
      tkAssert(semChkSharedHdl(ulIndex,DCMW_MUX_SEM,pPTDACur) == NO_ERROR);
      pMux = pShSemTbl[ulIndex];
    }
    else {
      /*
       *  Private Muxwait Semaphore.
       */
      tkAssert(semChkPrivateHdl(ulIndex,DCMW_MUX_SEM) == NO_ERROR);
      pMux = pPTDACur->pPrSemTbl[ulIndex];
    }

    usFlags = ((PSHUN)pMux)->SMux.usFlags;
    if ((cSemR = ((PSHUN)pMux)->SMux.cSemRec) == 0) {
      /*
       *  Empty muxwait.  Fill in the number of semaphores and
       *  the flags set on creation.
       */
      *pcSemRec = 0;
      *pflAttr = usFlags & SEM_MUXFLAGS;
      TKEndNoBlock();
      return (NO_ERROR);
    }

    /*
     *  Muxwait is NOT empty.
     */
    pSemR = ((PSHUN)pMux)->SMux.pSemRec;
    if (*pcSemRec < cSemR) {
      /*
       *  Make sure buffer passed in is not too small for the current
       *  mux list.
       */
      *pcSemRec = cSemR;
      TKEndNoBlock();
      return (ERROR_PARAM_TOO_SMALL);
    }
    /*
     *  Fill in the number of entries, the flags, and the mux list
     *  entries into the appropriate output parameters.
     */
    *pcSemRec = cSemR;
    *pflAttr = usFlags & SEM_MUXFLAGS;
    memcpy(pSemRec,pSemR + 1,
           sizeof(SEMRECORD) * ((PSHUN)pMux)->SMux.cSemRec);
    semAddSigInMux(pSemRec,*pcSemRec);

    /*
     *  Make sure all mux list entries are still valid.  Return
     *  the appropriate error code if they are not valid.
     */
    if ((rc = semChkSRInMux(pSemR,cSemR,usFlags)) != NO_ERROR) {
      tkAssert(rc != ERROR_EMPTY_MUXWAIT);
      TKEndNoBlock();
      return (rc);
    }
    TKEndNoBlock();
    return (NO_ERROR);
}
