/*static char *SCCSID = "@(#)timapi.c   6.4 91/11/05";*/
#define SCCSID  "@(#)timapi.c   6.4 91/11/05"
/***    TIMAPI.C
 *
 *      IBM/Microsoft Confidential
 *
 *      Copyright (c) IBM Corporation 1987, 1989
 *      Copyright (c) Microsoft Corporation 1987, 1989
 *
 *      All Rights Reserved
 *
 *      Title:
 *
 *      Timer Dos APIs
 *
 *      Description:
 *
 *      This file contains worker routines for:
 *
 *      DOSTimerStart       ==>     VR32TimerStart
 *      DOSTimerStop        ==>     VR32TimerStop
 *      DOSTimerAsync       ==>     VR32TimerAsync
 *
 *      VR32ITimerStart and VR32ITimerAsync are meant for V1.*
 *      timer APIs which allow RAM semaphores.
 *
 *      Modification History:
 *
 *      Created: Sudeep Bharati      01/20/90
 */

#define     INCL_TYPES                      // for basedef.h typedefs

#include    <basedef.h>
#define     FNO_NUM     FNO_TIMAPI_C
#include    <fnotask.h>
#include    <osmac.h>
#include    <osconst.h>
#include    <ostypes.h>
#include    <infoseg.h>
#include    <mi.h>
#include    <dossymc.h>
#include    <proc.h>
#include    <error.h>
#include    <error2.h>
#include    <sigdef.h>
#include    <except.h>
#include    <seldesc.h>
#include    <pgexport.h>
#include    <vmexport.h>
#include    <vmowner.h>
#include    <tkexport.h>
#include    <sched.h>
#include    <sem.h>
#include    <screen.h>
#include    <curdir.h>
#include    <dirent.h>
#include    <find.h>
#include    <tkptda.h>
#include    <kmode.h>
#include    <bsetib.h>
#include    <tklocal.h>
#include    <semxport.h>
#include    <kutil.h>
#include    <tom.h>
#include    <timapi.h>

/***    In brief timer API services are implemented as follows.
 *      DOSTIMER is the structure which represents a timer entity.
 *      When ever a timer is armed this structure is allcated and
 *      passed to Time out manager to service this timer request.
 *      All the active timers of same process are kept in a linked
 *      list in the ptda attached to the  "ptda_timerhead" head.
 *      In this structure timer properties like OldSemaphore versus
 *      new semaphore usage and one shot timer versus periodic
 *      timer are also stored. When a timer gets fired from Time
 *      out manager "TIMTomClear" routine gets control at interrupt
 *      time with interrupts enabled. New semaphores can not be cleared
 *      at interrupt time so this routine puts this timer entity on a list
 *      and hooks a global context hook. When the control is obtained
 *      at task time in TIMContextHook it traverses this list of
 *      fired timers and clears each semaphore. Here it may be an issue
 *      that in case of lengthy list, performance may be hit badly.
 *      On an average system there are so few timers fired at a time
 *      that this should not be any problem. But it saves us lot of
 *      resident memory which may be required for context hooks if
 *      allocated one per timer or one per PTDA or one per thread.
 *
 *      If DosStopTimer in its processing finds that timer is already
 *      fired but context hook has not yet got the control, it marks
 *      the timer as delinked so later when context hook gets control
 *      it will wake all these threads waiting for stopping the timer.
 *      So timer expiration takes precedence over StopTimer in case of
 *      a race condition.
 *
 *      HTIMER is USHORT so we can not pass the DOSTIMER address which
 *      is a 32 bit address. So we keep a global counter to get a free
 *      handle number. These handles are stored in bit array for quick
 *      access and freeing operations.
 *
 *      Only important point about race conditions is the pFiredTimerQueue,
 *      which is manipulated both at task and interrupt time. Rest
 *      all data structures are handled at task time and are resident,
 *      so there is no problem.
 */

