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

/*
 *      Semaphores - 32-bit Event Semaphore Routines
 *
 *      This is an OS/2 2.x specific file
 *      IBM Confidential
 *
 *      Copyright (c) IBM Corporation 1987, 1989
 *      Copyright (c) Microsoft Corporation 1987, 1989
 *
 *      All Rights Reserved
 *
 *
 *      June, 1994 This module was created by Ron Cadima to merge all the
 *         code associated with Event semaphores into one module and to
 *         optimize the code path in the event semaphore routines. The code
 *         in this module was originally moved from SEMVERIF.C, SEMALLOC.C
 *         and SEMWRKR.C and then modified.
 *
 *  2/19/96  Change Team 148148 Verify handle when semaphores
 *                              are opened with a null name.
 */

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

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

/***EP  VR32CreateEventSem - Create an Event Semaphore
 *
 *      VR32CreateEventSem creates and opens an event 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 event semaphore.  If the
 *      fState flag is set to false, then an implicit DosResetEventSem will be
 *      executed before returning.  This means that if a thread follows this
 *      call with a DosWaitEventSem, the thread will block.  If the fState flag
 *      is set to true, then an implicit DosPostEventSem will be executed.
 *      For the same scenario outlined above, this means that the thread will
 *      be able to run.
 *
 *      ENTRY   sef     - system entry frame
 *              pszName - asciiz name of Event, or NULL for anonymous Event
 *              phev    - ptr to receive event handle
 *              flAttr  - attributes:
 *                          DC_SEM_SHARED
 *              fState  - TRUE  => execute implicit DosPostEventSem before
 *                                 returning
 *                        FALSE => execute implicit DosResetEventSem before
 *                                 returning
 *
 *      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) ||
 *         (fState is other than TRUE or FALSE)
 *        return (ERROR_INVALID_PARAMETER)
 *      Endif
 *      If (pszName != NULL)
 *        validate pszName
 *        call semStoreStr to copy pszName into pSemName
 *      Endif
 *      rc = call SEMApiCreateEventSem with pSemName and hevTmp
 *      If (rc == error)
 *        free pSemName
 *      Else
 *        call TKSuDWord to copy hevTmp to phev
 *      Endif
 *      return (rc)
 */

ULONG cdecl VR32CreateEventSem(THUNKSEF sef,       /* system entry frame */
                               PSZ pszName,        /* semaphore name */
                               PHEV phev,          /* ptr to event handle */
                               ULONG flAttr,       /* attributes */
                               BOOL fState)        /* initial state of event */

{
    PSZ pSemName = NULL;       /* ptr to resident buffer for pszName */
    HEV hevTmp;                /* temporary handle */
    PHEV pStrEvt;              /* ptr to event 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_PM)) != (DC_SEM_SHARED | DC_SEM_PM)) ||
        ((fState != TRUE) && (fState != FALSE)))
      return (ERROR_INVALID_PARAMETER);

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

    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(&pStrEvt))) != NO_ERROR) {
        VMFreeKHB(VM_HKH_PUB_SWAPRW,pSemName);
        return (rc);
      }
    }
    /*
     *  Call the allocation/validation layer.
     */
    rc = SEMApiCreateEventSem(pSemName,SSToDS(&hevTmp),flAttr,fState);

    if (rc == NO_ERROR) {
      if (pSemName)
        *pStrEvt = semGetIndex(hevTmp);
      /*
       *  Copy handle back to user space.
       */
      if ((rc = TKSuDWord(hevTmp,phev)) != NO_ERROR) {
        SEMCloseEventSem(hevTmp,pPTDACur);
        /*
         *  The return code from CloseEvent is not checked here.
         *  The process is dying, so if the close returns error_sem_busy,
         *  semCleanProcDying will close the semaphore in the current
         *  process.  This could only happen in a race condition where
         *  a named semaphore was opened, set, 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.
         */
        semStrTblDelete(pSemName);
        VMFreeKHB(VM_HKH_PUB_SWAPRW,pSemName);
      }
    }

#ifdef PMDEBUG
    if (rc == NO_ERROR) {
      if (semIfShared(hevTmp))
        ((PSEVENT)pShSemTbl[semGetIndex(hevTmp)])->pulCreatAddr = phev;
      else
        ((PPEVENT)((pPTDACur->pPrSemTbl)[semGetIndex(hevTmp)]))
                  ->pulCreatAddr = phev;
    }
#endif

    return (rc);
}
/***EP  VR32OpenEventSem - Open an Event Semaphore
 *
 *      VR32OpenEventSem opens an event semaphore.  DosCreateEventSem must
 *      create the semaphore before VR32OpenEventSem can open it.  If the
 *      pszName is not null, then the semaphore is a named shared event
 *      semaphore.  Otherwise, the semaphore will be referenced by the
 *      semaphore handle passed in phev.  VR32OpenEventSem only returns the
 *      handle of the semaphore.  It does not test or change any of the values
 *      of the semaphore.  If phev 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 event 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 DosCreateEventSem.
 *
 *      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 DosCloseEventSem calls must equal the number of
 *      DosOpenEventSem and DosCreateEventSem calls.
 *
 *      ENTRY   sef     - system entry frame
 *              pszName - asciiz name, NULL => anonymous shared
 *              phev    - if psz is NULL, phev contains the handle of an
 *                        anonymous event 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 SEMApiOpenEventSem with pSemName and hevTmp
 *      If (rc == error)
 *        free pSemName
 *      Else
 *        call TKSuDWord to copy hevTmp to phev
 *      Endif
 *      return (rc)
 */

