Bug 409109: switching to new operation counting API. r=jst a=beltzner

This commit is contained in:
igor@mir2.org 2008-01-03 07:27:28 -08:00
Родитель 0e458d7af3
Коммит 74eea30220
3 изменённых файлов: 79 добавлений и 79 удалений

Просмотреть файл

@ -782,19 +782,17 @@ PrintWinCodebase(nsGlobalWindow *win)
}
#endif
// The number of branch callbacks between calls to JS_MaybeGC
#define MAYBE_GC_BRANCH_COUNT_MASK 0x00000fff // 4095
// The operation limit before we even check if our start timestamp is
// initialized. This is a fairly low number as we want to initialize the
// timestamp early enough to not waste much time before we get there, but we
// don't want to bother doing this too early as it's not generally necessary.
const PRUint32 INITIALIZE_TIME_OPERATION_LIMIT = 256 * 1000;
// The number of branch callbacks before we even check if our start
// timestamp is initialized. This is a fairly low number as we want to
// initialize the timestamp early enough to not waste much time before
// we get there, but we don't want to bother doing this too early as
// it's not generally necessary.
#define INITIALIZE_TIME_BRANCH_COUNT_MASK 0x000000ff // 255
// The operation limit between calls to JS_MaybeGC
const PRUint32 MAYBE_GC_OPERATION_LIMIT = INITIALIZE_TIME_OPERATION_LIMIT * 16;
// This function is called after each JS branch execution
JSBool JS_DLL_CALLBACK
nsJSContext::DOMBranchCallback(JSContext *cx, JSScript *script)
nsJSContext::DOMOperationCallback(JSContext *cx)
{
// Get the native context
nsJSContext *ctx = static_cast<nsJSContext *>(::JS_GetContextPrivate(cx));
@ -804,40 +802,34 @@ nsJSContext::DOMBranchCallback(JSContext *cx, JSScript *script)
return JS_TRUE;
}
PRUint32 callbackCount = ++ctx->mBranchCallbackCount;
if (callbackCount & INITIALIZE_TIME_BRANCH_COUNT_MASK) {
return JS_TRUE;
}
if (callbackCount == INITIALIZE_TIME_BRANCH_COUNT_MASK + 1 &&
LL_IS_ZERO(ctx->mBranchCallbackTime)) {
// Initialize mBranchCallbackTime to start timing how long the
if (LL_IS_ZERO(ctx->mOperationCallbackTime)) {
// Initialize mOperationCallbackTime to start timing how long the
// script has run
ctx->mBranchCallbackTime = PR_Now();
ctx->mIsTrackingChromeCodeTime =
::JS_IsSystemObject(cx, ::JS_GetGlobalObject(cx));
ctx->mOperationCallbackTime = PR_Now();
NS_ASSERTION(::JS_GetOperationLimit(cx) == INITIALIZE_TIME_OPERATION_LIMIT,
"The operation limit must match the initialization value");
::JS_SetOperationLimit(cx, MAYBE_GC_OPERATION_LIMIT);
return JS_TRUE;
}
if (callbackCount & MAYBE_GC_BRANCH_COUNT_MASK) {
return JS_TRUE;
}
NS_ASSERTION(::JS_GetOperationLimit(cx) == MAYBE_GC_OPERATION_LIMIT,
"The operation limit must match the long-running value");
// XXX Save the branch callback time so we can restore it after the GC,
// XXX Save the operation callback time so we can restore it after the GC,
// because GCing can cause JS to run on our context, causing our
// ScriptEvaluated to be called, and clearing our branch callback time and
// count. See bug 302333.
PRTime callbackTime = ctx->mBranchCallbackTime;
// ScriptEvaluated to be called, and clearing our operation callback time.
// See bug 302333.
PRTime callbackTime = ctx->mOperationCallbackTime;
// Run the GC if we get this far.
JS_MaybeGC(cx);
::JS_MaybeGC(cx);
// Now restore the callback time and count, in case they got reset.
ctx->mBranchCallbackTime = callbackTime;
ctx->mBranchCallbackCount = callbackCount;
// Now restore the callback time, in case they got reset.
ctx->mOperationCallbackTime = callbackTime;
PRBool isTrackingChromeCodeTime =
::JS_IsSystemObject(cx, ::JS_GetGlobalObject(cx));
PRTime now = PR_Now();
@ -846,7 +838,7 @@ nsJSContext::DOMBranchCallback(JSContext *cx, JSScript *script)
// Check the amount of time this script has been running, or if the
// dialog is disabled.
if (duration < (ctx->mIsTrackingChromeCodeTime ?
if (duration < (isTrackingChromeCodeTime ?
sMaxChromeScriptRunTime : sMaxScriptRunTime)) {
return JS_TRUE;
}
@ -871,7 +863,9 @@ nsJSContext::DOMBranchCallback(JSContext *cx, JSScript *script)
nsresult rv;
// Check if we should offer the option to debug
PRBool debugPossible = (cx->debugHooks->debuggerHandler != nsnull);
JSStackFrame* fp = ::JS_GetScriptedCaller(cx, NULL);
PRBool debugPossible = (fp != nsnull &&
cx->debugHooks->debuggerHandler != nsnull);
#ifdef MOZ_JSDEBUGGER
// Get the debugger service if necessary.
if (debugPossible) {
@ -939,6 +933,7 @@ nsJSContext::DOMBranchCallback(JSContext *cx, JSScript *script)
}
// Append file and line number information, if available
JSScript *script = fp ? ::JS_GetFrameScript(cx, fp) : nsnull;
if (script) {
const char *filename = ::JS_GetScriptFilename(cx, script);
if (filename) {
@ -988,23 +983,24 @@ nsJSContext::DOMBranchCallback(JSContext *cx, JSScript *script)
nsIPrefBranch *prefBranch = nsContentUtils::GetPrefBranch();
if (prefBranch) {
prefBranch->SetIntPref(ctx->mIsTrackingChromeCodeTime ?
prefBranch->SetIntPref(isTrackingChromeCodeTime ?
"dom.max_chrome_script_run_time" :
"dom.max_script_run_time", 0);
}
}
ctx->mBranchCallbackTime = PR_Now();
ctx->mOperationCallbackTime = PR_Now();
return JS_TRUE;
}
else if ((buttonPressed == 2) && debugPossible) {
// Debug the script
jsval rval;
switch(cx->debugHooks->debuggerHandler(cx, script, cx->fp->pc, &rval,
switch(cx->debugHooks->debuggerHandler(cx, script, ::JS_GetFramePC(cx, fp),
&rval,
cx->debugHooks->
debuggerHandlerData)) {
case JSTRAP_RETURN:
cx->fp->rval = rval;
fp->rval = rval;
return JS_TRUE;
case JSTRAP_ERROR:
cx->throwing = JS_FALSE;
@ -1092,9 +1088,7 @@ nsJSContext::nsJSContext(JSRuntime *aRuntime) : mGCOnDestruction(PR_TRUE)
++sContextCount;
mDefaultJSOptions = JSOPTION_PRIVATE_IS_NSISUPPORTS
| JSOPTION_NATIVE_BRANCH_CALLBACK
| JSOPTION_ANONFUNFIX;
mDefaultJSOptions = JSOPTION_PRIVATE_IS_NSISUPPORTS | JSOPTION_ANONFUNFIX;
// Let xpconnect resync its JSContext tracker. We do this before creating
// a new JSContext just in case the heap manager recycles the JSContext
@ -1113,7 +1107,8 @@ nsJSContext::nsJSContext(JSRuntime *aRuntime) : mGCOnDestruction(PR_TRUE)
JSOptionChangedCallback,
this);
::JS_SetBranchCallback(mContext, DOMBranchCallback);
::JS_SetOperationCallback(mContext, DOMOperationCallback,
INITIALIZE_TIME_OPERATION_LIMIT);
static JSLocaleCallbacks localeCallbacks =
{
@ -1129,10 +1124,8 @@ nsJSContext::nsJSContext(JSRuntime *aRuntime) : mGCOnDestruction(PR_TRUE)
mNumEvaluations = 0;
mTerminations = nsnull;
mScriptsEnabled = PR_TRUE;
mBranchCallbackCount = 0;
mBranchCallbackTime = LL_ZERO;
mOperationCallbackTime = LL_ZERO;
mProcessingScriptTag = PR_FALSE;
mIsTrackingChromeCodeTime = PR_FALSE;
}
nsJSContext::~nsJSContext()
@ -1167,8 +1160,8 @@ nsJSContext::Unlink()
// Clear our entry in the JSContext, bugzilla bug 66413
::JS_SetContextPrivate(mContext, nsnull);
// Clear the branch callback, bugzilla bug 238218
::JS_SetBranchCallback(mContext, nsnull);
// Clear the operation callback, bugzilla bug 238218
::JS_ClearOperationCallback(mContext);
// Unregister our "javascript.options.*" pref-changed callback.
nsContentUtils::UnregisterPrefCallback(js_options_dot_str,
@ -1475,10 +1468,12 @@ nsJSContext::EvaluateString(const nsAString& aScript,
return NS_ERROR_FAILURE;
}
// The result of evaluation, used only if there were no errors. This need
// not be a GC root currently, provided we run the GC only from the branch
// callback or from ScriptEvaluated. TODO: use JS_Begin/EndRequest to keep
// the GC from racing with JS execution on any thread.
// The result of evaluation, used only if there were no errors. This need
// not be a GC root currently, provided we run the GC only from the
// operation callback or from ScriptEvaluated.
//
// TODO: use JS_Begin/EndRequest to keep the GC from racing with JS
// execution on any thread.
jsval val;
nsJSContext::TerminationFuncHolder holder(this);
@ -1683,10 +1678,9 @@ nsJSContext::ExecuteScript(void *aScriptObject,
return NS_ERROR_FAILURE;
}
// The result of evaluation, used only if there were no errors. This need
// not be a GC root currently, provided we run the GC only from the branch
// callback or from ScriptEvaluated. TODO: use JS_Begin/EndRequest to keep
// the GC from racing with JS execution on any thread.
// The result of evaluation, used only if there were no errors. This need
// not be a GC root currently, provided we run the GC only from the
// operation callback or from ScriptEvaluated.
jsval val;
JSBool ok;
@ -3225,8 +3219,8 @@ nsJSContext::ScriptEvaluated(PRBool aTerminated)
}
#endif
mBranchCallbackCount = 0;
mBranchCallbackTime = LL_ZERO;
mOperationCallbackTime = LL_ZERO;
::JS_SetOperationLimit(mContext, INITIALIZE_TIME_OPERATION_LIMIT);
}
nsresult

Просмотреть файл

@ -277,10 +277,8 @@ private:
PRPackedBool mScriptsEnabled;
PRPackedBool mGCOnDestruction;
PRPackedBool mProcessingScriptTag;
PRPackedBool mIsTrackingChromeCodeTime;
PRUint32 mBranchCallbackCount;
PRTime mBranchCallbackTime;
PRTime mOperationCallbackTime;
PRUint32 mDefaultJSOptions;
// mGlobalWrapperRef is used only to hold a strong reference to the
@ -292,8 +290,7 @@ private:
static int PR_CALLBACK JSOptionChangedCallback(const char *pref, void *data);
static JSBool JS_DLL_CALLBACK DOMBranchCallback(JSContext *cx,
JSScript *script);
static JSBool JS_DLL_CALLBACK DOMOperationCallback(JSContext *cx);
};
class nsIJSRuntimeService;

Просмотреть файл

@ -3354,11 +3354,9 @@ public:
NS_DECL_ISUPPORTS
private:
static JSBool JS_DLL_CALLBACK ContextHolderBranchCallback(JSContext *cx,
JSScript *script);
static JSBool JS_DLL_CALLBACK ContextHolderOperationCallback(JSContext *cx);
XPCAutoJSContext mJSContext;
JSBranchCallback mOrigBranchCallback;
JSContext* mOrigCx;
};
@ -3366,38 +3364,49 @@ NS_IMPL_ISUPPORTS0(ContextHolder)
ContextHolder::ContextHolder(JSContext *aOuterCx, JSObject *aSandbox)
: mJSContext(JS_NewContext(JS_GetRuntime(aOuterCx), 1024), JS_FALSE),
mOrigBranchCallback(nsnull),
mOrigCx(aOuterCx)
{
if (mJSContext) {
if(mJSContext)
{
JS_SetOptions(mJSContext,
JSOPTION_DONT_REPORT_UNCAUGHT |
JSOPTION_PRIVATE_IS_NSISUPPORTS);
JS_SetGlobalObject(mJSContext, aSandbox);
JS_SetContextPrivate(mJSContext, this);
// Now cache the original branch callback
mOrigBranchCallback = JS_SetBranchCallback(aOuterCx, nsnull);
JS_SetBranchCallback(aOuterCx, mOrigBranchCallback);
if (mOrigBranchCallback) {
JS_SetBranchCallback(mJSContext, ContextHolderBranchCallback);
if(JS_GetOperationCallback(aOuterCx))
{
JS_SetOperationCallback(mJSContext, ContextHolderOperationCallback,
JS_GetOperationLimit(aOuterCx));
}
}
}
JSBool JS_DLL_CALLBACK
ContextHolder::ContextHolderBranchCallback(JSContext *cx, JSScript *script)
ContextHolder::ContextHolderOperationCallback(JSContext *cx)
{
ContextHolder* thisObject =
static_cast<ContextHolder*>(JS_GetContextPrivate(cx));
NS_ASSERTION(thisObject, "How did that happen?");
if (thisObject->mOrigBranchCallback) {
return (thisObject->mOrigBranchCallback)(thisObject->mOrigCx, script);
JSContext *origCx = thisObject->mOrigCx;
JSOperationCallback callback = JS_GetOperationCallback(origCx);
JSBool ok = JS_TRUE;
if(callback)
{
ok = callback(origCx);
callback = JS_GetOperationCallback(origCx);
if(callback)
{
// If the callback is still set in the original context, reflect
// a possibly updated operation limit into cx.
JS_SetOperationLimit(cx, JS_GetOperationLimit(origCx));
return ok;
}
}
return JS_TRUE;
JS_ClearOperationCallback(cx);
return ok;
}
/***************************************************************************/