/***    htimhook - hook handle for timer context hooking.
 *
 *      TOM calls timer services routine at interrupt time when a
 *      timer expires. We delay the processing till the task time
 *      of the thread.
 */

PVOID   htimhook;
ULONG   flArmed = 0;

/***    pFierdTimerQueue - queue on which fired timers are placed
 *
 *      This is the head of a linked list on which fired timers
 *      are kept to be processed in Context hook.
 */

dostimer_t  *pFiredTimerQueue = NULL;

/***    ahtimer - bit array to allocate free timer handles
 *
 *      The system keeps MIN (TMR_MINDEFAULT,maximum threads allowed
 *      in system). We keep a bit array to keep track of whch timer
 *      handles are in use and which are free for reuse. Please note
 *      in old 16 bit world it was possible to return the offset of
 *      timer entry address as htimer to the caller but in 32 bit
 *      world  we can not return a 16 bit address. These timers
 *      are to replace those old 16 bit entities.
 */
char    *ahtimer;
ULONG   ntimers;



/***EP+ VR32StartTimer - Start a timer
 *
 *      Verification routine for DOSTimerStart
 *
 *      ENTRY
 *          msInterval  -   timer duration
 *          hsem        -   Semaphore Handle
 *          phtimer     -   Pointer to timer handle
 *
 *      EXIT
 *          SUCCESS - returns 0 with phtimer filled with htimer
 *          FAILURE - returns
 *                      ERROR_TS_NOTIMER
 *                      ERROR_TS_SEMHANDLE
 *
 *      PCODE
 *          call SemFindType
 *          if error  return invalid semaphore
 *          if return type not event and system semaphore return error
 *          call rc = dhw_OpenEventSem(hsem) [DCR MS2036]
 *          if call failed
 *             return error to caller
 *          endif
 *          call rc = timArmtimer(msInterval,hsem,phtimer,32bitsem|periodic)
 *          if call failed
 *              call dhw_CloseEventSem(hsem) [DCR MS2036]
 *          return rc
 */

int cdecl
VR32StartTimer (sef,msInterval,hsem,phtimer)
THUNKSEF    sef;
ULONG       msInterval;
ULONG       *hsem;
PHTIMER     phtimer;
/*end*/
{
ULONG ulType, rc;

    // Validate that hsem is event semaphore
    if ((ulType = SEMFindType((HEV)hsem)) == 0)
        return ERROR_TS_SEMHANDLE;      // Not a valid 32 bit sem.

    if (!(ulType & DCE_EVENT_SEM && ulType & DC_SEM_SHARED))
        return ERROR_TS_SEMHANDLE;      // Not event sem or not a system one

    // Use devhlp OpenEventSem to allow post to this sem outside the
    // context of current process.  (DCR MS2036)
    if ((rc = dhw_OpenEventSem((HEV) hsem)) != NO_ERROR)
        return rc;

    // Arm the timer
    if (rc = timArmTimer (msInterval,       // timer interval
                         hsem,              // tested event semaphore
                         phtimer,           // return place for timer handle
                         NULL))             // Periodic and new semaphores
        (void) dhw_CloseEventSem((HEV) hsem); // If error, close event sem
    return rc;
}

/***EP+ VR32StopTimer - Stop a timer
 *
 *      Verification routine for DOSTimerStop
 *
 *      ENTRY
 *              htimer  -   Timer handle
 *
 *      EXIT
 *          SUCCESS - returns 0
 *          FAILURE - returns
 *                      ERROR_TS_HANDLE
 *
 *      PCODE
 *          return rc = timDisarmTimer (htimer)
 */

int cdecl
VR32StopTimer(sef,htimer)
THUNKSEF    sef;
HTIMER      htimer;
/*end*/
{
    if (htimer >= ntimers)
        return ERROR_TS_HANDLE;
    return (timDisarmTimer (htimer));
}