ULONG cdecl VR32OpenEventSem(THUNKSEF sef,       /* system entry frame */
                             PSZ pszName,        /* semaphore name */
                             PHEV phev)         /* ptr to event handle */

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


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

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

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


/***EP  VR32CloseEventSem - Close an Event Semaphore
 *
 *      VR32CloseEventSem closes an event semaphore that was previously opened
 *      via DosOpenEventSem or DosCreateEventSem.  If this is the last
 *      reference to this event, then the event semaphore is destroyed.
 *
 *      The ERROR_SEM_BUSY return code will be returned if the thread is trying
 *      to make the final close of a semaphore that has another thread in the
 *      same process still waiting on it.
 *
 *      The system semaphores reside in a memory buffer rather than on a disk
 *      file.  This means that when the last process which has a semaphore
 *      open exits or closes that semaphore, the semaphore disappears and must
 *      be recreated by its next user with DosCreateEventSem.
 *
 *      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 DosCloseEventSem calls must equal the number of
 *      DosOpenEventSem and DosCreateEventSem calls.
 *
 *      ENTRY   sef - system entry frame
 *              hev - handle of event to close
 *
 *      EXIT    Success - NO_ERROR
 *              Failure - ERROR_INVALID_HANDLE
 *                        ERROR_SEM_BUSY
 *
 *      PSEUDO CODE
 *
 *      return ( SEMApiCloseEventSem(hev,pPTDACur) )
 */

ULONG cdecl VR32CloseEventSem(THUNKSEF sef,     /* system entry frame */
                              HEV hev)          /* event handle */

{

    /*
     *  Check validity of event handle.
     */
    if (semChkHdl(hev,DCE_EVENT_SEM,pPTDACur) != NO_ERROR)
      return (ERROR_INVALID_HANDLE);

    /*
     *  Call the allocation/validation layer.
     */
    return ( SEMCloseEventSem(semStripSigBits(hev),pPTDACur) );
}

/***EP  VR32ResetEventSem - Reset an Event Semaphore
 *
 *      VR32ResetEventSem resets an event semaphore that has been previously
 *      created (via DosCreateEventSem) and opened (via DosOpenEventSem).
 *      The number of posts performed on the event before it was reset is
 *      returned to the caller in the pulPostCt parameter.  If the event
 *      was already reset, the ERROR_ALREADY_RESET return code is returned
 *      to the calling thread.  It is not reset a second time.
 *
 *      To reverse this operation, simply call DosPostEventSem.  This will
 *      post the event, so that any threads that were waiting for the event
 *      semaphore to be posted (via DosWaitEventSem) will be allowed to run.
 *
 *      ENTRY   sef       - system entry frame
 *              hev       - handle of event to reset
 *              pulPostCt - pointer to receive current number of posts
 *
 *      EXIT    Success - NO_ERROR
 *                        ERROR_ALREADY_RESET
 *              Failure - ERROR_INVALID_HANDLE
 *
 *      PSEUDO CODE
 *
 *      validate pulPostCt
 *      rc = call SEMApiResetEventSem with ulPost
 *      If (rc == NO_ERROR)
 *        call TKSuWord to copy ulPost to pulPostCt
 *      Endif
 *      return (rc)
 */

ULONG cdecl VR32ResetEventSem(THUNKSEF sef,     /* system entry frame */
                              HEV hev,          /* event handle */
                              PULONG pulPostCt) /* ptr to number of posts */

{
    ULONG ulPost;                /* temp storage for number of posts */
    ULONG rc;                    /* return code */
    ULONG rc2;                   /* another return code */


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

    /*
     *  Check validity of event handle.
     */
    if (semChkHdl(hev,DCE_EVENT_SEM,pPTDACur) != NO_ERROR)
      return (ERROR_INVALID_HANDLE);

    /*
     *  Call the worker layer.
     */
     // 98809 Changed semStripBits(hev) to hev in line below.
    rc = SEMResetEventSem(hev,SSToDS(&ulPost),pPTDACur);

    /*
     *  Copy the post count back to user space.
     */
    if ((rc2 = TKSuDWord(ulPost,pulPostCt)) != NO_ERROR)
       return(rc2);

    return (rc);
}

/***EP  VR32PostEventSem - Post an Event Semaphore
 *
 *      VR32PostEventSem posts an event semaphore that was previously reset
 *      via DosResetEventSem.  If the event is already posted, the post
 *      count is incremented and the ERROR_ALREADY_POSTED return code is
 *      returned to the calling thread.  It is not posted a second time.
 *      Otherwise, the event state is posted, the post count is incremented,
 *      and all threads that called DosWaitEventSem are made runnable.
 *
 *      ENTRY   sef - system entry frame
 *              hev - handle of event to post
 *
 *      EXIT    Success - NO_ERROR
 *                        ERROR_ALREADY_POSTED
 *              Failure - ERROR_INVALID_HANDLE
 *                        ERROR_TOO_MANY_POSTS
 *
 *      PSEUDO CODE
 *
 *      return ( SEMApiPostEventSem(hev) )
 */

ULONG cdecl VR32PostEventSem(THUNKSEF sef,       /* system entry frame */
                             HEV hev)            /* event handle */

{

    /*
     *  Check validity of event handle.
     */
    if (semChkHdl(hev,DCE_EVENT_SEM,pPTDACur) != NO_ERROR)
      return (ERROR_INVALID_HANDLE);

    /*
     *  Call the worker layer.
     */
    return ( SEMPostEventSem(semStripSigBits(hev),pPTDACur) );
}

