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

/*
 *      Semaphores - 32-bit Semaphore Allocation/Validation 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
 *  7/10/91  Steve Best b724626 get perfview counters to work
 *  1/22/92  Steve Best aa04504 decrease perfview overheadby only calling routines
 *                      if perfview is up.
 *  3/11/92  Steve Best b734380 remove perfview counters
 *  9/14/93  Charlie Schmitt 73736 validate mux bundle before allocating sem
 *  6/3/93   Ron Cadima. Remove code specific to particular type of semaphore,
 *             EVENT, Mutex, MUXWAIT, out of this module and into new modules
 *             SEMEVENT.C, SEMMTX.C, and SEMMUX.C.  Also update shared and
 *             private semaphore grow logic so that we only grow by 8 entries
 *             instead of doubling the number of entries. Look for RAC to see
 *             changed code.
 */

#define INCL_DOSP
#define OS2_NOPMAPI
#include <os2p.h>

#define INCL_TYPES
#include <basedef.h>
#define FNO_NUM FNO_SEMALLOC_C          /* for assertion macros */
#include <fnotask.h>                    /* MUST BE BEFORE osmac.h */
#include <osmac.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>
//b734380#include <pvwxport.h>
//b734380#include <perfvw.h>
//b734380extern BOOL fPerfview;                                                 //aa04504

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

/***LP  semSharedAlloc - Allocate All Items for Create Shared Sems
 *
 *      semSharedAlloc allocates all items necessary to create a
 *      shared semaphore.  First, the semaphore structure and
 *      an open queue node are allocated.  It then checks the
 *      global array and the local bitmap boundaries.   If either of
 *      them have reached their limit, they are expanded.  Since the
 *      allocation routine may block, the boundaries are repeatedly
 *      checked until they are not at their limit.  Once the new
 *      boundaries are found, the old structures are copied into the
 *      new structures and the old ones are freed.  The index of the
 *      first open spot (with the most significant bit set to 1) in
 *      the global array is returned in phsem.
 *
 *      ENTRY   ppSSem  - pointer to a Shared Semaphore
 *              ulSize  - size of the semaphore to allocate
 *              ppOpenN - pointer to an Open Queue node
 *              phsem   - pointer filled in with the semaphore handle
 *
 *      EXIT    Success - NO_ERROR
 *              Failure - ERROR_TOO_MANY_HANDLES
 *
 *      PSEUDO CODE
 *
 *      *ppSSem = allocate semaphore structure           \\ semaphore
 *      pOpenN = malloc new OpenQ structure              \\ open queue
 *      While (at global array boundary) || (at local bitmap boundary)
 *        If (at global array boundary) && (not at 64K limit)
 *          expand global array
 *        Endif
 *        If (local bitmap needs to grow)
 *          grow it
 *        Endif
 *      Endwhile
 *      copy from old array to new array if necessary
 *      copy from old bitmap to new bitmap if necessary
 *      set phsem to the index of an open spot in global array
 *      return (NO_ERROR)
 */

ULONG semSharedAlloc(PVOID *ppSSem,       /* ptr to Shared Semaphore */
                     ULONG ulSize,        /* size of sem to allocate */
                     POPENQ *ppOpenN,     /* ptr to Open Queue node */
                     PHSEM phsem)         /* ptr to semaphore handle */