/***EP+ VR32AsyncTimer - Start a Asynchronous timer
 *
 *      Verification routine for DOSTimerAsync
 *
 *      ENTRY
 *          msInterval  -   timer duration
 *          hsem        -   Semaphore Handle
 *          phtimer     -   Pointer to timer handle
 *
 *      EXIT
 *          SUCCESS - returns 0 with phtimer filled with htimer
 *          FAILURE - returns
 *                      ERROR_TS_NOTIMER
 *                      ERROR_TS_SEMHANDLE
 *
 *      PCODE
 *          call SemFindType
 *          if error  return invalid semaphore
 *          if return type not event and system semaphore return error
 *          call rc = dhw_OpenEventSem(hsem) [DCR MS2036]
 *          if call failed
 *             return error to caller
 *          endif
 *          call rc = timArmtimer(msInterval,hsem,phtimer,32bitsem|oneshot)
 *          if call failed
 *              call dhw_CloseEventSem(hsem) [DCR MS2036]
 *          return rc
 *
 * N.B.  Prior to DCR MS2036, this routine only checked that the semaphore
 *       was an event semaphore.  Now we also force the semaphore to be
 *       shared.
 *
 */

int cdecl
VR32AsyncTimer(sef,msInterval,hsem,phtimer)
THUNKSEF    sef;
ULONG       msInterval;
ULONG       *hsem;
PHTIMER     phtimer;
/*end*/
{
ULONG ulType, rc;

    // Validate that hsem is event semaphore
    if ((ulType = SEMFindType((HEV)hsem)) == 0)
        return ERROR_TS_SEMHANDLE;      // Not a valid 32 bit sem.

    if (!(ulType & DCE_EVENT_SEM && ulType & DC_SEM_SHARED))
        return ERROR_TS_SEMHANDLE;      // Not event sem or not a system one

    // Use devhlp OpenEventSem to allow post to this sem outside the
    // context of current process.  (DCR MS2036)
    if ((rc = dhw_OpenEventSem((HEV) hsem)) != NO_ERROR)
        return rc;

    // Arm the timer
    if (rc = timArmTimer (msInterval,       // timer interval
                         hsem,              // tested event semaphore
                         phtimer,           // return place for timer handle
                         TMR_ONESHOT))      // One shot and new semaphores
        (void) dhw_CloseEventSem((HEV) hsem); // if error, close event sem
    return rc;
}

/***EP+ VR32IStartTimer - Internal DosStartTimer to support V1.* semaphores
 *
 *      Verification routine for DOSTimerStart
 *
 *      ENTRY
 *          msInterval  -   timer duration
 *          hsem        -   Semaphore Handle
 *          phtimer     -   Pointer to timer handle
 *
 *      EXIT
 *          SUCCESS - returns 0 with phtimer filled with htimer
 *          FAILURE - returns
 *                      ERROR_TS_NOTIMER
 *                      ERROR_TS_SEMHANDLE
 *
 *      PCODE
 *          Validate hsem to be system semaphore
 *          If invalid sem return error
 *          call rc = timArmtimer(msInterval,hsem,phtimer,16bitsem|periodic)
 *          if failed decrement the semaphore ref. count
 *          return rc
 *
 */

int cdecl
VR32IStartTimer(sef,msInterval,hsem,phtimer)
THUNKSEF    sef;
ULONG       msInterval;
ULONG       *hsem;
PHTIMER     phtimer;
/*end*/
{
ULONG  rc;
    // Validate that semaphore pssed is system semaphore
    // if succeed will increment ref count of the semaphore
    if((hsem = (PULONG)TIMSEMHANDLE(hsem,1)) == 0)
        return ERROR_TS_SEMHANDLE;

    //Arm the timer
    if ((rc = timArmTimer(msInterval,    // timer interval
                          hsem,          // tested system semaphore
                          phtimer,       // return place for timer handle
                          TMR_OLDSEM)))  // periodic timer and old semaphores

        TIMSEMHANDLE(hsem,0);            // decrement sem ref count
    return rc;
}