/***EP  VR32WaitEventSem - Wait on an Event Semaphore
 *
 *      VR32WaitEventSem allows a thread to wait on an event semaphore that
 *      was previously created (via DosCreateEventSem) and opened (via
 *      DosOpenEventSem).  When an event semaphore is reset (via
 *      DosResetEventSem), any thread that calls DosWaitEventSem will be
 *      blocked.  When the event semaphore is posted (via DosPostEventSem),
 *      all threads waiting on the event are made runnable.  If the semaphore
 *      is posted when "wait" is called, the thread will continue to run and
 *      the routine will return immediately.
 *
 *      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,
 *      VR32WaitEventSem will return without blocking.  If a timeout of -1 is
 *      passed in, it will block indefinitely.
 *
 *      ENTRY   sef       - system entry frame
 *              hev       - handle of event to wait upon for posting
 *              ulTimeout - timeout in milliseconds
 *
 *      EXIT    Success - NO_ERROR
 *              Failure - ERROR_INTERRUPT
 *                        ERROR_INVALID_HANDLE
 *                        ERROR_TIMEOUT
 *
 *      PSEUDO CODE
 *
 *      return ( SEMApiWaitEventSem(hev,ulTimeout) )
 */

ULONG cdecl VR32WaitEventSem(THUNKSEF sef,         /* system entry frame */
                             HEV hev,              /* event handle */
                             ULONG ulTimeout)      /* timeout in milliseconds */

{
    /*
     *  Check validity of event handle.
     */
    if (semChkHdl(hev,DCE_EVENT_SEM,pPTDACur) != NO_ERROR)
      return (ERROR_INVALID_HANDLE);

    return(SEMWaitEventSem(semStripSigBits(hev),ulTimeout));
}

/***EP  VR32QueryEventSem - Query an Event Semaphore
 *
 *      VR32QueryEventSem will query information about a given event semaphore
 *      if the semaphore was previously opened by the process with a
 *      DosOpenEventSem.  If the event semaphore exists, the number of posts
 *      performed on the event is obtained and returned to the caller in the
 *      pulPostCt parameter.  If the event semaphore does not exist, then
 *      the ERROR_INVALID_HANDLE return code is returned to the caller.
 *
 *      ENTRY   sef       - system entry frame
 *              hev       - handle of event to query
 *              pulPostCt - pointer to receive the current number of
 *                          posts performed on the event
 *
 *      EXIT    Success - NO_ERROR
 *              Failure - ERROR_INVALID_HANDLE
 *                        ERROR_INVALID_PARAMETER
 *
 *      PSEUDO CODE
 *
 *      validate pulPostCt
 *      rc = call SEMApiQueryEventSem with ulPost
 *      If (rc == NO_ERROR)
 *        call TKSuWord to copy ulPost to pulPostCt
 *      Endif
 *      return (rc)
 */

ULONG cdecl VR32QueryEventSem(THUNKSEF sef,     /* system entry frame */
                              HEV hev,          /* event handle */
                              PULONG pulPostCt) /* ptr to number of posts */

{
    ULONG ulPost;                /* temp storage for number of posts */
    ULONG rc;                    /* return code */


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

    /*
     *  Check validity of event handle.
     */
    if (semChkHdl(hev,DCE_EVENT_SEM,pPTDACur) != NO_ERROR)
      return (ERROR_INVALID_HANDLE);

    /*
     *  Call the worker layer.
     */
    if ((rc = SEMQueryEventSem(semStripSigBits(hev),SSToDS(&ulPost))) == NO_ERROR )
      rc = TKSuDWord(ulPost,pulPostCt);

    return (rc);
}


/***EP  SEMApiCreateEventSem - Allocation to Create an Event Semaphore
 *
 *      SEMApiCreateEventSem is the allocation routine for the
 *      DosCreateEventSem 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 of Event, or NULL for anonymous Event
 *              phev    - ptr to receive event handle
 *              flAttr  - attributes:
 *                          DC_SEM_SHARED
 *              fState - TRUE  => execute implicit DosPostEventSem before
 *                                returning
 *                       FALSE => execute implicit DosResetEventSem before
 *                                returning
 *
 *      EXIT    Success - NO_ERROR
 *              Failure - ERROR_TOO_MANY_HANDLES
 *
 *      PSEUDO CODE
 *
 *      If (shared)                                // shared
 *        allocate shared event semaphore and open queue node
 *        rc = call semCreateEventSem with shared event 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 event semaphore
 *        rc = call semCreateEventSem with private event semaphore
 *        If (rc == error)
 *          set private table entry back to NULL
 *          decrement pPTDACur->ulPrTotUsed
 *          free private semaphore
 *        Endif
 *      Endif
 *      return (rc)
 */

ULONG SEMApiCreateEventSem(PSZ pszName,      /* semaphore name */
                           PHEV phev,        /* ptr to event handle */
                           ULONG flAttr,     /* attributes */
                           BOOL fState)      /* initial state of event */

{
    PSEVENT pSEvSem;            /* pointer to resident Shared Semaphore */
    PPEVENT pPEvSem;            /* 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 Event Semaphore.  Allocate appropriate items for
       *  a shared semaphore.
       */
      if ((rc = semSharedAlloc((PVOID*)(SSToDS(&pSEvSem)),sizeof(SEVENT),
                               SSToDS(&pOpenN),phev)) != NO_ERROR)
        return (rc);

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

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


/***EP  SEMApiOpenEventSem - Allocation to Open an Event Semaphore
 *
 *      SEMApiOpenEventSem is the allocation routine for the
 *      DosOpenEventSem 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
 *              phev    - if psz is NULL, phev contains the handle of an
 *                        anonymous event created by another process
 *              pptda   - pointer to PTDA to use; NULL for invalid pid
 *
 *      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 event 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 SEMApiOpenEventSem(PSZ pszName,   /* semaphore name */
                         PHEV phev,     /* ptr to event handle */
                         PPTDA pptda)   /* ptr to PTDA to use */

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


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

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

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

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