{
    ULONG ulHandle;             /* new handle value */
    ULONG rc;                   /* return code */

    *ppSSem = NULL;
    *ppOpenN = NULL;
    /*
     *  Allocate shared semaphore structure of size "ulSize."
     */
    if ((rc = allocKRHB(ulSize,(VMHOB)SEMSTRUCOWNER,ppSSem)) != NO_ERROR)
      return (rc);
    /*
     *  Allocate open queue structure.
     */
    if((rc=BMPGet(pBMPOpenQSem, ppOpenN)) != NO_ERROR) {  // PERF: 98488
      freeKRHB(*ppSSem);
      return (rc);
    }

    while ((ulShTblSize == ulShTotUsed) ||
           (pPTDACur->ulShBmpSize <= ulShNextFree)) {
      /*
       *  Check 64K limit on the table size.
       */
      if (ulShTotUsed == SEM_MAXTBLSIZE) {
        freeKRHB(*ppSSem);
        BMPFree(pBMPOpenQSem, *ppOpenN);   // PERF: 98488
        return (ERROR_TOO_MANY_HANDLES);
      }
      if (ulShTblSize == ulShTotUsed) {
        /*
         *  Need to reallocate new table to a larger size in order to
         *  add the new entry.
         */
        KSEMRequestMutex(hksShTbl,SEM_INDEFINITE_WAIT);
        if (ulShTblSize == ulShTotUsed) {

          /*
           *  ReAllocate the new shared semaphore table.
           */

/****************************************************************************/
/* RAC - Add SEM_TBLGROW entries to shared semaphore table when it is full  */

          if ((rc = VMReallocKHB(VM_HKH_PUB_RESRW,&(pShSemTbl),
                                 ((SEM_TBLGROW + ulShTblSize) * sizeof(PSEM)),
                                 0)) != NO_ERROR) {
            freeKRHB(*ppSSem);
            BMPFree(pBMPOpenQSem, *ppOpenN);  // PERF: 98488
            KSEMReleaseMutex(hksShTbl);
            return (rc);
          }
          /*
           *  Zero out the new entries and adjust the size of the table.
           */
          memset(&(pShSemTbl[ulShTblSize]),'\0',
                 (SEM_TBLGROW * SEM_DWORD_BYTES) );
          ulShTblSize += SEM_TBLGROW;
/****************************** END of RAC Changes  ************************/
        }
        KSEMReleaseMutex(hksShTbl);
      }
      if (pPTDACur->ulShBmpSize <= ulShNextFree) {
        /*
         *  Need to reallocate new bitmap to a larger size in order to
         *  add the new entry.
         */
        KSEMRequestMutex(pPTDACur->hksShBmp,SEM_INDEFINITE_WAIT);
        if (pPTDACur->ulShBmpSize <= ulShNextFree) {
          /*
           *  Allocate the new bitmap.
           */
          if ((rc = VMReallocKHB(VM_HKH_PUB_RESRW,&(pPTDACur->pShSemBmp),
                         (SEM_TBLINCR * pPTDACur->ulShBmpSize / SEM_BYTE_BITS),
                         0)) != NO_ERROR) {
            freeKRHB(*ppSSem);
            BMPFree(pBMPOpenQSem, *ppOpenN);   // PERF: 98488
            KSEMReleaseMutex(pPTDACur->hksShBmp);
            return (rc);
          }
          /*
           *  Zero out the new entries and adjust the size of the bitmap.
           */
          memset(&(pPTDACur->pShSemBmp[pPTDACur->ulShBmpSize / SEM_DWORD_BITS]),
                '\0',pPTDACur->ulShBmpSize / SEM_BYTE_BITS * (SEM_TBLINCR - 1));
          pPTDACur->ulShBmpSize *= SEM_TBLINCR;
        }
        KSEMReleaseMutex(pPTDACur->hksShBmp);
      }
    }

    /*
     *  Return the handle of the new entry.
     */
    semGetTblHdl(pShSemTbl,ulShTblSize, &ulShNextFree, SSToDS(&ulHandle));
    *phsem = ulHandle;
    return (NO_ERROR);
}


/***LP  semPrivateAlloc - Allocate All Items for Create Private Sems
 *
 *      semPrivateAlloc allocates all items necessary to create a
 *      private semaphore.  First, the semaphore structure is
 *      allocated.  It then checks the local array boundary.  If it
 *      has reached its limit, it is expanded.  Since the allocation
 *      routine may block, the boundary is repeatedly checked until it
 *      is not at its limit.  Once the new boundary is found, the old
 *      array is copied into the new array and the old one is freed.
 *      The index of the first open spot in the local array is returned
 *      in phsem.
 *
 *      ENTRY   ppPSem - pointer to a Private semaphore
 *              ulSize - size of the semaphore to allocate
 *              phsem  - pointer filled in with the semaphore handle
 *
 *      EXIT    Success - NO_ERROR
 *              Failure - ERROR_TOO_MANY_HANDLES
 *
 *      PSEUDO CODE
 *
 *      *ppPSem = allocate semaphore structure           \\ semaphore
 *      While (at local array boundary)
 *        check 64K limit of local array
 *        expand local array
 *      Endwhile
 *      copy from old array to new array if necessary
 *      set phsem to the index of an open spot in local array
 *      return (NO_ERROR)
 */

ULONG semPrivateAlloc(PVOID *ppPSem,        /* ptr to Private semaphore */
                      ULONG ulSize,         /* size of sem to allocate */
                      PHSEM phsem)          /* ptr to semaphore handle */

