bug 453157 - watchdog thread as an alternative to operation count. r=myself,mrbkap

This commit is contained in:
Andrei Saprykin 2009-01-05 22:09:23 +01:00
Родитель 03c0ca6035
Коммит 2e891c7f35
20 изменённых файлов: 768 добавлений и 234 удалений

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

@ -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 = \

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

@ -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) $^ > $@

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

@ -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.

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

@ -852,9 +852,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)
{
@ -1238,9 +1235,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 =
{

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

@ -823,8 +823,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,

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

@ -104,10 +104,11 @@ 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;
static uint32 gBranchCount;
static uint32 gBranchLimit;
#endif
int gExitCode = 0;
JSBool gQuitting = JS_FALSE;
@ -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
@ -416,7 +558,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':
@ -536,6 +680,7 @@ extern void js_InitJITStatsClass(JSContext *cx, JSObject *glob);
}
break;
#if JS_HAS_OPERATION_COUNT
case 'b':
if (++i == argc)
return usage();
@ -543,6 +688,7 @@ extern void js_InitJITStatsClass(JSContext *cx, JSObject *glob);
gBranchLimit = atoi(argv[i]);
gEnableBranchCallback = (gBranchLimit != 0);
break;
#endif
case 'c':
/* set stack chunk size */
@ -2158,6 +2304,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. */
@ -2743,12 +2921,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;
@ -2924,6 +3104,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),
@ -3005,6 +3188,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",
@ -3946,7 +4134,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;
}
@ -3994,6 +4189,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);
@ -4103,8 +4310,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;
}

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

@ -905,6 +905,7 @@ JS_BeginRequest(JSContext *cx)
{
#ifdef JS_THREADSAFE
JSRuntime *rt;
JSContextCallback cxCallback;
JS_ASSERT(cx->thread->id == js_CurrentThreadId());
if (!cx->requestDepth) {
@ -912,6 +913,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(). */
@ -925,6 +929,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++;
@ -985,7 +999,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;
}
@ -5249,6 +5265,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)
@ -5324,6 +5341,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)

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

@ -45,6 +45,7 @@
*/
#include <stddef.h>
#include <stdio.h>
#include "jsversion.h"
#include "js-config.h"
#include "jspubtd.h"
#include "jsutil.h"
@ -2170,6 +2171,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.
@ -2194,12 +2202,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)
@ -2227,6 +2229,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);

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

@ -251,7 +251,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;
@ -507,7 +511,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;
@ -1348,12 +1352,12 @@ js_GetErrorMessage(void *userRef, const char *locale, const uintN errorNumber)
JSBool
js_ResetOperationCount(JSContext *cx)
{
JS_ASSERT(cx->operationCount <= 0);
#if JS_HAS_OPERATION_COUNT
JSScript *script;
JSStackFrame *fp;
JS_ASSERT(cx->operationCount <= 0);
JS_ASSERT(cx->operationLimit > 0);
cx->operationCount = (int32) cx->operationLimit;
if (cx->operationCallbackIsSet)
return cx->operationCallback(cx);
@ -1369,6 +1373,14 @@ 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;
}

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

@ -741,7 +741,11 @@ struct JSContext {
* Operation count. It is declared as the first field in the struct to
* ensure the fastest possible access.
*/
#if !JS_HAS_OPERATION_COUNT
volatile int32 operationCount;
#else
int32 operationCount;
#endif
/* JSRuntime contextList linkage. */
JSCList link;
@ -855,8 +859,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. */
@ -882,6 +888,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;
@ -1061,7 +1071,7 @@ js_ContextFromLinkField(JSCList *link)
* 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);
/*
@ -1223,7 +1233,9 @@ 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)))
@ -1233,7 +1245,7 @@ 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) \
@ -1243,21 +1255,25 @@ extern JSErrorFormatString js_ErrorFormatString[JSErr_Limit];
* 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.

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

@ -2667,14 +2667,21 @@ 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 == 0 && !js_ResetOperationCount(cx)) \
goto error; \
JS_END_MACRO
#endif
#define BRANCH(n) \
JS_BEGIN_MACRO \
regs.pc += n; \

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

@ -594,7 +594,8 @@ typedef JSBool
typedef enum JSContextOp {
JSCONTEXT_NEW,
JSCONTEXT_DESTROY
JSCONTEXT_DESTROY,
JSCONTEXT_REQUEST_START
} JSContextOp;
/*
@ -607,6 +608,9 @@ typedef enum JSContextOp {
* 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.
*/

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

