/*static char *SCCSID = "@(#)seminout.c 6.6 92/03/21";*/
#define SCCSID  "@(#)seminout.c 6.6 92/03/21"

/*
 *      Semaphores - Semaphore Initialization and Exit 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
 */


#define INCL_TYPES
#include <basedef.h>
#define FNO_NUM FNO_SEMINOUT_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 <tkexport.h>
#include <sem.h>
#include <screen.h>
#include <curdir.h>
#include <dirent.h>
#include <find.h>
#include <semxport.h>
#include <tkptda.h>
#include <bsetib.h>
#include <tklocal.h>
#include <semlocal.h>
#include <string.h>

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

PSEM     *pShSemTbl;          /* ptr to the shared semaphore array table */
ULONG    ulShTblSize;         /* current size of shared table */
ULONG    ulShTotUsed;         /* number of spots in table currently used */
ULONG    ulShNextFree;        /* next free slot in pShSemTbl */
HKSEM    hksShTbl;            /* kernel semaphore for shared sem table */


PSEMTBLNODE   pShSemStrTbl;   /* pointer to shared string table */
ULONG    ulShSemStrTblSize;   /* size of shared string table */
ULONG    ulShSemStrTblUsed;   /* number of string table entries used */
PMUXQ    pFreeMux;            /* free MuxQ node pool */
ULONG    ulFreeMuxCount;      /* size of MuxQ node pool */
HKSEM    hksStrTbl;           /* kernel semaphore for string table */

HKSEM    hksSemRec;           /* kernel semaphore for mux semrecord */




/***EP  SEMInitPTDA - Initialize semaphore variables in PTDA
 *
 *      SEMInitPTDA initializes the variables in the PTDA used by the
 *      32-bit semaphore routines.
 *
 *      ENTRY   pptda - pointer to the current PTDA
 *
 *      EXIT    Success - NO_ERROR
 *              Failure - ULONG, error code
 *
 *      PSEUDO CODE
 *
 *      If ((rc = allocate private table) == NO_ERROR)
 *        zero out memory
 *        Table Size = SEM_PRTBLSIZE              // size in dwords
 *        Total Used = 0                          // no sems yet
 *        Next Free = 0
 *        If (rc = allocate shared bitmap) == NO_ERROR) {
 *          zero out memory
 *          Bitmap Size = SEM_SHTBLSIZE   // size in bits
 *          create the necessary kernel semaphores
 *        Else
 *          free private table
 *        Endif
 *      Endif
 *      return (rc)
 */

ULONG SEMInitPTDA(PPTDA pptda)             /* ptr to the ptda */

{
    ULONG rc;                   /* return code */


    pptda->pPrSemTbl = NULL;
    pptda->pShSemBmp = NULL;
    pptda->ulMtxOwned = 0;
    /*
     *  Allocate private semaphore table.  One of these is created everytime
     *  a process is started.
     */
    if ((rc = allocKRHB((sizeof(PSEM) * SEM_PRTBLSIZE),(VMHOB)SEMTABLEOWNER,
                        &(pptda->pPrSemTbl))) == NO_ERROR) {
      memset(pptda->pPrSemTbl,0,SEM_PRTBLSIZE * SEM_DWORD_BYTES);
      pptda->ulPrTblSize = SEM_PRTBLSIZE;      /* size of table in dwords */
      pptda->ulPrTotUsed = 0;                  /* no semaphores in table yet */
      pptda->ulPrNextFree = 0;                 /* empty table */

      /*
       *  Allocate per process bitmap for the shared semaphores.  This
       *  keeps track of the opens per process.  One of these is created
       *  everytime a process is started.
       */
      if ((rc = allocKRHB((sizeof(PSEM) * SEM_SHBMPSIZE),(VMHOB)SEMTABLEOWNER,
                      &(pptda->pShSemBmp))) == NO_ERROR) {
        memset(pptda->pShSemBmp,0,SEM_SHBMPSIZE * SEM_DWORD_BYTES);
        pptda->ulShBmpSize = SEM_SHBMPSIZE * SEM_DWORD_BITS;
                                               /* size of pShSemBmp in bits */

        /*
         *  Create the necessary kernel semaphores.
         */
        if ((rc = KSEMCreate(&(pptda->hksPrTbl),KRNL_MUTEXSEM)) == NO_ERROR)
          rc = KSEMCreate(&(pptda->hksShBmp),KRNL_MUTEXSEM);
      }
    }

    if (rc != NO_ERROR) {
      if (pptda->pPrSemTbl != NULL)
        freeKRHB(pptda->pPrSemTbl);
      if (pptda->pShSemBmp != NULL)
        freeKRHB(pptda->pShSemBmp);
    }
    return (rc);
}