{
    ULONG ulHandle;             /* new handle value */
    ULONG rc;                   /* return code */

    *ppPSem = NULL;
    /*
     *  Allocate private semaphore structure of size "ulSize."
     */
    if ((rc = allocKRHB(ulSize,(VMHOB)SEMSTRUCOWNER,ppPSem)) != NO_ERROR)
      return (rc);

    if (pPTDACur->ulPrTblSize == pPTDACur->ulPrTotUsed) {
      /*
       *  Need to reallocate new table to a larger size in order to
       *  add the new entry.
       */
      KSEMRequestMutex(pPTDACur->hksPrTbl,SEM_INDEFINITE_WAIT);

/****************************************************************************/
/* RAC - Add SEM_TBLGROW entries to shared semaphore table when it is full  */
/* Also changed while statement to if statement.                            */

      if ( pPTDACur->ulPrTblSize == pPTDACur->ulPrTotUsed ) {
        /*
         *  Check 64K limit on table size.
         */
        if (pPTDACur->ulPrTotUsed == SEM_MAXTBLSIZE) {
          freeKRHB(*ppPSem);
          KSEMReleaseMutex(pPTDACur->hksPrTbl);
          return (ERROR_TOO_MANY_HANDLES);
        }
        /*
         *  Allocate new table.
         */
        if ((rc = VMReallocKHB(VM_HKH_PUB_RESRW,&(pPTDACur->pPrSemTbl),
                         ((SEM_TBLGROW + pPTDACur->ulPrTblSize) * sizeof(PSEM)),
                          0)) != NO_ERROR) {
          freeKRHB(*ppPSem);
          KSEMReleaseMutex(pPTDACur->hksPrTbl);
          return (rc);
        }
        /*
         *  Zero out the new entries and adjust the size of the table.
         */
        memset(&(pPTDACur->pPrSemTbl[pPTDACur->ulPrTblSize]),'\0',
                                     SEM_DWORD_BYTES * (SEM_TBLGROW));
        pPTDACur->ulPrTblSize += SEM_TBLGROW;

/*********************** End of RAC changes **********************************/
      }
      KSEMReleaseMutex(pPTDACur->hksPrTbl);
    }

    /*
     *  Return the handle of the new entry.
     */
    semGetTblHdl(pPTDACur->pPrSemTbl,pPTDACur->ulPrTblSize,
                 &(pPTDACur->ulPrNextFree),SSToDS(&ulHandle));
    *phsem = ulHandle;
    return (NO_ERROR);
}


/***LP  semOpenSharedAlloc - Allocation to Open a Shared Semaphore
 *
 *      semOpenSharedAlloc allocates all of the necessary items to
 *      complete an open of a shared semaphore.  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
 *              phsem   - if psz is NULL, phsem contains the handle of an
 *                        anonymous semaphore created by another process
 *              ppOpenN - pointer to an open queue node
 *              pptda   - pointer to PTDA to use; NULL for invalid pid
 *
 *      EXIT    Success - NO_ERROR
 *              Failure - ERROR_INVALID_HANDLE
 *                        ERROR_INVALID_PARAMETER
 *                        ERROR_SEM_NOT_FOUND
 *
 *      PSEUDO CODE
 *
 *      *ppOpenN = allocate open queue node
 *      (*ppOpenN)->usOpenCt = SEM_OPENQFLAG     // flag to see if it's used
 *      If (pszName does NOT exist in table)
 *        return (ERROR_SEM_NOT_FOUND)
 *      Endif
 *      While (local bitmap at boundary)
 *        expand it
 *      Endwhile
 *      copy from old bitmap to new bitmap if necessary
 *      return (NO_ERROR)
 */

ULONG semOpenSharedAlloc(PSZ pszName,       /* semaphore name */
                         PHSEM phsem,       /* ptr to semaphore handle */
                         POPENQ *ppOpenN,   /* ptr to OpenQ node */
                         PPTDA pptda)       /* ptr to PTDA to use */

