/*static char *SCCSID = "@(#)semwrkr.c  6.9 92/03/11";*/
#define SCCSID  "@(#)semwrkr.c  6.9 92/03/11"

/*
 *      Semaphores - 32-bit Semaphore Worker 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
 *  4/29/91  Steve Best D614.12 perfview macro changes
 *  6/15/91  Steve Best b724626 fixes to get semaphore counters to work
 *  1/21/92  Steve Best aa04504 check to see if perfview is up before calls
 *                      are made to pvw routines.
 *  3/11/92  Steve Best b734380 take out perfview counters
 *  9/29/93  William Hsieh  73445  racing condition in semaphore corrupts PTree
 *  2/23/94  Integrate Fix 78979 into code. This is to SemMuxWaitSem.
 *
 *  6/3/94   Ron Cadima. Move all code associated with specific semaphore types
 *           ( Event, Mutex and MuxWait ) to new code modules that just contain
 *           the code for the specific type of semaphore. ( SEMEVENT.C, SEMMTX.C,
 *           SEMMUX.C )
 */


#define INCL_TYPES
#include <basedef.h>
#define FNO_NUM FNO_SEMWRKR_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 <error2.h>
#include <sigdef.h>
#include <except.h>
#include <dossymc.h>
#include <seldesc.h>
#include <pgexport.h>
#include <vmexport.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 <kmode.h>

/***LP  semSharedClose - Close (Free) a Shared Semaphore
 *
 *      semSharedClose is called when there are no opens left for
 *      the given semaphore.  The semaphore is removed from the
 *      shared semaphore table, the total number of shared semaphores
 *      used is decremented, and the semaphore structure is freed.
 *
 *      This routine may only be called by the worker layer.  We
 *      are in a NoBlock section when this is called, so we must
 *      end the no block section before calling any free routines.
 *      The NoBlock section must then be restarted again before
 *      returning, since the calling routines are expecting to be
 *      in a NoBlock section.
 *
 *      ENTRY   pSem    - ptr to semaphore
 *              ulIndex - index into table
 *              pszName - ptr to string in semaphore
 *
 *      EXIT    semaphore is freed
 *
 *      PSEUDO CODE
 *
 *      remove semaphore from shared table
 *      decrement ulShTotUsed
 *      reset ulShNextFree if necessary
 *      destroy semaphore (free memory)
 */

void semSharedClose(PSEM pSem,         /* ptr to semaphore */
                    ULONG ulIndex,     /* index into table */
                    PSZ pszName)       /* ptr to string in semaphore */

{
    /*
     *  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.
     *
     *  Must NULL table before free's since free can block.
     */
    pShSemTbl[ulIndex] = NULL;
    ulShTotUsed--;
    if (ulIndex < ulShNextFree)
      ulShNextFree = ulIndex;
    TKEndNoBlock();
    if (pszName != NULL) {
      /*
       *  Free both the string node in the storage tree and
       *  the psz from semaphore structure.
       */
      semStrTblDelete(pszName);
      VMFreeKHB(VM_HKH_PUB_SWAPRW,pszName);
    }
    /*
     *  Free perfview data block.
     */
//b734380if(fPerfview)    /* Is perfview up ? */                            //aa04504
//b734380    PVWKillInstance_Sem(pTCBCur, ((PSHUN)pSem)->SEvt.pPvwBlk);   //d61412
    /*
     *  Free the semaphore structure.
     */

    freeKRHB(pSem);
    TKBeginNoBlock();
}


/***LP  semPrivateClose - Close (Free) a Private Semaphore
 *
 *      semPrivateClose is called when there are no opens left for
 *      the given semaphore.  The semaphore is removed from the
 *      private semaphore table, the total number of private semaphores
 *      used is decremented, and the semaphore structure is freed.
 *
 *      This routine may only be called by the worker layer.  We
 *      are in a NoBlock section when this is called, so we must
 *      end the no block section before calling any free routines.
 *      The NoBlock section must then be restarted again before
 *      returning, since the calling routines are expecting to be
 *      in a NoBlock section.
 *
 *      ENTRY   pSem    - ptr to semaphore
 *              ulIndex - index into table
 *
 *      EXIT    semaphore is freed
 *
 *      PSEUDO CODE
 *
 *      remove semaphore from private table
 *      decrement pPTDACur->ulPrTotUsed
 *      reset pPTDACur->ulPrNextFree if necessary
 *      destroy semaphore (free memory)
 */

void semPrivateClose(PSEM pSem,          /* ptr to semaphore */
                     ULONG ulIndex)      /* index into table */

{
    /*
     *  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.
     *
     *  Must NULL table before free's since free can block.
     */
    pPTDACur->pPrSemTbl[ulIndex] = NULL;
    if (ulIndex < pPTDACur->ulPrNextFree)
      pPTDACur->ulPrNextFree = ulIndex;
    (pPTDACur->ulPrTotUsed)--;
    TKEndNoBlock();
    /*
     *  Free perfview data block.
     */

//b734380  if(fPerfview)    /* Is perfview up ? */                            //aa04504
//b734380     PVWKillInstance_Sem(pTCBCur, ((PPRUN)pSem)->PEvt.pPvwBlk);    //d61412
    /*
     *  Free the semaphore structure.
     */

    freeKRHB(pSem);
    TKBeginNoBlock();
}