@ -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

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

@ -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(f8bf005e-3700-411c-ba0c-e018075f22a4)]
[uuid(eb95710a-e112-44ae-abe1-6f8dfb58d7f8)]
interface nsIXPConnect : nsISupports
{
%{ C++
@ -785,4 +786,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] void setWatchdogLimit(in JSContextPtr cx, in PRIntervalTime limit);
};

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

@ -2392,6 +2392,18 @@ nsXPConnect::SetSafeJSContext(JSContext * aSafeJSContext)
return data->GetJSContextStack()->SetSafeJSContext(aSafeJSContext);
}
NS_IMETHODIMP_(void)
nsXPConnect::SetWatchdogLimit(JSContext *cx, PRIntervalTime limit)
{
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()

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

@ -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);
nsXPConnect::GetXPConnect()->SetWatchdogLimit(cx, limit);
return ok;
}
}

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

@ -52,6 +52,7 @@ XPCContext::XPCContext(XPCJSRuntime* aRuntime,
mPendingResult(NS_OK),
mSecurityManager(nsnull),
mException(nsnull),
mWatchdogLimit(0),
mCallingLangType(LANG_UNKNOWN),
mSecurityManagerFlags(0)
{

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

@ -240,6 +240,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;
}
@ -777,6 +785,93 @@ JSBool XPCJSRuntime::GCCallback(JSContext *cx, JSGCStatus status)
return JS_TRUE;
}
/* static */
void
XPCJSRuntime::WatchdogMain(void *arg)
{
XPCJSRuntime* self = (XPCJSRuntime*) arg;
JSRuntime* rt = self->GetJSRuntime();
PRBool isRunning = PR_TRUE;
do
{
PR_Lock(self->mWatchdogLock);
if (!self->mWatchdogThread) {
isRunning = PR_FALSE;
} else {
JS_LOCK_GC(rt);
PRIntervalTime ct = PR_IntervalNow();
PRIntervalTime newInterval = (PRIntervalTime) 0;
JSContext* iter = NULL;
JSContext* acx;
while((acx = js_ContextIterator(rt, JS_FALSE, &iter)))
{
if(acx->requestDepth)
{
XPCContext *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);
self->mCurrentInterval = newInterval
? newInterval
: PR_INTERVAL_NO_TIMEOUT;
#ifdef DEBUG
PRStatus status =
#endif
PR_WaitCondVar(self->mWatchdogWakeup, self->mCurrentInterval);
NS_ASSERTION(status == PR_SUCCESS, "Unexpected status");
}
PR_Unlock(self->mWatchdogLock);
} while (isRunning);
}
void
XPCJSRuntime::SetWatchdogLimit(JSContext *cx, PRIntervalTime newWatchdogLimit)
{
NS_ASSERTION(mWatchdogThread, "Watchdog thread must be running");
PRIntervalTime oldWatchdogLimit;
XPCContext *ccx = XPCContext::GetXPCContext(cx);
if(newWatchdogLimit == ccx->mWatchdogLimit)
return;
oldWatchdogLimit = ccx->mWatchdogLimit;
ccx->mWatchdogLimit = newWatchdogLimit;
if(oldWatchdogLimit > ccx->mWatchdogLimit ||
mCurrentInterval == PR_INTERVAL_NO_TIMEOUT)
{
WakeupWatchdog(cx);
}
}
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);
}
PRIntervalTime
XPCJSRuntime::GetWatchdogLimit(JSContext *cx)
{
return XPCContext::GetXPCContext(cx)->mWatchdogLimit;
}
/***************************************************************************/
#ifdef XPC_CHECK_WRAPPERS_AT_SHUTDOWN
@ -967,6 +1062,22 @@ XPCJSRuntime::~XPCJSRuntime()
if(mJSRuntime)
{
if(mWatchdogLock)
{
if(mWatchdogWakeup)
{
PR_Lock(mWatchdogLock);
PRThread *t = mWatchdogThread;
mWatchdogThread = NULL;
PR_NotifyCondVar(mWatchdogWakeup);
PR_Unlock(mWatchdogLock);
if(t)
PR_JoinThread(t);
JS_DESTROY_CONDVAR(mWatchdogWakeup);
}
JS_DESTROY_LOCK(mWatchdogLock);
}
JS_DestroyRuntime(mJSRuntime);
JS_ShutDown();
#ifdef DEBUG_shaver_off
@ -998,6 +1109,10 @@ XPCJSRuntime::XPCJSRuntime(nsXPConnect* aXPConnect)
mVariantRoots(nsnull),
mWrappedJSRoots(nsnull),
mObjectHolderRoots(nsnull),
mWatchdogLock(nsnull),
mWatchdogWakeup(nsnull),
mWatchdogThread(nsnull),
mCurrentInterval(PR_INTERVAL_NO_TIMEOUT),
mUnrootedGlobalCount(0)
{
#ifdef XPC_CHECK_WRAPPERS_AT_SHUTDOWN
@ -1041,6 +1156,25 @@ XPCJSRuntime::XPCJSRuntime(nsXPConnect* aXPConnect)
JS_SetContextCallback(mJSRuntime, ContextCallback);
JS_SetGCCallbackRT(mJSRuntime, GCCallback);
JS_SetExtraGCRoots(mJSRuntime, TraceJS, this);
mWatchdogLock = JS_NEW_LOCK();
if(mWatchdogLock)
{
mWatchdogWakeup = JS_NEW_CONDVAR(mWatchdogLock);
if (mWatchdogWakeup)
{
PR_Lock(mWatchdogLock);
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(!JS_DHashTableInit(&mJSHolders, JS_DHashGetStubOps(), nsnull,
@ -1052,6 +1186,7 @@ XPCJSRuntime::XPCJSRuntime(nsXPConnect* aXPConnect)
if(mJSRuntime && !JS_GetGlobalDebugHooks(mJSRuntime)->debuggerHandler)
xpc_InstallJSDebuggerKeywordHandler(mJSRuntime);
#endif
}
// static
@ -1073,7 +1208,8 @@ XPCJSRuntime::newXPCJSRuntime(nsXPConnect* aXPConnect)
self->GetNativeScriptableSharedMap() &&
self->GetDyingWrappedNativeProtoMap() &&
self->GetExplicitNativeWrapperMap() &&
self->GetMapLock())
self->GetMapLock() &&
self->mWatchdogThread)
{
return self;
}
@ -1101,7 +1237,7 @@ XPCJSRuntime::OnJSContextNew(JSContext *cx)
mStrJSVals[i] = STRING_TO_JSVAL(str);
}
}
if (!ok)
if(!ok)
return JS_FALSE;
XPCPerThreadData* tls = XPCPerThreadData::GetData(cx);
@ -1109,7 +1245,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());

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

