diff --git a/mozilla/xpcom/threads/MANIFEST_IDL b/mozilla/xpcom/threads/MANIFEST_IDL index 81bc391b453..f581e347643 100644 --- a/mozilla/xpcom/threads/MANIFEST_IDL +++ b/mozilla/xpcom/threads/MANIFEST_IDL @@ -1,6 +1,7 @@ nsIRunnable.idl nsIThread.idl nsIThreadPool.idl +nsITimerManager.idl nsIEventQueue.idl nsIEventQueueService.idl nsIScriptableTimer.idl diff --git a/mozilla/xpcom/threads/Makefile.in b/mozilla/xpcom/threads/Makefile.in index c3d2147f42e..0eafb9ac4fa 100644 --- a/mozilla/xpcom/threads/Makefile.in +++ b/mozilla/xpcom/threads/Makefile.in @@ -62,6 +62,7 @@ EXPORTS = \ XPIDLSRCS = \ nsIThread.idl \ nsIThreadPool.idl \ + nsITimerManager.idl \ nsIRunnable.idl \ nsIEventQueue.idl \ nsIEventQueueService.idl \ diff --git a/mozilla/xpcom/threads/TimerThread.cpp b/mozilla/xpcom/threads/TimerThread.cpp index 689eecdd19b..09583095da0 100644 --- a/mozilla/xpcom/threads/TimerThread.cpp +++ b/mozilla/xpcom/threads/TimerThread.cpp @@ -217,17 +217,17 @@ NS_IMETHODIMP TimerThread::Run() } #endif - // We are going to let the call to Fire here handle the release of the + // We are going to let the call to PostTimerEvent here handle the release of the // timer so that we don't end up releasing the timer on the TimerThread // instead of on the thread it targets. - timer->Fire(); + timer->PostTimerEvent(); timer = nsnull; lock.lock(); if (mShutdown) break; - // Update now, as Fire plus the locking may have taken a tick or two, + // Update now, as PostTimerEvent plus the locking may have taken a tick or two, // and we may goto next below. now = PR_IntervalNow(); } diff --git a/mozilla/xpcom/threads/makefile.win b/mozilla/xpcom/threads/makefile.win index 24c150307ca..fe1b4118190 100644 --- a/mozilla/xpcom/threads/makefile.win +++ b/mozilla/xpcom/threads/makefile.win @@ -45,6 +45,7 @@ XPIDLSRCS = \ .\nsIEventQueueService.idl \ .\nsIThread.idl \ .\nsIThreadPool.idl \ + .\nsITimerManager.idl \ .\nsIRunnable.idl \ .\nsIProcess.idl \ .\nsIScriptableTimer.idl \ diff --git a/mozilla/xpcom/threads/nsTimerImpl.cpp b/mozilla/xpcom/threads/nsTimerImpl.cpp index 9b15ddd6935..8d9967704df 100644 --- a/mozilla/xpcom/threads/nsTimerImpl.cpp +++ b/mozilla/xpcom/threads/nsTimerImpl.cpp @@ -36,10 +36,17 @@ #include "nsTimerImpl.h" #include "TimerThread.h" +#include "nsSupportsArray.h" + #include "nsIEventQueue.h" static TimerThread *gThread = nsnull; + // only the main thread supports idle timers. +static nsSupportsArray *gIdleTimers = nsnull; + +static PRBool gFireOnIdle = PR_FALSE; + #include "prmem.h" #include "prinit.h" @@ -193,6 +200,9 @@ void nsTimerImpl::Shutdown() gThread->Shutdown(); NS_RELEASE(gThread); + + gFireOnIdle = PR_FALSE; + NS_IF_RELEASE(gIdleTimers); } @@ -290,11 +300,11 @@ NS_IMETHODIMP_(void) nsTimerImpl::SetType(PRUint32 aType) { mType = (PRUint8)aType; // XXX if this is called, we should change the actual type.. this could effect - // repeating timers. we need to ensure in Process() that if mType has changed + // repeating timers. we need to ensure in Fire() that if mType has changed // during the callback that we don't end up with the timer in the queue twice. } -void nsTimerImpl::Process() +void nsTimerImpl::Fire() { if (mCanceled) return; @@ -349,7 +359,7 @@ void nsTimerImpl::Process() #ifdef DEBUG_TIMERS if (PR_LOG_TEST(gTimerLog, PR_LOG_DEBUG)) { PR_LOG(gTimerLog, PR_LOG_DEBUG, - ("[this=%p] Took %dms to fire process timer callback\n", + ("[this=%p] Took %dms to fire timer callback\n", this, PR_IntervalToMilliseconds(PR_IntervalNow() - now))); } #endif @@ -362,7 +372,7 @@ void nsTimerImpl::Process() } -struct MyEventType { +struct TimerEventType { PLEvent e; // arguments follow... #ifdef DEBUG_TIMERS @@ -371,22 +381,34 @@ struct MyEventType { }; -void* handleMyEvent(MyEventType* event) +void* handleTimerEvent(TimerEventType* event) { #ifdef DEBUG_TIMERS if (PR_LOG_TEST(gTimerLog, PR_LOG_DEBUG)) { PRIntervalTime now = PR_IntervalNow(); PR_LOG(gTimerLog, PR_LOG_DEBUG, - ("[this=%p] time between Fire() and Process(): %dms\n", + ("[this=%p] time between PostTimerEvent() and Fire(): %dms\n", event->e.owner, PR_IntervalToMilliseconds(now - event->mInit))); } #endif - NS_STATIC_CAST(nsTimerImpl*, event->e.owner)->Process(); + + if (gFireOnIdle) { + nsCOMPtr currentThread, mainThread; + nsIThread::GetCurrent(getter_AddRefs(currentThread)); + nsIThread::GetMainThread(getter_AddRefs(mainThread)); + if (currentThread == mainThread) { + gIdleTimers->AppendElement(NS_STATIC_CAST(nsITimer*, NS_STATIC_CAST(nsTimerImpl*, event->e.owner))); + + return NULL; + } + } + + NS_STATIC_CAST(nsTimerImpl*, event->e.owner)->Fire(); return NULL; } -void destroyMyEvent(MyEventType* event) +void destroyTimerEvent(TimerEventType* event) { nsTimerImpl *timer = NS_STATIC_CAST(nsTimerImpl*, event->e.owner); NS_RELEASE(timer); @@ -394,19 +416,19 @@ void destroyMyEvent(MyEventType* event) } -void nsTimerImpl::Fire() +void nsTimerImpl::PostTimerEvent() { // XXX we may want to reuse the PLEvent in the case of repeating timers. - MyEventType* event; + TimerEventType* event; // construct - event = PR_NEW(MyEventType); + event = PR_NEW(TimerEventType); if (event == NULL) return; // initialize PL_InitEvent((PLEvent*)event, this, - (PLHandleEventProc)handleMyEvent, - (PLDestroyEventProc)destroyMyEvent); + (PLHandleEventProc)handleTimerEvent, + (PLDestroyEventProc)destroyTimerEvent); // Since TimerThread addref'd 'this' for us, we don't need to addref here. // We will release in destroyMyEvent. @@ -479,3 +501,96 @@ NS_NewTimer(nsITimer* *aResult, nsTimerCallbackFunc aCallback, void *aClosure, *aResult = timer; return NS_OK; } + + +/** + * Timer Manager code + */ + +NS_IMPL_THREADSAFE_ISUPPORTS1(nsTimerManager, nsITimerManager) + +nsTimerManager::nsTimerManager() +{ + NS_INIT_REFCNT(); +} + +nsTimerManager::~nsTimerManager() +{ + +} + +NS_IMETHODIMP nsTimerManager::SetUseIdleTimers(PRBool aUseIdleTimers) +{ + if (aUseIdleTimers == PR_FALSE && gFireOnIdle == PR_TRUE) + return NS_ERROR_FAILURE; + + gFireOnIdle = aUseIdleTimers; + + if (gFireOnIdle && !gIdleTimers) { + gIdleTimers = new nsSupportsArray(); + if (!gIdleTimers) + return NS_ERROR_OUT_OF_MEMORY; + + NS_ADDREF(gIdleTimers); + } + + return NS_OK; +} + +NS_IMETHODIMP nsTimerManager::GetUseIdleTimers(PRBool *aUseIdleTimers) +{ + *aUseIdleTimers = gFireOnIdle; + return NS_OK; +} + +NS_IMETHODIMP nsTimerManager::HasIdleTimers(PRBool *aHasTimers) +{ + *aHasTimers = PR_FALSE; + + if (!gFireOnIdle) + return NS_OK; + + nsCOMPtr currentThread, mainThread; + nsIThread::GetCurrent(getter_AddRefs(currentThread)); + nsIThread::GetMainThread(getter_AddRefs(mainThread)); + + if (currentThread != mainThread) { + return NS_OK; + } + + PRUint32 count; + gIdleTimers->Count(&count); + *aHasTimers = (count != 0); + + return NS_OK; +} + +NS_IMETHODIMP nsTimerManager::FireNextIdleTimer() +{ + if (!gFireOnIdle) + return NS_OK; + + nsCOMPtr currentThread, mainThread; + nsIThread::GetCurrent(getter_AddRefs(currentThread)); + nsIThread::GetMainThread(getter_AddRefs(mainThread)); + + if (currentThread != mainThread) { + return NS_OK; + } + + PRUint32 count; + gIdleTimers->Count(&count); + + if (count > 0) { + nsTimerImpl *theTimer = NS_STATIC_CAST(nsTimerImpl*, NS_STATIC_CAST(nsITimer*, gIdleTimers->ElementAt(0))); // addrefs + + gIdleTimers->RemoveElement(NS_STATIC_CAST(nsITimer*, theTimer), 0); + + theTimer->Fire(); + + NS_RELEASE(theTimer); + } + // pull out each one starting at the beginning until no more are left and fire them. + + return NS_OK; +} diff --git a/mozilla/xpcom/threads/nsTimerImpl.h b/mozilla/xpcom/threads/nsTimerImpl.h index 0cebca315b0..78fa00babbd 100644 --- a/mozilla/xpcom/threads/nsTimerImpl.h +++ b/mozilla/xpcom/threads/nsTimerImpl.h @@ -94,7 +94,7 @@ public: friend class TimerThread; void Fire(); - void Process(); + void PostTimerEvent(); void SetDelayInternal(PRUint32 aDelay); NS_IMETHOD Init(nsTimerCallbackFunc aFunc, @@ -162,4 +162,27 @@ private: }; +#define NS_TIMERMANAGER_CONTRACTID "@mozilla.org/timer/manager;1" +#define NS_TIMERMANAGER_CLASSNAME "Timer Manager" +#define NS_TIMERMANAGER_CID \ +{ /* 4fe206fa-1dd2-11b2-8a0a-88bacbecc7d2 */ \ + 0x4fe206fa, \ + 0x1dd2, \ + 0x11b2, \ + {0x8a, 0x0a, 0x88, 0xba, 0xcb, 0xec, 0xc7, 0xd2} \ +} + +#include "nsITimerManager.h" + +class nsTimerManager : nsITimerManager +{ +public: + nsTimerManager(); + virtual ~nsTimerManager(); + + NS_DECL_ISUPPORTS + NS_DECL_NSITIMERMANAGER +}; + + #endif /* nsTimerImpl_h___ */