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

/*
 *      Semaphores - 32-bit Mutex 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 1994 - Ron Cadima This module was created to merge all
 *         code associated with Mutex semaphores into one module and to
 *         optimize the code path in the Mutex semaphore routines. The code
 *         in this module was originally moved from SEMVERIF.C, SEMALLOC.C
 *         and SEMWRKR.C and then modified.
 *
 *      92009 - 8/3/94 - RAC Add fix to see if semaphore is owned by thread
 *                       upon return from TKSLEEP with error code of
 *                       PM_EVENT_POSTED.  If it is, change return code to
 *                       NO_ERROR.
 *
 *     105375 11/18/94 - Roll defect 92885 into r207 sparse tree.
 *                       SemRequestOwnedMutex-check if thread owns
 *                       sem after return from TKSleep.
 *                       Changes are flagged with "92885".
 *
 *     113468 12/22/94   Moved ERROR_SEM_OWNER_DIED logic so that it
 *                       it is verified right after thread wakes up.
 *                       This is fix 107369 put in R206.
 *
 *     148148 2/19/96    Verify valid handle when semaphores
 *                       are opened with a null name.
 *
 *     148487 4/24/96    Don't just TKASSERT if semaphore handle is
 *                       invalid.  Return with ERROR_INVALID_HANDLE.
 *
 *     151677 05/08/96   Skip Nizinski Passing THUNKSEF as a parameter causes
 *                       the ds and es regs to be changed from flat (0168) to
 *                       the stack selector. This causes later problems in
 *                       routines such as TKSleep and pgSwitchContext which
 *                       assume ds and es are flat.
 */

#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

ULONG    SEMRequestUnownedMutex(HMTX,ULONG);
ULONG    cdecl SEMRequestOwnedMutex(THUNKSEF,HMTX,ULONG);
ULONG    SEMReleaseUnwaitedMutex(HMTX);
ULONG    cdecl SEMReleaseWaitedMutex(THUNKSEF,HMTX);
ULONG    SEMRequestOwnedMutexWkr(HMTX,ULONG);

/***EP  VR32CreateMutexSem - Create a Mutex Semaphore
 *
 *      VR32CreateMutexSem creates and opens a mutex semaphore.  If 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.
 *
 *      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 fState flag sets the initial state of the mutex semaphore.  If the
 *      fState flag is set to false, then the mutex is unowned.  If the fState
 *      flag is set to true, then the calling thread becomes the owner of the
 *      mutex and a request is executed.
 *
 *      ENTRY   sef     - system entry frame
 *              pszName - asciiz name, or NULL for an anonymous mutex
 *              phmtx   - pointer to receive a mutex handle
 *              flAttr  - attributes:
 *                          DC_SEM_SHARED
 *              fState  - TRUE  => initial state of mutex owned
 *                        FALSE => initial state of mutex unowned
 *
 *      EXIT    Success - NO_ERROR
 *              Failure - ERROR_DUPLICATE_NAME
 *                        ERROR_INVALID_NAME
 *                        ERROR_INVALID_PARAMETER
 *                        ERROR_TOO_MANY_HANDLES
 *
 *      PSEUDO CODE
 *
 *      If (flAttr has more than 1 bit set)
 *        return (ERROR_INVALID_PARAMETER)
 *      Endif
 *      If (pszName != NULL)
 *        validate pszName
 *        call semStoreStr to copy pszName into pSemName
 *      Endif
 *      rc = call SEMApiCreateMutexSem with pSemName and hmtxTmp
 *      If (rc == error)
 *        free pSemName
 *      Else
 *        call TKSuDWord to copy hmtxTmp to phmtx
 *      Endif
 *      return (rc)
 */

ULONG cdecl VR32CreateMutexSem(THUNKSEF sef,       /* system entry frame */
                               PSZ pszName,        /* semaphore name */
                               PHMTX phmtx,        /* ptr to handle */
                               ULONG flAttr,       /* attributes */
                               BOOL fState)        /* initial state */

{
    PSZ pSemName = NULL;     /* ptr to resident buffer for pszName */
    HMTX hmtxTmp;            /* temporary handle */
    PHMTX pStrMtx;           /* ptr to mutex handle in string structure */
    ULONG rc;                /* return code */


    /*
     *  Check flag attributes.
     *  Check fState is either TRUE or FALSE.
     */
    if (((flAttr | DC_SEM_SHARED) != DC_SEM_SHARED) ||
        ((fState != TRUE) && (fState != FALSE)))
      return (ERROR_INVALID_PARAMETER);

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

    if (pszName != NULL) {
      /*
       *  Validate name string passed in.
       */
      if ((rc = semValidateName(pszName,SSToDS(&pSemName))) != NO_ERROR) {
        return (rc);
      }
      /*
       *  Set shared flag and store the string node in the string table.
       */
      flAttr |= DC_SEM_SHARED;
      if ((rc = semStoreStr(pSemName,SSToDS(&pStrMtx))) != NO_ERROR) {
        VMFreeKHB(VM_HKH_PUB_SWAPRW,pSemName);
        return (rc);
      }
    }

    /*
     *  Call the allocation/validation layer.
     */
    rc = SEMApiCreateMutexSem(pSemName,SSToDS(&hmtxTmp),flAttr,fState);

    if (rc == NO_ERROR) {
      if (pSemName)
        *pStrMtx = semGetIndex(hmtxTmp);
      /*
       *  Copy handle back to user space.
       */
      if ((rc = TKSuDWord(hmtxTmp,phmtx)) != NO_ERROR) {
        SEMCloseMutexSem(hmtxTmp);
        /*
         *  The return code from CloseMutex is not checked here.
         *  The process is dying, so if the close gets error_sem_busy,
         *  semCleanProcDying will close the semaphore in the current
         *  process and return error_owner_died to any process
         *  waiting on the mutex if the current process owned it.
         *  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);
      }
    }
    else {
      if (pSemName) {
        /*
         *  Error.  Free string node from string table and string name
         *  from semaphore structure.
         */
        /*
          Be sure to delete the string table entry before releasing the
          storage.   defect 67077.
        */
        semStrTblDelete(pSemName);
        VMFreeKHB(VM_HKH_PUB_SWAPRW,pSemName);
      }
    }
#ifdef PMDEBUG
    if (rc == NO_ERROR) {
      if (semIfShared(hmtxTmp))
        ((PSMUTEX)pShSemTbl[semGetIndex(hmtxTmp)])->pulCreatAddr = phmtx;
      else
        ((PPMUTEX)((pPTDACur->pPrSemTbl)[semGetIndex(hmtxTmp)]))
                  ->pulCreatAddr = phmtx;
    }
#endif
    return (rc);

    sef;                  // Avoid compiler warning. 151677
}

/***EP  VR32OpenMutexSem - Open a Mutex Semaphore
 *
 *      VR32OpenMutexSem opens a mutex semaphore.  DosCreateMutexSem must
 *      create the semaphore before VR32OpenMutexSem can open it.  If the
 *      pszName is not null, then the semaphore is a named shared mutex
 *      semaphore.  Otherwise, the semaphore will be referenced by the
 *      semaphore handle passed in phmtx.  VR32OpenMutexSem only returns the
 *      handle of the semaphore.  It does not test or change any of the values
 *      of the semaphore.  If phmtx 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 that semaphore, the semaphore disappears and must
 *      be recreated by its next user with DosCreateMutexSem.
 *
 *      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 DosCloseMutexSem calls must equal the number of
 *      DosOpenMutexSem and DosCreateMutexSem calls.
 *
 *      ENTRY   sef     - system entry frame
 *              pszName - asciiz name, NULL => anonymous shared
 *              phmtx   - if pszName is NULL, phmtx contains the handle of
 *                        an anonymous mutex 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 (pszName != NULL)
 *        validate pszName
 *        call semSaveStr to copy pszName into pSemName
 *      Endif
 *      rc = call SEMApiOpenMutexSem with pSemName and hmtxTmp
 *      If (rc == error)
 *        free pSemName
 *      Else
 *        call TKSuDWord to copy hmtxTmp to phmtx
 *      Endif
 *      return (rc)
 */

