bug 129953 r=rjesup sr=rpotts
git-svn-id: svn://10.0.0.236/trunk@118588 18797224-902f-48f8-a5cc-f745e15eee43
This commit is contained in:
@@ -89,12 +89,21 @@ NS_METHOD nsAppShell::SetDispatchListener(nsDispatchListener* aDispatchListener)
|
||||
//
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
#include "nsITimerManager.h"
|
||||
|
||||
|
||||
NS_METHOD nsAppShell::Run(void)
|
||||
{
|
||||
NS_ADDREF_THIS();
|
||||
MSG msg;
|
||||
int keepGoing = 1;
|
||||
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsITimerManager> timerManager(do_GetService("@mozilla.org/timer/manager;1", &rv));
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
timerManager->SetUseIdleTimers(PR_TRUE);
|
||||
|
||||
gKeepGoing = 1;
|
||||
// Process messages
|
||||
do {
|
||||
@@ -116,15 +125,26 @@ NS_METHOD nsAppShell::Run(void)
|
||||
mDispatchListener->AfterDispatch();
|
||||
}
|
||||
} else {
|
||||
if (!gKeepGoing) {
|
||||
// In this situation, PostQuitMessage() was called, but the WM_QUIT
|
||||
// message was removed from the event queue by someone else -
|
||||
// (see bug #54725). So, just exit the loop as if WM_QUIT had been
|
||||
// reeceived...
|
||||
keepGoing = 0;
|
||||
|
||||
PRBool hasTimers;
|
||||
timerManager->HasIdleTimers(&hasTimers);
|
||||
if (hasTimers) {
|
||||
do {
|
||||
timerManager->FireNextIdleTimer();
|
||||
timerManager->HasIdleTimers(&hasTimers);
|
||||
} while (hasTimers && !::PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE));
|
||||
} else {
|
||||
// Block and wait for any posted application message
|
||||
::WaitMessage();
|
||||
|
||||
if (!gKeepGoing) {
|
||||
// In this situation, PostQuitMessage() was called, but the WM_QUIT
|
||||
// message was removed from the event queue by someone else -
|
||||
// (see bug #54725). So, just exit the loop as if WM_QUIT had been
|
||||
// reeceived...
|
||||
keepGoing = 0;
|
||||
} else {
|
||||
// Block and wait for any posted application message
|
||||
::WaitMessage();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -149,6 +169,10 @@ nsAppShell::GetNativeEvent(PRBool &aRealEvent, void *&aEvent)
|
||||
|
||||
BOOL gotMessage = false;
|
||||
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsITimerManager> timerManager(do_GetService("@mozilla.org/timer/manager;1", &rv));
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
do {
|
||||
// Give priority to system messages (in particular keyboard, mouse,
|
||||
// timer, and paint messages).
|
||||
@@ -158,8 +182,17 @@ nsAppShell::GetNativeEvent(PRBool &aRealEvent, void *&aEvent)
|
||||
|
||||
gotMessage = true;
|
||||
} else {
|
||||
// Block and wait for any posted application message
|
||||
::WaitMessage();
|
||||
PRBool hasTimers;
|
||||
timerManager->HasIdleTimers(&hasTimers);
|
||||
if (hasTimers) {
|
||||
do {
|
||||
timerManager->FireNextIdleTimer();
|
||||
timerManager->HasIdleTimers(&hasTimers);
|
||||
} while (hasTimers && !::PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE));
|
||||
} else {
|
||||
// Block and wait for any posted application message
|
||||
::WaitMessage();
|
||||
}
|
||||
}
|
||||
|
||||
} while (!gotMessage);
|
||||
|
||||
@@ -131,6 +131,7 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(nsConsoleService);
|
||||
NS_GENERIC_FACTORY_CONSTRUCTOR(nsAtomService);
|
||||
NS_GENERIC_FACTORY_CONSTRUCTOR(nsExceptionService);
|
||||
NS_GENERIC_FACTORY_CONSTRUCTOR(nsTimerImpl);
|
||||
NS_GENERIC_FACTORY_CONSTRUCTOR(nsTimerManager);
|
||||
|
||||
NS_GENERIC_FACTORY_CONSTRUCTOR(nsVariant);
|
||||
|
||||
@@ -248,6 +249,7 @@ static const nsModuleComponentInfo components[] = {
|
||||
COMPONENT(XPCOMPROXY, nsProxyObjectManager::Create),
|
||||
|
||||
COMPONENT(TIMER, nsTimerImplConstructor),
|
||||
COMPONENT(TIMERMANAGER, nsTimerManagerConstructor),
|
||||
|
||||
#define COMPONENT_SUPPORTS(TYPE, Type) \
|
||||
COMPONENT(SUPPORTS_##TYPE, nsSupports##Type##ImplConstructor)
|
||||
|
||||
@@ -218,17 +218,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 (!mProcessing)
|
||||
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();
|
||||
}
|
||||
|
||||
@@ -45,6 +45,7 @@ XPIDLSRCS = \
|
||||
.\nsIEventQueueService.idl \
|
||||
.\nsIThread.idl \
|
||||
.\nsIThreadPool.idl \
|
||||
.\nsITimerManager.idl \
|
||||
.\nsIRunnable.idl \
|
||||
.\nsIProcess.idl \
|
||||
.\nsIScriptableTimer.idl \
|
||||
|
||||
56
mozilla/xpcom/threads/nsITimerManager.idl
Normal file
56
mozilla/xpcom/threads/nsITimerManager.idl
Normal file
@@ -0,0 +1,56 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is mozilla.org code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Netscape Communications
|
||||
* Corporation. Portions created by the Initial Developer are
|
||||
* Copyright (C) 2002 the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Stuart Parmenter <pavlov@netscape.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*/
|
||||
|
||||
#include "nsISupports.idl"
|
||||
|
||||
[scriptable, uuid(8fce8c6a-1dd2-11b2-8352-8cdd2b965efc)]
|
||||
interface nsITimerManager : nsISupports
|
||||
{
|
||||
/**
|
||||
* A flag that turns on the use of idle timers on the main thread.
|
||||
* this should only be called once.
|
||||
*
|
||||
* By default, idle timers are off.
|
||||
*
|
||||
* One this is set to TRUE, you are expected to call hasIdleTimers/fireNextIdleTimer
|
||||
* when you have time in your main loop.
|
||||
*/
|
||||
attribute boolean useIdleTimers;
|
||||
|
||||
boolean hasIdleTimers();
|
||||
|
||||
// Fire next idle timers waiting on the current thread
|
||||
void fireNextIdleTimer();
|
||||
};
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -285,11 +295,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;
|
||||
@@ -317,7 +327,7 @@ void nsTimerImpl::Process()
|
||||
PRIntervalTime timeout = mTimeout;
|
||||
if (mType == NS_TYPE_REPEATING_PRECISE) {
|
||||
// Precise repeating timers advance mTimeout by mDelay without fail before
|
||||
// calling Process().
|
||||
// calling Fire().
|
||||
timeout -= mDelay;
|
||||
}
|
||||
gThread->UpdateFilter(mDelay, timeout, now);
|
||||
@@ -344,7 +354,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
|
||||
@@ -357,7 +367,7 @@ void nsTimerImpl::Process()
|
||||
}
|
||||
|
||||
|
||||
struct MyEventType {
|
||||
struct TimerEventType {
|
||||
PLEvent e;
|
||||
// arguments follow...
|
||||
#ifdef DEBUG_TIMERS
|
||||
@@ -366,22 +376,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<nsIThread> 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);
|
||||
@@ -389,19 +411,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.
|
||||
@@ -472,3 +494,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<nsIThread> 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<nsIThread> 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));
|
||||
|
||||
theTimer->Fire();
|
||||
|
||||
NS_RELEASE(theTimer);
|
||||
}
|
||||
// pull out each one starting at the beginning until no more are left and fire them.
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
@@ -84,8 +84,8 @@ public:
|
||||
|
||||
friend class TimerThread;
|
||||
|
||||
void PostTimerEvent();
|
||||
void Fire();
|
||||
void Process();
|
||||
void SetDelayInternal(PRUint32 aDelay);
|
||||
|
||||
NS_IMETHOD Init(nsTimerCallbackFunc aFunc,
|
||||
@@ -153,4 +153,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___ */
|
||||
|
||||
Reference in New Issue
Block a user