/***EP+ VR32IAsyncTimer - Internal DosTimerAsync for V1.* semaphores
 *
 *      Verification routine for DOSTimerAsync
 *
 *      ENTRY
 *          msInterval  -   timer duration
 *          hsem        -   Semaphore Handle
 *          phtimer     -   Pointer to timer handle
 *
 *      EXIT
 *          SUCCESS - returns 0 with phtimer filled with htimer
 *          FAILURE - returns
 *                      ERROR_TS_NOTIMER
 *                      ERROR_TS_SEMHANDLE
 *
 *      PCODE
 *          Validate hsem to be system semaphore
 *          If not return error
 *          call rc = timArmtimer(msInterval,hsem,phtimer,16bitsem|oneshot)
 *          if failed decrement the semaphore ref. count
 *          return rc
 */

int cdecl
VR32IAsyncTimer (sef,msInterval,hsem,phtimer)
THUNKSEF    sef;
ULONG       msInterval;
ULONG       *hsem;
PHTIMER     phtimer;
/*end*/
{
ULONG rc;
    // Validate that semaphore pssed is system semaphore
    // if succeed will increment ref count of the semaphore
    if((hsem = (PULONG)TIMSEMHANDLE(hsem,1)) == 0)
        return ERROR_TS_SEMHANDLE;

    // Validate address to receive timer handle
    if(TKSuWordNF(NA, phtimer) != 0)
        return(ERROR_INVALID_PARAMETER);

    //Arm the timer
    if ((rc = timArmTimer(msInterval,    // timer interval
                          hsem,          // tested system semaphore
                          phtimer,       // return place for timer handle
            TMR_ONESHOT | TMR_OLDSEM)))  // One shot timer and old semaphores

        TIMSEMHANDLE(hsem,0);            // decrement sem ref count
    return rc;
}


/***LP+ timArmTimer - Arm a timer
 *
 *  ENTRY
 *          msInterval  -   timer duration
 *          hsem        -   semaphore Handle
 *          phtimer     -   pointer to timer handle
 *          ulflags     -   timer flags
 *
 *  EXIT
 *          SUCCESS - returns 0
 *          FAILURE - returns error code
 *
 *  PCODE
 *          get a free timer handle
 *              if not available return ERROR_TS_NOHANDLE
 *          else
 *              Allocate timer structure
 *              if failed
 *                  return ERROR_TS_NOTIMER
 *              if msInterval == 0
 *                  msInterval++
 *              initialize timer fields
 *              call timLinkTimer to link timer in ptda
 *              call TomSet
 *              return 0
 */

ULONG timArmTimer (msInterval,hsem,phtimer,ulflags)
ULONG       msInterval;
ULONG       *hsem;
PHTIMER     phtimer;
ULONG       ulflags;
{
    ULONG         flags;
    dostimer_t    *pdostimer;
    USHORT        htimer;
    toe_worker_t  worker;


    // Get a free timer handle
    if ((htimer = (USHORT)TIMGSETHTIMER (TMR_GET,0)) >= ntimers)
        return ERROR_TS_NOTIMER;

    // Allocate timer structure from the resident heap.
    if (allocKRHB(sizeof(dostimer_t),
                         TIMEROWNER,
                         SSToDS(&pdostimer)) != NO_ERROR) {
        TIMGSETHTIMER (TMR_FREE, (ULONG)htimer);
        return ERROR_TS_NOTIMER;
    }

    // Adjust time interval if required  (BUGBUG -- shouldn't be required)
    if (!msInterval)
        msInterval = 1;


    worker = TOM_W_TIMCLEAR;  // timer clears a semaphore upon expiration
    flags = ulflags;

    // If the current thread is a fast dispatch thread (FDT), designate
    // timer to be a high resolution timer.  DCR 1315 has determined that
    // all class time critical threads are FDTs.   (see task\sutil.asm)
    // A global task time context hook resets periodic timers.  This context
    // hook needs to know whether the timer is hi resolution for the reset.

    if (pTCBCur->TCBPriClass == CLASS_TIME_CRITICAL) {
        worker |= TOM_HIRES_FLAG;  // Pass hi res status to TOMSet
        flags |= TMR_HIRES;        // Retain hi res status for future resets
    }

    // Initialize timer
    pdostimer->tmr_htimer  = htimer;
    pdostimer->tmr_hsem    = hsem;
    pdostimer->tmr_mstime  = msInterval;
    pdostimer->tmr_fl      = (USHORT)flags;
    pdostimer->tmr_pptda   = pPTDACur;
    pdostimer->tmr_fired   = NULL;

    // Link the timer in PTDA
    timLinkTimer (pdostimer);

    // Arm the timer with TOM
    TOMSet(&pdostimer->tmr_tom,msInterval,worker);

    if(phtimer != NULL) {                                   // 71546
    *phtimer = htimer;}                                     // 71546
    return 0;
}