/*
 *   Device Helper C worker routines.
 */

/***EP  dhw_OpenEventSem - Dev Help Open an Event Semaphore
 *
 *      dhw_OpenEventSem is the worker for the dh_OpenEventSem routine.
 *      This routine is callable from C and is exported for use by the
 *      kernel.  This routine calls directly into the allocation/validation
 *      layer to open the event semaphore.  All allocations and validations
 *      are done in SEMApiOpenEventSem.
 *
 *      ENTRY   hev - handle to a shared event semaphore
 *
 *      EXIT    Success - NO_ERROR
 *              Failure - ERROR_INVALID_HANDLE
 *                        ERROR_OUT_OF_MEMORY
 *                        ERROR_TOO_MANY_OPENS
 *
 *      PSEUDO CODE
 *
 *      return ( call SEMApiOpenEventSem )
 */

ULONG dhw_OpenEventSem(HEV hev)         /* shared event handle */

{
    return ( SEMApiOpenEventSem(NULL,SSToDS(&hev),NULL) );
}


/***EP  dhw_CloseEventSem - Dev Help Close an Event Semaphore
 *
 *      dhw_CloseEventSem is the worker for the dh_CloseEventSem routine.
 *      This routine is callable from C and is exported for use by the
 *      kernel.  This routine calls directly into the allocation/validation
 *      layer to close the event semaphore.  All allocations and validations
 *      are done in SEMApiCloseEventSem.
 *
 *      ENTRY   hev - handle to a shared event semaphore
 *
 *      EXIT    Success - NO_ERROR
 *              Failure - ERROR_INVALID_HANDLE
 *
 *      PSEUDO CODE
 *
 *      return ( call SEMApiCloseEventSem )
 */

ULONG dhw_CloseEventSem(HEV hev)         /* shared event handle */

{

    /*
     *  Check validity of event handle.
     */
    if (semChkHdl(hev,DCE_EVENT_SEM,NULL) != NO_ERROR)
      return (ERROR_INVALID_HANDLE);

    return ( SEMCloseEventSem(semStripSigBits(hev),NULL) );
}


/***EP  dhw_PostEventSem - Dev Help Post an Event Semaphore
 *
 *      dhw_PostEventSem is the worker for the dh_PostEventSem routine.
 *      This routine is callable from C and is exported for use by the
 *      kernel.  This routine calls directly into the allocation/validation
 *      layer to post the event semaphore.  All allocations and validations
 *      are done in SEMApiPostEventSem.
 *
 *      ENTRY   hev - handle to a shared event semaphore
 *
 *      EXIT    Success - NO_ERROR
 *                        ERROR_ALREADY_POSTED
 *              Failure - ERROR_INVALID_HANDLE
 *                        ERROR_TOO_MANY_POSTS
 *
 *      PSEUDO CODE
 *
 *      return ( call SEMApiPostEventSem )
 */

ULONG dhw_PostEventSem(HEV hev)         /* shared event handle */

{

    /*
     *  Check validity of event handle.
     */
    if (semChkHdl(hev,DCE_EVENT_SEM,NULL) != NO_ERROR)
      return (ERROR_INVALID_HANDLE);

    return ( SEMPostEventSem(semStripSigBits(hev),NULL) );
}


/***EP  dhw_ResetEventSem - Dev Help Reset an Event Semaphore
 *
 *      dhw_ResetEventSem is the worker for the dh_ResetEventSem routine.
 *      This routine is callable from C and is exported for use by the
 *      kernel.  This routine calls directly into the allocation/validation
 *      layer to reset the event semaphore.  All allocations and validations
 *      are done in SEMApiResetEventSem.
 *
 *      ENTRY   hev       - handle to a shared event semaphore
 *              pulPostCt - pointer to receive the number of posts
 *                            performed on the event before the reset
 *
 *      EXIT    Success - NO_ERROR
 *              Failure - ERROR_ALREADY_RESET
 *                        ERROR_INVALID_HANDLE
 *
 *      PSEUDO CODE
 *
 *      return ( call SEMApiResetEventSem )
 */

ULONG dhw_ResetEventSem(HEV hev,            /* shared event handle */
                        PULONG pulPostCt)   /* ptr to receive # of posts */

{

    /*
     *  Check validity of event handle.
     */
    if (semChkHdl(hev,DCE_EVENT_SEM,NULL) != NO_ERROR)
      return (ERROR_INVALID_HANDLE);

    return ( SEMResetEventSem(semStripSigBits(hev),pulPostCt,NULL) );
}