/***EP  SEMMarkOwnerDied - Mark Mutex Owner Died If Owned by Dying Thread
 *
 *      SEMMarkOwnerDied is called whenever a thread terminates itself.
 *      This routine goes through both the shared and the private tables to
 *      see if any of the mutex are owned by the dying thread.  If a mutex
 *      is owned by the dying thread, then the OWNER_DIED flag of the
 *      mutex is set and ALL threads waiting on the single mutex and ALL
 *      threads waiting on a mux containing the dead mutex will be awakened
 *      with ERROR_SEM_OWNER_DIED.
 *
 *      ENTRY   Thread1 - true if being called for thread 1
 *
 *      EXIT    Success - All dying threads marked with owner died
 *
 *      PSEUDO CODE
 *
 *      If (number of mutex owned > 0)
 *        For (each entry in shared table)
 *          If (entry != NULL)
 *            If ((mutex semaphore) && (mutex owned by dying thread))
 *              set DM_OWNER_DIED flag in mutex
 *              wakeup all threads waiting on mutex and mux containing mutex
 *            Endif
 *          Endif
 *        Endfor
 *        For (each entry in private table)
 *          If (entry != NULL)
 *            If ((mutex semaphore) && (mutex owned by dying thread))
 *              set DM_OWNER_DIED flag in mutex
 *              wakeup all threads waiting on mutex and mux containing mutex
 *            Endif
 *          Endif
 *        Endfor
 *      Endif
 */

void SEMMarkOwnerDied(ULONG Thread1)

