Fixing bug 42321. Delaying GC's two seconds when leaving a page (which is done when opening a window too), and don't start GC'ing until 10 seconds after the first request. r=bzbarsky@mit.edu, sr=brendan@mozilla.org
git-svn-id: svn://10.0.0.236/trunk@108996 18797224-902f-48f8-a5cc-f745e15eee43
This commit is contained in:
@@ -73,6 +73,7 @@
|
||||
#include "nsGUIEvent.h"
|
||||
#include "nsScriptNameSpaceManager.h"
|
||||
#include "nsIThread.h"
|
||||
#include "nsITimer.h"
|
||||
#include "nsDOMClassInfo.h"
|
||||
|
||||
#ifdef MOZ_LOGGING
|
||||
@@ -93,6 +94,13 @@ static NS_DEFINE_IID(kPrefServiceCID, NS_PREF_CID);
|
||||
static PRLogModuleInfo* gJSDiagnostics = nsnull;
|
||||
#endif
|
||||
|
||||
|
||||
#define NS_GC_DELAY 2000 // ms
|
||||
#define NS_FIRST_GC_DELAY 10000 // ms
|
||||
|
||||
static nsITimer *sGCTimer = nsnull;
|
||||
static PRBool sReadyForGC = PR_FALSE;
|
||||
|
||||
nsScriptNameSpaceManager *gNameSpaceManager;
|
||||
|
||||
void JS_DLL_CALLBACK
|
||||
@@ -431,13 +439,26 @@ nsJSContext::~nsJSContext()
|
||||
// Let xpconnect destroy the JSContext when it thinks the time is right.
|
||||
nsCOMPtr<nsIXPConnect> xpc(do_GetService(nsIXPConnect::GetCID()));
|
||||
if (xpc) {
|
||||
xpc->ReleaseJSContext(mContext, !mGCOnDestruction);
|
||||
PRBool do_gc = mGCOnDestruction && !sGCTimer && sReadyForGC;
|
||||
|
||||
xpc->ReleaseJSContext(mContext, !do_gc);
|
||||
} else {
|
||||
::JS_DestroyContext(mContext);
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS2(nsJSContext, nsIScriptContext, nsIXPCScriptNotify)
|
||||
// QueryInterface implementation for nsJSContext
|
||||
NS_INTERFACE_MAP_BEGIN(nsJSContext)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIScriptContext)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIXPCScriptNotify)
|
||||
NS_INTERFACE_MAP_ENTRY(nsITimerCallback)
|
||||
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIScriptContext)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
|
||||
NS_IMPL_ADDREF(nsJSContext)
|
||||
NS_IMPL_RELEASE(nsJSContext)
|
||||
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsJSContext::EvaluateStringWithValue(const nsAReadableString& aScript,
|
||||
@@ -1411,7 +1432,8 @@ nsJSContext::IsContextInitialized()
|
||||
NS_IMETHODIMP
|
||||
nsJSContext::GC()
|
||||
{
|
||||
::JS_GC(mContext);
|
||||
FireGCTimer();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@@ -1532,20 +1554,55 @@ nsJSContext::ScriptExecuted()
|
||||
return ScriptEvaluated(PR_FALSE);
|
||||
}
|
||||
|
||||
nsJSEnvironment *nsJSEnvironment::sTheEnvironment = nsnull;
|
||||
|
||||
nsJSEnvironment *
|
||||
nsJSEnvironment::GetScriptingEnvironment()
|
||||
NS_IMETHODIMP_(void)
|
||||
nsJSContext::Notify(nsITimer *timer)
|
||||
{
|
||||
if (nsnull == sTheEnvironment) {
|
||||
sTheEnvironment = new nsJSEnvironment();
|
||||
NS_IF_ADDREF(sTheEnvironment); // released in |Observe|
|
||||
}
|
||||
return sTheEnvironment;
|
||||
NS_ASSERTION(mContext, "No context in nsJSContext::Notify()!");
|
||||
|
||||
::JS_GC(mContext);
|
||||
|
||||
sReadyForGC = PR_TRUE;
|
||||
|
||||
NS_RELEASE(sGCTimer);
|
||||
}
|
||||
|
||||
void
|
||||
nsJSContext::FireGCTimer()
|
||||
{
|
||||
if (sGCTimer) {
|
||||
// There's already a timer for GC'ing, just clear newborn roots
|
||||
// and return
|
||||
|
||||
::JS_ClearNewbornRoots(mContext);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
nsComponentManager::CreateInstance("@mozilla.org/timer;1",
|
||||
nsnull,
|
||||
NS_GET_IID(nsITimer),
|
||||
(void **)&sGCTimer);
|
||||
|
||||
if (!sGCTimer) {
|
||||
NS_WARNING("Failed to create timer");
|
||||
|
||||
::JS_GC(mContext);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static PRBool first = PR_TRUE;
|
||||
|
||||
sGCTimer->Init(this, first ? NS_FIRST_GC_DELAY : NS_GC_DELAY,
|
||||
NS_PRIORITY_LOWEST);
|
||||
|
||||
first = PR_FALSE;
|
||||
}
|
||||
|
||||
nsIJSRuntimeService *nsJSEnvironment::sRuntimeService = nsnull;
|
||||
JSRuntime *nsJSEnvironment::sRuntime = nsnull;
|
||||
|
||||
const char kJSRuntimeServiceContractID[] = "@mozilla.org/js/xpc/RuntimeService;1";
|
||||
static int globalCount;
|
||||
static PRThread *gDOMThread;
|
||||
|
||||
static JSGCCallback gOldJSGCCallback;
|
||||
@@ -1558,31 +1615,23 @@ DOMGCCallback(JSContext *cx, JSGCStatus status)
|
||||
return gOldJSGCCallback ? gOldJSGCCallback(cx, status) : JS_TRUE;
|
||||
}
|
||||
|
||||
nsJSEnvironment::nsJSEnvironment()
|
||||
// static
|
||||
nsresult nsJSEnvironment::Init()
|
||||
{
|
||||
NS_INIT_ISUPPORTS();
|
||||
static PRBool isInitialized = PR_FALSE;
|
||||
|
||||
// So that we get deleted on XPCOM shutdown, set up an
|
||||
// observer.
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsIObserverService> observerService =
|
||||
do_GetService("@mozilla.org/observer-service;1", &rv);
|
||||
NS_ASSERTION(NS_SUCCEEDED(rv), "going to leak a nsJSEnvironment");
|
||||
if (NS_SUCCEEDED(rv))
|
||||
{
|
||||
observerService->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, PR_FALSE);
|
||||
if (isInitialized) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
mRuntimeService = nsnull;
|
||||
rv = nsServiceManager::GetService(kJSRuntimeServiceContractID,
|
||||
NS_GET_IID(nsIJSRuntimeService),
|
||||
(nsISupports**)&mRuntimeService);
|
||||
nsresult rv = nsServiceManager::GetService(kJSRuntimeServiceContractID,
|
||||
NS_GET_IID(nsIJSRuntimeService),
|
||||
(nsISupports**)&sRuntimeService);
|
||||
// get the JSRuntime from the runtime svc, if possible
|
||||
if (NS_FAILED(rv))
|
||||
return; // XXX swallow error! need Init()?
|
||||
rv = mRuntimeService->GetRuntime(&mRuntime);
|
||||
if (NS_FAILED(rv))
|
||||
return; // XXX swallow error! need Init()?
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = sRuntimeService->GetRuntime(&sRuntime);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
gDOMThread = PR_GetCurrentThread();
|
||||
|
||||
@@ -1599,8 +1648,10 @@ nsJSEnvironment::nsJSEnvironment()
|
||||
}
|
||||
#endif
|
||||
|
||||
NS_ASSERTION(!gOldJSGCCallback, "nsJSEnvironment created more than once");
|
||||
gOldJSGCCallback = ::JS_SetGCCallbackRT(mRuntime, DOMGCCallback);
|
||||
NS_ASSERTION(!gOldJSGCCallback,
|
||||
"nsJSEnvironment initialized more than once");
|
||||
|
||||
gOldJSGCCallback = ::JS_SetGCCallbackRT(sRuntime, DOMGCCallback);
|
||||
|
||||
// Set these global xpconnect options...
|
||||
nsCOMPtr<nsIXPConnect> xpc(do_GetService(nsIXPConnect::GetCID(), &rv));
|
||||
@@ -1615,68 +1666,74 @@ nsJSEnvironment::nsJSEnvironment()
|
||||
nsCOMPtr<nsILiveConnectManager> manager =
|
||||
do_GetService(nsIJVMManager::GetCID(), &rv);
|
||||
|
||||
// Should the JVM manager perhaps define methods for starting up LiveConnect?
|
||||
// Should the JVM manager perhaps define methods for starting up
|
||||
// LiveConnect?
|
||||
if (NS_SUCCEEDED(rv) && manager != nsnull) {
|
||||
PRBool started = PR_FALSE;
|
||||
rv = manager->StartupLiveConnect(mRuntime, started);
|
||||
rv = manager->StartupLiveConnect(sRuntime, started);
|
||||
}
|
||||
|
||||
globalCount++;
|
||||
isInitialized = NS_SUCCEEDED(rv);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsJSEnvironment::~nsJSEnvironment()
|
||||
// static
|
||||
void nsJSEnvironment::ShutDown()
|
||||
{
|
||||
if (--globalCount == 0) {
|
||||
delete gNameSpaceManager;
|
||||
gNameSpaceManager = nsnull;
|
||||
if (sGCTimer) {
|
||||
// We're being shut down, if we have a GC timer scheduled, cancel
|
||||
// it. The DOM factory will do one final GC once it's shut down.
|
||||
|
||||
sGCTimer->Cancel();
|
||||
|
||||
NS_RELEASE(sGCTimer);
|
||||
}
|
||||
|
||||
if (mRuntimeService)
|
||||
delete gNameSpaceManager;
|
||||
gNameSpaceManager = nsnull;
|
||||
|
||||
if (sRuntimeService) {
|
||||
nsServiceManager::ReleaseService(kJSRuntimeServiceContractID,
|
||||
mRuntimeService);
|
||||
sRuntimeService);
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS1(nsJSEnvironment,nsIObserver);
|
||||
|
||||
NS_IMETHODIMP nsJSEnvironment::Observe(nsISupports *aSubject,
|
||||
const char *aTopic,
|
||||
const PRUnichar *someData)
|
||||
// static
|
||||
nsresult
|
||||
nsJSEnvironment::CreateNewContext(nsIScriptContext **aContext)
|
||||
{
|
||||
NS_ASSERTION(!nsCRT::strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID),
|
||||
"not shutdown");
|
||||
NS_RELEASE_THIS(); // release ref from |GetScriptingEnvironment|
|
||||
*aContext = new nsJSContext(sRuntime);
|
||||
NS_ENSURE_TRUE(*aContext, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
NS_ADDREF(*aContext);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsIScriptContext* nsJSEnvironment::GetNewContext()
|
||||
{
|
||||
nsIScriptContext *context;
|
||||
context = new nsJSContext(mRuntime);
|
||||
NS_ADDREF(context);
|
||||
return context;
|
||||
}
|
||||
|
||||
nsresult
|
||||
NS_CreateScriptContext(nsIScriptGlobalObject *aGlobal,
|
||||
nsIScriptContext **aContext)
|
||||
{
|
||||
nsJSEnvironment *environment = nsJSEnvironment::GetScriptingEnvironment();
|
||||
if (!environment)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
nsresult rv = nsJSEnvironment::Init();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsIScriptContext *scriptContext = environment->GetNewContext();
|
||||
if (!scriptContext)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
*aContext = scriptContext;
|
||||
nsCOMPtr<nsIScriptContext> scriptContext;
|
||||
rv = nsJSEnvironment::CreateNewContext(getter_AddRefs(scriptContext));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Bind the script context and the global object
|
||||
nsresult rv = scriptContext->InitContext(aGlobal);
|
||||
rv = scriptContext->InitContext(aGlobal);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (aGlobal) {
|
||||
rv = aGlobal->SetContext(scriptContext);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
*aContext = scriptContext;
|
||||
|
||||
NS_ADDREF(*aContext);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
@@ -45,9 +45,12 @@
|
||||
#include "nsIObserver.h"
|
||||
#include "nsIScriptSecurityManager.h"
|
||||
#include "nsIXPCScriptNotify.h"
|
||||
#include "nsITimerCallback.h"
|
||||
|
||||
|
||||
class nsJSContext : public nsIScriptContext, public nsIXPCScriptNotify
|
||||
class nsJSContext : public nsIScriptContext,
|
||||
public nsIXPCScriptNotify,
|
||||
public nsITimerCallback
|
||||
{
|
||||
public:
|
||||
nsJSContext(JSRuntime *aRuntime);
|
||||
@@ -127,11 +130,16 @@ public:
|
||||
NS_IMETHOD SetGCOnDestruction(PRBool aGCOnDestruction);
|
||||
|
||||
NS_DECL_NSIXPCSCRIPTNOTIFY
|
||||
|
||||
NS_IMETHOD_(void) Notify(nsITimer *timer);
|
||||
|
||||
protected:
|
||||
nsresult InitClasses();
|
||||
nsresult InitializeExternalClasses();
|
||||
nsresult InitializeLiveConnectClasses();
|
||||
|
||||
void FireGCTimer();
|
||||
|
||||
private:
|
||||
JSContext *mContext;
|
||||
PRUint32 mNumEvaluations;
|
||||
@@ -165,24 +173,20 @@ private:
|
||||
|
||||
class nsIJSRuntimeService;
|
||||
|
||||
class nsJSEnvironment: public nsIObserver {
|
||||
class nsJSEnvironment
|
||||
{
|
||||
private:
|
||||
JSRuntime *mRuntime;
|
||||
nsIJSRuntimeService* mRuntimeService; /* XXXbe nsCOMPtr to service */
|
||||
static JSRuntime *sRuntime;
|
||||
static nsIJSRuntimeService *sRuntimeService;
|
||||
|
||||
public:
|
||||
static nsJSEnvironment *sTheEnvironment;
|
||||
|
||||
nsJSEnvironment();
|
||||
virtual ~nsJSEnvironment();
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIOBSERVER
|
||||
|
||||
nsIScriptContext* GetNewContext();
|
||||
static nsresult Init();
|
||||
|
||||
static nsJSEnvironment *GetScriptingEnvironment();
|
||||
|
||||
static nsresult CreateNewContext(nsIScriptContext **aContext);
|
||||
|
||||
static void ShutDown();
|
||||
};
|
||||
|
||||
/* prototypes */
|
||||
|
||||
@@ -177,11 +177,7 @@ nsJSUtils::GetStaticScriptGlobal(JSContext* aContext, JSObject* aObj,
|
||||
while ((parent = ::JS_GetParent(aContext, glob)))
|
||||
glob = parent;
|
||||
|
||||
#ifdef JS_THREADSAFE
|
||||
clazz = ::JS_GetClass(aContext, glob);
|
||||
#else
|
||||
clazz = ::JS_GetClass(glob);
|
||||
#endif
|
||||
clazz = JS_GET_CLASS(aContext, glob);
|
||||
|
||||
if (!clazz ||
|
||||
!(clazz->flags & JSCLASS_HAS_PRIVATE) ||
|
||||
|
||||
@@ -179,6 +179,7 @@ nsDOMSOFactory::Observe(nsISupports *aSubject,
|
||||
|
||||
GlobalWindowImpl::ShutDown();
|
||||
nsDOMClassInfo::ShutDown();
|
||||
nsJSEnvironment::ShutDown();
|
||||
|
||||
nsCOMPtr<nsIExceptionService> xs =
|
||||
do_GetService(NS_EXCEPTIONSERVICE_CONTRACTID);
|
||||
|
||||
Reference in New Issue
Block a user