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 18587 : NS_IMPL_THREADSAFE_ISUPPORTS2(TimerThread, nsIRunnable, nsIObserver)
56 :
57 1419 : 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 1419 : mMinTimerPeriod(0)
66 : {
67 1419 : }
68 :
69 2838 : TimerThread::~TimerThread()
70 : {
71 1419 : mThread = nsnull;
72 :
73 1419 : NS_ASSERTION(mTimers.IsEmpty(), "Timers remain in TimerThread::~TimerThread");
74 1419 : }
75 :
76 : nsresult
77 1419 : TimerThread::InitLocks()
78 : {
79 1419 : return NS_OK;
80 : }
81 :
82 : namespace {
83 :
84 : class TimerObserverRunnable : public nsRunnable
85 4092 : {
86 : public:
87 1023 : TimerObserverRunnable(nsIObserver* observer)
88 1023 : : mObserver(observer)
89 1023 : { }
90 :
91 : NS_DECL_NSIRUNNABLE
92 :
93 : private:
94 : nsCOMPtr<nsIObserver> mObserver;
95 : };
96 :
97 : NS_IMETHODIMP
98 1023 : TimerObserverRunnable::Run()
99 : {
100 : nsCOMPtr<nsIObserverService> observerService =
101 2046 : mozilla::services::GetObserverService();
102 1023 : if (observerService) {
103 1023 : observerService->AddObserver(mObserver, "sleep_notification", PR_FALSE);
104 1023 : observerService->AddObserver(mObserver, "wake_notification", PR_FALSE);
105 : }
106 1023 : return NS_OK;
107 : }
108 :
109 : } // anonymous namespace
110 :
111 21292 : nsresult TimerThread::Init()
112 : {
113 21292 : PR_LOG(gTimerLog, PR_LOG_DEBUG, ("TimerThread::Init [%d]\n", mInitialized));
114 :
115 21292 : if (mInitialized) {
116 20269 : if (!mThread)
117 0 : return NS_ERROR_FAILURE;
118 :
119 20269 : return NS_OK;
120 : }
121 :
122 1023 : if (PR_ATOMIC_SET(&mInitInProgress, 1) == 0) {
123 : // We hold on to mThread to keep the thread alive.
124 1023 : nsresult rv = NS_NewThread(getter_AddRefs(mThread), this);
125 1023 : if (NS_FAILED(rv)) {
126 0 : mThread = nsnull;
127 : }
128 : else {
129 2046 : nsRefPtr<TimerObserverRunnable> r = new TimerObserverRunnable(this);
130 1023 : if (NS_IsMainThread()) {
131 1020 : r->Run();
132 : }
133 : else {
134 3 : NS_DispatchToMainThread(r);
135 : }
136 : }
137 :
138 : {
139 2046 : MonitorAutoLock lock(mMonitor);
140 1023 : mInitialized = true;
141 1023 : mMonitor.NotifyAll();
142 : }
143 : }
144 : else {
145 0 : MonitorAutoLock lock(mMonitor);
146 0 : while (!mInitialized) {
147 0 : mMonitor.Wait();
148 : }
149 : }
150 :
151 1023 : if (!mThread)
152 0 : return NS_ERROR_FAILURE;
153 :
154 1023 : return NS_OK;
155 : }
156 :
157 1419 : nsresult TimerThread::Shutdown()
158 : {
159 1419 : PR_LOG(gTimerLog, PR_LOG_DEBUG, ("TimerThread::Shutdown begin\n"));
160 :
161 1419 : if (!mThread)
162 396 : return NS_ERROR_NOT_INITIALIZED;
163 :
164 2046 : nsTArray<nsTimerImpl*> timers;
165 : { // lock scope
166 2046 : MonitorAutoLock lock(mMonitor);
167 :
168 1023 : mShutdown = true;
169 :
170 : // notify the cond var so that Run() can return
171 1023 : if (mWaiting)
172 1023 : 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 1023 : timers.AppendElements(mTimers);
181 1023 : mTimers.Clear();
182 : }
183 :
184 1023 : PRUint32 timersCount = timers.Length();
185 1425 : for (PRUint32 i = 0; i < timersCount; i++) {
186 402 : nsTimerImpl *timer = timers[i];
187 402 : timer->ReleaseCallback();
188 402 : ReleaseTimerInternal(timer);
189 : }
190 :
191 1023 : mThread->Shutdown(); // wait for the thread to die
192 :
193 1023 : PR_LOG(gTimerLog, PR_LOG_DEBUG, ("TimerThread::Shutdown end\n"));
194 1023 : 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 4243 : void TimerThread::UpdateFilter(PRUint32 aDelay, TimeStamp aTimeout,
201 : TimeStamp aNow)
202 : {
203 4243 : TimeDuration slack = aTimeout - aNow;
204 4243 : double smoothSlack = 0;
205 : PRUint32 i, filterLength;
206 : static TimeDuration kFilterFeedbackMaxTicks =
207 4243 : TimeDuration::FromMilliseconds(FILTER_FEEDBACK_MAX);
208 : static TimeDuration kFilterFeedbackMinTicks =
209 4243 : TimeDuration::FromMilliseconds(-FILTER_FEEDBACK_MAX);
210 :
211 4243 : if (slack > kFilterFeedbackMaxTicks)
212 2 : slack = kFilterFeedbackMaxTicks;
213 4241 : else if (slack < kFilterFeedbackMinTicks)
214 348 : slack = kFilterFeedbackMinTicks;
215 :
216 4243 : mDelayLine[mDelayLineCounter & DELAY_LINE_LENGTH_MASK] =
217 4243 : slack.ToMilliseconds();
218 4243 : if (++mDelayLineCounter < DELAY_LINE_LENGTH) {
219 : // Startup mode: accumulate a full delay line before filtering.
220 2506 : PR_ASSERT(mTimeoutAdjustment.ToSeconds() == 0);
221 2506 : filterLength = 0;
222 : } else {
223 : // Past startup: compute number of filter taps based on mMinTimerPeriod.
224 1737 : if (mMinTimerPeriod == 0) {
225 18 : mMinTimerPeriod = (aDelay != 0) ? aDelay : 1;
226 1719 : } else if (aDelay != 0 && aDelay < mMinTimerPeriod) {
227 3 : mMinTimerPeriod = aDelay;
228 : }
229 :
230 1737 : filterLength = (PRUint32) (FILTER_DURATION / mMinTimerPeriod);
231 1737 : if (filterLength > DELAY_LINE_LENGTH)
232 505 : filterLength = DELAY_LINE_LENGTH;
233 1232 : else if (filterLength < 4)
234 1204 : filterLength = 4;
235 :
236 23239 : for (i = 1; i <= filterLength; i++)
237 21502 : smoothSlack += mDelayLine[(mDelayLineCounter-i) & DELAY_LINE_LENGTH_MASK];
238 1737 : smoothSlack /= filterLength;
239 :
240 : // XXXbe do we need amplification? hacking a fudge factor, need testing...
241 1737 : mTimeoutAdjustment = TimeDuration::FromMilliseconds(smoothSlack * 1.5);
242 : }
243 :
244 : #ifdef DEBUG_TIMERS
245 4243 : PR_LOG(gTimerLog, PR_LOG_DEBUG,
246 : ("UpdateFilter: smoothSlack = %g, filterLength = %u\n",
247 : smoothSlack, filterLength));
248 : #endif
249 4243 : }
250 :
251 : /* void Run(); */
252 1023 : NS_IMETHODIMP TimerThread::Run()
253 : {
254 2046 : 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 1023 : PRInt32 low = 0, high = 1;
260 11253 : while (PR_MicrosecondsToInterval(high) == 0)
261 9207 : 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 11253 : while (high-low > 1) {
267 9207 : PRInt32 mid = (high+low) >> 1;
268 9207 : if (PR_MicrosecondsToInterval(mid) == 0)
269 7161 : low = mid;
270 : else
271 2046 : 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 1023 : PRInt32 halfMicrosecondsIntervalResolution = high >> 1;
277 :
278 34770 : while (!mShutdown) {
279 : // Have to use PRIntervalTime here, since PR_WaitCondVar takes it
280 : PRIntervalTime waitFor;
281 :
282 32724 : if (mSleeping) {
283 : // Sleep for 0.1 seconds while not firing timers.
284 1 : waitFor = PR_MillisecondsToInterval(100);
285 : } else {
286 32723 : waitFor = PR_INTERVAL_NO_TIMEOUT;
287 32723 : TimeStamp now = TimeStamp::Now();
288 32723 : nsTimerImpl *timer = nsnull;
289 :
290 32723 : if (!mTimers.IsEmpty()) {
291 28181 : timer = mTimers[0];
292 :
293 28181 : 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 4298 : NS_ADDREF(timer);
302 4298 : RemoveTimerInternal(timer);
303 :
304 : {
305 : // We release mMonitor around the Fire call to avoid deadlock.
306 8596 : MonitorAutoUnlock unlock(mMonitor);
307 :
308 : #ifdef DEBUG_TIMERS
309 4298 : 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 4298 : 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 4298 : timer = nsnull;
337 : }
338 :
339 4298 : 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 4298 : now = TimeStamp::Now();
345 : }
346 : }
347 :
348 32740 : if (!mTimers.IsEmpty()) {
349 28049 : timer = mTimers[0];
350 :
351 28049 : 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 28049 : double microseconds = (timeout - now).ToMilliseconds()*1000;
361 28049 : if (microseconds < halfMicrosecondsIntervalResolution)
362 17 : goto next; // round down; execute event now
363 28032 : waitFor = PR_MicrosecondsToInterval(microseconds);
364 28032 : if (waitFor == 0)
365 6 : waitFor = 1; // round up, wait the minimum time we can wait
366 : }
367 :
368 : #ifdef DEBUG_TIMERS
369 32723 : 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 32724 : mWaiting = true;
381 32724 : mMonitor.Wait(waitFor);
382 32724 : mWaiting = false;
383 : }
384 :
385 1023 : return NS_OK;
386 : }
387 :
388 23043 : nsresult TimerThread::AddTimer(nsTimerImpl *aTimer)
389 : {
390 46086 : MonitorAutoLock lock(mMonitor);
391 :
392 : // Add the timer to our list.
393 23043 : PRInt32 i = AddTimerInternal(aTimer);
394 23043 : if (i < 0)
395 0 : return NS_ERROR_OUT_OF_MEMORY;
396 :
397 : // Awaken the timer thread.
398 23043 : if (mWaiting && i == 0)
399 13950 : mMonitor.Notify();
400 :
401 23043 : return NS_OK;
402 : }
403 :
404 3658 : nsresult TimerThread::TimerDelayChanged(nsTimerImpl *aTimer)
405 : {
406 7316 : MonitorAutoLock lock(mMonitor);
407 :
408 : // Our caller has a strong ref to aTimer, so it can't go away here under
409 : // ReleaseTimerInternal.
410 3658 : RemoveTimerInternal(aTimer);
411 :
412 3658 : PRInt32 i = AddTimerInternal(aTimer);
413 3658 : if (i < 0)
414 0 : return NS_ERROR_OUT_OF_MEMORY;
415 :
416 : // Awaken the timer thread.
417 3658 : if (mWaiting && i == 0)
418 360 : mMonitor.Notify();
419 :
420 3658 : return NS_OK;
421 : }
422 :
423 19936 : nsresult TimerThread::RemoveTimer(nsTimerImpl *aTimer)
424 : {
425 39872 : 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 19936 : if (!RemoveTimerInternal(aTimer))
435 1578 : return NS_ERROR_NOT_AVAILABLE;
436 :
437 : // Awaken the timer thread.
438 18358 : if (mWaiting)
439 18311 : mMonitor.Notify();
440 :
441 18358 : return NS_OK;
442 : }
443 :
444 : // This function must be called from within a lock
445 26701 : PRInt32 TimerThread::AddTimerInternal(nsTimerImpl *aTimer)
446 : {
447 26701 : if (mShutdown)
448 0 : return -1;
449 :
450 26701 : TimeStamp now = TimeStamp::Now();
451 26701 : PRUint32 count = mTimers.Length();
452 26701 : PRUint32 i = 0;
453 50107 : for (; i < count; i++) {
454 37070 : 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 74105 : if (now < timer->mTimeout + mTimeoutAdjustment &&
465 37035 : aTimer->mTimeout < timer->mTimeout) {
466 13664 : break;
467 : }
468 : }
469 :
470 26701 : if (!mTimers.InsertElementAt(i, aTimer))
471 0 : return -1;
472 :
473 26701 : aTimer->mArmed = true;
474 26701 : NS_ADDREF(aTimer);
475 26701 : return i;
476 : }
477 :
478 27892 : bool TimerThread::RemoveTimerInternal(nsTimerImpl *aTimer)
479 : {
480 27892 : if (!mTimers.RemoveElement(aTimer))
481 1593 : return false;
482 :
483 26299 : ReleaseTimerInternal(aTimer);
484 26299 : return true;
485 : }
486 :
487 26701 : void TimerThread::ReleaseTimerInternal(nsTimerImpl *aTimer)
488 : {
489 : // Order is crucial here -- see nsTimerImpl::Release.
490 26701 : aTimer->mArmed = false;
491 26701 : NS_RELEASE(aTimer);
492 26701 : }
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 4392 : }
|