{
    ULONG ulIndex;                  /* array index */
    ULONG rc;                       /* return code */


    /*
     *  Allocate open queue node.
     */
    if((rc=BMPGet(pBMPOpenQSem, ppOpenN)) != NO_ERROR)   // PERF: 98488
      return (rc);
    /*
     *  Set open queue flag - to see if open queue node is used.
     */
    (*ppOpenN)->usOpenCt = SEM_OPENQFLAG;

    if (pszName != NULL) {
      /*
       *  String name given to find the semaphore to open.
       *  Check to see that the name string given exists.
       */
      if (semGetStr(pszName,SSToDS(&ulIndex)) != NO_ERROR) {
          BMPFree(pBMPOpenQSem, *ppOpenN);  // PERF: 98488
        return (ERROR_SEM_NOT_FOUND);
      }

      /*
          The shared event semaphore creation first enters a string node
          in the string table (setting the hsem field to 0xffff), then
          enters the shared sem into the shared sem table, and then
          finally sets the hsem field of the string node to the index
          used into the shared sem table. If we get back an ulIndex of
          0xffff, the semaphore must not be fully established....

          defect 67077
       */
      if ( ulIndex == 0xffff ) {
        BMPFree(pBMPOpenQSem, *ppOpenN);  // PERF: 98488
        return (ERROR_SEM_NOT_FOUND);
      }

      /*
       *  Finding semaphore by name string.  Must make sure that the
       *  handle value given is either 0 or the correct handle value
       *  for the string given.
       */
      if ((*phsem != 0) && (semGetIndex(*phsem) != ulIndex)) {
        BMPFree(pBMPOpenQSem, *ppOpenN);   // PERF: 98488
        return (ERROR_INVALID_PARAMETER);
      }
      /*
       *  Set handle value of semaphore to open.
       */
      semAssertNoSigBits(ulIndex);
      *phsem = semGetShHdl(ulIndex);
    }
    else {
      /*
       *  No string name was given, so make sure the handle value
       *  given exists.
       */
      if (!semVerifySigBits(*phsem))
        return (ERROR_INVALID_HANDLE);
      ulIndex = semGetIndex(*phsem);
      if (ulIndex >= ulShTblSize)
        return (ERROR_INVALID_HANDLE);
    }

    if ((pptda != NULL) && (pptda->ulShBmpSize <= ulIndex)) {
      /*
       *  Need to reallocate the per process open bitmap.
       */
      KSEMRequestMutex(pptda->hksShBmp,SEM_INDEFINITE_WAIT);
      while (pptda->ulShBmpSize <= ulIndex) {
        /*
         *  Allocate new bitmap.
         */
        if ((rc = VMReallocKHB(VM_HKH_PUB_RESRW,&(pptda->pShSemBmp),
                         (SEM_TBLINCR * pptda->ulShBmpSize / SEM_BYTE_BITS),
                         0)) != NO_ERROR) {
          BMPFree(pBMPOpenQSem, *ppOpenN);   // PERF: 98488
          KSEMReleaseMutex(pptda->hksShBmp);
          return (rc);
        }
        /*
         *  Zero out the new portion of the bitmap and adjust the size of
         *  the table.
         */
        memset(&(pptda->pShSemBmp[pptda->ulShBmpSize / SEM_DWORD_BITS]),
              '\0',pptda->ulShBmpSize / SEM_BYTE_BITS * (SEM_TBLINCR - 1));
        pptda->ulShBmpSize *= SEM_TBLINCR;
        /*
         *  Make sure semaphore still exists.
         *  We only need to verify that the string is correct at this point.
         *  The handle value will still be within the limits of the table,
         *  since the tables do NOT shrink.
         */
        if (pszName != NULL) {
          /*
           *  Check string.
           */
          if (semGetStr(pszName,SSToDS(&ulIndex)) != NO_ERROR) {
            BMPFree(pBMPOpenQSem, *ppOpenN);   // PERF: 98488
            KSEMReleaseMutex(pptda->hksShBmp);
            return (ERROR_SEM_NOT_FOUND);
          }
          if ((*phsem != 0) && (semGetIndex(*phsem) != ulIndex)) {
            BMPFree(pBMPOpenQSem, *ppOpenN);   // PERF: 98488
            KSEMReleaseMutex(pptda->hksShBmp);
            return (ERROR_INVALID_PARAMETER);
          }
          semAssertNoSigBits(ulIndex);
          *phsem = semGetShHdl(ulIndex);
        }
      }
      KSEMReleaseMutex(pptda->hksShBmp);
    }
    return (NO_ERROR);
}