/***LP+ timDisarmTimer - Disarm a timer
 *
 *  ENTRY
 *          htimer - timer handle
 *
 *  EXIT
 *          SUCCESS - returns 0
 *          FAILURE - returns error code
 *
 *  PCODE
 *  loop:
 *          Query the timer on PTDA list
 *          if not found
 *              return ERROR_TS_HANDLE
 *          disable interrupts
 *          if expired flag is on
 *              Mark unlinked
 *              enable interrpts
 *              go to sleep
 *              go to loop
 *          call TOMCancel
 *          enable interrupts
 *          call timUnlinkTimer to remove timer from ptda list
 *          free timer structure
 *          return 0
 */

ULONG timDisarmTimer (htimer)
HTIMER  htimer;
{
ULONG       flags;
dostimer_t  *pdostimer;

    while (1){

        // Query the timer in PTDA list
        if (timQueryTimer (htimer,pPTDACur,SSToDS(&pdostimer))){
            return ERROR_TS_HANDLE;
        }

        flags = TKDisable ();

        if (pdostimer->tmr_fl & TMR_EXPIRED){
            pdostimer->tmr_fl |= TMR_UNLINKING;
            TKEnable (flags);
            TKSleep(pdostimer,-1,1,0);
            continue;
        }
        else{
            // Assertion check that timer is not on fired list
            TIMASSERT (pdostimer->tmr_fired == NULL);

            // Cancel the timer in TOM
            TOMCancel (&pdostimer->tmr_tom);
            TKEnable (flags);
        }

        // Unlink the timer in PTDA
        timUnlinkTimer (pdostimer);

        // Free the timer handle in bit array
        TIMGSETHTIMER (TMR_FREE, pdostimer->tmr_htimer);

        // if 16-bit semaphore, decrement sem ref count
        // else close the shared event sem
        if(pdostimer->tmr_fl & TMR_OLDSEM)
            TIMSEMHANDLE(pdostimer->tmr_hsem,0);
        else
        {
                // It is a 32-bit shared event sem, so call devhlp
                // CloseEventSem.  This is the corresponding close to the
                // devhlp OpenEventSem performed in
                // VR32StartTimer()/VR32AsyncTimer(), for DCR MS2036.
#ifdef MISCSTRICT
                TIMASSERT(dhw_CloseEventSem((HEV) (pdostimer->tmr_hsem))
                                == NO_ERROR);
#else
                dhw_CloseEventSem((HEV) (pdostimer->tmr_hsem));
#endif
        }

        // release the timer structure
        freeKRHB (pdostimer);

        return 0;
    }
}

/***LP+ timLinkTimer - Link a timer to ptda list
 *
 *  ENTRY
 *          pdostimer - timer to be linked
 *
 *  EXIT
 *          timer handle
 *
 *  PCODE
 *          if reusing of timer handles not in operation
 *              grab next htimer by incrementing the counter
 *              place it at the top of list
 *              return
 *          else
 *              search for highest free timer slot
 *              arm the timer at the correct place such that
 *              list remains sorted in descending order.
 *              return timer
 *
 */