{
    register ULONG ulTblCt;     /* counter to go through tables */
    register ULONG ulUsedCt;    /* counter for number of used spots in table */
    register PSEM pSem;         /* pointer to a semaphore */
    PID Pid;                    /* process id of dead mutex */
    TID Tid;                    /* thread id of dead mutex */
    USHORT usFlags;             /* semaphore flags */
    ulong_t T1Slot;             /* slot index of thread 1 */
    ulong_t rc;                 /* return code */


    if ((pPTDACur->ptda_ProcStatus & PS_XITALL) && (!Thread1)) {
      rc = TKTidTo_iSlot(pPTDACur, 1, SSToDS(&T1Slot));
      tkAssert(rc == 0);
    }

    /*
     *  Search down each entry in the SHARED semaphore table.
     */
    for (ulUsedCt=0,ulTblCt=0; ((pPTDACur->ulMtxOwned != 0) &&
                               (ulUsedCt < ulShTotUsed)); ulTblCt++) {
      tkAssert(ulTblCt < ulShTblSize);
      pSem = pShSemTbl[ulTblCt];
      if (pSem != NULL) {
        /*
         *  A semaphore exists at the current table entry.
         */
        ulUsedCt++;
        usFlags = ((PSHUN)pSem)->SMtx.usFlags;
        if ((usFlags & DCM_MUTEX_SEM) && (!(usFlags & DM_OWNER_DIED)) &&
            (((PSHUN)pSem)->SMtx.usSlotNum == pTCBCur->TCBNumber)) {
          /*
           *  Mutex is owned by the current dying thread and has not
           *  been previously marked as owner died.  If thread is in
           *  dying process and not thread 1 of dying process, must
           *  assign ownership to thread 1.  Otherwise, must now wake
           *  all threads waiting on the mutex and mark the mutex as
           *  owner died, in which case the mutex is invalid.
           */
          if ((pPTDACur->ptda_ProcStatus & PS_XITALL) && (!Thread1)) {
            ((PSHUN)pSem)->SMtx.usSlotNum = (USHORT)T1Slot;
          }
          else {
            (pPTDACur->ulMtxOwned)--;
            semWakeAllMtxNMux(pSem);
            semSlotToPidTid(((PSHUN)pSem)->SMtx.usSlotNum,SSToDS(&Pid),SSToDS(&Tid));
            ((PSHUN)pSem)->SMtx.pMuxQ = (PMUXQ)(Tid);
            semMakeFirstOpenQ(Pid,&(((PSHUN)pSem)->SMtx.pOpenQ));
            ((PSHUN)pSem)->SMtx.usFlags |= DM_OWNER_DIED;
          }
        }
      }
    }

    /*
     *  Search down each entry in the PRIVATE semaphore table.
     */
    for (ulUsedCt=0,ulTblCt=0; ((pPTDACur->ulMtxOwned != 0) &&
                             (ulUsedCt < pPTDACur->ulPrTotUsed)); ulTblCt++) {
      tkAssert(ulTblCt < pPTDACur->ulPrTblSize);
      pSem = pPTDACur->pPrSemTbl[ulTblCt];
      if (pSem != NULL) {
        /*
         *  A semaphore exists at the current table entry.
         */
        ulUsedCt++;
        usFlags = ((PPRUN)pSem)->PMtx.usFlags;
        if ((usFlags & DCM_MUTEX_SEM) && (!(usFlags & DM_OWNER_DIED)) &&
            (((PPRUN)pSem)->PMtx.usSlotNum == pTCBCur->TCBNumber)) {
          /*
           *  Mutex is owned by the current dying thread and has not
           *  been previously marked as owner died.  If thread is in
           *  dying process and not thread 1 of dying process, must
           *  assign ownership to thread 1.  Otherwise, must now wake
           *  all threads waiting on the mutex and mark the mutex as
           *  owner died, in which case the mutex is invalid.
           */
          if ((pPTDACur->ptda_ProcStatus & PS_XITALL) && (!Thread1)) {
            ((PPRUN)pSem)->PMtx.usSlotNum = (USHORT)T1Slot;
          }
          else {
            (pPTDACur->ulMtxOwned)--;
            semWakeAllMtxNMux(pSem);
            semSlotToPidTid(((PPRUN)pSem)->PMtx.usSlotNum,SSToDS(&Pid),
                            SSToDS(&Tid));
            ((PPRUN)pSem)->PMtx.pMuxQ = (PMUXQ)(Tid);
            ((PPRUN)pSem)->PMtx.usFlags |= DM_OWNER_DIED;
          }
        }
      }
    }
}


/***LP  semWakeAllMtxNMux - Wakeup All Threads Waiting on Mutex and Mux
 *
 *      semWakeAllMtxNMux is called when a mutex is put in the
 *      ERROR_SEM_OWNER_DIED state.  This routine will wakeup ALL threads
 *      waiting on the single mutex and ALL threads waiting on a mux
 *      that contains this mutex.
 *
 *      ENTRY   pSem - pointer to the dead mutex
 *
 *      EXIT    Success - appropriate threads are set running
 *
 *      PSEUDO CODE
 *
 *      call TKWakeup on all threads waiting on mutex
 *      For (each mux in MuxQ)
 *        call TKWakeup on all threads waiting on mux
 *      Endfor
 */

void semWakeAllMtxNMux(register PSEM pSem)    /* ptr to mutex */

{
    register PMUXQ pMQCur;       /* ptr to current mux queue node */
    register PMUXQ pMQTmp;       /* ptr to temporary mux queue node */


    /*
     *  Wake up all waiters of the mutex semaphore.
     */
    TKWakeup(pSem,SEM_WAKE_ALL,NULL);

    /*
     *  Wake up all waiters on the muxwait semaphore(s) that contain
     *  this mutex.
     */
    pMQCur = ((PSHUN)pSem)->SMtx.pMuxQ;
    while (pMQCur != NULL) {
      pMQTmp = pMQCur->pNextMux;
      semDelAllMuxQ(pMQCur->pMux);
      TKWakeup((void*)(pMQCur->pMux),SEM_WAKE_ALL,NULL);
      pMQCur = pMQTmp;
    }
}