/***LP  semOpenSem - Open A Semaphore, All 3 Types
 *
 *      semOpenSem is the worker function for the DosOpen...Sem API
 *      routines.  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.
 *
 *      semOpenSem opens a semaphore.  The semaphore must have been
 *      created before semOpenSem can open it.  The semaphore will be
 *      referenced by the semaphore handle passed in phsem.
 *      semOpenSem only returns the handle of the semaphore.  It does
 *      not test or change any of the values of the semaphore.
 *
 *      This routine is a general routine used by all three types of
 *      semaphores.
 *
 *      ENTRY   phsem     - pointer to the handle of a semaphore
 *              pOpenN    - pointer to a new OpenQ node (shared only)
 *              ppOpenQ   - pointer to the open queue of semaphore (shared only)
 *              pusOpenCt - pointer to open count of semaphore (private only)
 *              pptda     - pointer to PTDA to use; NULL for invalid pid
 *
 *      EXIT    Success - NO_ERROR
 *              Failure - ERROR_TOO_MANY_OPENS
 *
 *      PSEUDO CODE
 *
 *      If (shared)                                 // shared sem
 *        check 64K-1 open count limit
 *        search OpenQ for current pid
 *        If (exists)
 *          increment usOpenCt in existing OpenQ node
 *        Else
 *          pOpenN->pidOpener = pid in ptda or 0 if pptda is null (dhopen)
 *          pOpenN->usOpenCt = 1
 *          add node to OpenQ in order of pid
 *          set bit in local bitmap to 1
 *        Endif
 *      Else                                        // private sem
 *        check 64K-1 open count limit
 *        increment usOpenCt
 *      Endif
 *      return (NO_ERROR)
 */

ULONG semOpenSem(PHSEM phsem,         /* ptr to semaphore handle */
                 POPENQ pOpenN,       /* ptr to OpenQ node to add */
                 POPENQ *ppOpenQ,     /* ptr to open queue */
                 PUSHORT pusOpenCt,   /* ptr to open count in semaphore */
                 PPTDA pptda)         /* ptr to PTDA to use */

{
    register ULONG ulIndex;     /* array index */
    register PSEM pSem;         /* ptr to semaphore structure */
    POPENQ pCurOpenN;           /* ptr to current pid's OpenQ node */
    POPENQ pPreOpenN;           /* ptr to previous OpenQ node */
    PID pid;                    /* process id to use */
    ULONG fOpen;                /* open flag - set if already open */


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

    ulIndex = semGetIndex(*phsem);
    if (semIfShared(*phsem)) {
      /*
       *  Shared Semaphore.
       */
      pSem = pShSemTbl[ulIndex];
      tkAssert(ulIndex < ulShTblSize);

      /*
       *  Get the process id to use.  If pptda is not null, then use
       *  the process id from the given ptda.  If the pptda IS null,
       *  then use the invalid process id.
       */
      if (pptda != NULL) {
        tkAssert(ulIndex < pptda->ulShBmpSize);
        fOpen = semGetBit(ulIndex,pptda->pShSemBmp);
        pid = pptda->ptda_Pid;
      }
      else {
        fOpen = ((PSHUN)pSem)->SEvt.usFlags & DHO_SEM_OPEN;
        pid = SEM_INVALID_PID;
      }

      if (fOpen) {
        /*
         *  Semaphore is already open in this process.  Find the correct
         *  open queue node, check 64K - 1 per process open limit, and
         *  increment the open count.
         */
        semGetOpenQNode(*ppOpenQ,SSToDS(&pCurOpenN),SSToDS(&pPreOpenN),pid);
        if (pCurOpenN->usOpenCt == SEM_MAXOPENS) {
          TKEndNoBlock();
          return (ERROR_TOO_MANY_OPENS);
        }
        (pCurOpenN->usOpenCt)++;
      }
      else {
        /*
         *  Fill in the new open queue node and add it to the open queue.
         *  Set the appropriate bit in the per process bitmap.
         */
        pOpenN->pidOpener = pid;
        pOpenN->usOpenCt = 1;
        /*
         *  I can use the flags field to see if the owner died flag is
         *  set.  The flags field for all types of semaphores should
         *  always be in the same relative position in the structures
         *  (currently it's in position 1).  If the flags field moves
         *  positions within any of the structures, this will break.
         */
        semAddToOpenQ(pOpenN,ppOpenQ,
                      ((PSHUN)pSem)->SMtx.usFlags & DM_OWNER_DIED);
        if (pptda != NULL)
          semSetBit(ulIndex,pptda->pShSemBmp);
        else
          ((PSHUN)pSem)->SEvt.usFlags |= DHO_SEM_OPEN;
      }
    }
    else {
      /*
       *  Private Semaphore.
       */
      tkAssert(pptda == pPTDACur);
      tkAssert((ulIndex < pPTDACur->ulPrTblSize) &&
               (pPTDACur->pPrSemTbl[ulIndex] != NULL));

      /*
       *  Check 64K - 1 per process open limit.
       */
      if (*pusOpenCt == SEM_MAXOPENS) {
        TKEndNoBlock();
        return (ERROR_TOO_MANY_OPENS);
      }

      /*
       *  Increment open count.
       */
      (*pusOpenCt)++;
    }
    /*
     *  Add signature bits to handle to be passed back to allocation
     *  layer.
     */
    semAssertNoSigBits(*phsem);
    *phsem = semAddSigBits(*phsem);
    TKEndNoBlock();
    return (NO_ERROR);
}