VOID timLinkTimer (pdostimer)
dostimer_t *pdostimer;
{
dostimer_t      *ptimercur,*pth;

    pth = pdostimer->tmr_pptda->ptda_timerhead;

    // timer being placed at head of list
    pdostimer->tmr_next = pth;
    pdostimer->tmr_prev = NULL;
    if (pth)
        pth->tmr_prev = pdostimer;
    pdostimer->tmr_pptda->ptda_timerhead = pdostimer;

    return;
}

/***LP+ timUnlinkTimer - Unlink a timer in ptda list
 *
 *  ENTRY
 *          pdostimer - timer to be unlinked
 *
 *  EXIT
 *          None
 *
 *  PCODE
 *          delete timer from list
 */

VOID timUnlinkTimer (pdostimer)
dostimer_t      *pdostimer;
{

    // delink the timer from the head
    if (pdostimer->tmr_prev == NULL){
        pdostimer->tmr_pptda->ptda_timerhead = pdostimer->tmr_next;
        if (pdostimer->tmr_next)
            pdostimer->tmr_next->tmr_prev = NULL;
    }
    // delink the timer from middle
    else if (pdostimer->tmr_next){
        pdostimer->tmr_next->tmr_prev = pdostimer->tmr_prev;
        pdostimer->tmr_prev->tmr_next = pdostimer->tmr_next;
    }
    // delink the timer from tail
    else
        pdostimer->tmr_prev->tmr_next = NULL;
    return;
}


/***LP+ timQueryTimer - Query a timer in PTDA list given the htimer
 *
 *  ENTRY
 *          htimer - timer to be unlinked
 *          pptda - ptda in which to search
 *          ppdostimer - place where pdostimer will be returned
 *
 *  EXIT
 *          SUCCESS - returns 0; pdostimer has the dostimer
 *          FAILURE - returns ERROR_TS_HANDLE
 *
 *  PCODE
 *          search for timer in specified PTDA list
 *          if not found
 *              return error
 *          else
 *              fill in pdostimer
 */

ULONG timQueryTimer (htimer,pptda,ppdostimer)
HTIMER      htimer;
PPTDA       pptda;
dostimer_t  **ppdostimer;
{
register    dostimer_t  *pdtCur;

    // Get First entry on list
    pdtCur = pptda->ptda_timerhead;


    // search for a matching entry to htimer
    while (pdtCur){
        if (pdtCur->tmr_htimer == htimer){
            *ppdostimer = pdtCur;
            return 0;
        }
        pdtCur  = pdtCur->tmr_next;
    }

    // Timer handle not found
    return ERROR_TS_HANDLE;
}

/***EP+ TIMTomClear - Entry point to time out manager to call on
 *                    a timer expiration.
 *
 *      ENTRY
 *          ptoe - pointer to time out elemrnt associated with fired timer
 *
 *      EXIT
 *          None
 *
 *      CONTEXT
 *          Interrupt time with interrupts enabled
 *
 *      PCODE
 *          mark in associated timer structure that the timer is expired
 *          arm local context hook
 *          return
 */

VOID    TIMTomClear(ptoe)
PTOE    ptoe;
{
dostimer_t  *pdostimer;
ULONG     flags;

    flags = TKDisable();
    // Get timer from TOM element
    pdostimer = (dostimer_t *) ptoe;

    // If  context hook not active activate it.
    if(!flArmed){
        KMArmContextHook(htimhook,(PTCB)-1);
        flArmed = 1;
    }

    // Put the timer on fired list
    pdostimer->tmr_fl |= TMR_EXPIRED;
    pdostimer->tmr_fired = pFiredTimerQueue;
    pFiredTimerQueue = pdostimer;
    TKEnable (flags);
    return;
}

/***LP+ timContextHook - Entry point to process timer expiration in
 *                       the task context.
 *
 *      ENTRY
 *          pnull   - no reference data
 *          pcrf    - client register farme pointer
 *
 *      EXIT
 *          None
 *
 *      CONTEXT
 *          Task time
 *
 *      PCODE
 *          for each entry on list
 *              remove from fired list
 *              clear the semaphore
 *              if timer is one shot
 *                  timUnlinkTimer
 *                  free the timer
 *              else
 *                  TOMSet
 *              if UNLINKING flag set
 *                  TKWakeup()
 *          if no more entry clear armed flag
 */