ULONG cdecl VR32OpenMutexSem(THUNKSEF sef,       /* system entry frame */
                             PSZ pszName,        /* semaphore name */
                             PHMTX phmtx)        /* ptr to handle */

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


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

    /*
     *  Copy handle into kernel space.
     */
    if ((rc = TKFuDWord(SSToDS(&hmtxTmp),phmtx)) != 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 = SEMApiOpenMutexSem(pSemName,SSToDS(&hmtxTmp));

    if (pSemName != NULL)
      VMFreeKHB(VM_HKH_PUB_SWAPRW,pSemName);
    if (rc == NO_ERROR) {
      /*
       *  Copy handle back to user space.
       */
      if ((rc = TKSuDWord(hmtxTmp,phmtx)) != NO_ERROR) {
        SEMCloseMutexSem(hmtxTmp);
        /*
         *  The return code from CloseMutex is not checked here.
         *  The process is dying, so if the close gets error_sem_busy,
         *  semCleanProcDying will close the semaphore in the current
         *  process and return error_owner_died to any process
         *  waiting on the mutex if the current process owned it.
         *  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);

    sef;                  // Avoid compiler warning. 151677
}

/***EP  VR32CloseMutexSem - Close a Mutex Semaphore
 *
 *      VR32CloseMutexSem closes a mutex semaphore that was previously opened
 *      via DosOpenMutexSem or DosCreateMutexSem.  If this is the last
 *      reference to this mutex, then the mutex is destroyed.
 *
 *      When all processes using the semaphore issue a DosCloseMutexSem, the
 *      semaphore is deleted.  If a process terminates with open semaphores,
 *      the system closes the semaphores.  If any of the semaphores are owned
 *      by the current process, all waiters on the semaphore are awakened and
 *      any subsequent calls on the semaphore will get the ERROR_SEM_OWNER_DIED
 *      return code.  This means that the owner of the semaphore terminated
 *      without releasing it, so the resources may be in an indeterminate
 *      state.
 *
 *      The ERROR_SEM_BUSY return code will be returned if (1) the mutex is owned
 *      by this thread, (2) the thread is trying to make the final close of a
 *      semaphore currently owned by another thread in the same process, or
 *      (3) the thread is trying to make the final close of a semaphore while
 *      another thread in the same process is still waiting on the semaphore.
 *
 *      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 DosCreateMutexSem.
 *
 *      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 DosCloseMutexSem calls must equal the number of
 *      DosOpenMutexSem and DosCreateMutexSem calls.
 *
 *      ENTRY   sef  - system entry frame
 *              hmtx - handle of a mutex
 *
 *      EXIT    Success - NO_ERROR
 *              Failure - ERROR_INVALID_HANDLE
 *                        ERROR_SEM_BUSY
 *
 *      PSEUDO CODE
 *
 *      return ( SEMApiCloseMutexSem(hmtx) )
 */

ULONG cdecl VR32CloseMutexSem(THUNKSEF sef,       /* system entry frame */
                              HMTX hmtx)          /* mutex handle */

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

    /*
     *  Check validity of mutex handle.
     */
    if (semChkHdl(hmtx,DCM_MUTEX_SEM,pPTDACur) != NO_ERROR)
      return (ERROR_INVALID_HANDLE);
    /*
     *  Call the worker layer.
     */
    return ( SEMCloseMutexSem(hmtx) );

    sef;                  // Avoid compiler warning. 151677
}


/***EP  VR32RequestMutexSem - Request a Mutex Semaphore
 *
 *      VR32RequestMutexSem requests a mutex semaphore that has been previously
 *      created (via DosCreateMutexSem) and opened (via DosOpenMutexSem).  If
 *      a semaphore is unowned, VR32RequestMutexSem sets it to be owned and
 *      returns immediately to the caller.  If the semaphore is owned,
 *      VR32RequestMutexSem will block the thread until it is unowned, then try
 *      again.  The ulTimeout parameter places an upper boundary on the amount
 *      of time to block before returning, even though the semaphore is still
 *      owned.  If the semaphore is still in use at timeout, the ERROR_TIMEOUT
 *      return code will be returned.  If a timeout of zero is passed in,
 *      VR32RequestMutexSem will return without blocking.  If a timeout of -1
 *      is passed in, it will block indefinitely.
 *
 *      If a thread terminates while it owns a mutex semaphore, the
 *      ERROR_SEM_OWNER_DIED return code is returned to the next thread that
 *      calls DosRequestMutexSem.  That thread must then call DosCloseMutexSem
 *      so that the semaphore may be destroyed.
 *
 *      When a thread no longer requires the protected resource, it issues
 *      DosReleaseMutexSem to set the semaphore to be unowned.  Any threads
 *      that were blocked waiting for that semaphore can try to gain access
 *      to the semaphore (priority is assigned in the order of the requests).
 *
 *      The request/release calls may be nested.  A maximum of 64K requests is
 *      allowed per semaphore at any one time.  However, when a thread is
 *      finished using a semaphore (completes the critical section), the
 *      number of DosReleaseMutexSem calls must equal the number of
 *      DosRequestMutexSem calls.
 *
 *      ENTRY   sef       - system entry frame
 *              hmtx      - handle of a mutex
 *              ulTimeout - timeout in milliseconds
 *
 *      EXIT    Success - NO_ERROR
 *              Failure - ERROR_INTERRUPT
 *                        ERROR_INVALID_HANDLE
 *                        ERROR_SEM_OWNER_DIED
 *                        ERROR_TIMEOUT
 *                        ERROR_TOO_MANY_SEM_REQUESTS
 *
 *      PSEUDO CODE
 *
 *      return ( SEMRequestMutexSem(hmtx,ulTimeout) )
 */

ULONG cdecl VR32RequestMutexSem(THUNKSEF sef,     /* system entry frame */
                                HMTX hmtx,        /* mutex handle */
                                ULONG ulTimeout)  /* timeout in milliseconds */

{
    THUNKSEF dummy;
    ULONG rc;                         /* return code */

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

    /*
     *  Check validity of mutex handle.
     */
    if (semChkHdl(hmtx,DCM_MUTEX_SEM,pPTDACur) != NO_ERROR)
      return (ERROR_INVALID_HANDLE);

    if ((rc = SEMRequestUnownedMutex(semStripSigBits(hmtx),
                                     ulTimeout)) == ERROR_MUTEX_OWNED)
    {
         // The parameter DUMMY is passed by value instead of by reference.
         // The compiler uses REP MOVSD to make a copy on the stack. When
         // doing so, it PUSHes DS before and POPs DS after the copy, but it
         // does not do the same thing for ES. ES is changed to the value of
         // SS and never restored. This routine eventually calls TKSLEEP
         // which expects ES to equal DS, which is not true. A trap d will
         // be encountered when we attempt to do a MEMCPY in TKUPDATESTATE.
         // We substituted the call with an ASM version to avoid ES from
         // being altered and not restored.                                151677

      //    rc = SEMRequestOwnedMutex(dummy, semStripSigBits(hmtx), ulTimeout);

         ulong_t ulIndex = semStripSigBits(hmtx);		// D149854

         _asm
         {
           push ulTimeout
      //D149854          push hmtx
           push ulIndex						// D149854
           sub  esp,size dummy
           call dword ptr SEMRequestOwnedMutex
           add  esp,size dummy + 8
           mov  rc,eax
         };
    }

    return(rc);

    sef;                  // Avoid compiler warning. 151677
    dummy;                // Avoid compiler warning. 151677
}


/***EP  VR32ReleaseMutexSem - Release a Mutex Semaphore
 *
 *      VR32ReleaseMutexSem releases a mutex semaphore that was previously
 *      requested by DosRequestMutexSem.  The request count is decremented.
 *      If the count goes to zero (no more requests were made by the current
 *      thread), then another thread is given the opportunity to gain
 *      ownership of the mutex.  DosReleaseMutexSem cannot be issued against a
 *      mutex semaphore by any thread other than the owner.
 *
 *      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 has been released.  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 request/release calls may be nested.  A maximum of 64K requests is
 *      allowed per semaphore at any one time.  However, when a thread is
 *      finished using a semaphore (completes the critical section), the
 *      number of DosReleaseMutexSem calls must equal the number of
 *      DosRequestMutexSem calls.
 *
 *      ENTRY   sef  - system entry frame
 *              hmtx - handle of a mutex
 *
 *      EXIT    Success - NO_ERROR
 *              Failure - ERROR_INVALID_HANDLE
 *                        ERROR_NOT_OWNER
 *
 *      PSEUDO CODE
 *
 *      return ( SEMApiReleaseMutexSem(hmtx) )
 */

ULONG cdecl VR32ReleaseMutexSem(THUNKSEF sef,       /* system entry frame */
                                HMTX hmtx)          /* mutex handle */

{
    THUNKSEF dummy;
    ULONG rc;                         /* return code */

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

    /*
     *  Check validity of mutex handle.
     */
    if (semChkHdl(hmtx,DCM_MUTEX_SEM,pPTDACur) != NO_ERROR)
      return (ERROR_INVALID_HANDLE);

    if ((rc = SEMReleaseUnwaitedMutex(semStripSigBits(hmtx))) == ERROR_SEM_BUSY)
    {
         // The parameter DUMMY is passed by value instead of by reference.
         // The compiler uses REP MOVSD to make a copy on the stack. When
         // doing so, it PUSHes DS before and POPs DS after the copy, but it
         // does not do the same thing for ES. ES is changed to the value of
         // SS and never restored. This routine eventually calls TKSLEEP
         // which expects ES to equal DS, which is not true. A trap d will
         // be encountered when we attempt to do a MEMCPY in TKUPDATESTATE.
         // We substituted the call with an ASM version to avoid ES from
         // being altered and not restored.                                151677

      //    rc = SEMReleaseWaitedMutex(dummy, semStripSigBits(hmtx));

         {								// D149854
           ulong_t ulIndex = semStripSigBits(hmtx);		// D149854
           _asm
           {
       //D149854          push hmtx
             push ulIndex						// D149854
             sub  esp,size dummy
             call dword ptr SEMReleaseWaitedMutex
             add  esp,size dummy + 4
             mov  rc,eax
           };
         }								// D149854
    }

    return (rc);

    sef;                  // Avoid compiler warning. 151677
    dummy;                // Avoid compiler warning. 151677
}



/***EP  VR32QueryMutexSem - Query a Mutex Semaphore
 *
 *      VR32QueryMutexSem will query information about a given mutex semaphore
 *      if the semaphore was previously opened by the process with a
 *      DosOpenMutexSem.  If the mutex semaphore exists, the relative
 *      information is obtained and returned to the caller in the given
 *      parameters.  If the semaphore is owned, then owner information is
 *      returned.  If the semaphore is unowned, then zero is returned in the
 *      pulCount parameter.
 *
 *      The ERROR_SEM_OWNER_DIED return code is returned if the owner has died
 *      and the semaphore has not yet been destroyed by a call to
 *      DosCloseMutexSem.  In this case, all of the parameters are filled in
 *      with the information of the dead mutex owner.  That thread must then
 *      call DosCloseMutexSem so the semaphore may be destroyed.
 *
 *      The user's PID and TID are 32-bit values, while the PID and TID that
 *      the semaphore worker routines are using are 16-bit values.  The high
 *      words of the 32-bit values must therefore be zeroed if the low words
 *      are to be filled in with 16-bit values.
 *
 *      ENTRY   sef       - system entry frame
 *              hmtx      - handle of a mutex
 *              ppidOwner - filled in with the process ID of a mutex owner
 *                          (not filled in if semaphore is unowned)
 *              ptidOwner - filled in with the thread ID of a mutex owner
 *                          (not filled in if semaphore is unowned)
 *              pulCount  - filled in with the current number of
 *                          DosRequestMutexSem calls made by the owning
 *                          thread (ZERO if semaphore is unowned)
 *
 *      EXIT    Success - NO_ERROR
 *              Failure - ERROR_INVALID_HANDLE
 *                        ERROR_INVALID_PARAMETER
 *                        ERROR_SEM_OWNER_DIED
 *
 *      PSEUDO CODE
 *
 *      validate ppidOwner, ptidOwner, and pulCount
 *      rc = call SEMApiQueryMutexSem with pidTmp, tidTmp, ulCt
 *      If (rc == NO_ERROR)
 *        call TKSuDWord to copy ulpid to ppidOwner
 *        call TKSuDWord to copy ultid to ptidOwner
 *        call TKSuDWord to copy ulCt to pulCount
 *      Endif
 *      return (rc)
 */

ULONG cdecl VR32QueryMutexSem(THUNKSEF sef,       /* system entry frame */
                              HMTX hmtx,          /* mutex handle */
                              PULONG ppidOwner,   /* process id */
                              PULONG ptidOwner,   /* thread id */
                              PULONG pulCount)    /* number of requests */

{
    PID pidTmp;               /* temp 16-bit storage for process id */
    TID tidTmp;               /* temp 16-bit storage for thread id */
    ULONG ulpid;              /* temp 32-bit storage for process id */
    ULONG ultid;              /* temp 32-bit storage for thread id */
    ULONG ulCt;               /* temp storage for state of event */
    ULONG rc;                 /* return code */
    ULONG rc2;                /* another return code */


    /*
     *  Verify that user pointers are not in kernel space.
     */
    semChkNotKrnlSpace((PVOID)ppidOwner);
    semChkNotKrnlSpace((PVOID)ptidOwner);
    semChkNotKrnlSpace((PVOID)pulCount);

    /*
     *  Check validity of mutex handle.
     */
    if (semChkHdl(hmtx,DCM_MUTEX_SEM,pPTDACur) != NO_ERROR)
      return (ERROR_INVALID_HANDLE);

    /*
     *  Call the allocation/validation layer.
     */
    pidTmp = tidTmp = 0;

    rc = SEMQueryMutexSem(semStripSigBits(hmtx),SSToDS(&pidTmp),SSToDS(&tidTmp),
                             SSToDS(&ulCt));

    if ((rc == NO_ERROR) || (rc == ERROR_SEM_OWNER_DIED)) {
      /*
       *  Copy the pid, tid, and request count of the owner back to
       *  user space.
       */
      if (ulCt != 0) {
        ulpid = (ULONG)pidTmp;
        ultid = (ULONG)tidTmp;
        if (((rc2 = TKSuDWord(ulpid,ppidOwner)) != NO_ERROR) ||
            ((rc2 = TKSuDWord(ultid,ptidOwner)) != NO_ERROR))
          return (rc2);
      }
      if ((rc2 = TKSuDWord(ulCt,pulCount)) != NO_ERROR)
        return (rc2);
    }
    return (rc);

    sef;                  // Avoid compiler warning. 151677
}

/***EP  SEMApiCreateMutexSem - Allocation to Create a Mutex Semaphore
 *
 *      SEMApiCreateMutexSem is the allocation routine for the
 *      DosCreateMutexSem 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 exitting.
 *
 *      ENTRY   pszName - asciiz name, or NULL for an anonymous mutex
 *              phmtx   - pointer to receive a mutex handle
 *              flAttr  - attributes:
 *                           DC_SEM_SHARED
 *              fState  - TRUE => initial state of mutex owned
 *                        FALSE => initial state of mutex unowned
 *
 *      EXIT    Success - NO_ERROR
 *              Failure - ERROR_TOO_MANY_HANDLES
 *
 *      PSEUDO CODE
 *
 *      If (shared)                                // shared
 *        allocate shared mutex semaphore and open queue node
 *        rc = call semCreateMutexSem with shared mutex 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 mutex semaphore
 *        rc = call semCreateMutexSem with private mutex semaphore
 *        If (rc == error)
 *          set private table entry back to NULL
 *          decrement pPTDACur->ulPrTotUsed
 *          free private semaphore
 *        Endif
 *      Endif
 *      return (rc)
 */

ULONG SEMApiCreateMutexSem(PSZ pszName,      /* semaphore name */
                           PHMTX phmtx,      /* ptr to handle */
                           ULONG flAttr,     /* attributes */
                           BOOL fState)      /* initial state */

{
    PSMUTEX pSMtxSem;           /* pointer to resident Shared Semaphore */
    PPMUTEX pPMtxSem;           /* pointer to resident Private Semaphore */
    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) {
      /*
       *  Shared Mutex Semaphore.  Allocate appropriate items for
       *  a shared semaphore.
       */
      if ((rc = semSharedAlloc((PVOID*)(SSToDS(&pSMtxSem)),sizeof(SMUTEX),
                               SSToDS(&pOpenN),phmtx)) != NO_ERROR)
        return (rc);

      /*
       *  Call worker layer.
       */
      semInitSMtxSig(pSMtxSem);
      semInitOpenQSig(pOpenN);
      rc = semCreateMutexSem(pszName,phmtx,flAttr,fState,
                             (PMTXUN)pSMtxSem,pOpenN);
      if (rc != NO_ERROR) {
        /*
         *  Error.  Back out the create if necessary.  Free the semaphore
         *  structure and the open queue node.
         */
        ulIndex = semGetIndex(*phmtx);
        if ((ulIndex < ulShTblSize) && (pShSemTbl[ulIndex] == pSMtxSem)) {
          pShSemTbl[ulIndex] = NULL;
          ulShTotUsed--;
        }
        freeKRHB(pSMtxSem);
        BMPFree(pBMPOpenQSem, pOpenN);  // PERF: 98488
      }
    }
    else {
      /*
       *  Private Mutex Semaphore.  Allocate appropriate items for
       *  a private semaphore.
       */
      if ((rc = semPrivateAlloc((PVOID*)(SSToDS(&pPMtxSem)),sizeof(PMUTEX),
                                phmtx)) != NO_ERROR)
        return (rc);

      /*
       *  Call worker layer.
       */
      semInitPMtxSig(pPMtxSem);
      rc = semCreateMutexSem(pszName,phmtx,flAttr,fState,
                             (PMTXUN)pPMtxSem,NULL);
      if (rc != NO_ERROR) {
        /*
         *  Error.  Back out the create if necessary.  Free the semaphore
         *  structure.
         */
        ulIndex = semGetIndex(*phmtx);
        if ((ulIndex < pPTDACur->ulPrTblSize) &&
            (pPTDACur->pPrSemTbl[ulIndex] == pPMtxSem)) {
          pPTDACur->pPrSemTbl[ulIndex] = NULL;
          (pPTDACur->ulPrTotUsed)--;
        }
        freeKRHB(pPMtxSem);
      }
    }
    return (rc);
}


/***EP  SEMApiOpenMutexSem - Allocation to Open a Mutex Semaphore
 *
 *      SEMApiOpenMutexSem is the allocation routine for the
 *      DosOpenMutexSem 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
 *              phmtx   - if pszName is NULL, phmtx contains the handle of
 *                        an anonymous mutex 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 mutex 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 SEMApiOpenMutexSem(PSZ pszName,       /* semaphore name */
                         PHMTX phmtx)       /* ptr to handle */

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


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

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

      semInitOpenQSig(pOpenN);
      ulIndex = semGetIndex(*phmtx);
      pSem = pShSemTbl[ulIndex];
      /*
       *  Check to be sure semaphore exists and is a mutex semaphore.
       */
      if ((pSem == NULL) || (!(((PSHUN)pSem)->SMtx.usFlags & DCM_MUTEX_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.
       */
      *phmtx = semStripSigBits(*phmtx);
      rc = semOpenSem(phmtx,pOpenN,&((PSHUN)pSem)->SMtx.pOpenQ,NULL,pPTDACur);
      if ((rc != NO_ERROR) || (pOpenN->usOpenCt == SEM_OPENQFLAG))
        BMPFree(pBMPOpenQSem, pOpenN);  // PERF: 98488
      return (rc);
    }
    else {
      /*
       *  Private Mutex Semaphore.  Validate the handle.
       */
      if (!(semVerifySigBits(*phmtx)))                        //148148
        return (ERROR_INVALID_HANDLE);                        //148148
      ulIndex = semGetIndex(*phmtx);
      if (semChkPrivateHdl(ulIndex,DCM_MUTEX_SEM) != NO_ERROR)
        return (ERROR_INVALID_HANDLE);
      pSem = pPTDACur->pPrSemTbl[ulIndex];
      /*
       *  Call the worker layer.
       */
      *phmtx = semStripSigBits(*phmtx);
      return ( semOpenSem(phmtx,NULL,NULL,&((PPRUN)pSem)->PMtx.usOpenCt,pPTDACur) );
    }
}



/***LP  semCreateMutexSem - Create a Mutex Semaphore
 *
 *      semCreateMutexSem is the worker function for the DosCreateMutexSem
 *      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.
 *
 *      This routine creates and opens a mutex 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.
 *
 *      The fState flag sets the initial state of the mutex semaphore.  If the
 *      fState flag is set to false, then the mutex is unowned.  If the fState
 *      flag is set to true, then the calling thread becomes the owner of the
 *      mutex and a request is executed.
 *
 *      ENTRY   pszName - asciiz name, or NULL for an anonymous mutex
 *              phmtx   - pointer to receive a mutex handle
 *              flAttr  - attributes:
 *                          DC_SEM_SHARED
 *              fState  - TRUE  => initial state of mutex owned
 *                        FALSE => initial state of mutex unowned
 *              pMtxSem - pointer to a mutex semaphore
 *              pOpenN  - pointer to an Open Queue node
 *
 *      EXIT    Success - NO_ERROR
 *              Failure - none
 *
 *      PSEUDO CODE
 *
 *      If (shared)                                        // shared
 *        pMtxSem->usFlags = flAttr | DCM_MUTEX_SEM        // fill in structure
 *        pMtxSem->pMuxQ = NULL
 *        pMtxSem->usRequestCt = 0
 *        pMtxSem->usRequesterCt = 0
 *        pMtxSem->pOpenQ = NULL
 *        pMtxSem->usSlotNum = SEM_MUTEX_UNOWNED
 *        pMtxSem->pszName = pszName
 *        pShSemTbl[phmtx] = pMtxSem
 *        phmtx |= SEM_SHAREDMASK;                         // shared semaphore
 *        increment ulShTotUsed
 *        rc = call semOpenSem with open queue             // open mutex
 *      Else                                               // private
 *        pMtxSem->usFlags = flAttr | DCM_MUTEX_SEM        // fill in structure
 *        pMtxSem->pMuxQ = NULL
 *        pMtxSem->usRequestCt = 0
 *        pMtxSem->usRequesterCt = 0
 *        pMtxSem->usOpenCt = 0
 *        pMtxSem->usSlotNum = SEM_MUTEX_UNOWNED
 *        pPTDACur->pPrSemTbl[phmtx] = pMtxSem
 *        increment pPTDACur->ulPrTotUsed
 *        rc = call semOpenSem with open count             // open mutex
 *      Endif
 *      If ((rc == NO_ERROR) && (fState))
 *        rc = call SEMRequestMutexSem with SEM_INDEFINITE_WAIT // request
 *      Endif
 *      return (rc)
 */

ULONG semCreateMutexSem(PSZ pszName,        /* semaphore name */
                        PHMTX phmtx,        /* ptr to handle */
                        ULONG flAttr,       /* attributes */
                        BOOL fState,        /* initial state */
                        PMTXUN pMtxSem,     /* ptr to mutex semaphore */
                        POPENQ pOpenN)      /* ptr to Open Queue node */

{
    ULONG rc;                   /* return code */


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

    if (flAttr & DC_SEM_SHARED) {
      /*
       *  Shared Mutex Semaphore.
       */
      tkAssert(pShSemTbl[*phmtx] == NULL);
      /*
       * Fill in shared semaphore structure.
       */
      pMtxSem->ShMtx.usFlags = (USHORT)(flAttr | DCM_MUTEX_SEM);
      pMtxSem->ShMtx.pMuxQ = 0;
      pMtxSem->ShMtx.pOpenQ = 0;
      pMtxSem->ShMtx.usRequestCt = 0;
      pMtxSem->ShMtx.usRequesterCt = 0;
      pMtxSem->ShMtx.usSlotNum = SEM_MUTEX_UNOWNED;
      pMtxSem->ShMtx.pszName = pszName;

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

      /*
       *  Open mutex semaphore.
       */
      rc = semOpenSem(phmtx,pOpenN,&((PSHUN)pMtxSem)->SMtx.pOpenQ,NULL,pPTDACur);
    }
    else {
      /*
       *  Private Mutex Semaphore.
       */
      tkAssert(pPTDACur->pPrSemTbl[*phmtx] == NULL);
      /*
       *  Fill in private semaphore structure.
       */
      pMtxSem->PrMtx.usFlags = (USHORT)(flAttr | DCM_MUTEX_SEM);
      pMtxSem->PrMtx.pMuxQ = NULL;
      pMtxSem->PrMtx.usRequestCt = 0;
      pMtxSem->PrMtx.usRequesterCt = 0;
      pMtxSem->PrMtx.usOpenCt = 0;
      pMtxSem->PrMtx.usSlotNum = SEM_MUTEX_UNOWNED;

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

      /*
       *  Open mutex semaphore.
       */
      rc = semOpenSem(phmtx,NULL,NULL,&((PPRUN)pMtxSem)->PMtx.usOpenCt,pPTDACur);
    }

    if ((rc == NO_ERROR) && (fState))
      /*
       *  Request mutex semaphore with infinite wait.  If this blocks,
       *  we got a serious problem, since we're still trying to create
       *  it.
       */
      rc = SEMRequestUnownedMutex(semStripSigBits(*phmtx),SEM_INDEFINITE_WAIT);

    TKEndNoBlock();
    return (rc);
}


/***EP  SEMCloseMutexSem - Close a Mutex Semaphore
 *
 *      SEMCloseMutexSem is the worker function for the DosCloseMutexSem
 *      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.
 *
 *      SEMCloseMutexSem closes a mutex semaphore that was previously opened.
 *      If this is the last reference to this mutex, then the mutex is
 *      destroyed.
 *
 *      When all processes using the semaphore issue a "close," the
 *      semaphore is deleted.   If a process terminates with open semaphores,
 *      the system closes the semaphores.  If any of the semaphores are owned
 *      by the current process, all waiters on the semaphore are awakened and
 *      any subsequent calls on the semaphore will get the ERROR_SEM_OWNER_DIED
 *      return code.  This means that the owner of the semaphore terminated
 *      without releasing it, so the resources may be in an indeterminate
 *      state.
 *
 *      The ERROR_SEM_BUSY return code will be returned if (1) the mutex is owned
 *      by this thread, (2) the thread is trying to make the final close of a
 *      semaphore currently owned by another thread in the same process, or
 *      (3) the thread is trying to make the final close of a semaphore while
 *      another thread in the same process is still waiting on the semaphore.
 *
 *      ENTRY   hmtx - handle of a mutex
 *
 *      EXIT    Success - NO_ERROR
 *              Failure - ERROR_SEM_BUSY
 *
 *      PSEUDO CODE
 *
 *      If (shared)                              // shared
 *        search OpenQ for current pid
 *        decrement usOpenCt in OpenQ node
 *        If (usOpenCt == 0)
 *          If (!(usFlags & DM_OWNER_DIED)
 *            If ((mutex owned) && (pid of slot == current pid))
 *              increment usOpenCt
 *              return (ERROR_SEM_BUSY)
 *            Else
 *              If ((any single waiter has pid == current thread's pid) ||
 *                 (any mux waiter has pid == current thread's pid))
 *                increment usOpenCt
 *                return (ERROR_SEM_BUSY)
 *              Endif
 *            Endif
 *          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[hmtx] = NULL
 *          Endif
 *        Endif
 *      Else                                     // private
 *        decrement usOpenCt
 *        If (usOpenCt == 0)
 *          If (!(usFlags & DM_OWNER_DIED))
 *            If ((mutex owned) && (pid of slot == current pid))
 *              increment usOpenCt
 *              return (ERROR_SEM_BUSY)
 *            Else
 *              If ((any single waiter has pid == current thread's pid) ||
 *                  (any mux waiter has pid == current thread's pid))
 *                increment usOpenCt
 *                return (ERROR_SEM_BUSY)
 *              Endif
 *            Endif
 *          Endif
 *          destroy semaphore (free memory)
 *          pPTDACur->pPrSemTbl[hmtx] = NULL
 *        Endif
 *      Endif
 *      return (NO_ERROR)
 */

ULONG SEMCloseMutexSem(HMTX hmtx)         /* mutex handle */

{
    ULONG ulIndex;             /* array index */
    POPENQ pCurOpenN;          /* ptr to OpenQ node for current pid */
    POPENQ pPreOpenN;          /* ptr to previous OpenQ node */
    USHORT usSlot;             /* slot number of owning thread */
    PID Pid;                   /* process id of slot number */
    TID Tid;                   /* thread id of slot number */
    HMTX lhmtx;                /* Striooed version of mutex handle */
    register PSEM pMtx;        /* ptr to mutex semaphore */
    register POPENQ *ppOpenQ;  /* address of ptr to open queue */
    USHORT usDied = TRUE;      /* flag to denote if mutex in owner died state */
    USHORT usDelOpenQ = FALSE; /* flag to denote deleting current openq node */


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

    lhmtx = semStripSigBits(hmtx);

    ulIndex = semGetIndex(lhmtx);
    if (semIfShared(lhmtx)) {
      /*
       *  Shared Mutex Semaphore.
       */
      if(semChkSharedHdl(ulIndex,DCM_MUTEX_SEM,pPTDACur) == NO_ERROR) /* 148487*/
         pMtx = pShSemTbl[ulIndex];                                   /* 148487*/
      else {                                                          /* 148487*/
         TKEndNoBlock();                                              /* 148487*/
         return(ERROR_INVALID_HANDLE);                                /* 148487*/
      }
      /*
       *  Get open queue node for current process.
       */
      semGetOpenQNode(((PSHUN)pMtx)->SMtx.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 the semaphore is not owner died and that there
         *  are no threads in the current process waiting on this semaphore.
         */
        if (!(((PSHUN)pMtx)->SMtx.usFlags & DM_OWNER_DIED)) {
          /*
           *  Not owner died.
           */
          usDied = FALSE;
          usSlot = ((PSHUN)pMtx)->SMtx.usSlotNum;
          if (usSlot != SEM_MUTEX_UNOWNED) {
            /*
             *  Mutex is owned.  If owned by a thread in the current
             *  process, then final close cannot occur.
             */
            semSlotToPidTid(usSlot,SSToDS(&Pid),SSToDS(&Tid));
            if (Pid == pTCBCur->TCBpPTDA->ptda_Pid) {
              (pCurOpenN->usOpenCt)++;
              TKEndNoBlock();
              return (ERROR_SEM_BUSY);
            }
          }
          /*
           *  Check if there are any waiting threads.
           */
          if ((semSrchSnglForPid(pMtx) == NO_ERROR) ||
              (semSrchMuxForPid(pMtx) == NO_ERROR)) {
            (pCurOpenN->usOpenCt)++;
            TKEndNoBlock();
            return (ERROR_SEM_BUSY);
          }
        }
        semZeroBit(ulIndex,pPTDACur->pShSemBmp);
        ppOpenQ = &((PSHUN)pMtx)->SMtx.pOpenQ;

        /*
         *  Must NOT remove first node in open queue if the semaphore
         *  is in the owner died state.  The only time this node may
         *  be removed is if it is the only node left in the open
         *  queue list.
         */
        if (!((usDied) && (pCurOpenN == *ppOpenQ))) {
          /*
           *  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,ppOpenQ);
          usDelOpenQ = TRUE;
        }
        /*
         *  If owner died and there is only one node left in the open
         *  queue, then this node may be removed.
         */
        if ((usDied) && ((*ppOpenQ)->usOpenCt == 0) &&
            ((*ppOpenQ)->pNextOpen == NULL)) {
          semAssertOpenQSig(*ppOpenQ);
          usDelOpenQ = TRUE;
          pCurOpenN = *ppOpenQ;
          *ppOpenQ = NULL;
        }

        if (*ppOpenQ == 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.
           */
          semAssertSMtxSig(pMtx);
          semSharedClose(pMtx,ulIndex,((PSHUN)pMtx)->SMtx.pszName);
        }
        if (usDelOpenQ) {
          /*
           *  Free the open queue node.
           */
          semAssertOpenQSig(pCurOpenN);
          TKEndNoBlock();
          BMPFree(pBMPOpenQSem, pCurOpenN);  // PERF: 98488
          TKBeginNoBlock();
        }
      }
    }
    else {
      /*
       *  Private Mutex Semaphore.
       */
      if(semChkPrivateHdl(ulIndex,DCM_MUTEX_SEM) == NO_ERROR)        /* 148487*/
         pMtx = pPTDACur->pPrSemTbl[ulIndex];
      else {                                                         /* 148487*/
         TKEndNoBlock();                                             /* 148487*/
         return(ERROR_INVALID_HANDLE);                               /* 148487*/
      }

      if (--(((PPRUN)pMtx)->PMtx.usOpenCt) == 0) {
        /*
         *  About to make final close of semaphore.
         *  Must make sure the semaphore is not owner died and that there
         *  are no threads waiting on this semaphore.
         */
        if (!(((PPRUN)pMtx)->PMtx.usFlags & DM_OWNER_DIED)) {
          /*
           *  Not owner died.
           */
          usSlot = ((PPRUN)pMtx)->PMtx.usSlotNum;
          if (usSlot != SEM_MUTEX_UNOWNED) {
            /*
             *  Mutex is owned.  If owned by a thread in the current
             *  process, then final close cannot occur.
             */
            semSlotToPidTid(usSlot,SSToDS(&Pid),SSToDS(&Tid));
            if (Pid == pTCBCur->TCBpPTDA->ptda_Pid) {
              (((PPRUN)pMtx)->PMtx.usOpenCt)++;
              TKEndNoBlock();
              return (ERROR_SEM_BUSY);
            }
          }
          /*
           *  Check if there are any waiting threads.
           */
          if ((semSrchSnglForPid(pMtx) == NO_ERROR) ||
              (semSrchMuxForPid(pMtx) == NO_ERROR) ||
              (((PSHUN)pMtx)->SMtx.usRequesterCt > 0)) {   //73445

            (((PPRUN)pMtx)->PMtx.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.
         */
        semAssertPMtxSig(pMtx);
        semPrivateClose(pMtx,ulIndex);
      }
    }
    TKEndNoBlock();
    return (NO_ERROR);
}



/***EP  SEMRequestUnownedMutex - Request an Unowned Mutex Semaphore
 *
 *      SEMRequestUnownedMutex is the initial worker function for the
 *      DosRequestMutexSem API routine.  It performs as many tasks as
 *      possible without having to be in kernel mode.  It CAN execute
 *      in kernel mode, however.
 *
 *      SEMRequestUnownedMutex requests a mutex semaphore that has been
 *      previously created and opened.  If a semaphore is unowned, this
 *      routine sets it to be owned and returns immediately to the caller.
 *      If the semaphore is owned, SEMRequestUnownedMutex will return
 *      ERROR_MUTEX_OWNED to the caller, which indicates that the caller
 *      should immediately invoke SEMRequestOwnedMutex with the identical
 *      parameters.  The ulTimeout parameter places an upper boundary on
 *      the amount of time to block before returning, even though the
 *      semaphore is still owned.  If a timeout of zero is passed in,
 *      SEMRequestUnownedMutex will return ERROR_TIMEOUT.
 *
 *      If a thread terminates while it owns a mutex semaphore, the
 *      ERROR_SEM_OWNER_DIED return code is returned to the next thread to
 *      "request" the semaphore.  That thread must then "close" the mutex
 *      semaphore so that it may be destroyed.
 *
 *      ENTRY   hmtx      - handle of a mutex
 *              ulTimeout - timeout in milliseconds
 *
 *      EXIT    Success - NO_ERROR
 *              Failure - ERROR_MUTEX_OWNED
 *                        ERROR_SEM_OWNER_DIED
 *                        ERROR_TIMEOUT
 *                        ERROR_TOO_MANY_SEM_REQUESTS
 *
 *      PSEUDO CODE
 *
 *      If (shared)                                      // shared
 *        pMtx = pShSemTbl[ulIndex]
 *      Else                                             // private
 *        pMtx = pPTDACur->pPrSemTbl[ulIndex];
 *      Endif
 *      If (usFlags & DM_OWNER_DIED)                     // owner died
 *        return (ERROR_SEM_OWNER_DIED)
 *      Endif
 *      If (usRequestCt == 64K - 1)                      // check request limit
 *        return (ERROR_TOO_MANY_SEM_REQUESTS)
 *      Endif
 *      If (usSlotNum == SEM_MUTEX_UNOWNED)              // unowned
 *        usSlotNum = current thread's slot number
 *        usRequestCt = 1
 *        increment pPTDACur->ulMtxOwned
 *      Else                                             // owned
 *        If (usSlotNum == current thread's slot number)
 *          increment usRequestCt
 *        Else
 *          If (ulTimeout == SEM_IMMEDIATE_RETURN)
 *            return (ERROR_TIMEOUT)
 *          Else
 *            return (ERROR_MUTEX_OWNED)
 *          Endif
 *        Endif
 *      Endif
 *      return (NO_ERROR);
 */

ULONG SEMRequestUnownedMutex(HMTX hmtx,          /* mutex handle */
                             ULONG ulTimeout)    /* timeout in milliseconds */

{
    ULONG ulIndex;                    /* array index */
    register PSEM pMtx;               /* ptr to mutex semaphore */


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

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

    /*
     *  Get pointer to correct table, shared or private.
     */
    ulIndex = semGetIndex(hmtx);
    if (semIfShared(hmtx)) {
      /*
       *  Shared Mutex Semaphore.
       */
      if(semChkSharedHdl(ulIndex,DCM_MUTEX_SEM,pPTDACur) == NO_ERROR)  /* 148487*/
         pMtx = pShSemTbl[ulIndex];
      else {                                                           /* 148487*/
         TKEndNoBlock();                                               /* 148487*/
         return(ERROR_INVALID_HANDLE);                                 /* 148487*/
      }
    }
    else {
      /*
       *  Private Mutex Semaphore.
       */
      if(semChkPrivateHdl(ulIndex,DCM_MUTEX_SEM) == NO_ERROR)          /* 148487*/
         pMtx = pPTDACur->pPrSemTbl[ulIndex];
      else {                                                           /* 148487*/
         TKEndNoBlock();                                               /* 148487*/
         return(ERROR_INVALID_HANDLE);                                 /* 148487*/
      }
    }

    /*
     *  Check for owner died state.
     */
    if (((PSHUN)pMtx)->SMtx.usFlags & DM_OWNER_DIED) {
      TKEndNoBlock();
      return (ERROR_SEM_OWNER_DIED);
    }

    /*
     *  Check 64K - 1 request count limit.
     */
    if (((PSHUN)pMtx)->SMtx.usRequestCt == SEM_MAXREQUESTS) {
      TKEndNoBlock();
      return (ERROR_TOO_MANY_SEM_REQUESTS);
    }

    if (((PSHUN)pMtx)->SMtx.usSlotNum == SEM_MUTEX_UNOWNED) {
      /*
       *  Mutex is unowned.  Set owner to current slot number,
       *  set the request to 1, and increment the number of
       *  mutexes owned.
       */
      tkAssert(((PSHUN)pMtx)->SMtx.usRequestCt == 0);
      ((PSHUN)pMtx)->SMtx.usSlotNum = pTCBCur->TCBNumber;
      ((PSHUN)pMtx)->SMtx.usRequestCt = 1;
      pPTDACur->ulMtxOwned++;

      /***PV - Perfview measurement hook
       */


//b734380  if(fPerfview)    /* Is perfview up ? */                            //aa04504
//b734380      PvwStartSet((PULONG)(((PSHUN)pMtx)->SMtx.pPvwBlk));              //b724624

    }
    else {
      /*
       *  Mutex is owned.
       */
      if (((PSHUN)pMtx)->SMtx.usSlotNum == pTCBCur->TCBNumber) {
        /*
         *  Mutex is owned by current thread.  Increment request count.
         */
        tkAssert(((PSHUN)pMtx)->SMtx.usRequestCt > 0);
        ((PSHUN)pMtx)->SMtx.usRequestCt++;
      }
      else {
        /*
         *  Mutex is owned by a different thread.
         *  Must return immediately if timeout is 0.  Otherwise, thread
         *  will block.
         */
        TKEndNoBlock();
        if (ulTimeout == SEM_IMMEDIATE_RETURN)
          return (ERROR_TIMEOUT);
        else
          return (ERROR_MUTEX_OWNED);
      }
    }
    TKEndNoBlock();
    return (NO_ERROR);
}



/***EP  SEMRequestOwnedMutexWkr - Request an Owned Mutex Semaphore
 *
 *      SEMRequestOwnedMutex is the secondary worker function for the
 *      DosRequestMutexSem API routine, and is the complement of the
 *      SEMRequestUnownedMutex function.  SEMRequestOwnedMutex must be
 *      called in kernel mode immediately after SEMRequestUnownedMutex
 *      if SEMRequestUnownedMutex returned ERROR_MUTEX_OWNED.
 *
 *      SEMRequestOwnedMutex requests a mutex semaphore that has been
 *      previously created and opened.  If the semaphore is indeed owned,
 *      SEMRequestOwnedMutex will block the thread until it is unowned,
 *      then try again.  The ulTimeout parameter places an upper boundary
 *      on the amount of time to block before returning, even though the
 *      semaphore is still owned.  If the semaphore is still in use at
 *      timeout, the ERROR_TIMEOUT return code will be returned.
 *
 *      If a thread terminates while it owns a mutex semaphore, the
 *      ERROR_SEM_OWNER_DIED return code is returned to the next thread to
 *      "request" the semaphore.  That thread must then "close" the mutex
 *      semaphore so that it may be destroyed.
 *
 *      ENTRY   hmtx      - handle of a mutex
 *              ulTimeout - timeout in milliseconds
 *
 *      EXIT    Success - NO_ERROR
 *              Failure - ERROR_INTERRUPT
 *                        ERROR_SEM_OWNER_DIED
 *                        ERROR_TIMEOUT
 *
 *      PSEUDO CODE
 *
 *      If (shared)                                      // shared
 *        pMtx = pShSemTbl[ulIndex]
 *      Else                                             // private
 *        pMtx = pPTDACur->pPrSemTbl[ulIndex];
 *      Endif
 *      call TKSleep on mutex
 *      If (usFlags & DM_OWNER_DIED)
 *        return (ERROR_SEM_OWNER_DIED)
 *      Endif
 *      return (NO_ERROR);
 */

ULONG SEMRequestOwnedMutexWkr(HMTX hmtx,          /* mutex handle */
                              ULONG ulTimeout)    /* timeout in milliseconds */

{
    ULONG ulIndex;                    /* array index */
    ULONG ulStableMsCount;            /* hold area for millisecond count 92885 */
    register PSEM pMtx;               /* ptr to mutex semaphore */
    ULONG rc;                         /* return code */


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

    /*
     *  Get pointer to correct table, shared or private.
     */
    ulIndex = semGetIndex(hmtx);
    if (semIfShared(hmtx)) {
      /*
       *  Shared Mutex Semaphore.
       */
      if(semChkSharedHdl(ulIndex,DCM_MUTEX_SEM,pPTDACur) == NO_ERROR)/* 148487*/
         pMtx = pShSemTbl[ulIndex];
      else {                                                         /* 148487*/
         TKEndNoBlock();                                             /* 148487*/
         return(ERROR_INVALID_HANDLE);                               /* 148487*/
      }
    }
    else {
      /*
       *  Private Mutex Semaphore.
       */
      if(semChkPrivateHdl(ulIndex,DCM_MUTEX_SEM) == NO_ERROR)        /* 148487*/
         pMtx = pPTDACur->pPrSemTbl[ulIndex];
      else {                                                         /* 148487*/
         TKEndNoBlock();                                             /* 148487*/
         return(ERROR_INVALID_HANDLE);                               /* 148487*/
      }
    }

    tkAssert(((PSHUN)pMtx)->SMtx.usSlotNum != SEM_MUTEX_UNOWNED);

#ifdef PMDEBUG
    pTCBCur->TCB_SemInfo = semAddSigBits(hmtx);
    if (semIfShared(hmtx))
       pTCBCur->TCB_SemDebugAddr = (ULONG)((PSMUTEX)pMtx)->pulCreatAddr;
    else
       pTCBCur->TCB_SemDebugAddr = (ULONG)((PPMUTEX)pMtx)->pulCreatAddr;
#endif

    TKEndNoBlock();


SleepAgain:                                                             //92885
    ((PSHUN)pMtx)->SMtx.usRequesterCt++;

/***************************  RAC ***************************************/
/* If Timeout Value is 0, then perform a yield instead of a Sleep.      */

    if (ulTimeout == 0 ) {
       TKYield();
       rc = ERROR_TIMEOUT;
    } else {
       rc = TKSleep(pMtx,ulTimeout,0,WAKEUP_SINGLE);
    } /* endif */

    ((PSHUN)pMtx)->SMtx.usRequesterCt--;

    TKBeginNoBlock();                                          //107369 - 113468
                                                               //107369 - 113468
    /*                                                         //107369 - 113468
     *  Check for owner died state.                            //107369 - 113468
     */                                                        //107369 - 113468
    if (((PSHUN)pMtx)->SMtx.usFlags & DM_OWNER_DIED) {         //107369 - 113468
      TKEndNoBlock();                                          //107369 - 113468
      return (ERROR_SEM_OWNER_DIED);                           //107369 - 113468
    }                                                          //107369 - 113468
    TKEndNoBlock();                                            //107369 - 113468

// DEFECT 88581
// Defect 88581 adds defect 88185 and defect 76822 from R206

    if (rc == NO_ERROR) {                                             //88581
        if (((PSHUN)pMtx)->SMtx.usSlotNum != pTCBCur->TCBNumber)  {   //88581

          if (ulTimeout == -1) {                                      //92885
             goto SleepAgain;                                         //92885
          }                                                           //92885
          else {                                                      //92885
             ulStableMsCount = SISData.SIS_MsCount;                   //92885
             /* ulStableMsCount is assigned the current msec count so that */
             /* we have a stable msec count that we KNOW cannot change  -  */
             /* this ensures that ulTimeout cannot get a negative value in */
             /* the assignment below.                                      */

             if (pTCBCur->TCBtoe.toe_cmsec > ulStableMsCount) {       //92885
                ulTimeout = pTCBCur->TCBtoe.toe_cmsec - ulStableMsCount; //92885
                goto SleepAgain;                                      //92885
             }                                                        //92885
             else {                                                   //92885
                return (ERROR_TIMEOUT);                               //92885
             }                                                        //92885
          }                                                           //92885

        }                                                             //88581
    }                                                                 //88581
    else {  /* rc != NO_ERROR */                                      //88581


/*******   92009 - Add check for PM_EVENT_POSTED. If it is and thread ******/
/*******           owns the semaphore, change return code to NO_ERROR ******/
        if ( ( (rc == ERROR_TIMEOUT) || (rc == PM_EVENT_POSTED) ) &&                                  //88581
/*******   End 92009                                                  ******/

            (((PSHUN)pMtx)->SMtx.usSlotNum == pTCBCur->TCBNumber)) {  //88581
            rc = NO_ERROR;                                            //88581
            }                                                         //88581
        else  {  /* rc != ERROR_TIMEOUT or PM_EVENT_POSTED */         //88581
#ifdef  PMDEBUG                                                       //88581
            pTCBCur->TCB_SemDebugAddr = -1;                           //88581
#endif                                                                //88581
            return (rc);                                              //88581
        }                                                             //88581
    }                                                                 //88581

    return (NO_ERROR);
}

/***EP  SEMRequestOwnedMutex - Request an Owned Mutex Semaphore
 *
 *      SEMRequestOwnedMutex is the secondary worker function for the
 *      DosRequestMutexSem API routine, and is the complement of the
 *      SEMRequestUnownedMutex function.  SEMRequestOwnedMutex must be
 *      called in kernel mode immediately after SEMRequestUnownedMutex
 *      if SEMRequestUnownedMutex returned ERROR_MUTEX_OWNED.
 *
 *      SEMRequestOwnedMutex requests a mutex semaphore that has been
 *      previously created and opened.  If the semaphore is indeed owned,
 *      SEMRequestOwnedMutex will block the thread until it is unowned,
 *      then try again.  The ulTimeout parameter places an upper boundary
 *      on the amount of time to block before returning, even though the
 *      semaphore is still owned.  If the semaphore is still in use at
 *      timeout, the ERROR_TIMEOUT return code will be returned.
 *
 *      If a thread terminates while it owns a mutex semaphore, the
 *      ERROR_SEM_OWNER_DIED return code is returned to the next thread to
 *      "request" the semaphore.  That thread must then "close" the mutex
 *      semaphore so that it may be destroyed.
 *
 *      ENTRY   hmtx      - handle of a mutex
 *              ulTimeout - timeout in milliseconds
 *
 *      EXIT    Success - NO_ERROR
 *              Failure - ERROR_INTERRUPT
 *                        ERROR_SEM_OWNER_DIED
 *                        ERROR_TIMEOUT
 *
 *      PSEUDO CODE
 *
 *      If (shared)                                      // shared
 *        pMtx = pShSemTbl[ulIndex]
 *      Else                                             // private
 *        pMtx = pPTDACur->pPrSemTbl[ulIndex];
 *      Endif
 *      call TKSleep on mutex
 *      If (usFlags & DM_OWNER_DIED)
 *        return (ERROR_SEM_OWNER_DIED)
 *      Endif
 *      return (NO_ERROR);
 */

ULONG cdecl SEMRequestOwnedMutex(THUNKSEF sef,       /* system entry frame */
                                 HMTX hmtx,          /* mutex handle */
                                 ULONG ulTimeout)    /* timeout in milliseconds */

{
      return(SEMRequestOwnedMutexWkr(hmtx,ulTimeout));

      sef;                  // Avoid compiler warning. 151677
}



/***EP  SEMReleaseMutexSem - Release a Mutex Semaphore
 *
 *      SEMReleaseMutexSem combines the doings of the two worker functions
 *      for the DosReleaseMutexSem API routine.  SEMReleaseMutexSem first
 *      invokes SEMReleaseUnwaitedMutex.  If the semaphore is being waited
 *      upon, however, it then invokes SEMReleaseWaitedMutex.
 *      SEMReleaseMutexSem must be called in kernel mode in the event that
 *      the semaphore is being waited upon.
 *
 *      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 has been released.  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.
 *
 *      ENTRY   hmtx - handle of a mutex
 *
 *      EXIT    Success - NO_ERROR
 *              Failure - ERROR_NOT_OWNER
 *                        ERROR_SEM_OWNER_DIED
 *
 *      PSEUDO CODE
 *
 *      call SEMReleaseUnwaitedMutex on semaphore
 *      If semaphore is being waited upon
 *        call SEMReleaseWaitedMutex on semaphore
 *      Endif
 *      return SEMReleaseXXXMutex error code
 */

ULONG SEMReleaseMutexSem(HMTX hmtx)          /* mutex handle */

{
    THUNKSEF dummy;
    ULONG rc;                         /* return code */


    if ((rc = SEMReleaseUnwaitedMutex(hmtx)) == ERROR_SEM_BUSY)
    {
        // The parameter DUMMY is passed by value instead of by reference.
        // The compiler uses REP MOVSD to make a copy on the stack. When
        // doing so, it PUSHes DS before and POPs DS after the copy, but it
        // does not do the same thing for ES. ES is changed to the value of
        // SS and never restored. This routine eventually calls TKSLEEP
        // which expects ES to equal DS, which is not true. A trap d will
        // be encountered when we attempt to do a MEMCPY in TKUPDATESTATE.
        // We substituted the call with an ASM version to avoid ES from
        // being altered and not restored.                                151677

      //    rc = SEMReleaseWaitedMutex(dummy, semStripSigBits(hmtx));

        {								// D149854
          ulong_t ulIndex = semStripSigBits(hmtx);		// D149854
          _asm
          {
        //D149854          push hmtx
            push ulIndex						// D149854
            sub  esp,size dummy
            call dword ptr SEMReleaseWaitedMutex
            add  esp,size dummy + 4
            mov  rc,eax
          };
        }								// D149854
    }

    return (rc);

    dummy;                  // Avoid compiler warning. 151677
}


/***EP  SEMReleaseUnwaitedMutex - Release a Mutex Semaphore Not Being Waited Upon
 *
 *      SEMReleaseUnwaitedMutex is the initial worker function for the
 *      DosReleaseMutexSem API routine.  It performs as many tasks as
 *      possible without having to be in kernel mode.  It CAN execute
 *      in kernel mode, however.
 *
 *      SEMReleaseUnwaitedMutex releases a mutex semaphore that was previously
 *      requested.  The request count is decremented.   If the count goes to
 *      zero (no more requests were made by the current thread), then another
 *      thread is given the opportunity to gain ownership of the mutex.  If
 *      there are other threads waiting to gain ownership of the mutex,
 *      SEMReleaseUnwaitedMutex will return ERROR_SEM_BUSY to the caller,
 *      which indicates that the caller should immediately invoke
 *      SEMReleaseWaitedMutex with the identical parameter.  A "release"
 *      cannot be issued against a mutex semaphore by any thread other than
 *      the owner.
 *
 *      ENTRY   hmtx - handle of a mutex
 *
 *      EXIT    Success - NO_ERROR
 *              Failure - ERROR_NOT_OWNER
 *                        ERROR_SEM_OWNER_DIED
 *                        ERROR_SEM_BUSY
 *
 *      PSEUDO CODE
 *
 *      If (shared)                                        // shared
 *        pMtx = pShSemTbl[hmtx]
 *      Else
 *        pMtx = pPTDACur->pPrSemTbl[hmtx]
 *      Endif
 *      If (usSlotNum != current thread's slot number)
 *        return (ERROR_NOT_OWNER)
 *      Endif
 *      usSlotNum = SEM_MUTEX_UNOWNED
 *      decrement usRequestCt
 *      If (usRequestCt > 0)
 *        return (NO_ERROR)
 *      Endif
 *      decrement pPTDACur->ulMtxOwned
 *      If semaphore is being waited upon
 *        return (ERROR_SEM_BUSY)
 *      Else
 *        return (NO_ERROR)
 *      Endif
 */

ULONG SEMReleaseUnwaitedMutex(HMTX hmtx)         /* mutex handle */

{
    register PSEM pMtx;       /* ptr to mutex semaphore */
    ULONG ulIndex;            /* array index */


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

//  Moved to API32.ASM...
//
//  /*
//   *  Check validity of mutex handle.
//   */
//  if (semChkHdl(hmtx,DCM_MUTEX_SEM,pPTDACur) != NO_ERROR)
//    return (ERROR_INVALID_HANDLE);

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

    /*
     *  Get pointer to correct table, shared or private.
     */
    ulIndex = semGetIndex(hmtx);
    if (semIfShared(hmtx)) {
      /*
       *  Shared Mutex Semaphore.
       */
      if(semChkSharedHdl(ulIndex,DCM_MUTEX_SEM,pPTDACur) == NO_ERROR)/* 148487*/
         pMtx = pShSemTbl[ulIndex];
      else {                                                         /* 148487*/
         TKEndNoBlock();                                             /* 148487*/
         return(ERROR_INVALID_HANDLE);                               /* 148487*/
      }
    }
    else {
      /*
       *  Private Mutex Semaphore.
       */
      if(semChkPrivateHdl(ulIndex,DCM_MUTEX_SEM) == NO_ERROR)        /* 148487*/
         pMtx = pPTDACur->pPrSemTbl[ulIndex];
      else {                                                         /* 148487*/
         TKEndNoBlock();                                             /* 148487*/
         return(ERROR_INVALID_HANDLE);                               /* 148487*/
      }
    }

    /*
     *  Check owner died state.
     */
    if (((PSHUN)pMtx)->SMtx.usFlags & DM_OWNER_DIED) {
      TKEndNoBlock();
      return (ERROR_SEM_OWNER_DIED);
    }

    /*
     *  Check if current thread owns the mutex.
     */
    if (((PSHUN)pMtx)->SMtx.usSlotNum != pTCBCur->TCBNumber) {
      TKEndNoBlock();
      return (ERROR_NOT_OWNER);
    }

    /*
     *  Decrement the request count.  If it is not 0, return.
     *  Otherwise, mark it as unowned, decrement the number of
     *  mutexes owned, and wake up the next waiter.
     */
    if ((--(((PSHUN)pMtx)->SMtx.usRequestCt)) > 0) {
      TKEndNoBlock();
      return (NO_ERROR);
    }

    tkAssert(((PSHUN)pMtx)->SMtx.usRequestCt == 0);
    ((PSHUN)pMtx)->SMtx.usSlotNum = SEM_MUTEX_UNOWNED;
    pPTDACur->ulMtxOwned--;

    /***PV - Perfview measurement hook
     */


//b734380  if(fPerfview)    /* Is perfview up ? */                            //aa04504
//b734380    PvwEndSet((PULONG)(((PSHUN)pMtx)->SMtx.pPvwBlk));                  //b724626

    TKEndNoBlock();
    if (((PSHUN)pMtx)->SMtx.usRequesterCt || ((PSHUN)pMtx)->SMtx.pMuxQ)
        return (ERROR_SEM_BUSY);
    else
        return (NO_ERROR);
}


/***EP  SEMReleaseWaitedMutex - Release a Mutex Semaphore Being Waited Upon
 *
 *      SEMReleaseWaitedMutex is the secondary worker function for the
 *      DosReleaseMutexSem API routine, and is the complement of the
 *      SEMReleaseUnwaitedMutex function.  SEMReleaseWaitedMutex must be
 *      called in kernel mode immediately after SEMReleaseUnwaitedMutex
 *      if SEMReleaseUnwaitedMutex returned ERROR_SEM_BUSY.
 *
 *      SEMReleaseWaitedMutex releases a mutex semaphore that was previously
 *      requested.  If the semaphore is indeed being waited upon,
 *      SEMReleaseWaitedMutex will wake the thread with the highest priority.
 *      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 has been released.  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.
 *
 *      ENTRY   hmtx - handle of a mutex
 *
 *      EXIT    Success - NO_ERROR
 *              Failure - ERROR_NOT_OWNER
 *                        ERROR_SEM_OWNER_DIED
 *
 *      PSEUDO CODE
 *
 *      If (shared)                                        // shared
 *        pMtx = pShSemTbl[hmtx]
 *      Else
 *        pMtx = pPTDACur->pPrSemTbl[hmtx]
 *      Endif
 *      wakeup single waiter or mux waiter based on priority of thread
 *      return (NO_ERROR)
 */

ULONG cdecl SEMReleaseWaitedMutex(THUNKSEF sef,       /* system entry frame */
                                  HMTX hmtx)          /* mutex handle */

{
    register PSEM pMtx;       /* ptr to mutex semaphore */
    ULONG ulIndex;            /* array index */


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

    /*
     *  Get pointer to correct table, shared or private.
     */
    ulIndex = semGetIndex(hmtx);
    if (semIfShared(hmtx)) {
      /*
       *  Shared Mutex Semaphore.
       */
      if(semChkSharedHdl(ulIndex,DCM_MUTEX_SEM,pPTDACur) == NO_ERROR)/* 148487*/
         pMtx = pShSemTbl[ulIndex];
      else {                                                         /* 148487*/
         TKEndNoBlock();                                             /* 148487*/
         return(ERROR_INVALID_HANDLE);                               /* 148487*/
      }
    }
    else {
      /*
       *  Private Mutex Semaphore.
       */
      if(semChkPrivateHdl(ulIndex,DCM_MUTEX_SEM) == NO_ERROR)        /* 148487*/
         pMtx = pPTDACur->pPrSemTbl[ulIndex];
      else {                                                         /* 148487*/
         TKEndNoBlock();                                             /* 148487*/
         return(ERROR_INVALID_HANDLE);                               /* 148487*/
      }
    }

    tkAssert(((PSHUN)pMtx)->SMtx.usSlotNum == SEM_MUTEX_UNOWNED);
    tkAssert(((PSHUN)pMtx)->SMtx.usRequesterCt || ((PSHUN)pMtx)->SMtx.pMuxQ);

    semWakeOnRelMtx(pMtx,hmtx);
    TKEndNoBlock();
    return (NO_ERROR);

    sef;                    // Avoid compiler warning. 151677
}


/***EP  SEMQueryMutexSem - Query a Mutex Semaphore
 *
 *      SEMQueryMutexSem is the worker function for the DosQueryMutexSem
 *      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.
 *
 *      SEMQueryMutexSem will query information about a given mutex
 *      semaphore if the semaphore was previously opened by the process.
 *      If the mutex semaphore exists, the relative information is obtained
 *      and returned to the caller in the given parameters.  If the
 *      semaphore is owned, then owner information is returned.  If the
 *      semaphore is unowned, then zero is returned in the pulCount parameter.
 *
 *      The ERROR_SEM_OWNER_DIED return code is returned if the owner has
 *      died and the semaphore has not yet been destroyed by a "close" call.
 *      In this case, the parameters are NOT filled in with the information
 *      of the dead mutex owner.  The calling thread must then close the
 *      mutex so that the semaphore may be destroyed.
 *
 *      ENTRY   hmtx      - handle of a mutex
 *              ppidOwner - filled in with the process ID of a mutex owner
 *              ptidOwner - filled in with the thread ID of a mutex owner
 *              pulCount  - filled in with the current number of
 *                          DosRequestMutexSem calls made by the owning
 *                          thread (ZERO if semaphore is unowned)
 *
 *      EXIT    Success - NO_ERROR
 *              Failure - ERROR_SEM_OWNER_DIED
 *
 *      PSEUDO CODE
 *
 *      If (shared)                                   // shared
 *        pMtx = pShSemTbl[hmtx]
 *      Else
 *        pMtx = pPTDACur->pPrSemTbl[hmtx]            // private
 *      Endif
 *      If (usFlags & DM_OWNER_DIED)
 *        return (ERROR_SEM_OWNER_DIED)
 *      Endif
 *      ppidOwner = pid for pMtx->usSlotNum
 *      ptidOwner = tid for pMtx->usSlotNum
 *      pulCount = pMtx->usRequestCt
 *      return (NO_ERROR)
 */

ULONG SEMQueryMutexSem(HMTX hmtx,           /* mutex handle */
                       PPID ppidOwner,      /* process id */
                       PTID ptidOwner,      /* thread id */
                       PULONG pulCount)     /* number of requests */

{
    register PSEM pMtx;        /* ptr to mutex 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(hmtx);
    if (semIfShared(hmtx)) {
      /*
       *  Shared Mutex Semaphore.
       */
      if(semChkSharedHdl(ulIndex,DCM_MUTEX_SEM,pPTDACur) == NO_ERROR)/* 148487*/
         pMtx = pShSemTbl[ulIndex];
      else {                                                         /* 148487*/
         TKEndNoBlock();                                             /* 148487*/
         return(ERROR_INVALID_HANDLE);                               /* 148487*/
      }
    }
    else {
      /*
       *  Private Mutex Semaphore.
       */
      if(semChkPrivateHdl(ulIndex,DCM_MUTEX_SEM) == NO_ERROR)        /* 148487*/
         pMtx = pPTDACur->pPrSemTbl[ulIndex];
      else {                                                         /* 148487*/
         TKEndNoBlock();                                             /* 148487*/
         return(ERROR_INVALID_HANDLE);                               /* 148487*/
      }
    }

    if (((PSHUN)pMtx)->SMtx.usSlotNum == SEM_MUTEX_UNOWNED) {
      /*
       *  Mutex is unowned.  Return request count of 0.
       */
      tkAssert(((PSHUN)pMtx)->SMtx.usRequestCt == 0);
      *pulCount = 0;
    }
    else {
      /*
       *  Mutex is owned.  Return request count, process id, and
       *  thread id of current owner.
       */
      tkAssert(((PSHUN)pMtx)->SMtx.usRequestCt > 0);
      *pulCount = ((PSHUN)pMtx)->SMtx.usRequestCt;
      if (((PSHUN)pMtx)->SMtx.usFlags & DM_OWNER_DIED) {
        /*
         *  Owner died state.  Return process id and thread id
         *  of owner who died owning the mutex.
         */
        if (semIfShared(hmtx))
          *ppidOwner = ((PSHUN)pMtx)->SMtx.pOpenQ->pidOpener;
        else
          *ppidOwner = pPTDACur->ptda_Pid;
        *ptidOwner = (TID)(((PSHUN)pMtx)->SMtx.pMuxQ);
        TKEndNoBlock();
        return (ERROR_SEM_OWNER_DIED);
      }
      semSlotToPidTid(((PSHUN)pMtx)->SMtx.usSlotNum,ppidOwner,ptidOwner);
    }
    TKEndNoBlock();
    return (NO_ERROR);
}