/***LP  semAllocWaitMuxQs - Allocate MuxQ Nodes for Mux
 *
 *      semAllocWaitMuxQs allocates a linked list of MuxQ nodes
 *      for a muxwait semaphore.  It goes through the list of SemRecords
 *      in the mux to determine how many MuxQ nodes to allocate.  Since
 *      the allocation routine may block, it checks to see that the
 *      number of MuxQ nodes allocated is at least as large as the
 *      number of mutexes/events in the mux.  If not, it will continue
 *      to allocate MuxQ nodes until there are enough to cover the mux.
 *
 *      ENTRY   ulIndex  - index of a mux semaphore
 *              ppSemTbl - address of the pointer to the semaphore table
 *              ppMuxQN  - pointer filled in with the list of MuxQ nodes
 *
 *      EXIT    Success - NO_ERROR
 *              Failure - ERROR_INVALID_HANDLE
 *
 *      PSEUDO CODE
 *
 *      If ((cSemRec = pSemTbl[hmux]->cSemRec) == 0)     // empty mux
 *        return (NO_ERROR)
 *      Endif
 *      pMuxQN = allocate a mux queue node
 *
 *      MuxCt = 1
 *      While (MuxCt < cSemRec))                   // build muxq node list
 *        For (; MuxCt < cSemRec; MuxCt++)
 *          pLastMQ = allocate a mux queue node
 *          add pLastMQ onto front of list
 *        Endfor
 *        If ((current pid does not have sem open) || (not muxwait sem))
 *          free mux queue nodes (if any)
 *          return (ERROR_INVALID_HANDLE)
 *        Endif
 *        cSemRec = pSemTbl[hmux]->cSemRec
 *      Endwhile
 *      return (NO_ERROR)
 */

ULONG semAllocWaitMuxQs(ULONG ulIndex,       /* index of a mux semaphore */
                        PSEMTBL *ppSemTbl,  /* addr of ptr to sem table */
                        PMUXQ *ppMuxQN)     /* ptr to list of MuxQ nodes */

{
    register USHORT usMuxCt = 1;      /* number of mux queue nodes allocated */
    PMUXQ pNewMQ;                     /* ptr to last MuxQ node in list */
    register USHORT uscSemRec;        /* number of semrecords in mux */
    ULONG rc;                         /* return code */


    if ((uscSemRec = ((PSMUX)((*ppSemTbl)[ulIndex]))->cSemRec) == 0)
      return (NO_ERROR);
    if (ulFreeMuxCount > 0) {
      /*
       *  Take mux queue node off of mux queue free list.
       */
      *ppMuxQN = pFreeMux;
      pFreeMux = pFreeMux->pNextMux;
      ulFreeMuxCount--;
      semAssertMuxQSig(*ppMuxQN);
    }
    /*
     *  Otherwise, allocate new mux queue node.
     */
    else if ((rc = allocKRHB(sizeof(MUXQ),(VMHOB)SEMMUXQOWNER,ppMuxQN)) != NO_ERROR)
      return (rc);
    /*
     *  Initialize mux queue signature and set its next pointer to NULL.
     */
    semInitMuxQSig(*ppMuxQN);
    (*ppMuxQN)->pNextMux = NULL;

    while (usMuxCt < uscSemRec) {
      for (; usMuxCt < uscSemRec; usMuxCt++) {
        /*
         *  For each entry in mux list, get a mux queue node.
         *  The mux queue list will be a linked list of the mux
         *  queue nodes to be used by "WaitMuxWait" at the worker layer.
         */
        if (ulFreeMuxCount > 0) {
          /*
           *  Take mux queue node off of mux queue free list.
           */
          pNewMQ = pFreeMux;
          pFreeMux = pFreeMux->pNextMux;
          ulFreeMuxCount--;
        }
        /*
         *  Otherwise, allocate new mux queue node.
         */
        else if ((rc = allocKRHB(sizeof(MUXQ),(VMHOB)SEMMUXQOWNER,SSToDS(&(pNewMQ))))
                                != NO_ERROR) {
          semFreeMuxQList(*ppMuxQN);
          return (rc);
        }
        semInitMuxQSig(pNewMQ);
        pNewMQ->pNextMux = *ppMuxQN;
        *ppMuxQN = pNewMQ;
      }
      /*
       *  Recheck table entry.  If it has changed since we've done
       *  the allocations, then return an error.
       */
      if (((*ppSemTbl)[ulIndex] == NULL) ||
          (!(((PSMUX)((*ppSemTbl)[ulIndex]))->usFlags & DCMW_MUX_SEM))) {
        semFreeMuxQList(*ppMuxQN);
        return (ERROR_INVALID_HANDLE);
      }
      uscSemRec = ((PSMUX)((*ppSemTbl)[ulIndex]))->cSemRec;
    }
    return (NO_ERROR);
}