/***LP  semCreateEventSem - Create an Event Semaphore
 *
 *      semCreateEventSem is the worker function for the DosCreateEventSem
 *      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 an event 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 event semaphore.  If the
 *      fState flag is set to false, then an implicit DosResetEventSem will be
 *      executed before returning.  This means that if a thread follows this
 *      call with a DosWaitEventSem, the thread will block.  If the fState
 *      flag is set to true, then an implicit DosPostEventSem will be
 *      executed.  For the same scenario outlined above, this means that the
 *      thread will be able to run.
 *
 *      ENTRY   pszName - asciiz name of Event, or NULL for anonymous Event
 *              phev    - ptr to receive event handle
 *              flAttr  - attributes:
 *                          DC_SEM_SHARED
 *              fState  - TRUE  => execute implicit DosPostEventSem before
 *                                 returning
 *                        FALSE => execute implicit DosResetEventSem before
 *                                 returning
 *              pEvSem  - pointer to an event semaphore
 *              pOpenN  - pointer to an Open Queue node
 *
 *      EXIT    Success - NO_ERROR
 *              Failure - none
 *
 *      PSEUDO CODE
 *
 *      If (shared)                                  // shared
 *        pEvSem->pMuxQ = NULL;                      // fill in structure
 *        pEvSem->pOpenQ = NULL;
 *        pEvSem->pszName = pszName;
 *        If (fState)                                // post event
 *          pEvSem->usFlags = (flAttr | DCE_EVENT_SEM | DE_POSTED)
 *        Else                                       // reset event
 *          pEvSem->usFlags = (flAttr | DCE_EVENT_SEM)
 *        Endif
 *        pShSemTbl[phev] = pEvSem
 *        phev |= SEM_SHAREDMASK
 *        increment ulShTotUsed
 *        return ( call semOpenSem with open queue )   // open event
 *      Else                                           // anonymous private
 *        pEvSem->pMuxQ = NULL                         // fill in structure
 *        pEvSem->usOpenCt = 0
 *        If (fState)                                  // post event
 *          pEvSem->usFlags = (flAttr | DCE_EVENT_SEM | DE_POSTED)
 *        Else                                         // reset event
 *          pEvSem->usFlags = (flAttr | DCE_EVENT_SEM)
 *        Endif
 *        pPTDACur->pPrSemTbl[phev] = pEvSem
 *        increment pPTDACur->ulPrTotUsed
 *        return ( call semOpenSem with open count )   // open event
 *      Endif
 */

ULONG semCreateEventSem(PSZ pszName,       /* semaphore name */
                        PHEV phev,         /* ptr to event handle */
                        ULONG flAttr,      /* attributes */
                        BOOL fState,       /* initial state of event */
                        register PEVTUN pEvSem, /* ptr to event semaphore */
                        POPENQ pOpenN)     /* ptr to Open Queue node */

{
    ULONG rc;                     /* return code */
    ULONG flPMSem = 0;            /* Creating PM Semaphore  98809*/

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

    if (flAttr & DC_SEM_SHARED) {
      /*
       *  Shared Event Semaphore.
       */
      tkAssert(pShSemTbl[*phev] == NULL);
      /*
       * Fill in shared semaphore structure.
       */
      pEvSem->ShEvt.pMuxQ = NULL;
      pEvSem->ShEvt.pOpenQ = NULL;
      pEvSem->ShEvt.pszName = pszName;

/******************************* PSEM Begin ******************************/
/*                                                                       */
/*  Check to see if the semaphore is being used as a PM Semaphore.       */
/*  If it is, then set the TCB address in the semaphore control block    */
/*  to 0, and mask that bit off from the flag fields before it gets      */
/*  saved in the Semaphore control block flag field                      */
/*                                                                       */
/******************************* PSEM Begin ******************************/

      if ( flAttr & DC_SEM_PM ) {
         pEvSem->ShEvt.ptcb = 0;
         flAttr &= ~DC_SEM_PM;
         flPMSem = 1;               //98809
      } /* endif */
/******************************* PSEM end  ********************************/

      if (fState) {                              /* post event */
        pEvSem->ShEvt.usFlags = (USHORT)(flAttr | DCE_EVENT_SEM | DE_POSTED);
        pEvSem->PrEvt.usPostCt = 1;
      }
      else {                                     /* reset event */
        pEvSem->ShEvt.usFlags = (USHORT)(flAttr | DCE_EVENT_SEM);
        pEvSem->PrEvt.usPostCt = 0;

      }

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

      /*
       *  Open event semaphore.
       */
      rc = semOpenSem(phev,pOpenN,&((PSHUN)pEvSem)->SEvt.pOpenQ,NULL,pPTDACur);

      /* If it's a PM Semaphore, indicate */         //98809
      if (flPMSem)                                   //98809
         {                                           //98809
         *phev |= SEM_PMSEMCALL;                     //98809
         }                                           //98809
    }
    else {
      /*
       *  Private Event Semaphore.
       */
      tkAssert(pPTDACur->pPrSemTbl[*phev] == NULL);
      /*
       *  Fill in private semaphore structure.
       */
      pEvSem->PrEvt.pMuxQ = NULL;
      pEvSem->PrEvt.usOpenCt = 0;
      if (fState) {                              /* post event */
        pEvSem->PrEvt.usFlags = (USHORT)(flAttr | DCE_EVENT_SEM | DE_POSTED);
        pEvSem->PrEvt.usPostCt = 1;
      }
      else {                                     /* reset event */
        pEvSem->PrEvt.usFlags = (USHORT)(flAttr | DCE_EVENT_SEM);
        pEvSem->PrEvt.usPostCt = 0;

        /***PV - Perfview measurement hook
         */
//b734380  if(fPerfview)    /* Is perfview up ? */                            //aa04504
//b734380    PvwStartSet((PULONG)(pEvSem->ShEvt.pPvwBlk));                    //b724626
      }

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

      /*
       *  Open event semaphore.
       */
      rc = semOpenSem(phev,NULL,NULL,&((PPRUN)pEvSem)->PEvt.usOpenCt,pPTDACur);
    }
    TKEndNoBlock();
    return (rc);
}