/***EP  SEMCleanProcDying - Cleanup Semaphores When Process Dying
 *
 *      SEMCleanProcDying is called after exitlist to clean up
 *      the open queues of any semaphores that the dying process has
 *      open.  If the open queue of a semaphore becomes empty, then
 *      the semaphore is deleted from the table.  Otherwise, the
 *      semaphore will remain in the table until the processes
 *      that have the semaphore open perform the correct number of
 *      close operations.  This routine only affects the shared table
 *      of semaphores.  Since the process is dying, there is no need
 *      to touch the private table because the private table will die
 *      with the process.
 *
 *      ENTRY   void
 *
 *      EXIT    Success - shared table is cleaned up
 *
 *      PSEUDO CODE
 *
 *      call SEMMarkOwnerDied for thread 1
 *      For (each entry in shared table)
 *        If (entry != NULL)
 *          If (semaphore open by current process)
 *            remove OpenQ node from OpenQ of semaphore
 *            If (open queue is now empty)
 *              free semaphore
 *              set entry = NULL
 *            Endif
 *          Endif
 *        Endif
 *      Endfor
 *      call SEMTerminatePTDA
 */

void SEMCleanProcDying(void)

{
    register ULONG ulTblCt = 0;   /* counter to go through table */
    register ULONG ulUsedCt = 0;  /* counter for number of used spots in tbl */
    POPENQ pRemOpenN;             /* open queue node to remove */
    POPENQ pRemOpenN1;            /* extra open queue node to remove */   //d59051
    POPENQ pPreOpenN;             /* open queue node previous to pRemOpenN */
    POPENQ *ppOpenQN;             /* address of ptr to open queue */
    PMUXQ pMuxQ;                  /* ptr to a mux queue */
    ULONG ulTmpCt;                /* temporary count for total used */
    register PSEM pSem;           /* pointer to a semaphore */
    register USHORT usFlags;      /* flag attributes */
    ULONG ulOpen;                 /* set if current process has sem open */
    ULONG ulDied;                 /* set if owner died flag set */
    ULONG ulFree;                 /* set if pRemOpenN should be freed */
    ULONG ulLast;                 /* set if pRemOpenN1should be freed */  //d59051


    SEMMarkOwnerDied(TRUE);
    ulTmpCt = ulShTotUsed;
    /*
     *  Search down each entry in the SHARED semaphore table.
     */
    for (; ((ulTblCt < pPTDACur->ulShBmpSize) && (ulTblCt < ulShTblSize) &&
            (ulUsedCt < ulTmpCt)); ulTblCt++) {

      pSem = pShSemTbl[ulTblCt];
      if (pSem != NULL) {
        /*
         *  A semaphore exists at the current table entry.
         */
        ulUsedCt++;
        ulOpen = semGetBit(ulTblCt,pPTDACur->pShSemBmp);
        usFlags = ((PSHUN)pSem)->SEvt.usFlags;
        ulDied = usFlags & DM_OWNER_DIED;

        /*
         *  If semaphore is either open in the current dying process
         *  or is in the owner died state, set the local pointer to
         *  the open queue and the mux queue.
         */
        if (ulOpen || ulDied) {
          if (usFlags & DCE_EVENT_SEM) {
            /*
             *  Event Semaphore.
             */
            ppOpenQN = &((PSHUN)pSem)->SEvt.pOpenQ;
            pMuxQ = ((PSHUN)pSem)->SEvt.pMuxQ;
          }
          else if (usFlags & DCM_MUTEX_SEM) {
            /*
             *  Mutex Semaphore.
             */
            ppOpenQN = &((PSHUN)pSem)->SMtx.pOpenQ;
            pMuxQ = ((PSHUN)pSem)->SMtx.pMuxQ;
          }
          else {
            /*
             *  Muxwait Semaphore.
             */
            ppOpenQN = &((PSHUN)pSem)->SMux.pOpenQ;
            pMuxQ = NULL;
          }

          ulFree = ulLast = 0;                                            //d59051
          if (ulOpen) {
            /*
             *  Semaphore is open in the current process.  Get the
             *  open queue node to delete => pRemOpenN.
             */
            semGetOpenQNode(*ppOpenQN,SSToDS(&pRemOpenN),SSToDS(&pPreOpenN),
                            pPTDACur->ptda_Pid);
            tkAssert(pRemOpenN != NULL);
            if ((usFlags & DM_OWNER_DIED) && (pRemOpenN == *ppOpenQN)) {
              /*
               *  Mutex is in the owner died state.
               */
              if ((*ppOpenQN)->pNextOpen == NULL) {
                /*
                 *  No other process has the mutex open, so null out the
                 *  open queue and the mux queue of the mutex.  The freeing
                 *  of the open queue node will be done later - we are
                 *  not allowed to block yet.
                 */
                *ppOpenQN = NULL;
                pMuxQ = NULL;
                ulFree = 1;
              }
              else
                /*
                 *  Other processes still have the owner died mutex open,
                 *  so simply set the open count of the first open queue
                 *  node's (the one that tells which process owned it when
                 *  it became owner died) open count to 0.
                 */
                (*ppOpenQN)->usOpenCt = 0;
            }
            else {
              /*
               *  Delete the open queue node from the open queue.
               *  This routine will not free the open queue.  The freeing
               *  of the open queue node will be done later - we are
               *  not allowed to block yet.
               */
              semDelFromOpenQ(pRemOpenN,pPreOpenN,ppOpenQN);
              ulFree = 1;
            }
          }
          else if (((*ppOpenQN)->usOpenCt == 0) &&
                   ((*ppOpenQN)->pNextOpen == NULL)) {
            /*
             *  Mutex is in the owner died state and there are no other
             *  processes that have the semaphore open.  Simply null out
             *  the open queue for the mutex.  The open queue node to
             *  be freed is saved in pRemOpenN and will be deleted later -
             *  we are not allowed to block yet.
             */
            pRemOpenN = *ppOpenQN;
            *ppOpenQN = NULL;
            pMuxQ = NULL;
            ulFree = 1;
          }

          /*  d59051
           *  If the sem is open, and has DM_OWNER_DIED set, and we are
           *  removing the last node in the open queue (i.e. not the first
           *  node in the open queue. It indicates which pid caused the
           *  DM_OWNER_DIED bit to be set. See SEMMarkOwnerDied)
           *  remove the first node so we can delete the semaphore.
           */
          if ((ulOpen) && ((*ppOpenQN) != NULL)) {
             if ((usFlags & DM_OWNER_DIED) && ((*ppOpenQN)->pNextOpen == NULL)
                 && ((*ppOpenQN)->usOpenCt == 0)) {
                pRemOpenN1 = *ppOpenQN;
                *ppOpenQN = NULL;
                /*
                 * For mutex sems, SEMMarkOwnerDied sets the pMuxQ
                 * field to the tid of the thread that owned the
                 * semaphore when it died.
                 */
                if (usFlags & DCM_MUTEX_SEM)
                   pMuxQ = NULL;
                ulLast = 1;
             }
          }

          if (*ppOpenQN == NULL) {
            /*
             *  No processes have the semaphore open, so remove the
             *  semaphore from the table and free the appropriate
             *  structures.
             *
             *  Must NULL table before free's since free can block.
             */
            pShSemTbl[ulTblCt] = NULL;
            ulShTotUsed--;
            if (ulTblCt < ulShNextFree)
              ulShNextFree = ulTblCt;
            tkAssert(pMuxQ == NULL);
            if (usFlags & DCE_EVENT_SEM) {
              /*
               *  Event semaphore.
               */
              semAssertSEvtSig(pSem);
              if (((PSHUN)pSem)->SEvt.pszName != NULL) {
                /*
                 *  Free both the string node from the string table and
                 *  the string name from the semaphore.
                 */
                semStrTblDelete(((PSHUN)pSem)->SEvt.pszName);
                VMFreeKHB(VM_HKH_PUB_SWAPRW, ((PSHUN)pSem)->SEvt.pszName);
              }
            }
            else if (usFlags & DCM_MUTEX_SEM) {
              /*
               *  Mutex semaphore.
               */
              semAssertSMtxSig(pSem);
              if (((PSHUN)pSem)->SMtx.pszName != NULL) {
                /*
                 *  Free both the string node from the string table and
                 *  the string name from the semaphore.
                 */
                semStrTblDelete(((PSHUN)pSem)->SMtx.pszName);
                VMFreeKHB(VM_HKH_PUB_SWAPRW, ((PSHUN)pSem)->SMtx.pszName);
              }
            }
            else {
              /*
               *  Muxwait semaphore.
               */
              semAssertSMuxSig(pSem);
              tkAssert(((PSHUN)pSem)->SMux.usWaitCt == 0);
              if (((PSHUN)pSem)->SMux.pszName != NULL) {
                /*
                 *  Free both the string node from the string table and
                 *  the string name from the semaphore.
                 */
                semStrTblDelete(((PSHUN)pSem)->SMux.pszName);
                VMFreeKHB(VM_HKH_PUB_SWAPRW, ((PSHUN)pSem)->SMux.pszName);
              }
              /*
               *  Free the semrecord from the mux if it still exists.
               */
              if (((PSHUN)pSem)->SMux.pSemRec != NULL)
                freeKRHB(((PSHUN)pSem)->SMux.pSemRec);
            }
            /*
             *  Free perfview data block.
            */
//b734380   if(fPerfview)    /* Is perfview up ? */                      //aa04504
//b734380     PVWKillInstance_Sem(pTCBCur, ((PSHUN)pSem)->SEvt.pPvwBlk); //aa04504
            /*
             *  Free the semaphore structure.
             */
            freeKRHB(pSem);
            TKResched();
          }
          /*  d59051
           *  If open queue node's still have to be freed, free them.
           */
          if (ulFree) {
            semAssertOpenQSig(pRemOpenN);
            BMPFree(pBMPOpenQSem, pRemOpenN); // PERF: 98488
          }
          if (ulLast) {
            semAssertOpenQSig(pRemOpenN1);
            BMPFree(pBMPOpenQSem, pRemOpenN1);  // PERF: 98488
          }
        }
      }
    }
    /*
     *  Free the semaphore tables in the PTDA.
     */
    SEMTerminatePTDA(pPTDACur);
    /*
     *  Update the mux queue free list.
     */
    semUpdateFreeMuxQ(1);
}


