LCOV - code coverage report
Current view: directory - xpcom/threads - TimerThread.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 209 192 91.9 %
Date: 2012-04-21 Functions: 26 25 96.2 %

       1                 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
       2                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
       3                 :  *
       4                 :  * ***** BEGIN LICENSE BLOCK *****
       5                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
       6                 :  *
       7                 :  * The contents of this file are subject to the Mozilla Public License Version
       8                 :  * 1.1 (the "License"); you may not use this file except in compliance with
       9                 :  * the License. You may obtain a copy of the License at
      10                 :  * http://www.mozilla.org/MPL/
      11                 :  *
      12                 :  * Software distributed under the License is distributed on an "AS IS" basis,
      13                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
      14                 :  * for the specific language governing rights and limitations under the
      15                 :  * License.
      16                 :  *
      17                 :  * The Original Code is mozilla.org code.
      18                 :  *
      19                 :  * The Initial Developer of the Original Code is
      20                 :  * Netscape Communications Corporation.
      21                 :  * Portions created by the Initial Developer are Copyright (C) 2001
      22                 :  * the Initial Developer. All Rights Reserved.
      23                 :  *
      24                 :  * Contributor(s):
      25                 :  *   Stuart Parmenter <pavlov@netscape.com>
      26                 :  *
      27                 :  * Alternatively, the contents of this file may be used under the terms of
      28                 :  * either of the GNU General Public License Version 2 or later (the "GPL"),
      29                 :  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      30                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      31                 :  * of those above. If you wish to allow use of your version of this file only
      32                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      33                 :  * use your version of this file under the terms of the MPL, indicate your
      34                 :  * decision by deleting the provisions above and replace them with the notice
      35                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      36                 :  * the provisions above, a recipient may use your version of this file under
      37                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      38                 :  *
      39                 :  * ***** END LICENSE BLOCK ***** */
      40                 : 
      41                 : #include "nsTimerImpl.h"
      42                 : #include "TimerThread.h"
      43                 : 
      44                 : #include "nsThreadUtils.h"
      45                 : #include "pratom.h"
      46                 : 
      47                 : #include "nsIObserverService.h"
      48                 : #include "nsIServiceManager.h"
      49                 : #include "mozilla/Services.h"
      50                 : 
      51                 : #include <math.h>
      52                 : 
      53                 : using namespace mozilla;
      54                 : 
      55           18173 : NS_IMPL_THREADSAFE_ISUPPORTS2(TimerThread, nsIRunnable, nsIObserver)
      56                 : 
      57            1365 : TimerThread::TimerThread() :
      58                 :   mInitInProgress(0),
      59                 :   mInitialized(false),
      60                 :   mMonitor("TimerThread.mMonitor"),
      61                 :   mShutdown(false),
      62                 :   mWaiting(false),
      63                 :   mSleeping(false),
      64                 :   mDelayLineCounter(0),
      65            1365 :   mMinTimerPeriod(0)
      66                 : {
      67            1365 : }
      68                 : 
      69            2730 : TimerThread::~TimerThread()
      70                 : {
      71            1365 :   mThread = nsnull;
      72                 : 
      73            1365 :   NS_ASSERTION(mTimers.IsEmpty(), "Timers remain in TimerThread::~TimerThread");
      74            1365 : }
      75                 : 
      76                 : nsresult
      77            1365 : TimerThread::InitLocks()
      78                 : {
      79            1365 :   return NS_OK;
      80                 : }
      81                 : 
      82                 : namespace {
      83                 : 
      84                 : class TimerObserverRunnable : public nsRunnable
      85            4020 : {
      86                 : public:
      87            1005 :   TimerObserverRunnable(nsIObserver* observer)
      88            1005 :     : mObserver(observer)
      89            1005 :   { }
      90                 : 
      91                 :   NS_DECL_NSIRUNNABLE
      92                 : 
      93                 : private:
      94                 :   nsCOMPtr<nsIObserver> mObserver;
      95                 : };
      96                 : 
      97                 : NS_IMETHODIMP
      98            1005 : TimerObserverRunnable::Run()
      99                 : {
     100                 :   nsCOMPtr<nsIObserverService> observerService =
     101            2010 :     mozilla::services::GetObserverService();
     102            1005 :   if (observerService) {
     103            1005 :     observerService->AddObserver(mObserver, "sleep_notification", PR_FALSE);
     104            1005 :     observerService->AddObserver(mObserver, "wake_notification", PR_FALSE);
     105                 :   }
     106            1005 :   return NS_OK;
     107                 : }
     108                 : 
     109                 : } // anonymous namespace
     110                 : 
     111           21337 : nsresult TimerThread::Init()
     112                 : {
     113           21337 :   PR_LOG(gTimerLog, PR_LOG_DEBUG, ("TimerThread::Init [%d]\n", mInitialized));
     114                 : 
     115           21337 :   if (mInitialized) {
     116           20332 :     if (!mThread)
     117               0 :       return NS_ERROR_FAILURE;
     118                 : 
     119           20332 :     return NS_OK;
     120                 :   }
     121                 : 
     122            1005 :   if (PR_ATOMIC_SET(&mInitInProgress, 1) == 0) {
     123                 :     // We hold on to mThread to keep the thread alive.
     124            1005 :     nsresult rv = NS_NewThread(getter_AddRefs(mThread), this);
     125            1005 :     if (NS_FAILED(rv)) {
     126               0 :       mThread = nsnull;
     127                 :     }
     128                 :     else {
     129            2010 :       nsRefPtr<TimerObserverRunnable> r = new TimerObserverRunnable(this);
     130            1005 :       if (NS_IsMainThread()) {
     131            1001 :         r->Run();
     132                 :       }
     133                 :       else {
     134               4 :         NS_DispatchToMainThread(r);
     135                 :       }
     136                 :     }
     137                 : 
     138                 :     {
     139            2010 :       MonitorAutoLock lock(mMonitor);
     140            1005 :       mInitialized = true;
     141            1005 :       mMonitor.NotifyAll();
     142                 :     }
     143                 :   }
     144                 :   else {
     145               0 :     MonitorAutoLock lock(mMonitor);
     146               0 :     while (!mInitialized) {
     147               0 :       mMonitor.Wait();
     148                 :     }
     149                 :   }
     150                 : 
     151            1005 :   if (!mThread)
     152               0 :     return NS_ERROR_FAILURE;
     153                 : 
     154            1005 :   return NS_OK;
     155                 : }
     156                 : 
     157            1365 : nsresult TimerThread::Shutdown()
     158                 : {
     159            1365 :   PR_LOG(gTimerLog, PR_LOG_DEBUG, ("TimerThread::Shutdown begin\n"));
     160                 : 
     161            1365 :   if (!mThread)
     162             360 :     return NS_ERROR_NOT_INITIALIZED;
     163                 : 
     164            2010 :   nsTArray<nsTimerImpl*> timers;
     165                 :   {   // lock scope
     166            2010 :     MonitorAutoLock lock(mMonitor);
     167                 : 
     168            1005 :     mShutdown = true;
     169                 : 
     170                 :     // notify the cond var so that Run() can return
     171            1005 :     if (mWaiting)
     172             987 :       mMonitor.Notify();
     173                 : 
     174                 :     // Need to copy content of mTimers array to a local array
     175                 :     // because call to timers' ReleaseCallback() (and release its self)
     176                 :     // must not be done under the lock. Destructor of a callback
     177                 :     // might potentially call some code reentering the same lock
     178                 :     // that leads to unexpected behavior or deadlock.
     179                 :     // See bug 422472.
     180            1005 :     timers.AppendElements(mTimers);
     181            1005 :     mTimers.Clear();
     182                 :   }
     183                 : 
     184            1005 :   PRUint32 timersCount = timers.Length();
     185            1383 :   for (PRUint32 i = 0; i < timersCount; i++) {
     186             378 :     nsTimerImpl *timer = timers[i];
     187             378 :     timer->ReleaseCallback();
     188             378 :     ReleaseTimerInternal(timer);
     189                 :   }
     190                 : 
     191            1005 :   mThread->Shutdown();    // wait for the thread to die
     192                 : 
     193            1005 :   PR_LOG(gTimerLog, PR_LOG_DEBUG, ("TimerThread::Shutdown end\n"));
     194            1005 :   return NS_OK;
     195                 : }
     196                 : 
     197                 : // Keep track of how early (positive slack) or late (negative slack) timers
     198                 : // are running, and use the filtered slack number to adaptively estimate how
     199                 : // early timers should fire to be "on time".
     200            5190 : void TimerThread::UpdateFilter(PRUint32 aDelay, TimeStamp aTimeout,
     201                 :                                TimeStamp aNow)
     202                 : {
     203            5190 :   TimeDuration slack = aTimeout - aNow;
     204            5190 :   double smoothSlack = 0;
     205                 :   PRUint32 i, filterLength;
     206                 :   static TimeDuration kFilterFeedbackMaxTicks =
     207            5190 :     TimeDuration::FromMilliseconds(FILTER_FEEDBACK_MAX);
     208                 :   static TimeDuration kFilterFeedbackMinTicks =
     209            5190 :     TimeDuration::FromMilliseconds(-FILTER_FEEDBACK_MAX);
     210                 : 
     211            5190 :   if (slack > kFilterFeedbackMaxTicks)
     212               8 :     slack = kFilterFeedbackMaxTicks;
     213            5182 :   else if (slack < kFilterFeedbackMinTicks)
     214            1155 :     slack = kFilterFeedbackMinTicks;
     215                 : 
     216            5190 :   mDelayLine[mDelayLineCounter & DELAY_LINE_LENGTH_MASK] =
     217            5190 :     slack.ToMilliseconds();
     218            5190 :   if (++mDelayLineCounter < DELAY_LINE_LENGTH) {
     219                 :     // Startup mode: accumulate a full delay line before filtering.
     220            3258 :     PR_ASSERT(mTimeoutAdjustment.ToSeconds() == 0);
     221            3258 :     filterLength = 0;
     222                 :   } else {
     223                 :     // Past startup: compute number of filter taps based on mMinTimerPeriod.
     224            1932 :     if (mMinTimerPeriod == 0) {
     225              24 :       mMinTimerPeriod = (aDelay != 0) ? aDelay : 1;
     226            1908 :     } else if (aDelay != 0 && aDelay < mMinTimerPeriod) {
     227               4 :       mMinTimerPeriod = aDelay;
     228                 :     }
     229                 : 
     230            1932 :     filterLength = (PRUint32) (FILTER_DURATION / mMinTimerPeriod);
     231            1932 :     if (filterLength > DELAY_LINE_LENGTH)
     232            1651 :       filterLength = DELAY_LINE_LENGTH;
     233             281 :     else if (filterLength < 4)
     234             179 :       filterLength = 4;
     235                 : 
     236           57262 :     for (i = 1; i <= filterLength; i++)
     237           55330 :       smoothSlack += mDelayLine[(mDelayLineCounter-i) & DELAY_LINE_LENGTH_MASK];
     238            1932 :     smoothSlack /= filterLength;
     239                 : 
     240                 :     // XXXbe do we need amplification?  hacking a fudge factor, need testing...
     241            1932 :     mTimeoutAdjustment = TimeDuration::FromMilliseconds(smoothSlack * 1.5);
     242                 :   }
     243                 : 
     244                 : #ifdef DEBUG_TIMERS
     245            5190 :   PR_LOG(gTimerLog, PR_LOG_DEBUG,
     246                 :          ("UpdateFilter: smoothSlack = %g, filterLength = %u\n",
     247                 :           smoothSlack, filterLength));
     248                 : #endif
     249            5190 : }
     250                 : 
     251                 : /* void Run(); */
     252            1005 : NS_IMETHODIMP TimerThread::Run()
     253                 : {
     254            2010 :   MonitorAutoLock lock(mMonitor);
     255                 : 
     256                 :   // We need to know how many microseconds give a positive PRIntervalTime. This
     257                 :   // is platform-dependent, we calculate it at runtime now.
     258                 :   // First we find a value such that PR_MicrosecondsToInterval(high) = 1
     259            1005 :   PRInt32 low = 0, high = 1;
     260           11055 :   while (PR_MicrosecondsToInterval(high) == 0)
     261            9045 :     high <<= 1;
     262                 :   // We now have
     263                 :   //    PR_MicrosecondsToInterval(low)  = 0
     264                 :   //    PR_MicrosecondsToInterval(high) = 1
     265                 :   // and we can proceed to find the critical value using binary search
     266           11055 :   while (high-low > 1) {
     267            9045 :     PRInt32 mid = (high+low) >> 1;
     268            9045 :     if (PR_MicrosecondsToInterval(mid) == 0)
     269            7035 :       low = mid;
     270                 :     else
     271            2010 :       high = mid;
     272                 :   }
     273                 : 
     274                 :   // Half of the amount of microseconds needed to get positive PRIntervalTime.
     275                 :   // We use this to decide how to round our wait times later
     276            1005 :   PRInt32 halfMicrosecondsIntervalResolution = high >> 1;
     277                 : 
     278           39066 :   while (!mShutdown) {
     279                 :     // Have to use PRIntervalTime here, since PR_WaitCondVar takes it
     280                 :     PRIntervalTime waitFor;
     281                 : 
     282           37056 :     if (mSleeping) {
     283                 :       // Sleep for 0.1 seconds while not firing timers.
     284               1 :       waitFor = PR_MillisecondsToInterval(100);
     285                 :     } else {
     286           37055 :       waitFor = PR_INTERVAL_NO_TIMEOUT;
     287           37055 :       TimeStamp now = TimeStamp::Now();
     288           37055 :       nsTimerImpl *timer = nsnull;
     289                 : 
     290           37055 :       if (!mTimers.IsEmpty()) {
     291           31152 :         timer = mTimers[0];
     292                 : 
     293           31152 :         if (now >= timer->mTimeout + mTimeoutAdjustment) {
     294                 :     next:
     295                 :           // NB: AddRef before the Release under RemoveTimerInternal to avoid
     296                 :           // mRefCnt passing through zero, in case all other refs than the one
     297                 :           // from mTimers have gone away (the last non-mTimers[i]-ref's Release
     298                 :           // must be racing with us, blocked in gThread->RemoveTimer waiting
     299                 :           // for TimerThread::mMonitor, under nsTimerImpl::Release.
     300                 : 
     301            5308 :           NS_ADDREF(timer);
     302            5308 :           RemoveTimerInternal(timer);
     303                 : 
     304                 :           {
     305                 :             // We release mMonitor around the Fire call to avoid deadlock.
     306           10616 :             MonitorAutoUnlock unlock(mMonitor);
     307                 : 
     308                 : #ifdef DEBUG_TIMERS
     309            5308 :             if (PR_LOG_TEST(gTimerLog, PR_LOG_DEBUG)) {
     310               0 :               PR_LOG(gTimerLog, PR_LOG_DEBUG,
     311                 :                      ("Timer thread woke up %fms from when it was supposed to\n",
     312                 :                       fabs((now - timer->mTimeout).ToMilliseconds())));
     313                 :             }
     314                 : #endif
     315                 : 
     316                 :             // We are going to let the call to PostTimerEvent here handle the
     317                 :             // release of the timer so that we don't end up releasing the timer
     318                 :             // on the TimerThread instead of on the thread it targets.
     319            5308 :             if (NS_FAILED(timer->PostTimerEvent())) {
     320                 :               nsrefcnt rc;
     321               0 :               NS_RELEASE2(timer, rc);
     322                 :             
     323                 :               // The nsITimer interface requires that its users keep a reference
     324                 :               // to the timers they use while those timers are initialized but
     325                 :               // have not yet fired.  If this ever happens, it is a bug in the
     326                 :               // code that created and used the timer.
     327                 :               //
     328                 :               // Further, note that this should never happen even with a
     329                 :               // misbehaving user, because nsTimerImpl::Release checks for a
     330                 :               // refcount of 1 with an armed timer (a timer whose only reference
     331                 :               // is from the timer thread) and when it hits this will remove the
     332                 :               // timer from the timer thread and thus destroy the last reference,
     333                 :               // preventing this situation from occurring.
     334               0 :               NS_ASSERTION(rc != 0, "destroyed timer off its target thread!");
     335                 :             }
     336            5308 :             timer = nsnull;
     337                 :           }
     338                 : 
     339            5308 :           if (mShutdown)
     340               0 :             break;
     341                 : 
     342                 :           // Update now, as PostTimerEvent plus the locking may have taken a
     343                 :           // tick or two, and we may goto next below.
     344            5308 :           now = TimeStamp::Now();
     345                 :         }
     346                 :       }
     347                 : 
     348           37085 :       if (!mTimers.IsEmpty()) {
     349           30968 :         timer = mTimers[0];
     350                 : 
     351           30968 :         TimeStamp timeout = timer->mTimeout + mTimeoutAdjustment;
     352                 : 
     353                 :         // Don't wait at all (even for PR_INTERVAL_NO_WAIT) if the next timer
     354                 :         // is due now or overdue.
     355                 :         //
     356                 :         // Note that we can only sleep for integer values of a certain
     357                 :         // resolution. We use halfMicrosecondsIntervalResolution, calculated
     358                 :         // before, to do the optimal rounding (i.e., of how to decide what
     359                 :         // interval is so small we should not wait at all).
     360           30968 :         double microseconds = (timeout - now).ToMilliseconds()*1000;
     361           30968 :         if (microseconds < halfMicrosecondsIntervalResolution)
     362              30 :           goto next; // round down; execute event now
     363           30938 :         waitFor = PR_MicrosecondsToInterval(microseconds);
     364           30938 :         if (waitFor == 0)
     365               7 :           waitFor = 1; // round up, wait the minimum time we can wait
     366                 :       }
     367                 : 
     368                 : #ifdef DEBUG_TIMERS
     369           37055 :       if (PR_LOG_TEST(gTimerLog, PR_LOG_DEBUG)) {
     370               0 :         if (waitFor == PR_INTERVAL_NO_TIMEOUT)
     371               0 :           PR_LOG(gTimerLog, PR_LOG_DEBUG,
     372                 :                  ("waiting for PR_INTERVAL_NO_TIMEOUT\n"));
     373                 :         else
     374               0 :           PR_LOG(gTimerLog, PR_LOG_DEBUG,
     375                 :                  ("waiting for %u\n", PR_IntervalToMilliseconds(waitFor)));
     376                 :       }
     377                 : #endif
     378                 :     }
     379                 : 
     380           37056 :     mWaiting = true;
     381           37056 :     mMonitor.Wait(waitFor);
     382           37056 :     mWaiting = false;
     383                 :   }
     384                 : 
     385            1005 :   return NS_OK;
     386                 : }
     387                 : 
     388           23975 : nsresult TimerThread::AddTimer(nsTimerImpl *aTimer)
     389                 : {
     390           47950 :   MonitorAutoLock lock(mMonitor);
     391                 : 
     392                 :   // Add the timer to our list.
     393           23975 :   PRInt32 i = AddTimerInternal(aTimer);
     394           23975 :   if (i < 0)
     395               0 :     return NS_ERROR_OUT_OF_MEMORY;
     396                 : 
     397                 :   // Awaken the timer thread.
     398           23975 :   if (mWaiting && i == 0)
     399           15454 :     mMonitor.Notify();
     400                 : 
     401           23975 :   return NS_OK;
     402                 : }
     403                 : 
     404            3651 : nsresult TimerThread::TimerDelayChanged(nsTimerImpl *aTimer)
     405                 : {
     406            7302 :   MonitorAutoLock lock(mMonitor);
     407                 : 
     408                 :   // Our caller has a strong ref to aTimer, so it can't go away here under
     409                 :   // ReleaseTimerInternal.
     410            3651 :   RemoveTimerInternal(aTimer);
     411                 : 
     412            3651 :   PRInt32 i = AddTimerInternal(aTimer);
     413            3651 :   if (i < 0)
     414               0 :     return NS_ERROR_OUT_OF_MEMORY;
     415                 : 
     416                 :   // Awaken the timer thread.
     417            3651 :   if (mWaiting && i == 0)
     418             398 :     mMonitor.Notify();
     419                 : 
     420            3651 :   return NS_OK;
     421                 : }
     422                 : 
     423           20040 : nsresult TimerThread::RemoveTimer(nsTimerImpl *aTimer)
     424                 : {
     425           40080 :   MonitorAutoLock lock(mMonitor);
     426                 : 
     427                 :   // Remove the timer from our array.  Tell callers that aTimer was not found
     428                 :   // by returning NS_ERROR_NOT_AVAILABLE.  Unlike the TimerDelayChanged case
     429                 :   // immediately above, our caller may be passing a (now-)weak ref in via the
     430                 :   // aTimer param, specifically when nsTimerImpl::Release loses a race with
     431                 :   // TimerThread::Run, must wait for the mMonitor auto-lock here, and during the
     432                 :   // wait Run drops the only remaining ref to aTimer via RemoveTimerInternal.
     433                 : 
     434           20040 :   if (!RemoveTimerInternal(aTimer))
     435            1736 :     return NS_ERROR_NOT_AVAILABLE;
     436                 : 
     437                 :   // Awaken the timer thread.
     438           18304 :   if (mWaiting)
     439           17946 :     mMonitor.Notify();
     440                 : 
     441           18304 :   return NS_OK;
     442                 : }
     443                 : 
     444                 : // This function must be called from within a lock
     445           27626 : PRInt32 TimerThread::AddTimerInternal(nsTimerImpl *aTimer)
     446                 : {
     447           27626 :   if (mShutdown)
     448               0 :     return -1;
     449                 : 
     450           27626 :   TimeStamp now = TimeStamp::Now();
     451           27626 :   PRUint32 count = mTimers.Length();
     452           27626 :   PRUint32 i = 0;
     453           47467 :   for (; i < count; i++) {
     454           34334 :     nsTimerImpl *timer = mTimers[i];
     455                 : 
     456                 :     // Don't break till we have skipped any overdue timers.
     457                 : 
     458                 :     // XXXbz why?  Given our definition of overdue in terms of
     459                 :     // mTimeoutAdjustment, aTimer might be overdue already!  Why not
     460                 :     // just fire timers in order?
     461                 : 
     462                 :     // XXX does this hold for TYPE_REPEATING_PRECISE?  /be
     463                 : 
     464           68638 :     if (now < timer->mTimeout + mTimeoutAdjustment &&
     465           34304 :         aTimer->mTimeout < timer->mTimeout) {
     466           14493 :       break;
     467                 :     }
     468                 :   }
     469                 : 
     470           27626 :   if (!mTimers.InsertElementAt(i, aTimer))
     471               0 :     return -1;
     472                 : 
     473           27626 :   aTimer->mArmed = true;
     474           27626 :   NS_ADDREF(aTimer);
     475           27626 :   return i;
     476                 : }
     477                 : 
     478           28999 : bool TimerThread::RemoveTimerInternal(nsTimerImpl *aTimer)
     479                 : {
     480           28999 :   if (!mTimers.RemoveElement(aTimer))
     481            1751 :     return false;
     482                 : 
     483           27248 :   ReleaseTimerInternal(aTimer);
     484           27248 :   return true;
     485                 : }
     486                 : 
     487           27626 : void TimerThread::ReleaseTimerInternal(nsTimerImpl *aTimer)
     488                 : {
     489                 :   // Order is crucial here -- see nsTimerImpl::Release.
     490           27626 :   aTimer->mArmed = false;
     491           27626 :   NS_RELEASE(aTimer);
     492           27626 : }
     493                 : 
     494               1 : void TimerThread::DoBeforeSleep()
     495                 : {
     496               1 :   mSleeping = true;
     497               1 : }
     498                 : 
     499               1 : void TimerThread::DoAfterSleep()
     500                 : {
     501               1 :   mSleeping = true; // wake may be notified without preceding sleep notification
     502               5 :   for (PRUint32 i = 0; i < mTimers.Length(); i ++) {
     503               4 :     nsTimerImpl *timer = mTimers[i];
     504                 :     // get and set the delay to cause its timeout to be recomputed
     505                 :     PRUint32 delay;
     506               4 :     timer->GetDelay(&delay);
     507               4 :     timer->SetDelay(delay);
     508                 :   }
     509                 : 
     510                 :   // nuke the stored adjustments, so they get recalibrated
     511               1 :   mTimeoutAdjustment = TimeDuration(0);
     512               1 :   mDelayLineCounter = 0;
     513               1 :   mSleeping = false;
     514               1 : }
     515                 : 
     516                 : 
     517                 : /* void observe (in nsISupports aSubject, in string aTopic, in wstring aData); */
     518                 : NS_IMETHODIMP
     519               2 : TimerThread::Observe(nsISupports* /* aSubject */, const char *aTopic, const PRUnichar* /* aData */)
     520                 : {
     521               2 :   if (strcmp(aTopic, "sleep_notification") == 0)
     522               1 :     DoBeforeSleep();
     523               1 :   else if (strcmp(aTopic, "wake_notification") == 0)
     524               1 :     DoAfterSleep();
     525                 : 
     526               2 :   return NS_OK;
     527            4188 : }

Generated by: LCOV version 1.7