/***EP  SEMCloseEventSem - Close an Event Semaphore
 *
 *      SEMCloseEventSem is the worker function for the DosCloseEventSem
 *      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.
 *
 *      SEMCloseEventSem closes an event semaphore that was previously
 *      opened.  If this is the last reference to this event, then the event
 *      semaphore is destroyed.
 *
 *      The ERROR_SEM_BUSY return code will be returned if the thread is trying
 *      to make the final close of a semaphore that has another thread in the
 *      same process still waiting on it.
 *
 *      ENTRY   hev   - handle of event to close
 *              pptda - pointer to PTDA to use; NULL for invalid pid
 *
 *      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 ((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
 *          remove OpenQ node from OpenQ list
 *          put a 0 in appropriate bit in local bitmap
 *          If (OpenQ is empty)
 *            destroy semaphore (free memory)
 *            pShSemTbl[hev] = NULL
 *          Endif
 *        Endif
 *      Else                                     // private
 *        decrement usOpenCt
 *        If (usOpenCt == 0)
 *          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
 *          destroy semaphore (free memory)
 *          pPTDACur->pPrSemTbl[hev] = NULL
 *        Endif
 *      Endif
 *      return (NO_ERROR)
 */

ULONG SEMCloseEventSem(HEV hev,       /* event handle */
                       PPTDA pptda)   /* ptr to PTDA to use */

