From d4582a89b74000b1dbd2fbb8723143606d7eb615 Mon Sep 17 00:00:00 2001 From: Andrei Saprykin Date: Mon, 24 Nov 2008 00:15:46 +0100 Subject: [PATCH] bug 453157 - watchdog thread as an alternative to operation counting, r=igor,mrbkap a19b2=beltzner (CLOSED TREE) --- build/Makefile.in | 6 + build/pgo/Makefile.in | 6 + build/pgo/automation.py.in | 8 + dom/src/base/nsJSEnvironment.cpp | 18 +- dom/src/threads/nsDOMThreadService.cpp | 4 +- js/src/js.cpp | 227 ++++++++++++++++++++++++- js/src/jsapi.cpp | 50 +++++- js/src/jsapi.h | 26 ++- js/src/jscntxt.cpp | 20 ++- js/src/jscntxt.h | 52 ++++-- js/src/jsinterp.cpp | 15 +- js/src/jspubtd.h | 30 ++-- js/src/jsversion.h | 8 + js/src/xpconnect/idl/nsIXPConnect.idl | 19 ++- js/src/xpconnect/src/nsXPConnect.cpp | 76 +++++---- js/src/xpconnect/src/xpccomponents.cpp | 182 ++++++++++---------- js/src/xpconnect/src/xpccontext.cpp | 1 + js/src/xpconnect/src/xpcjsruntime.cpp | 169 ++++++++++++++++-- js/src/xpconnect/src/xpcprivate.h | 102 ++++++----- testing/mochitest/Makefile.in | 6 + 20 files changed, 785 insertions(+), 240 deletions(-) diff --git a/build/Makefile.in b/build/Makefile.in index d1cc3a5f5ac..98dabea6a77 100644 --- a/build/Makefile.in +++ b/build/Makefile.in @@ -109,6 +109,12 @@ else AUTOMATION_PPARGS += -DIS_TEST_BUILD=0 endif +ifeq ($(MOZ_DEBUG), 1) +AUTOMATION_PPARGS += -DIS_DEBUG_BUILD=1 +else +AUTOMATION_PPARGS += -DIS_DEBUG_BUILD=0 +endif + _LEAKTEST_DIR = $(DEPTH)/_leaktest _LEAKTEST_FILES = \ diff --git a/build/pgo/Makefile.in b/build/pgo/Makefile.in index fca787c2a3c..cf00bd156c7 100644 --- a/build/pgo/Makefile.in +++ b/build/pgo/Makefile.in @@ -114,6 +114,12 @@ else AUTOMATION_PPARGS += -DIS_TEST_BUILD=0 endif +ifeq ($(MOZ_DEBUG), 1) +AUTOMATION_PPARGS += -DIS_DEBUG_BUILD=1 +else +AUTOMATION_PPARGS += -DIS_DEBUG_BUILD=0 +endif + automation.py: automation.py.in $(PYTHON) $(topsrcdir)/config/Preprocessor.py \ $(AUTOMATION_PPARGS) $(DEFINES) $(ACDEFINES) $^ > $@ diff --git a/build/pgo/automation.py.in b/build/pgo/automation.py.in index c054fd051d4..6b9f1ec9d8f 100644 --- a/build/pgo/automation.py.in +++ b/build/pgo/automation.py.in @@ -84,6 +84,7 @@ UNIXISH = not IS_WIN32 and not IS_MAC #expand DEFAULT_APP = "./" + __BROWSER_PATH__ #expand CERTS_DIR = __CERTS_DIR__ #expand IS_TEST_BUILD = __IS_TEST_BUILD__ +#expand IS_DEBUG_BUILD = __IS_DEBUG_BUILD__ ########### # LOGGING # @@ -314,6 +315,13 @@ user_pref("camino.warn_when_closing", false); // Camino-only, harmless to others """ prefs.append(part) + # Increase the max script run time 10-fold for debug builds + if (IS_DEBUG_BUILD): + prefs.append("""\ +user_pref("dom.max_script_run_time", 100); +user_pref("dom.max_chrome_script_run_time", 200); +""") + locations = readLocations() # Grant God-power to all the privileged servers on which tests run. diff --git a/dom/src/base/nsJSEnvironment.cpp b/dom/src/base/nsJSEnvironment.cpp index 4b46e0752c6..78eedbddc9f 100644 --- a/dom/src/base/nsJSEnvironment.cpp +++ b/dom/src/base/nsJSEnvironment.cpp @@ -850,9 +850,6 @@ PrintWinCodebase(nsGlobalWindow *win) } #endif -// The accumulated operation weight before we call MaybeGC -const PRUint32 MAYBE_GC_OPERATION_WEIGHT = 5000 * JS_OPERATION_WEIGHT_BASE; - static void MaybeGC(JSContext *cx) { @@ -932,24 +929,24 @@ nsJSContext::DOMOperationCallback(JSContext *cx) if (nsContentUtils::GetBoolPref("dom.prevent_oom_dialog", PR_FALSE)) return JS_FALSE; - + nsCOMPtr prompt = GetPromptFromContext(ctx); - + nsXPIDLString title, msg; rv = nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES, "LowMemoryTitle", title); - + rv |= nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES, "LowMemoryMessage", msg); - + //GetStringFromName can return NS_OK and still give NULL string if (NS_FAILED(rv) || !title || !msg) { NS_ERROR("Failed to get localized strings."); return JS_FALSE; } - + prompt->Alert(title, msg); return JS_FALSE; } @@ -1236,9 +1233,8 @@ nsJSContext::nsJSContext(JSRuntime *aRuntime) : mGCOnDestruction(PR_TRUE) nsContentUtils::RegisterPrefCallback(js_options_dot_str, JSOptionChangedCallback, this); - - ::JS_SetOperationCallback(mContext, DOMOperationCallback, - MAYBE_GC_OPERATION_WEIGHT); + ::JS_SetOperationCallback(mContext, DOMOperationCallback); + nsContentUtils::XPConnect()->SetWatchdogLimit(mContext, PR_TicksPerSecond()/10); static JSLocaleCallbacks localeCallbacks = { diff --git a/dom/src/threads/nsDOMThreadService.cpp b/dom/src/threads/nsDOMThreadService.cpp index b9519d463eb..25ef7b2b556 100644 --- a/dom/src/threads/nsDOMThreadService.cpp +++ b/dom/src/threads/nsDOMThreadService.cpp @@ -733,8 +733,8 @@ nsDOMThreadService::CreateJSContext() JS_SetErrorReporter(cx, DOMWorkerErrorReporter); - JS_SetOperationCallback(cx, DOMWorkerOperationCallback, - 100 * JS_OPERATION_WEIGHT_BASE); + JS_SetOperationCallback(cx, DOMWorkerOperationCallback); + nsContentUtils::XPConnect()->SetWatchdogLimit(cx, PR_TicksPerSecond()/100); static JSSecurityCallbacks securityCallbacks = { nsDOMWorkerSecurityManager::JSCheckAccess, diff --git a/js/src/js.cpp b/js/src/js.cpp index 9ffc84796a3..674c140fb6a 100644 --- a/js/src/js.cpp +++ b/js/src/js.cpp @@ -104,8 +104,9 @@ static size_t gMaxStackSize = 500000; static jsuword gStackBase; static size_t gScriptStackQuota = JS_DEFAULT_SCRIPT_STACK_QUOTA; - +#if JS_HAS_OPERATION_COUNT static JSBool gEnableBranchCallback = JS_FALSE; +#endif static uint32 gBranchCount; static uint32 gBranchLimit; @@ -117,6 +118,18 @@ FILE *gOutFile = NULL; static JSBool reportWarnings = JS_TRUE; static JSBool compileOnly = JS_FALSE; +#if !JS_HAS_OPERATION_COUNT +/* + * Variables to support watchdog thread. + */ +static PRLock *gWatchdogLock; +static PRCondVar *gWatchdogWakeup; +static PRBool gWatchdogRunning; +static PRThread *gWatchdogThread; +static PRIntervalTime gCurrentInterval; +static PRIntervalTime gWatchdogLimit; +#endif + typedef enum JSShellErrNum { #define MSG_DEF(name, number, count, exception, format) \ name = number, @@ -169,6 +182,7 @@ GetLine(JSContext *cx, char *bufp, FILE *file, const char *prompt) { return JS_TRUE; } +#if JS_HAS_OPERATION_COUNT static JSBool my_BranchCallback(JSContext *cx, JSScript *script) { @@ -197,6 +211,132 @@ my_BranchCallback(JSContext *cx, JSScript *script) #endif return JS_TRUE; } +#endif + +#if !JS_HAS_OPERATION_COUNT +static void +ShutdownWatchdog() +{ + PRThread *t; + + PR_Lock(gWatchdogLock); + gWatchdogRunning = PR_FALSE; + t = gWatchdogThread; + gWatchdogThread = NULL; + PR_NotifyCondVar(gWatchdogWakeup); + PR_Unlock(gWatchdogLock); + if (t) + PR_JoinThread(t); +} + +static void +WakeupWatchdog() +{ + PR_Lock(gWatchdogLock); + if (gWatchdogThread && gWatchdogLimit && + (gCurrentInterval == PR_INTERVAL_NO_TIMEOUT || + gCurrentInterval > gWatchdogLimit)) { + PR_NotifyCondVar(gWatchdogWakeup); + } + PR_Unlock(gWatchdogLock); + +} + +static JSBool +ShellOperationCallback(JSContext *cx) +{ + if (gWatchdogLimit) { + fprintf(stderr, "Error: Script is running too long\n"); + return JS_FALSE; + } + return JS_TRUE; +} + +static void +WatchdogMain(void *arg) +{ + JSRuntime *rt = (JSRuntime *) arg; + PRStatus status; + PRBool isRunning; + + do { + JSContext *iter = NULL; + JSContext *acx; + JSBool isContextRunning = JS_FALSE; + PRIntervalTime ct = PR_IntervalNow(); + + PR_Lock(gWatchdogLock); + if (gWatchdogLimit) { + JS_LOCK_GC(rt); + while ((acx = js_ContextIterator(rt, JS_FALSE, &iter))) { + if (acx->requestDepth) { + if (ct - acx->startTime > gWatchdogLimit) + JS_TriggerOperationCallback(acx); + if (!isContextRunning) + isContextRunning = JS_TRUE; + } + } + JS_UNLOCK_GC(rt); + } + gCurrentInterval = (isContextRunning && gWatchdogLimit) + ? gWatchdogLimit + : PR_INTERVAL_NO_TIMEOUT; + if (gWatchdogRunning) + status = PR_WaitCondVar(gWatchdogWakeup, gCurrentInterval); + isRunning = gWatchdogRunning; + PR_Unlock(gWatchdogLock); + } while (isRunning && status == PR_SUCCESS); +} + +/* + * Get the watchdog limit associated with the watchdog callback. + */ +static PRIntervalTime +GetWatchdogLimit(JSContext *cx) +{ + return gWatchdogLimit; +} + +/* + * Change the watchdog limit associated with the watchdog callback. This API + * function may be called only when the result of JS_GetOperationCallback(cx) + * is not null. + */ +static JSBool +SetWatchdogLimit(JSContext *cx, PRIntervalTime newWatchdogLimit) +{ + if (newWatchdogLimit == gWatchdogLimit) + return JS_TRUE; + + gWatchdogLimit = newWatchdogLimit; + + /* + * Start a new watchdog thread if it has not been started. If it has been + * started wake up the thread and cause the watchdog rescheduling. + */ + PR_Lock(gWatchdogLock); + if (!gWatchdogThread) { + gWatchdogRunning = PR_TRUE; + gWatchdogThread = + PR_CreateThread(PRThreadType(PR_USER_THREAD), + WatchdogMain, + cx->runtime, + PRThreadPriority(PR_PRIORITY_NORMAL), + PRThreadScope(PR_LOCAL_THREAD), + PRThreadState(PR_JOINABLE_THREAD), + 0); + } + PR_Unlock(gWatchdogLock); + if (!gWatchdogThread) { + JS_ReportError(cx, "Failed to create watchdog thread"); + return JS_FALSE; + } + + WakeupWatchdog(); + return JS_TRUE; +} + +#endif static void SetContextOptions(JSContext *cx) @@ -217,10 +357,12 @@ SetContextOptions(JSContext *cx) } JS_SetThreadStackLimit(cx, stackLimit); JS_SetScriptStackQuota(cx, gScriptStackQuota); +#if JS_HAS_OPERATION_COUNT if (gEnableBranchCallback) { JS_SetBranchCallback(cx, my_BranchCallback); JS_ToggleOptions(cx, JSOPTION_NATIVE_BRANCH_CALLBACK); } +#endif } static void @@ -380,7 +522,9 @@ ProcessArgs(JSContext *cx, JSObject *obj, char **argv, int argc) break; } switch (argv[i][1]) { +#if JS_HAS_OPERATION_COUNT case 'b': +#endif case 'c': case 'f': case 'e': @@ -471,7 +615,7 @@ extern void js_InitJITStatsClass(JSContext *cx, JSObject *glob); &jitstats_class, NULL, 0); #endif break; - + case 'o': if (++i == argc) return usage(); @@ -501,10 +645,12 @@ extern void js_InitJITStatsClass(JSContext *cx, JSObject *glob); } break; +#if JS_HAS_OPERATION_COUNT case 'b': gBranchLimit = atoi(argv[++i]); gEnableBranchCallback = (gBranchLimit != 0); break; +#endif case 'c': /* set stack chunk size */ @@ -1451,7 +1597,7 @@ DisassFile(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) JSScript *script; JSBool ok; uint32 oldopts; - + if (!argc) return JS_TRUE; @@ -1471,7 +1617,7 @@ DisassFile(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) obj = JS_NewScriptObject(cx, script); if (!obj) return JS_FALSE; - + *rval = OBJECT_TO_JSVAL(obj); /* I like to root it, root it. */ ok = Disassemble(cx, obj, 1, rval, rval); /* gross, but works! */ *rval = JSVAL_VOID; @@ -2124,6 +2270,38 @@ ThrowError(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) return JS_FALSE; } +#if !JS_HAS_OPERATION_COUNT + +static JSBool +WatchdogInterval(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + if (argc > 1) { + JS_ReportError(cx, "Wrong number of arguments"); + return JS_FALSE; + } + + if (argc == 0) + return JS_NewDoubleValue(cx, GetWatchdogLimit(cx), rval); + + jsdouble interval; + if (!JS_ValueToNumber(cx, argv[0], &interval)) + return JS_FALSE; + + /* NB: The next condition take negative values and NaNs into account. */ + if (!(interval >= 0.0)) { + JS_ReportError(cx, "Negative or NaN argument value"); + return JS_FALSE; + } + if (interval > 1800.0) { + JS_ReportError(cx, "Excessive argument value"); + return JS_FALSE; + } + + return SetWatchdogLimit(cx, (PRIntervalTime) (interval * PR_TicksPerSecond())); +} + +#endif + #define LAZY_STANDARD_CLASSES /* A class for easily testing the inner/outer object callbacks. */ @@ -2684,12 +2862,14 @@ Scatter(JSContext *cx, uintN argc, jsval *vp) JSBool ok; jsrefcount rc; +#if JS_HAS_OPERATION_COUNT if (!gEnableBranchCallback) { /* Enable the branch callback, for periodic scope-sharing. */ gEnableBranchCallback = JS_TRUE; JS_SetBranchCallback(cx, my_BranchCallback); JS_ToggleOptions(cx, JSOPTION_NATIVE_BRANCH_CALLBACK); } +#endif sd.lock = NULL; sd.cvar = NULL; @@ -2865,6 +3045,9 @@ static JSFunctionSpec shell_functions[] = { JS_FS("stringsAreUTF8", StringsAreUTF8, 0,0,0), JS_FS("testUTF8", TestUTF8, 1,0,0), JS_FS("throwError", ThrowError, 0,0,0), +#if !JS_HAS_OPERATION_COUNT + JS_FS("watchint", WatchdogInterval, 1,0,0), +#endif #ifdef DEBUG JS_FS("dis", Disassemble, 1,0,0), JS_FS("disfile", DisassFile, 1,0,0), @@ -2946,6 +3129,11 @@ static const char *const shell_help_messages[] = { "stringsAreUTF8() Check if strings are UTF-8 encoded", "testUTF8(mode) Perform UTF-8 tests (modes are 1 to 4)", "throwError() Throw an error from JS_ReportError", +#if !JS_HAS_OPERATION_COUNT +"watchint(interval) Set watchdog interval to the specified number" +" of seconds. If parameters are not specified it returns watchdog interval" +" in seconds", +#endif #ifdef DEBUG "dis([fun]) Disassemble functions into bytecodes", "disfile('foo.js') Disassemble script file into bytecodes", @@ -3887,7 +4075,14 @@ ContextCallback(JSContext *cx, uintN contextOp) JS_SetErrorReporter(cx, my_ErrorReporter); JS_SetVersion(cx, JSVERSION_LATEST); SetContextOptions(cx); +#if !JS_HAS_OPERATION_COUNT + JS_SetOperationCallback(cx, ShellOperationCallback); + } else if (contextOp == JSCONTEXT_REQUEST_START && + cx->runtime->state != JSRTS_LANDING) { + WakeupWatchdog(); +#endif } + return JS_TRUE; } @@ -3934,6 +4129,18 @@ main(int argc, char **argv, char **envp) rt = JS_NewRuntime(64L * 1024L * 1024L); if (!rt) return 1; + +#if !JS_HAS_OPERATION_COUNT + gWatchdogLock = JS_NEW_LOCK(); + if (!gWatchdogLock) + return 1; + gWatchdogWakeup = JS_NEW_CONDVAR(gWatchdogLock); + if (!gWatchdogWakeup) + return 1; + gWatchdogLimit = 0; + gWatchdogThread = NULL; +#endif + JS_SetContextCallback(rt, ContextCallback); cx = JS_NewContext(rt, gStackChunkSize); @@ -4043,8 +4250,20 @@ main(int argc, char **argv, char **envp) JS_EndRequest(cx); #endif +#if !JS_HAS_OPERATION_COUNT + ShutdownWatchdog(); +#endif + +#if !JS_HAS_OPERATION_COUNT + if (gWatchdogWakeup) + JS_DESTROY_CONDVAR(gWatchdogWakeup); + if (gWatchdogLock) + JS_DESTROY_LOCK(gWatchdogLock); +#endif + JS_DestroyContext(cx); JS_DestroyRuntime(rt); JS_ShutDown(); + return result; } diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index db713c82629..4341e63835d 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -903,6 +903,7 @@ JS_BeginRequest(JSContext *cx) { #ifdef JS_THREADSAFE JSRuntime *rt; + JSContextCallback cxCallback; JS_ASSERT(cx->thread->id == js_CurrentThreadId()); if (!cx->requestDepth) { @@ -910,6 +911,9 @@ JS_BeginRequest(JSContext *cx) /* Wait until the GC is finished. */ rt = cx->runtime; +#if !JS_HAS_OPERATION_COUNT + cx->startTime = PR_IntervalNow(); +#endif JS_LOCK_GC(rt); /* NB: we use cx->thread here, not js_GetCurrentThread(). */ @@ -923,6 +927,16 @@ JS_BeginRequest(JSContext *cx) cx->requestDepth = 1; cx->outstandingRequests++; JS_UNLOCK_GC(rt); + + cxCallback = cx->runtime->cxCallback; + if (cxCallback) { +#ifdef DEBUG + JSBool callbackStatus = +#endif + cxCallback(cx, JSCONTEXT_REQUEST_START); + JS_ASSERT(callbackStatus); + } + return; } cx->requestDepth++; @@ -983,7 +997,9 @@ JS_EndRequest(JSContext *cx) rt->requestCount--; if (rt->requestCount == 0) JS_NOTIFY_REQUEST_DONE(rt); - +#if !JS_HAS_OPERATION_COUNT + cx->startTime = 0; +#endif JS_UNLOCK_GC(rt); return; } @@ -3130,7 +3146,7 @@ DefinePropertyById(JSContext *cx, JSObject *obj, jsid id, jsval value, attrs, flags, tinyid, NULL); } return OBJ_DEFINE_PROPERTY(cx, obj, id, value, getter, setter, attrs, - NULL); + NULL); } static JSBool @@ -3807,7 +3823,7 @@ JS_HasUCProperty(JSContext *cx, JSObject *obj, JSProperty *prop; CHECK_REQUEST(cx); - ok = LookupUCProperty(cx, obj, name, namelen, + ok = LookupUCProperty(cx, obj, name, namelen, JSRESOLVE_QUALIFIED | JSRESOLVE_DETECTING, &obj2, &prop); if (ok) { @@ -5244,6 +5260,7 @@ JS_CallFunctionValue(JSContext *cx, JSObject *obj, jsval fval, uintN argc, return ok; } +#if JS_HAS_OPERATION_COUNT JS_PUBLIC_API(void) JS_SetOperationCallback(JSContext *cx, JSOperationCallback callback, uint32 operationLimit) @@ -5319,6 +5336,33 @@ JS_SetBranchCallback(JSContext *cx, JSBranchCallback cb) } return oldcb; } +#else + +JS_PUBLIC_API(void) +JS_SetOperationCallback(JSContext *cx, JSOperationCallback callback) +{ + cx->operationCallback = callback; +} + +JS_PUBLIC_API(void) +JS_ClearOperationCallback(JSContext *cx) +{ + cx->operationCallback = NULL; +} + +JS_PUBLIC_API(JSOperationCallback) +JS_GetOperationCallback(JSContext *cx) +{ + return cx->operationCallback; +} + +#endif + +JS_PUBLIC_API(void) +JS_TriggerOperationCallback(JSContext *cx) +{ + cx->operationCount = 0; +} JS_PUBLIC_API(JSBool) JS_IsRunning(JSContext *cx) diff --git a/js/src/jsapi.h b/js/src/jsapi.h index 78421929151..db250c80537 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -45,6 +45,7 @@ */ #include #include +#include "jsversion.h" #include "js-config.h" #include "jspubtd.h" #include "jsutil.h" @@ -2166,6 +2167,13 @@ extern JS_PUBLIC_API(JSBool) JS_CallFunctionValue(JSContext *cx, JSObject *obj, jsval fval, uintN argc, jsval *argv, jsval *rval); +extern JS_PUBLIC_API(void) +JS_ClearOperationCallback(JSContext *cx); + +extern JS_PUBLIC_API(JSOperationCallback) +JS_GetOperationCallback(JSContext *cx); + +#if JS_HAS_OPERATION_COUNT /* * The maximum value of the operation limit to pass to JS_SetOperationCallback * and JS_SetOperationLimit. @@ -2190,12 +2198,6 @@ extern JS_PUBLIC_API(void) JS_SetOperationCallback(JSContext *cx, JSOperationCallback callback, uint32 operationLimit); -extern JS_PUBLIC_API(void) -JS_ClearOperationCallback(JSContext *cx); - -extern JS_PUBLIC_API(JSOperationCallback) -JS_GetOperationCallback(JSContext *cx); - /* * Get the operation limit associated with the operation callback. This API * function may be called only when the result of JS_GetOperationCallback(cx) @@ -2223,6 +2225,18 @@ JS_SetOperationLimit(JSContext *cx, uint32 operationLimit); */ extern JS_PUBLIC_API(JSBranchCallback) JS_SetBranchCallback(JSContext *cx, JSBranchCallback cb); +#else +/* + * Set the operation callback that the engine calls while resetting + * context. + */ +extern JS_PUBLIC_API(void) +JS_SetOperationCallback(JSContext *cx, JSOperationCallback callback); + +#endif + +extern JS_PUBLIC_API(void) +JS_TriggerOperationCallback(JSContext *cx); extern JS_PUBLIC_API(JSBool) JS_IsRunning(JSContext *cx); diff --git a/js/src/jscntxt.cpp b/js/src/jscntxt.cpp index c06a334cb6f..4212a9f4e45 100644 --- a/js/src/jscntxt.cpp +++ b/js/src/jscntxt.cpp @@ -241,7 +241,11 @@ js_NewContext(JSRuntime *rt, size_t stackChunkSize) memset(cx, 0, sizeof *cx); cx->runtime = rt; +#if JS_OPERATION_COUNT JS_ClearOperationCallback(cx); +#else + cx->operationCount = 1; +#endif cx->debugHooks = &rt->globalDebugHooks; #if JS_STACK_GROWTH_DIRECTION > 0 cx->stackLimit = (jsuword)-1; @@ -516,7 +520,7 @@ js_ValidContextPointer(JSRuntime *rt, JSContext *cx) return JS_FALSE; } -JSContext * +JS_FRIEND_API(JSContext *) js_ContextIterator(JSRuntime *rt, JSBool unlocked, JSContext **iterp) { JSContext *cx = *iterp; @@ -1371,10 +1375,10 @@ js_GetErrorMessage(void *userRef, const char *locale, const uintN errorNumber) JSBool js_ResetOperationCount(JSContext *cx) { - JSScript *script; - JS_ASSERT(cx->operationCount <= 0); - JS_ASSERT(cx->operationLimit > 0); + +#if JS_HAS_OPERATION_COUNT + JSScript *script; cx->operationCount = (int32) cx->operationLimit; if (cx->operationCallbackIsSet) @@ -1390,5 +1394,13 @@ js_ResetOperationCount(JSContext *cx) if (script || JS_HAS_OPTION(cx, JSOPTION_NATIVE_BRANCH_CALLBACK)) return ((JSBranchCallback) cx->operationCallback)(cx, script); } +#else + JSOperationCallback operationCallback; + + cx->operationCount = 1; + operationCallback = cx->operationCallback; + if (operationCallback) + return operationCallback(cx); +#endif return JS_TRUE; } diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h index c201f1f8823..07a965050cc 100644 --- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -739,7 +739,11 @@ struct JSContext { * Operation count. It is declared early in the structure as a frequently * accessed field. */ +#if !JS_HAS_OPERATION_COUNT + volatile int32 operationCount; +#else int32 operationCount; +#endif #if JS_HAS_XML_SUPPORT /* @@ -848,8 +852,10 @@ struct JSContext { * but operationCallback is not null, operationCallback stores the branch * callback. */ +#if JS_HAS_OPERATION_COUNT uint32 operationCallbackIsSet : 1; uint32 operationLimit : 31; +#endif JSOperationCallback operationCallback; /* Interpreter activation count. */ @@ -875,6 +881,10 @@ struct JSContext { ((JSContext *)((char *)(tl) - offsetof(JSContext, threadLinks))) #endif +#if !JS_HAS_OPERATION_COUNT + PRIntervalTime startTime; /* time when the context thread was started */ +#endif + /* PDL of stack headers describing stack slots not rooted by argv, etc. */ JSStackHeader *stackHeaders; @@ -1038,7 +1048,7 @@ js_ValidContextPointer(JSRuntime *rt, JSContext *cx); * If unlocked, acquire and release rt->gcLock around *iterp update; otherwise * the caller must be holding rt->gcLock. */ -extern JSContext * +extern JS_FRIEND_API(JSContext *) js_ContextIterator(JSRuntime *rt, JSBool unlocked, JSContext **iterp); /* @@ -1200,9 +1210,11 @@ extern JSErrorFormatString js_ErrorFormatString[JSErr_Limit]; * This macro can run the full GC. Return true if it is OK to continue and * false otherwise. */ -#define JS_CHECK_OPERATION_LIMIT(cx, weight) \ +#if JS_HAS_OPERATION_COUNT + +# define JS_CHECK_OPERATION_LIMIT(cx, weight) \ (JS_CHECK_OPERATION_WEIGHT(weight), \ - (((cx)->operationCount -= (weight)) > 0 || js_ResetOperationCount(cx))) + (((cx)->operationCount -= (weight)) > 0 || js_ResetOperationCount(cx))) /* * A version of JS_CHECK_OPERATION_LIMIT that just updates the operation count @@ -1210,31 +1222,35 @@ extern JSErrorFormatString js_ErrorFormatString[JSErr_Limit]; * the count to 0 when it becomes negative to prevent a wrap-around when the * macro is called repeatably. */ -#define JS_COUNT_OPERATION(cx, weight) \ +# define JS_COUNT_OPERATION(cx, weight) \ ((void)(JS_CHECK_OPERATION_WEIGHT(weight), \ - (cx)->operationCount = ((cx)->operationCount > 0) \ - ? (cx)->operationCount - (weight) \ - : 0)) + (cx)->operationCount = ((cx)->operationCount > 0) \ + ? (cx)->operationCount - (weight) \ + : 0)) /* * The implementation of the above macros assumes that subtracting weights * twice from a positive number does not wrap-around INT32_MIN. */ -#define JS_CHECK_OPERATION_WEIGHT(weight) \ +# define JS_CHECK_OPERATION_WEIGHT(weight) \ (JS_ASSERT((uint32) (weight) > 0), \ JS_ASSERT((uint32) (weight) < JS_BIT(30))) /* Relative operations weights. */ -#define JSOW_JUMP 1 -#define JSOW_ALLOCATION 100 -#define JSOW_LOOKUP_PROPERTY 5 -#define JSOW_GET_PROPERTY 10 -#define JSOW_SET_PROPERTY 20 -#define JSOW_NEW_PROPERTY 200 -#define JSOW_DELETE_PROPERTY 30 -#define JSOW_ENTER_SHARP JS_OPERATION_WEIGHT_BASE -#define JSOW_SCRIPT_JUMP JS_OPERATION_WEIGHT_BASE - +# define JSOW_JUMP 1 +# define JSOW_ALLOCATION 100 +# define JSOW_LOOKUP_PROPERTY 5 +# define JSOW_GET_PROPERTY 10 +# define JSOW_SET_PROPERTY 20 +# define JSOW_NEW_PROPERTY 200 +# define JSOW_DELETE_PROPERTY 30 +# define JSOW_ENTER_SHARP JS_OPERATION_WEIGHT_BASE +# define JSOW_SCRIPT_JUMP JS_OPERATION_WEIGHT_BASE +#else +# define JS_CHECK_OPERATION_LIMIT(cx, weight) \ + (((cx)->operationCount) > 0 || js_ResetOperationCount(cx)) +# define JS_COUNT_OPERATION(cx, weight) ((void) 0) +#endif /* * Reset the operation count and call the operation callback assuming that the * operation limit is reached. diff --git a/js/src/jsinterp.cpp b/js/src/jsinterp.cpp index 3131507577e..cece7379f36 100644 --- a/js/src/jsinterp.cpp +++ b/js/src/jsinterp.cpp @@ -2665,14 +2665,23 @@ js_Interpret(JSContext *cx) * Prepare to call a user-supplied branch handler, and abort the script * if it returns false. */ -#define CHECK_BRANCH() \ +#if JS_HAS_OPERATION_COUNT +# define CHECK_BRANCH() \ JS_BEGIN_MACRO \ if ((cx->operationCount -= JSOW_SCRIPT_JUMP) <= 0) { \ if (!js_ResetOperationCount(cx)) \ goto error; \ } \ JS_END_MACRO - +#else +# define CHECK_BRANCH() \ + JS_BEGIN_MACRO \ + if (cx->operationCount < 1) { \ + if (!js_ResetOperationCount(cx)) \ + goto error; \ + } \ + JS_END_MACRO +#endif #define BRANCH(n) \ JS_BEGIN_MACRO \ regs.pc += n; \ @@ -5951,7 +5960,7 @@ js_Interpret(JSContext *cx) } TRACE_2(DefLocalFunSetSlot, slot, obj); - + fp->slots[slot] = OBJECT_TO_JSVAL(obj); END_CASE(JSOP_DEFLOCALFUN) diff --git a/js/src/jspubtd.h b/js/src/jspubtd.h index 1e3f33aff06..cd67cbd5d1e 100644 --- a/js/src/jspubtd.h +++ b/js/src/jspubtd.h @@ -106,8 +106,8 @@ typedef enum JSAccessMode { JSACC_PROTO = 0, /* XXXbe redundant w.r.t. id */ JSACC_PARENT = 1, /* XXXbe redundant w.r.t. id */ - /* - * enum value #2 formerly called JSACC_IMPORT, + /* + * enum value #2 formerly called JSACC_IMPORT, * gap preserved for liveconnect ABI compatibility. */ @@ -594,21 +594,25 @@ typedef JSBool typedef enum JSContextOp { JSCONTEXT_NEW, - JSCONTEXT_DESTROY + JSCONTEXT_DESTROY, + JSCONTEXT_REQUEST_START } JSContextOp; /* * The possible values for contextOp when the runtime calls the callback are: - * JSCONTEXT_NEW JS_NewContext successfully created a new JSContext - * instance. The callback can initialize the instance as - * required. If the callback returns false, the instance - * will be destroyed and JS_NewContext returns null. In - * this case the callback is not called again. - * JSCONTEXT_DESTROY One of JS_DestroyContext* methods is called. The - * callback may perform its own cleanup and must always - * return true. - * Any other value For future compatibility the callback must do nothing - * and return true in this case. + * JSCONTEXT_NEW JS_NewContext successfully created a new JSContext + * instance. The callback can initialize the instance as + * required. If the callback returns false, the instance + * will be destroyed and JS_NewContext returns null. In + * this case the callback is not called again. + * JSCONTEXT_DESTROY One of JS_DestroyContext* methods is called. The + * callback may perform its own cleanup and must always + * return true. + * JSCONTEXT_REQUEST_START JS_BeginRequest was called with requestDepth == 0. + * This callback can be used to notify other components + * that execution has begun on this context. + * Any other value For future compatibility the callback must do nothing + * and return true in this case. */ typedef JSBool (* JSContextCallback)(JSContext *cx, uintN contextOp); diff --git a/js/src/jsversion.h b/js/src/jsversion.h index b16953be814..22268cb9070 100644 --- a/js/src/jsversion.h +++ b/js/src/jsversion.h @@ -241,3 +241,11 @@ /* Feature-test macro for evolving destructuring support. */ #define JS_HAS_DESTRUCTURING_SHORTHAND (JS_HAS_DESTRUCTURING == 2) + +#ifndef JS_HAS_OPERATION_COUNT +# if defined(MOZILLA_VERSION) || defined(JS_THREADSAFE) +# define JS_HAS_OPERATION_COUNT 0 +# else +# define JS_HAS_OPERATION_COUNT 1 +# endif +#endif diff --git a/js/src/xpconnect/idl/nsIXPConnect.idl b/js/src/xpconnect/idl/nsIXPConnect.idl index 79eec6c1618..46c039c93ad 100644 --- a/js/src/xpconnect/idl/nsIXPConnect.idl +++ b/js/src/xpconnect/idl/nsIXPConnect.idl @@ -70,6 +70,7 @@ [ptr] native nsScriptObjectTracerPtr(nsScriptObjectTracer); [ref] native nsCCTraversalCallbackRef(nsCycleCollectionTraversalCallback); [ptr] native nsAXPCNativeCallContextPtr(nsAXPCNativeCallContext); + native PRIntervalTime(PRIntervalTime); /***************************************************************************/ @@ -405,7 +406,7 @@ interface nsIXPCFunctionThisTranslator : nsISupports { 0xbd, 0xd6, 0x0, 0x0, 0x64, 0x65, 0x73, 0x74 } } %} -[uuid(d4c6bc06-2a4f-4315-90ec-d12904aca046)] +[uuid(eb95710a-e112-44ae-abe1-6f8dfb58d7f8)] interface nsIXPConnect : nsISupports { %{ C++ @@ -767,4 +768,20 @@ interface nsIXPConnect : nsISupports in PRUint32 flags, in PRUint32 interfaceCount, [array, size_is(interfaceCount)] in nsIIDPtr interfaceArray); + + /** + * Returns the value of the watchdog limit for the specified context. + * @param cx + * A context + */ + [noscript,notxpcom] PRIntervalTime getWatchdogLimit(in JSContextPtr cx); + + /** + * Sets a new value of the watchdog limit. + * @param cx + * A context + * @param limit + * New value of the watchdog limit. + */ + [noscript,notxpcom] PRBool setWatchdogLimit(in JSContextPtr cx, in PRIntervalTime limit); }; diff --git a/js/src/xpconnect/src/nsXPConnect.cpp b/js/src/xpconnect/src/nsXPConnect.cpp index 99997ebc06b..90f733e1430 100644 --- a/js/src/xpconnect/src/nsXPConnect.cpp +++ b/js/src/xpconnect/src/nsXPConnect.cpp @@ -319,13 +319,13 @@ static PRBool NameTester(nsIInterfaceInfoManager* manager, const void* data, *info; } -static nsresult FindInfo(InfoTester tester, const void* data, +static nsresult FindInfo(InfoTester tester, const void* data, nsIInterfaceInfoSuperManager* iism, nsIInterfaceInfo** info) { if(tester(iism, data, info)) return NS_OK; - + // If not found, then let's ask additional managers. PRBool yes; @@ -345,9 +345,9 @@ static nsresult FindInfo(InfoTester tester, const void* data, return NS_OK; } } - + return NS_ERROR_NO_INTERFACE; -} +} nsresult nsXPConnect::GetInfoForIID(const nsIID * aIID, nsIInterfaceInfo** info) @@ -434,7 +434,7 @@ nsXPConnect::Collect() // 1. marking of the roots in category 1 by having the JS GC do its marking // 2. cycle collection // 3. marking of the roots in category 2 by - // XPCJSRuntime::TraceXPConnectRoots + // XPCJSRuntime::TraceXPConnectRoots // 4. sweeping of unmarked JS objects // // During cycle collection, marked JS objects (and the objects they hold) @@ -520,7 +520,7 @@ NoteJSRoot(JSTracer *trc, void *thing, uint32 kind) } #endif -nsresult +nsresult nsXPConnect::BeginCycleCollection(nsCycleCollectionTraversalCallback &cb) { #ifdef DEBUG_CC @@ -588,7 +588,7 @@ nsXPConnect::RecordTraversal(void *p, nsISupports *s) } #endif -nsresult +nsresult nsXPConnect::FinishCycleCollection() { #ifdef DEBUG_CC @@ -875,7 +875,7 @@ nsXPConnect::Traverse(void *p, nsCycleCollectionTraversalCallback &cb) if(traceKind != JSTRACE_OBJECT) return NS_OK; - + JSObject *obj = static_cast(p); JSClass* clazz = OBJ_GET_CLASS(cx, obj); @@ -1205,7 +1205,7 @@ nsXPConnect::WrapNative(JSContext * aJSContext, NS_ASSERTION(!XPCNativeWrapper::IsNativeWrapper(returnObj), "Shouldn't be returning a native wrapper here"); #endif - + return NS_OK; } @@ -1692,7 +1692,7 @@ nsXPConnect::GetFunctionThisTranslator(const nsIID & aIID, } /* void setSafeJSContextForCurrentThread (in JSContextPtr cx); */ -NS_IMETHODIMP +NS_IMETHODIMP nsXPConnect::SetSafeJSContextForCurrentThread(JSContext * cx) { XPCCallContext ccx(NATIVE_CALLER); @@ -1713,10 +1713,10 @@ nsXPConnect::ClearAllWrappedNativeSecurityPolicies() } /* void restoreWrappedNativePrototype (in JSContextPtr aJSContext, in JSObjectPtr aScope, in nsIClassInfo aClassInfo, in nsIXPConnectJSObjectHolder aPrototype); */ -NS_IMETHODIMP -nsXPConnect::RestoreWrappedNativePrototype(JSContext * aJSContext, - JSObject * aScope, - nsIClassInfo * aClassInfo, +NS_IMETHODIMP +nsXPConnect::RestoreWrappedNativePrototype(JSContext * aJSContext, + JSObject * aScope, + nsIClassInfo * aClassInfo, nsIXPConnectJSObjectHolder * aPrototype) { XPCCallContext ccx(NATIVE_CALLER, aJSContext); @@ -1874,10 +1874,10 @@ nsXPConnect::GetXPCWrappedNativeJSClassInfo(const JSClass **clazz, } /* nsIXPConnectJSObjectHolder getWrappedNativePrototype (in JSContextPtr aJSContext, in JSObjectPtr aScope, in nsIClassInfo aClassInfo); */ -NS_IMETHODIMP -nsXPConnect::GetWrappedNativePrototype(JSContext * aJSContext, - JSObject * aScope, - nsIClassInfo *aClassInfo, +NS_IMETHODIMP +nsXPConnect::GetWrappedNativePrototype(JSContext * aJSContext, + JSObject * aScope, + nsIClassInfo *aClassInfo, nsIXPConnectJSObjectHolder **_retval) { XPCCallContext ccx(NATIVE_CALLER, aJSContext); @@ -1893,14 +1893,14 @@ nsXPConnect::GetWrappedNativePrototype(JSContext * aJSContext, XPCWrappedNative::GatherProtoScriptableCreateInfo(aClassInfo, &sciProto); AutoMarkingWrappedNativeProtoPtr proto(ccx); - proto = XPCWrappedNativeProto::GetNewOrUsed(ccx, scope, aClassInfo, + proto = XPCWrappedNativeProto::GetNewOrUsed(ccx, scope, aClassInfo, &sciProto, JS_FALSE, OBJ_IS_NOT_GLOBAL); if(!proto) return UnexpectedFailure(NS_ERROR_FAILURE); nsIXPConnectJSObjectHolder* holder; - *_retval = holder = XPCJSObjectHolder::newHolder(ccx, + *_retval = holder = XPCJSObjectHolder::newHolder(ccx, proto->GetJSProtoObject()); if(!holder) return UnexpectedFailure(NS_ERROR_FAILURE); @@ -1971,7 +1971,7 @@ nsXPConnect::UpdateXOWs(JSContext* aJSContext, } /* void releaseJSContext (in JSContextPtr aJSContext, in PRBool noGC); */ -NS_IMETHODIMP +NS_IMETHODIMP nsXPConnect::ReleaseJSContext(JSContext * aJSContext, PRBool noGC) { NS_ASSERTION(aJSContext, "bad param"); @@ -1979,8 +1979,8 @@ nsXPConnect::ReleaseJSContext(JSContext * aJSContext, PRBool noGC) if(tls) { XPCCallContext* ccx = nsnull; - for(XPCCallContext* cur = tls->GetCallContext(); - cur; + for(XPCCallContext* cur = tls->GetCallContext(); + cur; cur = cur->GetPrevCallContext()) { if(cur->GetJSContext() == aJSContext) @@ -1989,11 +1989,11 @@ nsXPConnect::ReleaseJSContext(JSContext * aJSContext, PRBool noGC) // Keep looping to find the deepest matching call context. } } - + if(ccx) { #ifdef DEBUG_xpc_hacker - printf("!xpc - deferring destruction of JSContext @ %p\n", + printf("!xpc - deferring destruction of JSContext @ %p\n", (void *)aJSContext); #endif ccx->SetDestroyJSContextInDestructor(JS_TRUE); @@ -2002,12 +2002,12 @@ nsXPConnect::ReleaseJSContext(JSContext * aJSContext, PRBool noGC) } // else continue on and synchronously destroy the JSContext ... - NS_ASSERTION(!tls->GetJSContextStack() || + NS_ASSERTION(!tls->GetJSContextStack() || !tls->GetJSContextStack()-> DEBUG_StackHasJSContext(aJSContext), "JSContext still in threadjscontextstack!"); } - + if(noGC) JS_DestroyContextNoGC(aJSContext); else @@ -2128,7 +2128,7 @@ nsXPConnect::DebugDumpEvalInJSStackFrame(PRUint32 aFrameNumber, const char *aSou } /* JSVal variantToJS (in JSContextPtr ctx, in JSObjectPtr scope, in nsIVariant value); */ -NS_IMETHODIMP +NS_IMETHODIMP nsXPConnect::VariantToJS(JSContext* ctx, JSObject* scope, nsIVariant* value, jsval* _retval) { NS_PRECONDITION(ctx, "bad param"); @@ -2143,7 +2143,7 @@ nsXPConnect::VariantToJS(JSContext* ctx, JSObject* scope, nsIVariant* value, jsv nsresult rv = NS_OK; if(!XPCVariant::VariantDataToJS(ccx, value, scope, &rv, _retval)) { - if(NS_FAILED(rv)) + if(NS_FAILED(rv)) return rv; return NS_ERROR_FAILURE; @@ -2153,7 +2153,7 @@ nsXPConnect::VariantToJS(JSContext* ctx, JSObject* scope, nsIVariant* value, jsv } /* nsIVariant JSToVariant (in JSContextPtr ctx, in JSVal value); */ -NS_IMETHODIMP +NS_IMETHODIMP nsXPConnect::JSToVariant(JSContext* ctx, jsval value, nsIVariant** _retval) { NS_PRECONDITION(ctx, "bad param"); @@ -2165,7 +2165,7 @@ nsXPConnect::JSToVariant(JSContext* ctx, jsval value, nsIVariant** _retval) return NS_ERROR_FAILURE; *_retval = XPCVariant::newVariant(ccx, value); - if(!(*_retval)) + if(!(*_retval)) return NS_ERROR_FAILURE; return NS_OK; @@ -2173,7 +2173,7 @@ nsXPConnect::JSToVariant(JSContext* ctx, jsval value, nsIVariant** _retval) /* void flagSystemFilenamePrefix (in string filenamePrefix, * in PRBool aWantNativeWrappers); */ -NS_IMETHODIMP +NS_IMETHODIMP nsXPConnect::FlagSystemFilenamePrefix(const char *aFilenamePrefix, PRBool aWantNativeWrappers) { @@ -2376,6 +2376,18 @@ nsXPConnect::SetSafeJSContext(JSContext * aSafeJSContext) return data->GetJSContextStack()->SetSafeJSContext(aSafeJSContext); } +NS_IMETHODIMP_(PRBool) +nsXPConnect::SetWatchdogLimit(JSContext *cx, PRIntervalTime limit) +{ + return GetRuntime()->SetWatchdogLimit(cx, limit); +} + +NS_IMETHODIMP_(PRIntervalTime) +nsXPConnect::GetWatchdogLimit(JSContext *cx) +{ + return GetRuntime()->GetWatchdogLimit(cx); +} + /* These are here to be callable from a debugger */ JS_BEGIN_EXTERN_C JS_EXPORT_API(void) DumpJSStack() diff --git a/js/src/xpconnect/src/xpccomponents.cpp b/js/src/xpconnect/src/xpccomponents.cpp index 0e52b66115d..4383d41b875 100644 --- a/js/src/xpconnect/src/xpccomponents.cpp +++ b/js/src/xpconnect/src/xpccomponents.cpp @@ -765,7 +765,7 @@ nsXPCComponents_InterfacesByID::CanSetProperty(const nsIID * iid, const PRUnicha -class nsXPCComponents_Classes : +class nsXPCComponents_Classes : public nsIXPCComponents_Classes, public nsIXPCScriptable, public nsIClassInfo @@ -783,9 +783,9 @@ public: }; /***************************************************************************/ -/* void getInterfaces (out PRUint32 count, [array, size_is (count), retval] +/* void getInterfaces (out PRUint32 count, [array, size_is (count), retval] out nsIIDPtr array); */ -NS_IMETHODIMP +NS_IMETHODIMP nsXPCComponents_Classes::GetInterfaces(PRUint32 *aCount, nsIID * **aArray) { PRUint32 count = 2; @@ -818,8 +818,8 @@ oom: } /* nsISupports getHelperForLanguage (in PRUint32 language); */ -NS_IMETHODIMP -nsXPCComponents_Classes::GetHelperForLanguage(PRUint32 language, +NS_IMETHODIMP +nsXPCComponents_Classes::GetHelperForLanguage(PRUint32 language, nsISupports **retval) { *retval = nsnull; @@ -827,7 +827,7 @@ nsXPCComponents_Classes::GetHelperForLanguage(PRUint32 language, } /* readonly attribute string contractID; */ -NS_IMETHODIMP +NS_IMETHODIMP nsXPCComponents_Classes::GetContractID(char * *aContractID) { *aContractID = nsnull; @@ -835,7 +835,7 @@ nsXPCComponents_Classes::GetContractID(char * *aContractID) } /* readonly attribute string classDescription; */ -NS_IMETHODIMP +NS_IMETHODIMP nsXPCComponents_Classes::GetClassDescription(char * *aClassDescription) { static const char classDescription[] = "XPCComponents_Classes"; @@ -844,7 +844,7 @@ nsXPCComponents_Classes::GetClassDescription(char * *aClassDescription) } /* readonly attribute nsCIDPtr classID; */ -NS_IMETHODIMP +NS_IMETHODIMP nsXPCComponents_Classes::GetClassID(nsCID * *aClassID) { *aClassID = nsnull; @@ -852,7 +852,7 @@ nsXPCComponents_Classes::GetClassID(nsCID * *aClassID) } /* readonly attribute PRUint32 implementationLanguage; */ -NS_IMETHODIMP +NS_IMETHODIMP nsXPCComponents_Classes::GetImplementationLanguage( PRUint32 *aImplementationLanguage) { @@ -861,7 +861,7 @@ nsXPCComponents_Classes::GetImplementationLanguage( } /* readonly attribute PRUint32 flags; */ -NS_IMETHODIMP +NS_IMETHODIMP nsXPCComponents_Classes::GetFlags(PRUint32 *aFlags) { *aFlags = nsIClassInfo::THREADSAFE; @@ -869,7 +869,7 @@ nsXPCComponents_Classes::GetFlags(PRUint32 *aFlags) } /* [notxpcom] readonly attribute nsCID classIDNoAlloc; */ -NS_IMETHODIMP +NS_IMETHODIMP nsXPCComponents_Classes::GetClassIDNoAlloc(nsCID *aClassIDNoAlloc) { return NS_ERROR_NOT_AVAILABLE; @@ -1038,9 +1038,9 @@ public: }; /***************************************************************************/ -/* void getInterfaces (out PRUint32 count, [array, size_is (count), retval] +/* void getInterfaces (out PRUint32 count, [array, size_is (count), retval] out nsIIDPtr array); */ -NS_IMETHODIMP +NS_IMETHODIMP nsXPCComponents_ClassesByID::GetInterfaces(PRUint32 *aCount, nsIID * **aArray) { PRUint32 count = 2; @@ -1073,8 +1073,8 @@ oom: } /* nsISupports getHelperForLanguage (in PRUint32 language); */ -NS_IMETHODIMP -nsXPCComponents_ClassesByID::GetHelperForLanguage(PRUint32 language, +NS_IMETHODIMP +nsXPCComponents_ClassesByID::GetHelperForLanguage(PRUint32 language, nsISupports **retval) { *retval = nsnull; @@ -1082,7 +1082,7 @@ nsXPCComponents_ClassesByID::GetHelperForLanguage(PRUint32 language, } /* readonly attribute string contractID; */ -NS_IMETHODIMP +NS_IMETHODIMP nsXPCComponents_ClassesByID::GetContractID(char * *aContractID) { *aContractID = nsnull; @@ -1090,7 +1090,7 @@ nsXPCComponents_ClassesByID::GetContractID(char * *aContractID) } /* readonly attribute string classDescription; */ -NS_IMETHODIMP +NS_IMETHODIMP nsXPCComponents_ClassesByID::GetClassDescription(char * *aClassDescription) { static const char classDescription[] = "XPCComponents_Interfaces"; @@ -1099,7 +1099,7 @@ nsXPCComponents_ClassesByID::GetClassDescription(char * *aClassDescription) } /* readonly attribute nsCIDPtr classID; */ -NS_IMETHODIMP +NS_IMETHODIMP nsXPCComponents_ClassesByID::GetClassID(nsCID * *aClassID) { *aClassID = nsnull; @@ -1107,7 +1107,7 @@ nsXPCComponents_ClassesByID::GetClassID(nsCID * *aClassID) } /* readonly attribute PRUint32 implementationLanguage; */ -NS_IMETHODIMP +NS_IMETHODIMP nsXPCComponents_ClassesByID::GetImplementationLanguage( PRUint32 *aImplementationLanguage) { @@ -1116,7 +1116,7 @@ nsXPCComponents_ClassesByID::GetImplementationLanguage( } /* readonly attribute PRUint32 flags; */ -NS_IMETHODIMP +NS_IMETHODIMP nsXPCComponents_ClassesByID::GetFlags(PRUint32 *aFlags) { *aFlags = nsIClassInfo::THREADSAFE; @@ -1124,7 +1124,7 @@ nsXPCComponents_ClassesByID::GetFlags(PRUint32 *aFlags) } /* [notxpcom] readonly attribute nsCID classIDNoAlloc; */ -NS_IMETHODIMP +NS_IMETHODIMP nsXPCComponents_ClassesByID::GetClassIDNoAlloc(nsCID *aClassIDNoAlloc) { return NS_ERROR_NOT_AVAILABLE; @@ -1312,9 +1312,9 @@ public: }; /***************************************************************************/ -/* void getInterfaces (out PRUint32 count, [array, size_is (count), retval] +/* void getInterfaces (out PRUint32 count, [array, size_is (count), retval] out nsIIDPtr array); */ -NS_IMETHODIMP +NS_IMETHODIMP nsXPCComponents_Results::GetInterfaces(PRUint32 *aCount, nsIID * **aArray) { PRUint32 count = 2; @@ -1347,8 +1347,8 @@ oom: } /* nsISupports getHelperForLanguage (in PRUint32 language); */ -NS_IMETHODIMP -nsXPCComponents_Results::GetHelperForLanguage(PRUint32 language, +NS_IMETHODIMP +nsXPCComponents_Results::GetHelperForLanguage(PRUint32 language, nsISupports **retval) { *retval = nsnull; @@ -1356,7 +1356,7 @@ nsXPCComponents_Results::GetHelperForLanguage(PRUint32 language, } /* readonly attribute string contractID; */ -NS_IMETHODIMP +NS_IMETHODIMP nsXPCComponents_Results::GetContractID(char * *aContractID) { *aContractID = nsnull; @@ -1364,7 +1364,7 @@ nsXPCComponents_Results::GetContractID(char * *aContractID) } /* readonly attribute string classDescription; */ -NS_IMETHODIMP +NS_IMETHODIMP nsXPCComponents_Results::GetClassDescription(char * *aClassDescription) { static const char classDescription[] = "XPCComponents_Interfaces"; @@ -1373,7 +1373,7 @@ nsXPCComponents_Results::GetClassDescription(char * *aClassDescription) } /* readonly attribute nsCIDPtr classID; */ -NS_IMETHODIMP +NS_IMETHODIMP nsXPCComponents_Results::GetClassID(nsCID * *aClassID) { *aClassID = nsnull; @@ -1381,7 +1381,7 @@ nsXPCComponents_Results::GetClassID(nsCID * *aClassID) } /* readonly attribute PRUint32 implementationLanguage; */ -NS_IMETHODIMP +NS_IMETHODIMP nsXPCComponents_Results::GetImplementationLanguage( PRUint32 *aImplementationLanguage) { @@ -1390,7 +1390,7 @@ nsXPCComponents_Results::GetImplementationLanguage( } /* readonly attribute PRUint32 flags; */ -NS_IMETHODIMP +NS_IMETHODIMP nsXPCComponents_Results::GetFlags(PRUint32 *aFlags) { *aFlags = nsIClassInfo::THREADSAFE; @@ -1398,7 +1398,7 @@ nsXPCComponents_Results::GetFlags(PRUint32 *aFlags) } /* [notxpcom] readonly attribute nsCID classIDNoAlloc; */ -NS_IMETHODIMP +NS_IMETHODIMP nsXPCComponents_Results::GetClassIDNoAlloc(nsCID *aClassIDNoAlloc) { return NS_ERROR_NOT_AVAILABLE; @@ -1544,9 +1544,9 @@ private: }; /***************************************************************************/ -/* void getInterfaces (out PRUint32 count, [array, size_is (count), retval] +/* void getInterfaces (out PRUint32 count, [array, size_is (count), retval] out nsIIDPtr array); */ -NS_IMETHODIMP +NS_IMETHODIMP nsXPCComponents_ID::GetInterfaces(PRUint32 *aCount, nsIID * **aArray) { PRUint32 count = 2; @@ -1579,8 +1579,8 @@ oom: } /* nsISupports getHelperForLanguage (in PRUint32 language); */ -NS_IMETHODIMP -nsXPCComponents_ID::GetHelperForLanguage(PRUint32 language, +NS_IMETHODIMP +nsXPCComponents_ID::GetHelperForLanguage(PRUint32 language, nsISupports **retval) { *retval = nsnull; @@ -1588,7 +1588,7 @@ nsXPCComponents_ID::GetHelperForLanguage(PRUint32 language, } /* readonly attribute string contractID; */ -NS_IMETHODIMP +NS_IMETHODIMP nsXPCComponents_ID::GetContractID(char * *aContractID) { *aContractID = nsnull; @@ -1596,7 +1596,7 @@ nsXPCComponents_ID::GetContractID(char * *aContractID) } /* readonly attribute string classDescription; */ -NS_IMETHODIMP +NS_IMETHODIMP nsXPCComponents_ID::GetClassDescription(char * *aClassDescription) { static const char classDescription[] = "XPCComponents_Interfaces"; @@ -1605,7 +1605,7 @@ nsXPCComponents_ID::GetClassDescription(char * *aClassDescription) } /* readonly attribute nsCIDPtr classID; */ -NS_IMETHODIMP +NS_IMETHODIMP nsXPCComponents_ID::GetClassID(nsCID * *aClassID) { *aClassID = nsnull; @@ -1613,7 +1613,7 @@ nsXPCComponents_ID::GetClassID(nsCID * *aClassID) } /* readonly attribute PRUint32 implementationLanguage; */ -NS_IMETHODIMP +NS_IMETHODIMP nsXPCComponents_ID::GetImplementationLanguage( PRUint32 *aImplementationLanguage) { @@ -1622,7 +1622,7 @@ nsXPCComponents_ID::GetImplementationLanguage( } /* readonly attribute PRUint32 flags; */ -NS_IMETHODIMP +NS_IMETHODIMP nsXPCComponents_ID::GetFlags(PRUint32 *aFlags) { *aFlags = nsIClassInfo::THREADSAFE; @@ -1630,7 +1630,7 @@ nsXPCComponents_ID::GetFlags(PRUint32 *aFlags) } /* [notxpcom] readonly attribute nsCID classIDNoAlloc; */ -NS_IMETHODIMP +NS_IMETHODIMP nsXPCComponents_ID::GetClassIDNoAlloc(nsCID *aClassIDNoAlloc) { return NS_ERROR_NOT_AVAILABLE; @@ -1771,9 +1771,9 @@ private: }; /***************************************************************************/ -/* void getInterfaces (out PRUint32 count, [array, size_is (count), retval] +/* void getInterfaces (out PRUint32 count, [array, size_is (count), retval] out nsIIDPtr array); */ -NS_IMETHODIMP +NS_IMETHODIMP nsXPCComponents_Exception::GetInterfaces(PRUint32 *aCount, nsIID * **aArray) { PRUint32 count = 2; @@ -1806,8 +1806,8 @@ oom: } /* nsISupports getHelperForLanguage (in PRUint32 language); */ -NS_IMETHODIMP -nsXPCComponents_Exception::GetHelperForLanguage(PRUint32 language, +NS_IMETHODIMP +nsXPCComponents_Exception::GetHelperForLanguage(PRUint32 language, nsISupports **retval) { *retval = nsnull; @@ -1815,7 +1815,7 @@ nsXPCComponents_Exception::GetHelperForLanguage(PRUint32 language, } /* readonly attribute string contractID; */ -NS_IMETHODIMP +NS_IMETHODIMP nsXPCComponents_Exception::GetContractID(char * *aContractID) { *aContractID = nsnull; @@ -1823,7 +1823,7 @@ nsXPCComponents_Exception::GetContractID(char * *aContractID) } /* readonly attribute string classDescription; */ -NS_IMETHODIMP +NS_IMETHODIMP nsXPCComponents_Exception::GetClassDescription(char * *aClassDescription) { static const char classDescription[] = "XPCComponents_Interfaces"; @@ -1832,7 +1832,7 @@ nsXPCComponents_Exception::GetClassDescription(char * *aClassDescription) } /* readonly attribute nsCIDPtr classID; */ -NS_IMETHODIMP +NS_IMETHODIMP nsXPCComponents_Exception::GetClassID(nsCID * *aClassID) { *aClassID = nsnull; @@ -1840,7 +1840,7 @@ nsXPCComponents_Exception::GetClassID(nsCID * *aClassID) } /* readonly attribute PRUint32 implementationLanguage; */ -NS_IMETHODIMP +NS_IMETHODIMP nsXPCComponents_Exception::GetImplementationLanguage( PRUint32 *aImplementationLanguage) { @@ -1849,7 +1849,7 @@ nsXPCComponents_Exception::GetImplementationLanguage( } /* readonly attribute PRUint32 flags; */ -NS_IMETHODIMP +NS_IMETHODIMP nsXPCComponents_Exception::GetFlags(PRUint32 *aFlags) { *aFlags = nsIClassInfo::THREADSAFE; @@ -1857,7 +1857,7 @@ nsXPCComponents_Exception::GetFlags(PRUint32 *aFlags) } /* [notxpcom] readonly attribute nsCID classIDNoAlloc; */ -NS_IMETHODIMP +NS_IMETHODIMP nsXPCComponents_Exception::GetClassIDNoAlloc(nsCID *aClassIDNoAlloc) { return NS_ERROR_NOT_AVAILABLE; @@ -2064,9 +2064,9 @@ private: }; /***************************************************************************/ -/* void getInterfaces (out PRUint32 count, [array, size_is (count), retval] +/* void getInterfaces (out PRUint32 count, [array, size_is (count), retval] out nsIIDPtr array); */ -NS_IMETHODIMP +NS_IMETHODIMP nsXPCConstructor::GetInterfaces(PRUint32 *aCount, nsIID * **aArray) { PRUint32 count = 2; @@ -2099,8 +2099,8 @@ oom: } /* nsISupports getHelperForLanguage (in PRUint32 language); */ -NS_IMETHODIMP -nsXPCConstructor::GetHelperForLanguage(PRUint32 language, +NS_IMETHODIMP +nsXPCConstructor::GetHelperForLanguage(PRUint32 language, nsISupports **retval) { *retval = nsnull; @@ -2108,7 +2108,7 @@ nsXPCConstructor::GetHelperForLanguage(PRUint32 language, } /* readonly attribute string contractID; */ -NS_IMETHODIMP +NS_IMETHODIMP nsXPCConstructor::GetContractID(char * *aContractID) { *aContractID = nsnull; @@ -2116,7 +2116,7 @@ nsXPCConstructor::GetContractID(char * *aContractID) } /* readonly attribute string classDescription; */ -NS_IMETHODIMP +NS_IMETHODIMP nsXPCConstructor::GetClassDescription(char * *aClassDescription) { static const char classDescription[] = "XPCComponents_Interfaces"; @@ -2125,7 +2125,7 @@ nsXPCConstructor::GetClassDescription(char * *aClassDescription) } /* readonly attribute nsCIDPtr classID; */ -NS_IMETHODIMP +NS_IMETHODIMP nsXPCConstructor::GetClassID(nsCID * *aClassID) { *aClassID = nsnull; @@ -2133,7 +2133,7 @@ nsXPCConstructor::GetClassID(nsCID * *aClassID) } /* readonly attribute PRUint32 implementationLanguage; */ -NS_IMETHODIMP +NS_IMETHODIMP nsXPCConstructor::GetImplementationLanguage( PRUint32 *aImplementationLanguage) { @@ -2142,7 +2142,7 @@ nsXPCConstructor::GetImplementationLanguage( } /* readonly attribute PRUint32 flags; */ -NS_IMETHODIMP +NS_IMETHODIMP nsXPCConstructor::GetFlags(PRUint32 *aFlags) { *aFlags = nsIClassInfo::THREADSAFE; @@ -2150,7 +2150,7 @@ nsXPCConstructor::GetFlags(PRUint32 *aFlags) } /* [notxpcom] readonly attribute nsCID classIDNoAlloc; */ -NS_IMETHODIMP +NS_IMETHODIMP nsXPCConstructor::GetClassIDNoAlloc(nsCID *aClassIDNoAlloc) { return NS_ERROR_NOT_AVAILABLE; @@ -2329,9 +2329,9 @@ private: }; /***************************************************************************/ -/* void getInterfaces (out PRUint32 count, [array, size_is (count), retval] +/* void getInterfaces (out PRUint32 count, [array, size_is (count), retval] out nsIIDPtr array); */ -NS_IMETHODIMP +NS_IMETHODIMP nsXPCComponents_Constructor::GetInterfaces(PRUint32 *aCount, nsIID * **aArray) { PRUint32 count = 2; @@ -2364,8 +2364,8 @@ oom: } /* nsISupports getHelperForLanguage (in PRUint32 language); */ -NS_IMETHODIMP -nsXPCComponents_Constructor::GetHelperForLanguage(PRUint32 language, +NS_IMETHODIMP +nsXPCComponents_Constructor::GetHelperForLanguage(PRUint32 language, nsISupports **retval) { *retval = nsnull; @@ -2373,7 +2373,7 @@ nsXPCComponents_Constructor::GetHelperForLanguage(PRUint32 language, } /* readonly attribute string contractID; */ -NS_IMETHODIMP +NS_IMETHODIMP nsXPCComponents_Constructor::GetContractID(char * *aContractID) { *aContractID = nsnull; @@ -2381,7 +2381,7 @@ nsXPCComponents_Constructor::GetContractID(char * *aContractID) } /* readonly attribute string classDescription; */ -NS_IMETHODIMP +NS_IMETHODIMP nsXPCComponents_Constructor::GetClassDescription(char * *aClassDescription) { static const char classDescription[] = "XPCComponents_Interfaces"; @@ -2390,7 +2390,7 @@ nsXPCComponents_Constructor::GetClassDescription(char * *aClassDescription) } /* readonly attribute nsCIDPtr classID; */ -NS_IMETHODIMP +NS_IMETHODIMP nsXPCComponents_Constructor::GetClassID(nsCID * *aClassID) { *aClassID = nsnull; @@ -2398,7 +2398,7 @@ nsXPCComponents_Constructor::GetClassID(nsCID * *aClassID) } /* readonly attribute PRUint32 implementationLanguage; */ -NS_IMETHODIMP +NS_IMETHODIMP nsXPCComponents_Constructor::GetImplementationLanguage( PRUint32 *aImplementationLanguage) { @@ -2407,7 +2407,7 @@ nsXPCComponents_Constructor::GetImplementationLanguage( } /* readonly attribute PRUint32 flags; */ -NS_IMETHODIMP +NS_IMETHODIMP nsXPCComponents_Constructor::GetFlags(PRUint32 *aFlags) { *aFlags = nsIClassInfo::THREADSAFE; @@ -2415,7 +2415,7 @@ nsXPCComponents_Constructor::GetFlags(PRUint32 *aFlags) } /* [notxpcom] readonly attribute nsCID classIDNoAlloc; */ -NS_IMETHODIMP +NS_IMETHODIMP nsXPCComponents_Constructor::GetClassIDNoAlloc(nsCID *aClassIDNoAlloc) { return NS_ERROR_NOT_AVAILABLE; @@ -3220,7 +3220,7 @@ xpc_CreateSandboxObject(JSContext * cx, jsval * vp, nsISupports *prinOrSop) if (!principal || NS_FAILED(rv)) { if (NS_SUCCEEDED(rv)) rv = NS_ERROR_FAILURE; - + return rv; } } @@ -3386,7 +3386,7 @@ public: private: static JSBool ContextHolderOperationCallback(JSContext *cx); - + XPCAutoJSContext mJSContext; JSContext* mOrigCx; }; @@ -3405,11 +3405,17 @@ ContextHolder::ContextHolder(JSContext *aOuterCx, JSObject *aSandbox) JS_SetGlobalObject(mJSContext, aSandbox); JS_SetContextPrivate(mJSContext, this); - if(JS_GetOperationCallback(aOuterCx)) + PRIntervalTime watchdogLimit = + nsXPConnect::GetXPConnect()->GetWatchdogLimit(aOuterCx); + + if(watchdogLimit) { - JS_SetOperationCallback(mJSContext, ContextHolderOperationCallback, - JS_GetOperationLimit(aOuterCx)); + JS_SetOperationCallback(mJSContext, + ContextHolderOperationCallback); + nsXPConnect::GetXPConnect()->SetWatchdogLimit(mJSContext, + watchdogLimit); } + } } @@ -3431,7 +3437,9 @@ ContextHolder::ContextHolderOperationCallback(JSContext *cx) { // If the callback is still set in the original context, reflect // a possibly updated operation limit into cx. - JS_SetOperationLimit(cx, JS_GetOperationLimit(origCx)); + PRIntervalTime limit = + nsXPConnect::GetXPConnect()->GetWatchdogLimit(origCx); + ok = nsXPConnect::GetXPConnect()->SetWatchdogLimit(cx, limit); return ok; } } @@ -3736,9 +3744,9 @@ NS_INTERFACE_MAP_END_THREADSAFE NS_IMPL_THREADSAFE_ADDREF(nsXPCComponents) NS_IMPL_THREADSAFE_RELEASE(nsXPCComponents) -/* void getInterfaces (out PRUint32 count, [array, size_is (count), retval] +/* void getInterfaces (out PRUint32 count, [array, size_is (count), retval] out nsIIDPtr array); */ -NS_IMETHODIMP +NS_IMETHODIMP nsXPCComponents::GetInterfaces(PRUint32 *aCount, nsIID * **aArray) { PRUint32 count = 2; @@ -3777,8 +3785,8 @@ oom: } /* nsISupports getHelperForLanguage (in PRUint32 language); */ -NS_IMETHODIMP -nsXPCComponents::GetHelperForLanguage(PRUint32 language, +NS_IMETHODIMP +nsXPCComponents::GetHelperForLanguage(PRUint32 language, nsISupports **retval) { *retval = nsnull; @@ -3786,7 +3794,7 @@ nsXPCComponents::GetHelperForLanguage(PRUint32 language, } /* readonly attribute string contractID; */ -NS_IMETHODIMP +NS_IMETHODIMP nsXPCComponents::GetContractID(char * *aContractID) { *aContractID = nsnull; @@ -3794,7 +3802,7 @@ nsXPCComponents::GetContractID(char * *aContractID) } /* readonly attribute string classDescription; */ -NS_IMETHODIMP +NS_IMETHODIMP nsXPCComponents::GetClassDescription(char * *aClassDescription) { static const char classDescription[] = "XPCComponents"; @@ -3803,7 +3811,7 @@ nsXPCComponents::GetClassDescription(char * *aClassDescription) } /* readonly attribute nsCIDPtr classID; */ -NS_IMETHODIMP +NS_IMETHODIMP nsXPCComponents::GetClassID(nsCID * *aClassID) { *aClassID = nsnull; @@ -3811,7 +3819,7 @@ nsXPCComponents::GetClassID(nsCID * *aClassID) } /* readonly attribute PRUint32 implementationLanguage; */ -NS_IMETHODIMP +NS_IMETHODIMP nsXPCComponents::GetImplementationLanguage( PRUint32 *aImplementationLanguage) { @@ -3820,7 +3828,7 @@ nsXPCComponents::GetImplementationLanguage( } /* readonly attribute PRUint32 flags; */ -NS_IMETHODIMP +NS_IMETHODIMP nsXPCComponents::GetFlags(PRUint32 *aFlags) { *aFlags = nsIClassInfo::THREADSAFE; @@ -3828,7 +3836,7 @@ nsXPCComponents::GetFlags(PRUint32 *aFlags) } /* [notxpcom] readonly attribute nsCID classIDNoAlloc; */ -NS_IMETHODIMP +NS_IMETHODIMP nsXPCComponents::GetClassIDNoAlloc(nsCID *aClassIDNoAlloc) { return NS_ERROR_NOT_AVAILABLE; diff --git a/js/src/xpconnect/src/xpccontext.cpp b/js/src/xpconnect/src/xpccontext.cpp index f6f95f53414..7d52a68e18f 100644 --- a/js/src/xpconnect/src/xpccontext.cpp +++ b/js/src/xpconnect/src/xpccontext.cpp @@ -52,6 +52,7 @@ XPCContext::XPCContext(XPCJSRuntime* aRuntime, mPendingResult(NS_OK), mSecurityManager(nsnull), mException(nsnull), + mWatchdogLimit(0), mCallingLangType(LANG_UNKNOWN), mSecurityManagerFlags(0) { diff --git a/js/src/xpconnect/src/xpcjsruntime.cpp b/js/src/xpconnect/src/xpcjsruntime.cpp index 6a22af73088..7681b7c40c8 100644 --- a/js/src/xpconnect/src/xpcjsruntime.cpp +++ b/js/src/xpconnect/src/xpcjsruntime.cpp @@ -216,7 +216,7 @@ static JSDHashOperator DetachedWrappedNativeProtoMarker(JSDHashTable *table, JSDHashEntryHdr *hdr, uint32 number, void *arg) { - XPCWrappedNativeProto* proto = + XPCWrappedNativeProto* proto = (XPCWrappedNativeProto*)((JSDHashEntryStub*)hdr)->key; proto->Mark(); @@ -239,6 +239,14 @@ ContextCallback(JSContext *cx, uintN operation) { delete XPCContext::GetXPCContext(cx); } + else if(operation == JSCONTEXT_REQUEST_START) + { + // If we're called during context creation, we will assert if we + // try to call XPCContext::GetXPCContext. + if(!cx->data2) + return JS_TRUE; + self->WakeupWatchdog(cx); + } } return JS_TRUE; } @@ -309,7 +317,7 @@ void XPCJSRuntime::TraceJS(JSTracer* trc, void* data) // them here. for(XPCRootSetElem *e = self->mObjectHolderRoots; e ; e = e->GetNextRoot()) static_cast(e)->TraceJS(trc); - + if(self->GetXPConnect()->ShouldTraceRoots()) { // Only trace these if we're not cycle-collecting, the cycle collector @@ -527,7 +535,7 @@ JSBool XPCJSRuntime::GCCallback(JSContext *cx, JSGCStatus status) case JSGC_MARK_END: { NS_ASSERTION(!self->mDoingFinalization, "bad state"); - + // mThreadRunningGC indicates that GC is running { // scoped lock XPCAutoLock lock(self->GetMapLock()); @@ -550,8 +558,8 @@ JSBool XPCJSRuntime::GCCallback(JSContext *cx, JSGCStatus status) Enumerate(WrappedJSDyingJSObjectFinder, &data); } - // Do cleanup in NativeInterfaces. This part just finds - // member cloned function objects that are about to be + // Do cleanup in NativeInterfaces. This part just finds + // member cloned function objects that are about to be // collected. It does not deal with collection of interfaces or // sets at this point. CX_AND_XPCRT_Data data = {cx, self}; @@ -716,7 +724,7 @@ JSBool XPCJSRuntime::GCCallback(JSContext *cx, JSGCStatus status) if(threadLock) { // Do the marking... - + { // scoped lock nsAutoLock lock(threadLock); @@ -735,7 +743,7 @@ JSBool XPCJSRuntime::GCCallback(JSContext *cx, JSGCStatus status) // possibly be valid. if(ccxp->CanGetTearOff()) { - XPCWrappedNativeTearOff* to = + XPCWrappedNativeTearOff* to = ccxp->GetTearOff(); if(to) to->Mark(); @@ -744,7 +752,7 @@ JSBool XPCJSRuntime::GCCallback(JSContext *cx, JSGCStatus status) } } } - + // Do the sweeping... XPCWrappedNativeScope::SweepAllWrappedNativeTearOffs(); } @@ -820,6 +828,126 @@ JSBool XPCJSRuntime::GCCallback(JSContext *cx, JSGCStatus status) return JS_TRUE; } +/* static */ +void +XPCJSRuntime::WatchdogMain(void *arg) +{ + XPCJSRuntime *xpcrt = (XPCJSRuntime *) arg; + JSRuntime *rt = xpcrt->GetJSRuntime(); + PRStatus status; + PRBool isRunning; + + do + { + JSContext *iter = NULL; + JSContext *acx; + PRIntervalTime newInterval = (PRIntervalTime) 0; + XPCContext *ccx; + + PRIntervalTime ct = PR_IntervalNow(); + PR_Lock(xpcrt->mWatchdogLock); + JS_LOCK_GC(rt); + + while((acx = js_ContextIterator(rt, JS_FALSE, &iter))) + { + if(acx->requestDepth) + { + ccx = XPCContext::GetXPCContext(acx); + if(ccx->mWatchdogLimit && + ct - acx->startTime > ccx->mWatchdogLimit) + { + JS_TriggerOperationCallback(acx); + } + if(newInterval > ccx->mWatchdogLimit || !newInterval) + newInterval = ccx->mWatchdogLimit; + } + } + JS_UNLOCK_GC(rt); + + xpcrt->mCurrentInterval = newInterval ? newInterval + : PR_INTERVAL_NO_TIMEOUT; + if (xpcrt->mWatchdogRunning) + status = PR_WaitCondVar(xpcrt->mWatchdogWakeup, + xpcrt->mCurrentInterval); + isRunning = xpcrt->mWatchdogRunning; + PR_Unlock(xpcrt->mWatchdogLock); + } while (isRunning && status == PR_SUCCESS); +} + +PRBool +XPCJSRuntime::SetWatchdogLimit(JSContext *cx, PRIntervalTime newWatchdogLimit) +{ + PRBool isRunning; + PRIntervalTime oldWatchdogLimit; + XPCContext *ccx = XPCContext::GetXPCContext(cx); + + if(newWatchdogLimit == ccx->mWatchdogLimit) + return PR_TRUE; + + oldWatchdogLimit = ccx->mWatchdogLimit; + ccx->mWatchdogLimit = newWatchdogLimit; + + /* + * Start a new watchdog thread if it has not been started. If it has been + * started wake up the thread and cause the watchdog rescheduling. + */ + PR_Lock(mWatchdogLock); + isRunning = !!mWatchdogThread; + + if(!isRunning) + { + mWatchdogRunning = PR_TRUE; + mWatchdogThread = + PR_CreateThread(PRThreadType(PR_USER_THREAD), + WatchdogMain, + this, + PRThreadPriority(PR_PRIORITY_NORMAL), + PRThreadScope(PR_LOCAL_THREAD), + PRThreadState(PR_JOINABLE_THREAD), + 0); + } + PR_Unlock(mWatchdogLock); + if(!mWatchdogThread) + return PR_FALSE; + if(isRunning && + (oldWatchdogLimit > ccx->mWatchdogLimit || + mCurrentInterval == PR_INTERVAL_NO_TIMEOUT)) + WakeupWatchdog(cx); + return PR_TRUE; + +} + +void +XPCJSRuntime::WakeupWatchdog(JSContext *cx) +{ + XPCContext *ccx = XPCContext::GetXPCContext(cx); + PR_Lock(mWatchdogLock); + if(mCurrentInterval == PR_INTERVAL_NO_TIMEOUT || + (ccx && mCurrentInterval > ccx->mWatchdogLimit)) + PR_NotifyCondVar(mWatchdogWakeup); + PR_Unlock(mWatchdogLock); +} + +PRBool +XPCJSRuntime::ShutdownWatchdog() +{ + PR_Lock(mWatchdogLock); + mWatchdogRunning = PR_FALSE; + PRThread *t = mWatchdogThread; + mWatchdogThread = NULL; + PR_NotifyCondVar(mWatchdogWakeup); + PR_Unlock(mWatchdogLock); + if(t) + PR_JoinThread(t); + return PR_TRUE; +} + +PRIntervalTime +XPCJSRuntime::GetWatchdogLimit(JSContext *cx) +{ + return XPCContext::GetXPCContext(cx)->mWatchdogLimit; +} + /***************************************************************************/ #ifdef XPC_CHECK_WRAPPERS_AT_SHUTDOWN @@ -850,7 +978,7 @@ static JSDHashOperator DetachedWrappedNativeProtoShutdownMarker(JSDHashTable *table, JSDHashEntryHdr *hdr, uint32 number, void *arg) { - XPCWrappedNativeProto* proto = + XPCWrappedNativeProto* proto = (XPCWrappedNativeProto*)((JSDHashEntryStub*)hdr)->key; proto->SystemIsBeingShutDown((JSContext*)arg); @@ -1011,6 +1139,12 @@ XPCJSRuntime::~XPCJSRuntime() mClearedGlobalObjects.ops = nsnull; } + ShutdownWatchdog(); + if(mWatchdogWakeup) + JS_DESTROY_CONDVAR(mWatchdogWakeup); + if(mWatchdogLock) + JS_DESTROY_LOCK(mWatchdogLock); + if(mJSRuntime) { JS_DestroyRuntime(mJSRuntime); @@ -1043,7 +1177,11 @@ XPCJSRuntime::XPCJSRuntime(nsXPConnect* aXPConnect) mDoingFinalization(JS_FALSE), mVariantRoots(nsnull), mWrappedJSRoots(nsnull), - mObjectHolderRoots(nsnull) + mObjectHolderRoots(nsnull), + mWatchdogLock(nsnull), + mWatchdogWakeup(nsnull), + mWatchdogThread(nsnull), + mCurrentInterval(PR_INTERVAL_NO_TIMEOUT) { #ifdef XPC_CHECK_WRAPPERS_AT_SHUTDOWN DEBUG_WrappedNativeHashtable = @@ -1098,6 +1236,10 @@ XPCJSRuntime::XPCJSRuntime(nsXPConnect* aXPConnect) if(mJSRuntime && !JS_GetGlobalDebugHooks(mJSRuntime)->debuggerHandler) xpc_InstallJSDebuggerKeywordHandler(mJSRuntime); #endif + + mWatchdogLock = JS_NEW_LOCK(); + if (mWatchdogLock) + mWatchdogWakeup = JS_NEW_CONDVAR(mWatchdogLock); } // static @@ -1119,7 +1261,8 @@ XPCJSRuntime::newXPCJSRuntime(nsXPConnect* aXPConnect) self->GetNativeScriptableSharedMap() && self->GetDyingWrappedNativeProtoMap() && self->GetExplicitNativeWrapperMap() && - self->GetMapLock()) + self->GetMapLock() && + self->mWatchdogWakeup) { return self; } @@ -1147,7 +1290,7 @@ XPCJSRuntime::OnJSContextNew(JSContext *cx) mStrJSVals[i] = STRING_TO_JSVAL(str); } } - if (!ok) + if(!ok) return JS_FALSE; XPCPerThreadData* tls = XPCPerThreadData::GetData(cx); @@ -1155,7 +1298,7 @@ XPCJSRuntime::OnJSContextNew(JSContext *cx) return JS_FALSE; XPCContext* xpc = new XPCContext(this, cx); - if (!xpc) + if(!xpc) return JS_FALSE; JS_SetThreadStackLimit(cx, tls->GetStackLimit()); diff --git a/js/src/xpconnect/src/xpcprivate.h b/js/src/xpconnect/src/xpcprivate.h index b7aa0e90213..be365b0067a 100644 --- a/js/src/xpconnect/src/xpcprivate.h +++ b/js/src/xpconnect/src/xpcprivate.h @@ -275,22 +275,22 @@ extern const char XPC_XPCONNECT_CONTRACTID[]; typedef PRMonitor XPCLock; -static inline void xpc_Wait(XPCLock* lock) +static inline void xpc_Wait(XPCLock* lock) { NS_ASSERTION(lock, "xpc_Wait called with null lock!"); #ifdef DEBUG - PRStatus result = + PRStatus result = #endif PR_Wait(lock, PR_INTERVAL_NO_TIMEOUT); NS_ASSERTION(PR_SUCCESS == result, "bad result from PR_Wait!"); } -static inline void xpc_NotifyAll(XPCLock* lock) +static inline void xpc_NotifyAll(XPCLock* lock) { NS_ASSERTION(lock, "xpc_NotifyAll called with null lock!"); #ifdef DEBUG - PRStatus result = -#endif + PRStatus result = +#endif PR_NotifyAll(lock); NS_ASSERTION(PR_SUCCESS == result, "bad result from PR_NotifyAll!"); } @@ -510,7 +510,7 @@ public: NS_IMETHOD Unroot(void *p); NS_IMETHOD Traverse(void *p, nsCycleCollectionTraversalCallback &cb); - + // nsCycleCollectionLanguageRuntime virtual nsresult BeginCycleCollection(nsCycleCollectionTraversalCallback &cb); virtual nsresult FinishCycleCollection(); @@ -738,6 +738,10 @@ public: ~XPCJSRuntime(); + PRIntervalTime GetWatchdogLimit(JSContext *cx); + PRBool SetWatchdogLimit(JSContext *cx, PRIntervalTime limit); + void WakeupWatchdog(JSContext *cx); + #ifdef XPC_CHECK_WRAPPERS_AT_SHUTDOWN void DEBUG_AddWrappedNative(nsIXPConnectWrappedNative* wrapper) {XPCAutoLock lock(GetMapLock()); @@ -759,6 +763,9 @@ private: XPCJSRuntime(); // no implementation XPCJSRuntime(nsXPConnect* aXPConnect); + PRBool ShutdownWatchdog(); + static void WatchdogMain(void *args); + private: static const char* mStrings[IDX_TOTAL_COUNT]; jsid mStrIDs[IDX_TOTAL_COUNT]; @@ -786,6 +793,15 @@ private: XPCRootSetElem *mObjectHolderRoots; JSDHashTable mJSHolders; JSDHashTable mClearedGlobalObjects; + + /* + * Variables to support watchdog thread. + */ + PRLock *mWatchdogLock; + PRCondVar *mWatchdogWakeup; + PRBool mWatchdogRunning; + PRThread *mWatchdogThread; + PRIntervalTime mCurrentInterval; }; /***************************************************************************/ @@ -808,26 +824,26 @@ public: JSContext* GetJSContext() const {return mJSContext;} enum LangType {LANG_UNKNOWN, LANG_JS, LANG_NATIVE}; - + LangType GetCallingLangType() const { return mCallingLangType; } LangType SetCallingLangType(LangType lt) { - LangType tmp = mCallingLangType; - mCallingLangType = lt; + LangType tmp = mCallingLangType; + mCallingLangType = lt; return tmp; } - JSBool CallerTypeIsJavaScript() const + JSBool CallerTypeIsJavaScript() const { return LANG_JS == mCallingLangType; } - JSBool CallerTypeIsNative() const + JSBool CallerTypeIsNative() const { return LANG_NATIVE == mCallingLangType; } - JSBool CallerTypeIsKnown() const + JSBool CallerTypeIsKnown() const { return LANG_UNKNOWN != mCallingLangType; } @@ -901,6 +917,7 @@ private: nsresult mPendingResult; nsIXPCSecurityManager* mSecurityManager; nsIException* mException; + PRIntervalTime mWatchdogLimit; LangType mCallingLangType; PRUint16 mSecurityManagerFlags; @@ -1244,7 +1261,7 @@ public: {return mScriptObjectPrincipal ? mScriptObjectPrincipal->GetPrincipal() : nsnull;} #endif - + JSObject* GetPrototypeJSFunction() const {return mPrototypeJSFunction;} @@ -1977,7 +1994,7 @@ public: // Yes, we *do* need to mark the mScriptableInfo in both cases. void Mark() const - {mSet->Mark(); + {mSet->Mark(); if(mScriptableInfo) mScriptableInfo->Mark();} #ifdef DEBUG @@ -2499,9 +2516,9 @@ public: JSObject* aJSObj, nsISimpleEnumerator** aEnumerate); - static nsresult GetNamedPropertyAsVariant(XPCCallContext& ccx, + static nsresult GetNamedPropertyAsVariant(XPCCallContext& ccx, JSObject* aJSObj, - jsval aName, + jsval aName, nsIVariant** aResult); virtual ~nsXPCWrappedJSClass(); @@ -2737,7 +2754,7 @@ public: * @param scope the default scope to put on the new JSObject's __parent__ * chain * @param pErr [out] relevant error code, if any. - */ + */ static JSBool NativeData2JS(XPCCallContext& ccx, jsval* d, const void* s, const nsXPTType& type, const nsID* iid, JSObject* scope, nsresult* pErr); @@ -2771,7 +2788,7 @@ public: static JSBool GetNativeInterfaceFromJSObject(XPCCallContext& ccx, void** dest, JSObject* src, - const nsID* iid, + const nsID* iid, nsresult* pErr); static JSBool JSObject2NativeInterface(XPCCallContext& ccx, void** dest, JSObject* src, @@ -2792,7 +2809,7 @@ public: * @param scope the default scope to put on the new JSObjects' __parent__ * chain * @param pErr [out] relevant error code, if any. - */ + */ static JSBool NativeArray2JS(XPCCallContext& ccx, jsval* d, const void** s, const nsXPTType& type, const nsID* iid, @@ -2842,7 +2859,6 @@ public: private: XPCConvert(); // not implemented - }; /***************************************************************************/ @@ -2880,7 +2896,7 @@ public: static void ThrowBadResult(nsresult rv, nsresult result, XPCCallContext& ccx); static void ThrowBadParam(nsresult rv, uintN paramNum, XPCCallContext& ccx); #ifdef XPC_IDISPATCH_SUPPORT - static void ThrowCOMError(JSContext* cx, unsigned long COMErrorCode, + static void ThrowCOMError(JSContext* cx, unsigned long COMErrorCode, nsresult rv = NS_ERROR_XPC_COM_ERROR, const EXCEPINFO * exception = nsnull); #endif @@ -3287,7 +3303,7 @@ private: static XPCPerThreadData* gThreads; static PRUintn gTLSIndex; - // Cached value of cx->thread on the main thread. + // Cached value of cx->thread on the main thread. static void *sMainJSThread; // Cached per thread data for the main thread. Only safe to access @@ -3595,7 +3611,7 @@ private: JSContext *mCX; jsrefcount mDepth; }; - + /*****************************************/ @@ -3691,7 +3707,7 @@ public: ~AutoResolveName() { #ifdef DEBUG - jsval old = + jsval old = #endif mTLS->SetResolveName(mOld); NS_ASSERTION(old == mCheck, "Bad Nesting!"); @@ -3717,17 +3733,17 @@ public: } void AutoTrace(JSTracer* trc) {} private: - XPCMarkableJSVal(); // not implemented + XPCMarkableJSVal(); // not implemented jsval mVal; jsval* mValPtr; -}; +}; /***************************************************************************/ -// AutoMarkingPtr is the base class for the various AutoMarking pointer types -// below. This system allows us to temporarily protect instances of our garbage -// collected types after they are constructed but before they are safely +// AutoMarkingPtr is the base class for the various AutoMarking pointer types +// below. This system allows us to temporarily protect instances of our garbage +// collected types after they are constructed but before they are safely // attached to other rooted objects. -// This base class has pure virtual support for marking. +// This base class has pure virtual support for marking. class AutoMarkingPtr { @@ -3736,15 +3752,15 @@ public: : mNext(nsnull), mTLS(ccx.GetThreadData()) {Link();} virtual ~AutoMarkingPtr() {Unlink();} - - void Link() + + void Link() {if(!mTLS) return; - AutoMarkingPtr** list = mTLS->GetAutoRootsAdr(); + AutoMarkingPtr** list = mTLS->GetAutoRootsAdr(); mNext = *list; *list = this;} - void Unlink() + void Unlink() {if(!mTLS) return; - AutoMarkingPtr** cur = mTLS->GetAutoRootsAdr(); + AutoMarkingPtr** cur = mTLS->GetAutoRootsAdr(); while(*cur != this) { NS_ASSERTION(*cur, "This object not in list!"); cur = &(*cur)->mNext; @@ -3754,7 +3770,7 @@ public: } AutoMarkingPtr* GetNext() {return mNext;} - + virtual void TraceJS(JSTracer* trc) = 0; virtual void MarkAfterJSFinalize() = 0; @@ -3803,7 +3819,7 @@ DEFINE_AUTO_MARKING_PTR_TYPE(AutoMarkingWrappedNativePtr, XPCWrappedNative) DEFINE_AUTO_MARKING_PTR_TYPE(AutoMarkingWrappedNativeTearOffPtr, XPCWrappedNativeTearOff) DEFINE_AUTO_MARKING_PTR_TYPE(AutoMarkingWrappedNativeProtoPtr, XPCWrappedNativeProto) DEFINE_AUTO_MARKING_PTR_TYPE(AutoMarkingJSVal, XPCMarkableJSVal) - + #define DEFINE_AUTO_MARKING_ARRAY_PTR_TYPE(class_, type_) \ class class_ : public AutoMarkingPtr \ { \ @@ -3858,9 +3874,9 @@ protected: \ DEFINE_AUTO_MARKING_ARRAY_PTR_TYPE(AutoMarkingNativeInterfacePtrArrayPtr, XPCNativeInterface) - + // Note: It looked like I would need one of these AutoMarkingPtr types for -// XPCNativeScriptableInfo in order to manage marking its +// XPCNativeScriptableInfo in order to manage marking its // XPCNativeScriptableShared member during construction. But AFAICT we build // these and bind them to rooted things so immediately that this just is not // needed. @@ -3903,8 +3919,8 @@ public: // the case when mJSVal is JSVAL_STRING, since we don't own the data in // that case. - // We #define and iid so that out module local code can use QI to detect - // if a given nsIVariant is in fact an XPCVariant. + // We #define and iid so that out module local code can use QI to detect + // if a given nsIVariant is in fact an XPCVariant. NS_DECLARE_STATIC_IID_ACCESSOR(XPCVARIANT_IID) static XPCVariant* newVariant(XPCCallContext& ccx, jsval aJSVal); @@ -3922,8 +3938,8 @@ public: * chain * @param pErr [out] relevant error code, if any. * @param pJSVal [out] the resulting jsval. - */ - static JSBool VariantDataToJS(XPCCallContext& ccx, + */ + static JSBool VariantDataToJS(XPCCallContext& ccx, nsIVariant* variant, JSObject* scope, nsresult* pErr, jsval* pJSVal); diff --git a/testing/mochitest/Makefile.in b/testing/mochitest/Makefile.in index 4041e8f6da5..157cfd1cb73 100644 --- a/testing/mochitest/Makefile.in +++ b/testing/mochitest/Makefile.in @@ -126,6 +126,12 @@ else TEST_DRIVER_PPARGS += -DIS_TEST_BUILD=0 endif +ifeq ($(MOZ_DEBUG), 1) +TEST_DRIVER_PPARGS += -DIS_DEBUG_BUILD=1 +else +TEST_DRIVER_PPARGS += -DIS_DEBUG_BUILD=0 +endif + runtests.py: runtests.py.in $(PYTHON) $(topsrcdir)/config/Preprocessor.py \ $(TEST_DRIVER_PPARGS) $(DEFINES) $(ACDEFINES) $^ > $@