/***EP  SEMTerminatePTDA - Free Tables In PTDA Used By Semaphores
 *
 *      SEMTerminatePTDA is called when a process is terminating or
 *      when an EXEC fails after SEMInitPTDA has been called successfully.
 *      The tables allocated in the PTDA for the 32-bit semaphores are freed
 *      at this time.
 *
 *      ENTRY   pptda           - PTDA pointer
 *
 *      EXIT    Success - table and bitmap are freed
 *
 *      PSEUDO CODE
 *
 *      For (each semaphore in the private table)
 *        free MuxQ nodes, or SemRecords, if any
 *        free semaphore
 *      Endfor
 *      free the private semaphore table
 *      free the bitmap for the shared semaphores
 *      destroy the kernel semaphores
 */

void SEMTerminatePTDA(pptda)
PPTDA pptda;
{
    register ULONG ulTblCt;     /* counter for table */
    register ULONG ulUsedCt;    /* counter for number of used spots in table */
    register PSEM pSem;         /* ptr to a semaphore */
    USHORT usFlags;             /* flag attributes */


    /*
     *  Search down each entry in the PRIVATE semaphore table.
     */
    for (ulTblCt=0,ulUsedCt=0; ((ulTblCt < pptda->ulPrTblSize) &&
                        (ulUsedCt < pptda->ulPrTotUsed)); ulTblCt++) {
      pSem = pptda->pPrSemTbl[ulTblCt];
      if (pSem != NULL) {
        /*
         *  A semaphore exists at the current table entry.
         */
        ulUsedCt++;
        usFlags = ((PPRUN)pSem)->PMtx.usFlags;
        if (usFlags & DCE_EVENT_SEM) {
          /*
           *  Event sempahore.  Free mux queue list if one exists.
           */
          semAssertPEvtSig(pSem);
          if (((PPRUN)pSem)->PEvt.pMuxQ)
            semFreeMuxQList(((PPRUN)pSem)->PEvt.pMuxQ);
        }
        else if (usFlags & DCM_MUTEX_SEM) {
          /*
           *  Mutex sempahore.  Free mux queue list if one exists and
           *  not in owner died state.
           */
          semAssertPMtxSig(pSem);
          if ((!(usFlags & DM_OWNER_DIED)) && (((PPRUN)pSem)->PMtx.pMuxQ))
            semFreeMuxQList(((PPRUN)pSem)->PMtx.pMuxQ);
        }
        else {
          /*
           *  Mux sempahore.  Free semrecord.
           */
          semAssertPMuxSig(pSem);
          if (((PPRUN)pSem)->PMux.pSemRec != NULL)
            freeKRHB(((PPRUN)pSem)->PMux.pSemRec);
        }
        /*
         *  Free perfview data block.
        */

//b734380if(fPerfview)    /* Is perfview up ? */                        //aa04504
//b734380 PVWKillInstance_Sem(pTCBCur, ((PPRUN)pSem)->PEvt.pPvwBlk);   //aa04504
        /*
         *  Free semaphore structure.
         */
        freeKRHB(pSem);
        TKResched();
      }
    }
    /*
     *  Free private semaphore table.
     *  Free per process bitmap for shared semaphore opens.
     *  Free kernel semaphores for private table and bitmap.
     */
    freeKRHB(pptda->pPrSemTbl);                /* free private sem table */
    freeKRHB(pptda->pShSemBmp);                /* free shared bitmap */
    KSEMDestroy(pptda->hksPrTbl);
    KSEMDestroy(pptda->hksShBmp);
}