{
    register ULONG ulIndex;     /* array index */
    POPENQ pCurOpenN;           /* ptr to OpenQ node for current pid */
    POPENQ pPreOpenN;           /* ptr to previous OpenQ node */
    register PSEM pEvt;         /* ptr to the event semaphore */
    PID pid;                    /* process id to use */


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

    ulIndex = semGetIndex(hev);
    if (semIfShared(hev)) {
      /*
       *  Shared Event Semaphore.
       */
       // 98809 Changed ulIndex to hev in line below
      tkAssert(semChkSharedHdl(hev,DCE_EVENT_SEM,pptda) == NO_ERROR);
      pEvt = pShSemTbl[ulIndex];

      /*
       *  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);
        pid = pptda->ptda_Pid;
      }
      else {
        pid = SEM_INVALID_PID;
      }

      /*
       *  Get open queue node for the process.
       */
      semGetOpenQNode(((PSHUN)pEvt)->SEvt.pOpenQ,SSToDS(&pCurOpenN),
                      SSToDS(&pPreOpenN),pid);
      tkAssert(pCurOpenN != NULL);
      if ((--(pCurOpenN->usOpenCt)) == 0) {
        /*
         *  About to make final close of semaphore for given process.
         *  Must make sure there are no threads in the given process
         *  waiting on this semaphore (if not special pid).
         */
        if ((pid != SEM_INVALID_PID) &&
            ((semSrchSnglForPid(pEvt) == NO_ERROR) ||
             (semSrchMuxForPid(pEvt) == NO_ERROR))) {
          (pCurOpenN->usOpenCt)++;
          TKEndNoBlock();
          return (ERROR_SEM_BUSY);
        }
        /*
         *  Delete open queue from semaphore's open queue list.
         *  Zero out the appropriate bit in the per process bitmap.
         *  The open queue node must be freed at the end of the
         *  routine (frees can block).
         */
        semDelFromOpenQ(pCurOpenN,pPreOpenN,&((PSHUN)pEvt)->SEvt.pOpenQ);
        if (pptda != NULL)
          semZeroBit(ulIndex,pptda->pShSemBmp);
        else
          ((PSHUN)pEvt)->SEvt.usFlags &= ~DHO_SEM_OPEN;
        if (((PSHUN)pEvt)->SEvt.pOpenQ == NULL) {
          /*
           *  No more opens left for this semaphore, so remove it
           *  from the semaphore table, decrement the total number of
           *  shared semaphores used, and free the structure.
           */

          semAssertSEvtSig(pEvt);
          semSharedClose(pEvt,ulIndex,((PSHUN)pEvt)->SEvt.pszName);
        }
        /*
         *  Free the open queue node.
         */
        TKEndNoBlock();
        semAssertOpenQSig(pCurOpenN);
        BMPFree(pBMPOpenQSem, pCurOpenN); // PERF: 98488
        TKBeginNoBlock();
      }
    }
    else {
      /*
       *  Private Event Semaphore.
       */
      tkAssert(pptda == pPTDACur);
      tkAssert(semChkPrivateHdl(ulIndex,DCE_EVENT_SEM) == NO_ERROR);
      pEvt = pPTDACur->pPrSemTbl[ulIndex];
      if ((--(((PPRUN)pEvt)->PEvt.usOpenCt)) == 0) {
        /*
         *  About to make final close of semaphore.
         *  Must make sure there are no threads waiting on this semaphore.
         */
        if ((semSrchSnglForPid(pEvt) == NO_ERROR) ||
            (semSrchMuxForPid(pEvt) == NO_ERROR)) {
          (((PPRUN)pEvt)->PEvt.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.
         */
        semAssertPEvtSig(pEvt);
        semPrivateClose(pEvt,ulIndex);
      }
    }
    TKEndNoBlock();
    return (NO_ERROR);
}


/***EP  SEMResetEventSem - Reset an Event Semaphore
 *
 *      SEMResetEventSem is the worker function for the DosResetEventSem
 *      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.
 *
 *      SEMResetEventSem resets an event semaphore that has been previously
 *      created and opened.  The number of posts performed on the event
 *      before it was reset is returned to the caller in pulPostCt.  If the
 *      event was already reset, the ERROR_ALREADY_RESET return code is
 *      returned to the calling thread.  It is not reset a second time.
 *
 *      ENTRY   hev       - handle of event to reset
 *              pulPostCt - pointer to receive current number of posts
 *              pptda     - pointer to PTDA to use
 *
 *      EXIT    Success - NO_ERROR
 *                        ERROR_ALREADY_RESET
 *              Failure - none
 *
 *      PSEUDO CODE
 *
 *      If (shared)                                     // shared
 *        pEvt = pShSemTbl[hev]
 *      Else                                            // private
 *        pEvt = pPTDACur->pPrSemTbl[hev]
 *      Endif
 *
 *      *pulPostCt = pEvt->usPostCt                     // number of posts
 *      If (reset)
 *        return (ERROR_ALREADY_RESET)
 *      Endif
 *      pEvt->usFlags &= SEM_RESET_EV                   // reset event
 *      return (NO_ERROR)
 */

ULONG SEMResetEventSem(HEV hev,            /* event handle */
                       PULONG pulPostCt,   /* ptr to number of posts */
                       PPTDA pptda)        /* ptr to PTDA to use */

{
    register PSEM pEvt;             /* pointer to the event */
    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(hev);
    if (semIfShared(hev)) {
      /*
       *  Shared Event Semaphore.
       */
       // 98809 Changed ulIndex to hev in line below
      tkAssert(semChkSharedHdl(hev,DCE_EVENT_SEM,pptda) == NO_ERROR);
      pEvt = pShSemTbl[ulIndex];
    }
    else {
      /*
       *  Private Event Semaphore.
       */
      tkAssert(pptda == pPTDACur);
      tkAssert(semChkPrivateHdl(ulIndex,DCE_EVENT_SEM) == NO_ERROR);
      pEvt = pPTDACur->pPrSemTbl[ulIndex];
    }

    /*
     *  Check if already reset.
     */
    *pulPostCt = (ULONG)((PSHUN)pEvt)->SEvt.usPostCt;
    if (!(((PSHUN)pEvt)->SEvt.usFlags & DE_POSTED)) {
      tkAssert(*pulPostCt == 0);
      TKEndNoBlock();
      return (ERROR_ALREADY_RESET);
    }
    /*
     *  Mark the event as being reset and set the post count to 0.
     */
    ((PSHUN)pEvt)->SEvt.usFlags &= SEM_RESET_EV;
    ((PSHUN)pEvt)->SEvt.usPostCt = 0;

    TKEndNoBlock();
    return (NO_ERROR);
}


/***EP  SEMPostEventSem - Post an Event Semaphore
 *
 *      SEMPostEventSem is the worker function for the DosPostEventSem
 *      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.
 *
 *      SEMPostEventSem posts an event semaphore that was previously reset.
 *      If the event is already posted, the post count is incremented and
 *      the ERROR_ALREADY_POSTED return code is returned to the calling
 *      thread.  It is not posted a second time.  Otherwise, the event
 *      state is posted, the post count is incremented, and all waiting
 *      threads are made runnable.
 *
 *      ENTRY   hev   - handle of event to post
 *              pptda - pointer to PTDA to use
 *
 *      EXIT    Success - NO_ERROR
 *                        ERROR_ALREADY_POSTED
 *              Failure - ERROR_TOO_MANY_POSTS
 *
 *      PSEUDO CODE
 *
 *      If (shared)                                        // shared
 *        pEvt = pShSemTbl[hev]
 *      Else                                               // private
 *        pEvt = pPTDACur->pPrSemTbl[hev]
 *      Endif
 *      If (posted)
 *        If (post count == 64K-1)
 *          return (ERROR_TOO_MANY_POSTS)
 *        Endif
 *        increment post count
 *        return (ERROR_ALREADY_POSTED)
 *      Endif
 *      pEvt->usFlags |= DE_POSTED                         // post event
 *      increment post count
 *      run all single waiters and any muxwait waiters that are ready
 *      return (NO_ERROR)
 */

ULONG SEMPostEventSem(HEV hev,         /* event handle */
                      PPTDA pptda)     /* ptr to PTDA to use */

{
    register PSEM pEvt;         /* pointer to the event */
    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(hev);
    if (semIfShared(hev)) {
      /*
       *  Shared Event Semaphore.
       */
       // 98809 Changed ulIndex to hev in line below
      tkAssert(semChkSharedHdl(hev,DCE_EVENT_SEM,pptda) == NO_ERROR);
      pEvt = pShSemTbl[ulIndex];
    }
    else {
      /*
       *  Private Event Semaphore.
       */
      tkAssert(semChkPrivateHdl(ulIndex,DCE_EVENT_SEM) == NO_ERROR);
      pEvt = pPTDACur->pPrSemTbl[ulIndex];
    }

    /*
     *  Check if already posted and if too many posts.
     */
    if (((PSHUN)pEvt)->SEvt.usFlags & DE_POSTED) {
      tkAssert(((PSHUN)pEvt)->SEvt.usPostCt > 0);
      if (((PSHUN)pEvt)->SEvt.usPostCt == SEM_MAXPOSTS) {
        TKEndNoBlock();
        return (ERROR_TOO_MANY_POSTS);
      }
      /*
       *  Increment the post count.
       */
      ((PSHUN)pEvt)->SEvt.usPostCt++;
      TKEndNoBlock();
      return (ERROR_ALREADY_POSTED);
    }

    /*
     *  Mark the event as being posted and set the post count to 1.
     */
    tkAssert(((PSHUN)pEvt)->SEvt.usPostCt == 0);
    ((PSHUN)pEvt)->SEvt.usFlags |= DE_POSTED;
    ((PSHUN)pEvt)->SEvt.usPostCt = 1;

    /*
     *  Wake up all single waiters and appropriate mux waiters.
     */
    TKWakeup(pEvt,SEM_WAKE_ALL,NULL);
    if (((PSHUN)pEvt)->SEvt.pMuxQ != NULL)
      semPostEvtInMux(pEvt,hev);

    /*
     *  Check 16-bit muxwaits and wake up appropriate waiters.
     */
    if (((PSHUN)pEvt)->SEvt.usFlags & DE_16BIT_MW) {
      ((PSHUN)pEvt)->SEvt.usFlags &= ~DE_16BIT_MW;
      semEventWakeMuxWait(semAddSigBits(hev));
    }
    TKEndNoBlock();
    return (NO_ERROR);
}


/***EP  SEMWaitEventSem - Wait on an Event Semaphore
 *
 *      SEMWaitEventSem is the worker function for the DosWaitEventSem
 *      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.
 *
 *      SEMWaitEventSem allows a thread to wait on an event semaphore that
 *      was previously created and opened.      When an event semaphore is
 *      reset, any thread that calls "wait" will be blocked.  When the event
 *      semaphore is posted, all threads waiting on the event are made
 *      runnable.  If the semaphore is posted when "wait" is called, the
 *      thread will continue to run and the routine will return immediately.
 *
 *      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,
 *      SEMWaitEventSem will return without blocking.  If a timeout of -1
 *      is passed in, it will block indefinitely.
 *
 *      ENTRY   hev       - handle of event to wait upon for posting
 *              ulTimeout - timeout in milliseconds
 *
 *      EXIT    Success - NO_ERROR
 *              Failure - ERROR_INTERRUPT
 *                        ERROR_TIMEOUT
 *
 *      PSEUDO CODE
 *
 *      If (shared)                             // shared
 *        pEvt = pShSemTbl[hev]
 *      Else                                    // private
 *        pEvt = pPTDACur->pPrSemTbl[hev]
 *      Endif
 *      If (reset)
 *        If (ulTimeout == SEM_IMMEDIATE_RETURN)
 *          return (ERROR_TIMEOUT)
 *        Endif
 *        return ( call TKSleep on event )
 *      Endif
 *      return (NO_ERROR)
 */

ULONG SEMWaitEventSem(HEV hev,              /* event handle */
                      ULONG ulTimeout)      /* timeout in milliseconds */

{
    ULONG ulIndex;              /* array index */
    register PSEM pEvt;         /* pointer to the event semaphore */
    ULONG rc;                   /* Return code */


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

    /*
     *  Get pointer to correct table, shared or private.
     */
    ulIndex = semGetIndex(hev);
    if (semIfShared(hev)) {
      /*
       *  Shared Event Semaphore.
       */
       // 98809 Changed ulIndex to hev in line below
      tkAssert(semChkSharedHdl(hev,DCE_EVENT_SEM,pPTDACur) == NO_ERROR);
      pEvt = pShSemTbl[ulIndex];
    }
    else {
      /*
       *  Private Event Semaphore.
       */
      tkAssert(semChkPrivateHdl(ulIndex,DCE_EVENT_SEM) == NO_ERROR);
      pEvt = pPTDACur->pPrSemTbl[ulIndex];
    }

    if (!(((PSHUN)pEvt)->SEvt.usFlags & DE_POSTED)) {
      /*
       *  Semaphore is reset.  Return immediately if timeout is 0.
       *  Otherwise, block the thread.
       */
      if (ulTimeout == SEM_IMMEDIATE_RETURN) {
        TKEndNoBlock();
        return (ERROR_TIMEOUT);
      }

#ifdef PMDEBUG
      pTCBCur->TCB_SemInfo = semAddSigBits(hev);
      if (semIfShared(hev))
         pTCBCur->TCB_SemDebugAddr = (ULONG)((PSEVENT)pEvt)->pulCreatAddr;
      else
         pTCBCur->TCB_SemDebugAddr = (ULONG)((PPEVENT)pEvt)->pulCreatAddr;
#endif

      TKEndNoBlock();                            /* allow block for sleep */

      /***PV - Perfview measurement hook
       */


      rc = TKSleep(pEvt,ulTimeout,0,WAKEUP_SINGLE);

      /***PV - Perfview measurement hook
       */


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

      return (rc);
    }
    TKEndNoBlock();
    return (NO_ERROR);
}


/***EP  SEMQueryEventSem - Query an Event Semaphore
 *
 *      SEMQueryEventSem is the worker function for the DosQueryEventSem
 *      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.
 *
 *      SEMQueryEventSem will query information about a given event
 *      semaphore if the semaphore was previously opened by the process.
 *      If the event semaphore exists, the number of posts performed on
 *      the event is obtained and returned to the caller in the pulPostCt
 *      parameter.
 *
 *      ENTRY   hev       - handle of event to query
 *              pulPostCt - pointer to receive current number of posts
 *
 *      EXIT    Success - NO_ERROR
 *              Failure - none
 *
 *      PSEUDO CODE
 *
 *      pulPostCt = number of posts
 *      return (NO_ERROR)
 */

ULONG SEMQueryEventSem(HEV hev,             /* event handle */
                       PULONG pulPostCt)    /* ptr to number of posts */

{
    ULONG ulIndex;              /* array index */


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


    ulIndex = semGetIndex(hev);
    if (semIfShared(hev)) {
      /*
       *  Shared Event Semaphore.  Return post count.
       */
       // 98809 Changed ulIndex to hev in line below
      tkAssert(semChkSharedHdl(hev,DCE_EVENT_SEM,pPTDACur) == NO_ERROR);
      *pulPostCt = (ULONG)((PSHUN)pShSemTbl[ulIndex])->SEvt.usPostCt;
    }
    else {
      /*
       *  Private Event Semaphore.  Return post count.
       */
      tkAssert(semChkPrivateHdl(ulIndex,DCE_EVENT_SEM) == NO_ERROR);
      *pulPostCt = (ULONG)((PPRUN)pPTDACur->pPrSemTbl[ulIndex])->PEvt.usPostCt;
    }
    TKEndNoBlock();
    return (NO_ERROR);
}