VOID pascal near timContextHook(pnull, pcrf)
VOID *pnull;
VOID *pcrf;
{
register    dostimer_t  *pdostimer;
ULONG     flags;
toe_worker_t  worker;
#ifdef MISCSTRICT
ULONG     rc;
#endif

    while (1){
        flags = TKDisable();

        if (pFiredTimerQueue){

            // Unlink from the fired list
            pdostimer = pFiredTimerQueue;
            pFiredTimerQueue = pFiredTimerQueue->tmr_fired;
            pdostimer->tmr_fired = NULL;
            TKEnable (flags);

            // Clear the semaphore
            if (pdostimer->tmr_fl & TMR_OLDSEM)
                TIMSEMCLEAR(pdostimer->tmr_hsem);
            else
            {
                // It is a 32-bit shared event sem, so call devhlp
                // PostEventSem.  (DCR MS2036)
#ifdef MISCSTRICT
                rc = dhw_PostEventSem((HEV)pdostimer->tmr_hsem);
                TIMASSERT(rc == NO_ERROR ||
                          rc == ERROR_ALREADY_POSTED ||
                          rc == ERROR_TOO_MANY_POSTS);
#else
                dhw_PostEventSem((HEV)pdostimer->tmr_hsem);
#endif
            }
            // If one shot then remove from PTDA list
            if (pdostimer->tmr_fl & TMR_ONESHOT){
                timUnlinkTimer (pdostimer);
            }
            else {
                // It is periodic timer so arm again on ptda list

                worker = TOM_W_TIMCLEAR;  // timer expiration clears semaphore

                // If timer was hi resolution, reset at hi resolution.  It
                // is possible that the thread is no longer fast dispatch,
                // but it is slow to check if the timer's semaphore's thread's
                // priority has changed.  A periodic timer initialized as
                // hi resolution will continue to be hi resolution even if
                // the initiating thread is no longer a fast dispatch thread.

                if (pdostimer->tmr_fl & TMR_HIRES) {
                    worker |= TOM_HIRES_FLAG;
                }

                TOMSet (&pdostimer->tmr_tom,
                        pdostimer->tmr_mstime,
                        worker);
            }
            // Mark that timer is no more on fired list
            pdostimer->tmr_fl &= ~TMR_EXPIRED;

            // If threads waiting to stop the timer wake them up.
            if (pdostimer->tmr_fl & TMR_UNLINKING)
                TKWakeup (pdostimer, 0, NULL);

            // If timer was one shot, free timer
            if (pdostimer->tmr_fl & TMR_ONESHOT){
                // if old semaphore, decrement sem ref count
                // else close the shared event sem
                if (pdostimer->tmr_fl & TMR_OLDSEM)
                    TIMSEMHANDLE(pdostimer->tmr_hsem,0);
                else
                {
                    // It is a 32-bit shared event sem, so call devhlp
                    // CloseEventSem.  This is the corresponding close to the
                    // devhlp OpenEventSem performed in VR32AsyncTimer(),
                    // for DCR MS2036.
#ifdef MISCSTRICT
                    TIMASSERT(dhw_CloseEventSem((HEV) (pdostimer->tmr_hsem))
                                        == NO_ERROR);
#else
                    dhw_CloseEventSem((HEV) (pdostimer->tmr_hsem));
#endif
                }
                TIMGSETHTIMER(TMR_FREE,pdostimer->tmr_htimer);
                freeKRHB (pdostimer);
            }
            continue;
        }
        else{
            flArmed = 0;
            TKEnable (flags);
            return;
        }
    }
}


