home *** CD-ROM | disk | FTP | other *** search
/ Tools / WinSN5.0Ver.iso / NETSCAP.50 / WIN1998.ZIP / ns / cmd / winfe / timer.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  1998-04-08  |  9.8 KB  |  325 lines

  1. /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  2.  *
  3.  * The contents of this file are subject to the Netscape Public License
  4.  * Version 1.0 (the "NPL"); you may not use this file except in
  5.  * compliance with the NPL.  You may obtain a copy of the NPL at
  6.  * http://www.mozilla.org/NPL/
  7.  *
  8.  * Software distributed under the NPL is distributed on an "AS IS" basis,
  9.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
  10.  * for the specific language governing rights and limitations under the
  11.  * NPL.
  12.  *
  13.  * The Initial Developer of this code under the NPL is Netscape
  14.  * Communications Corporation.  Portions created by Netscape are
  15.  * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
  16.  * Reserved.
  17.  */
  18.  
  19. #include "stdafx.h"
  20.  
  21. #include "timer.h"
  22. #include "feselect.h"
  23. #include "cxprint.h"
  24.  
  25. // structure to keep track of FE_SetTimeOut objects
  26. typedef struct WinTimeStruct {
  27.     TimeoutCallbackFunction   fn;
  28.     void                    * closure;
  29.     DWORD                     dwFireTime;
  30.     struct WinTimeStruct    * pNext;
  31. } WinTime;
  32.  
  33. // the one and only list of objects waiting for FE_SetTimeOut
  34. WinTime *gTimeOutList = NULL;
  35.  
  36. //  Process timeouts now.
  37. //  Called once per round of fire.
  38. UINT uTimeoutTimer = 0;
  39. DWORD dwNextFire = (DWORD)-1;
  40. DWORD dwSyncHack = 0;
  41.  
  42. void CALLBACK EXPORT FireTimeout(HWND hWnd, UINT uMessage, UINT uTimerID, DWORD dwTime)
  43. {
  44.     static BOOL bCanEnter = TRUE;
  45.  
  46.     //  Don't allow old timer messages in here.
  47.     if(uMessage != WM_TIMER)    {
  48.         ASSERT(0);
  49.         return;
  50.     }
  51.     if(uTimerID != uTimeoutTimer)   {
  52.         return;
  53.     }
  54.  
  55.     //  Block only one entry into this function, or else.
  56.     if(bCanEnter)   {
  57.         bCanEnter = FALSE;
  58.         // see if we need to fork off any timeout functions
  59.         if(gTimeOutList)    {
  60.             wfe_ProcessTimeouts(dwTime);
  61.         }
  62.         bCanEnter = TRUE;
  63.     }
  64. }
  65.  
  66. //  Function to correctly have the timer be set.
  67. void SyncTimeoutPeriod(DWORD dwTickCount)
  68. {
  69.     //  May want us to set tick count ourselves.
  70.     if(dwTickCount == 0)    {
  71.         if(dwSyncHack == 0) {
  72.             dwTickCount = GetTickCount();
  73.         }
  74.         else    {
  75.             dwTickCount = dwSyncHack;
  76.         }
  77.     }
  78.  
  79.     ASSERT(!uTimeoutTimer || (uTimeoutTimer && !(theApp.m_pMainWnd == NULL || theApp.m_pMainWnd->m_hWnd == NULL)));
  80.  
  81.     //  If there's no list, we should clear the timer.
  82.     if(!gTimeOutList && theApp.m_pMainWnd && theApp.m_pMainWnd->m_hWnd)    {
  83.         if(uTimeoutTimer)   {
  84.             VERIFY(::KillTimer(theApp.m_pMainWnd->m_hWnd, 777));
  85.             uTimeoutTimer = 0;
  86.             dwNextFire = (DWORD)-1;
  87.         }
  88.         //  Explicitly clear the idle binding if set previously.
  89.         theApp.m_bIdleProcessTimeouts = FALSE;
  90.     }
  91.     else if(theApp.m_pMainWnd && theApp.m_pMainWnd->m_hWnd)   {
  92.         //  See if we need to clear the current timer.
  93.         //  Curcumstances are that if the timer will not
  94.         //      fire on time for the next timeout.
  95.         BOOL bSetTimer = FALSE;
  96.         WinTime *pTimeout = gTimeOutList;
  97.         if(uTimeoutTimer)   {
  98.             if(pTimeout->dwFireTime != dwNextFire)   {
  99.                 //  There is no need to kill the timer if we are just going to set it again.
  100.                 //  Windows will simply respect the new time interval passed in.
  101.                 //  VERIFY(::KillTimer(theApp.m_pMainWnd->m_hWnd, 777));
  102.                 uTimeoutTimer = 0;
  103.                 dwNextFire = (DWORD)-1;
  104.  
  105.                 //  Set the timer.
  106.                 bSetTimer = TRUE;
  107.             }
  108.         }
  109.         else    {
  110.             //  No timer set, attempt.
  111.             bSetTimer = TRUE;
  112.         }
  113.  
  114.         if(bSetTimer)   {
  115.             DWORD dwFireWhen = pTimeout->dwFireTime > dwTickCount ?
  116.                 pTimeout->dwFireTime - dwTickCount : 0;
  117.             if(dwFireWhen > UINT_MAX)   {
  118.                 dwFireWhen = UINT_MAX;
  119.             }
  120.             UINT uFireWhen = CASTUINT(dwFireWhen);
  121.  
  122.             ASSERT(uTimeoutTimer == 0);
  123.             uTimeoutTimer = ::SetTimer(
  124.                 theApp.m_pMainWnd->m_hWnd, 
  125.                 777, 
  126.                 uFireWhen, 
  127.                 FireTimeout);
  128.  
  129.             if(uTimeoutTimer)   {
  130.                 //  Set the fire time.
  131.                 dwNextFire = pTimeout->dwFireTime;
  132.             }
  133.         }
  134.  
  135.         //  See if the app should attempt to bind idle processing to the
  136.         //      timer code (in the event no timers could be allocated).
  137.         if(uTimeoutTimer)   {
  138.             theApp.m_bIdleProcessTimeouts = FALSE;
  139.         }
  140.         else    {
  141.             ASSERT(0);
  142.             theApp.m_bIdleProcessTimeouts = TRUE;
  143.         }
  144.     }
  145. }
  146.  
  147. /* this function should register a function that will
  148.  * be called after the specified interval of time has
  149.  * elapsed.  This function should return an id 
  150.  * that can be passed to FE_ClearTimeout to cancel 
  151.  * the Timeout request.
  152.  *
  153.  * A) Timeouts never fail to trigger, and
  154.  * B) Timeouts don't trigger *before* their nominal timestamp expires, and
  155.  * C) Timeouts trigger in the same ordering as their timestamps
  156.  *
  157.  * After the function has been called it is unregistered
  158.  * and will not be called again unless re-registered.
  159.  *
  160.  * func:    The function to be invoked upon expiration of
  161.  *          the Timeout interval 
  162.  * closure: Data to be passed as the only argument to "func"
  163.  * msecs:   The number of milli-seconds in the interval
  164.  */
  165. PUBLIC void * 
  166. FE_SetTimeout(TimeoutCallbackFunction func, void * closure, uint32 msecs)
  167. {
  168.     WinTime * pTime = new WinTime;
  169.     if(!pTime)
  170.         return(NULL);
  171.  
  172.     // fill it out
  173.     DWORD dwNow = GetTickCount();
  174.     pTime->fn = func;
  175.     pTime->closure = closure;
  176.     pTime->dwFireTime = (DWORD) msecs + dwNow;
  177.     //CLM: Always clear this else the last timer added will have garbage!
  178.     //     (Win16 revealed this bug!)
  179.     pTime->pNext = NULL;
  180.     
  181.     // add it to the list
  182.     if(!gTimeOutList) {        
  183.         // no list add it
  184.         gTimeOutList = pTime;
  185.     } else {
  186.  
  187.         // is it before everything else on the list?
  188.         if(pTime->dwFireTime < gTimeOutList->dwFireTime) {
  189.  
  190.             pTime->pNext = gTimeOutList;
  191.             gTimeOutList = pTime;
  192.  
  193.         } else {
  194.  
  195.             WinTime * pPrev = gTimeOutList;
  196.             WinTime * pCurrent = gTimeOutList;
  197.  
  198.             while(pCurrent && (pCurrent->dwFireTime <= pTime->dwFireTime)) {
  199.                 pPrev = pCurrent;
  200.                 pCurrent = pCurrent->pNext;
  201.             }
  202.  
  203.             ASSERT(pPrev);
  204.  
  205.             // insert it after pPrev (this could be at the end of the list)
  206.             pTime->pNext = pPrev->pNext;
  207.             pPrev->pNext = pTime;
  208.  
  209.         }
  210.  
  211.     }
  212.  
  213.     //  Sync the timer fire period.
  214.     SyncTimeoutPeriod(dwNow);
  215.  
  216.     return(pTime);
  217. }
  218.  
  219.  
  220. /* This function cancels a Timeout that has previously been
  221.  * set.  
  222.  * Callers should not pass in NULL or a timer_id that
  223.  * has already expired.
  224.  */
  225. PUBLIC void 
  226. FE_ClearTimeout(void * pStuff)
  227. {
  228.     WinTime * pTimer = (WinTime *) pStuff;
  229.  
  230.     if(!pTimer) {
  231.         return;
  232.     }
  233.  
  234.     if(gTimeOutList == pTimer) {
  235.  
  236.         // first element in the list lossage
  237.         gTimeOutList = pTimer->pNext;
  238.  
  239.     } else {
  240.  
  241.         // walk until no next pointer
  242.         for(WinTime * p = gTimeOutList; p && p->pNext && (p->pNext != pTimer); p = p->pNext)
  243.             ;
  244.  
  245.         // if we found something valid pull it out of the list
  246.         if(p && p->pNext && p->pNext == pTimer) {
  247.             p->pNext = pTimer->pNext;
  248.  
  249.         } else {
  250.             // get out before we delete something that looks bogus
  251.             return;
  252.         }
  253.  
  254.     }
  255.  
  256.     // if we got here it must have been a valid element so trash it
  257.     delete pTimer;
  258.  
  259.     //  If there's now no be sure to clear the timer.
  260.     SyncTimeoutPeriod(0);
  261. }
  262.  
  263. //
  264. // Walk down the timeout list and launch anyone appropriate
  265. // Cleaned up logic 04-30-96 GAB
  266. //
  267. void wfe_ProcessTimeouts(DWORD dwNow)
  268. {
  269.     WinTime *p = gTimeOutList;
  270.     if(dwNow == 0)   {
  271.         dwNow = GetTickCount();
  272.     }
  273.  
  274.     BOOL bCalledSync = FALSE;
  275.  
  276.     //  Don't fire timeouts while in the PrintAbortProc, or we will go
  277.     //      reentrant into the GDI code of the print driver if
  278.     //      someone is doing drawing via timeouts.
  279.     if(CPrintCX::m_bGlobalBlockDisplay) {
  280.         //  Be sure to get the next timeout period right, however.
  281.         SyncTimeoutPeriod(dwNow);
  282.         bCalledSync = TRUE;
  283.         
  284.         //  Get Out
  285.         return;
  286.     }
  287.  
  288.     //  Set the hack, such that when FE_ClearTimeout
  289.     //      calls SyncTimeoutPeriod, that GetTickCount()
  290.     //      overhead is not incurred.
  291.     dwSyncHack = dwNow;
  292.     
  293.     // loop over all entries
  294.     while(p) {
  295.         // send it
  296.         if(p->dwFireTime < dwNow) {
  297.             //  Fire it.
  298.             (*p->fn) (p->closure);
  299.  
  300.             //  Clear the timer.
  301.             //  Period synced.
  302.             FE_ClearTimeout(p);
  303.             bCalledSync = TRUE;
  304.  
  305.             //  Reset the loop (can't look at p->pNext now, and called
  306.             //      code may have added/cleared timers).
  307.             //  (could do this by going recursive and returning).
  308.             p = gTimeOutList;
  309.         } else {
  310.             //    Make sure we fire an timer.
  311.             //  Also, we need to check to see if things are backing up (they
  312.             //      may be asking to be fired long before we ever get to them,
  313.             //      and we don't want to pass in negative values to the real
  314.             //      timer code, or it takes days to fire....
  315.             if(bCalledSync == FALSE)    {
  316.                 SyncTimeoutPeriod(dwNow);
  317.                 bCalledSync = TRUE;
  318.             }
  319.             //  Get next timer.
  320.             p = p->pNext;
  321.         }
  322.     }
  323.     dwSyncHack = 0;
  324. }
  325.