@ -737,6 +737,10 @@ public:
~XPCJSRuntime();
PRIntervalTime GetWatchdogLimit(JSContext *cx);
void SetWatchdogLimit(JSContext *cx, PRIntervalTime limit);
void WakeupWatchdog(JSContext *cx);
#ifdef XPC_CHECK_WRAPPERS_AT_SHUTDOWN
void DEBUG_AddWrappedNative(nsIXPConnectWrappedNative* wrapper)
{XPCAutoLock lock(GetMapLock());
@ -758,6 +762,8 @@ private:
XPCJSRuntime(); // no implementation
XPCJSRuntime(nsXPConnect* aXPConnect);
static void WatchdogMain(void *args);
private:
static const char* mStrings[IDX_TOTAL_COUNT];
jsid mStrIDs[IDX_TOTAL_COUNT];
@ -784,6 +790,14 @@ private:
XPCRootSetElem *mWrappedJSRoots;
XPCRootSetElem *mObjectHolderRoots;
JSDHashTable mJSHolders;
/*
* Variables to support watchdog thread.
*/
PRLock *mWatchdogLock;
PRCondVar *mWatchdogWakeup;
PRThread *mWatchdogThread;
PRIntervalTime mCurrentInterval;
uintN mUnrootedGlobalCount;
};
@ -900,6 +914,7 @@ private:
nsresult mPendingResult;
nsIXPCSecurityManager* mSecurityManager;
nsIException* mException;
PRIntervalTime mWatchdogLimit;
LangType mCallingLangType;
PRUint16 mSecurityManagerFlags;
@ -2844,7 +2859,6 @@ public:
private:
XPCConvert(); // not implemented
};
/***************************************************************************/

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

@ -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) $^ > $@