#pragma code_seg("CINIT_TEXT", "CODE")

/***EP  SEMInitShrTbl - Initialize Shared Semaphore Table
 *
 *      SEMInitShrTbl initializes the variables needed to maintain
 *      the shared semaphore table for the 32-bit semaphore routines.
 *
 *      ENTRY   void
 *
 *      EXIT    Success - NO_ERROR
 *              Failure - ULONG, error code
 *
 *      PSEUDO CODE
 *
 *      If ((rc = allocate shared table) == NO_ERROR)
 *        zero out the memory
 *        Table Size = SEM_SHTBLSIZE                // size in dwords
 *        Total Used = 0                            // no sems yet
 *        Next Free = 0
 *        If ((rc = allocate string hash table) == NO_ERROR)
 *          zero out the memory
 *          String Table Size = SEM_SHTBLSIZE       // size in dwords
 *          String Total Used = 0                   // no strings yet
 *          create the necessary kernel semaphores
 *        Else
 *          free shared table
 *        Endif
 *      Endif
 *      return (rc)
 */

ULONG SEMInitShrTbl(void)

{
    ULONG rc;             /* return code */


    pShSemTbl = NULL;
    pShSemStrTbl = NULL;
    pFreeMux = NULL;
    ulFreeMuxCount = 0;
    /*
     *  Allocate shared semaphore table.
     */
    if ((rc = allocKRHB((sizeof(PSEM) * SEM_SHTBLSIZE),(VMHOB)SEMTABLEOWNER,
                        &pShSemTbl)) == NO_ERROR) {
      memset(pShSemTbl,0,SEM_SHTBLSIZE * SEM_DWORD_BYTES);
      ulShTblSize = SEM_SHTBLSIZE;         /* size of table in dwords */
      ulShTotUsed = 0;                     /* no semaphores in table yet */
      ulShNextFree = 0;                    /* starts as an empty table */

      /*
       *  Allocate string table.  This is for the string of the named
       *  shared semaphores.
       */
      if ((rc = allocKRHB((sizeof(SEMTBLNODE) * SEM_STRTBLSIZE),(VMHOB)SEMTABLEOWNER,
                          &pShSemStrTbl)) == NO_ERROR) {
        memset(pShSemStrTbl,0,(sizeof(SEMTBLNODE) * SEM_STRTBLSIZE));
        ulShSemStrTblSize = SEM_STRTBLSIZE;   /* size of table in dwords */
        ulShSemStrTblUsed = 0;               /* no strings in table yet */

        /*
         *  Create the necessary kernel semaphores.
         */
        if ((rc = KSEMCreate(&hksShTbl,KRNL_MUTEXSEM)) == NO_ERROR) {
          if ((rc = KSEMCreate(&hksStrTbl,KRNL_MUTEXSEM)) == NO_ERROR) {
              rc = KSEMCreate(&hksSemRec,KRNL_MUTEXSEM);
          }
        }
      }
    }

    if (rc != NO_ERROR) {
      if (pShSemTbl != NULL)
        freeKRHB(pShSemTbl);
      if (pShSemStrTbl != NULL)
        freeKRHB(pShSemStrTbl);
    }
    return (rc);
}


#pragma code_seg()