/***LP+ timProcTerminate - Cancel all the timers on a terminating
 *                         process.
 *
 *
 *      ENTRY
 *          pptda    - PTDA of terminating process
 *
 *      EXIT
 *          None
 *
 *      CONTEXT
 *          Task time
 *
 *      PCODE
 *          Interrupt disabled processing
 *              Cancel all unexpired timers
 *              remove all timers of this process from fired list
 *          Interrupt enabled processing
 *              free timers to resident heap
 *
 */

VOID    timProcTerminate (pptda)
PPTDA   pptda;
{
register    dostimer_t *pdostimer;
dostimer_t  *pfired,*plast = NULL;
ULONG       flags,timcounter=0;

    pdostimer = pptda->ptda_timerhead;

    // Cancel unfired timers
    while (pdostimer) {

        flags = TKDisable ();
        if (!(pdostimer->tmr_fl & TMR_EXPIRED))
            TOMCancel (&pdostimer->tmr_tom);
        TKEnable (flags);
        pdostimer = pdostimer->tmr_next;
    }

    // Fired queue will be very small at any time so this is
    // minimal from intrrupt disabling time point of view.

    flags = TKDisable ();
    pfired = pFiredTimerQueue;
    while(pfired){
        if(pfired->tmr_pptda != pptda){
            plast = pfired;
        }
        else{
            if(plast)
                plast->tmr_fired = pfired->tmr_fired;
            else
                pFiredTimerQueue = pfired->tmr_fired;
        }
        pfired = pfired->tmr_fired;
    }
    TKEnable (flags);

    // No timers on ptda list
    pdostimer = pptda->ptda_timerhead;
    pptda->ptda_timerhead = NULL;

    // free timers to resident heap
    while (pdostimer){
        plast = pdostimer;
        pdostimer = pdostimer->tmr_next;
        TIMGSETHTIMER (TMR_FREE, plast->tmr_htimer);
        if(plast->tmr_fl & TMR_OLDSEM)
            TIMSEMHANDLE(plast->tmr_hsem,0); // old sem, decrement sem ref count
        else
        {
                // It is a 32-bit shared event sem, so call devhlp
                // CloseEventSem.  This is the corresponding close to the
                // devhlp OpenEventSem performed in
                // VR32StartTimer()/VR32AsyncTimer(), for DCR MS2036.
#ifdef MISCSTRICT
                TIMASSERT(dhw_CloseEventSem((HEV) (plast->tmr_hsem))
                                == NO_ERROR);
#else
                dhw_CloseEventSem((HEV) (plast->tmr_hsem));
#endif
        }
        freeKRHB (plast);
        timcounter++;
    }

    return;
}


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

/***EP+ TIMInit - Initialize timer services
 *
 *      Entry
 *          None
 *
 *      Exit
 *          SUCESS  - returns 0
 *          FAILURE - returns
 *                      ERROR_NOT_ENOUGH_MEMORY
 *                      ERROR_INTERRUPTED
 *
 *      PCODE
 *          rc = KMAllocHook allocate hook for context hooking
 *          Allocate timer handles bit array.
 *          return rc
 */



ULONG TIMInit (VOID)
{
ULONG   rc,cb,i;

    if ((rc = KMAllocHook(timContextHook,     // timer context hook
                          0,                  // Hook type is not special
                          0,                  // no hook specific data
                          0,                  // no reference data
                          (PTCB)-1,           // global hook
                          &htimhook)) != NO_ERROR)
        return rc;

    // MaxThreads includes 0th thread, so subtract one.
    ntimers = (MaxThreads < TMR_MINDEFAULT ? TMR_MINDEFAULT : MaxThreads) - 1 ;

    // bytes for ntimers bits
    cb = ntimers / 8;
    cb += (ntimers % 8 ? 1 : 0);

    if ((rc = allocKRHB(cb,
                        TIMEROWNER,
                        &ahtimer)) != NO_ERROR)
        KMFreeHook(htimhook);
    else
        for (i=0; i<cb ; i++)
            ahtimer [i] = 0;

    return rc;
}

#pragma code_seg()
