Bug 842745 - Modify timer in JS shell function to take an arbitrary JS function. r=dvander

This commit is contained in:
Nicholas D. Matsakis 2013-02-19 09:23:31 -05:00
parent eaf92b0373
commit 073ed19b99

View File

@ -115,7 +115,8 @@ static uintptr_t gStackBase;
*/
static double MAX_TIMEOUT_INTERVAL = 1800.0;
static double gTimeoutInterval = -1.0;
static volatile bool gCanceled = false;
static volatile bool gTimedOut = false;
static js::Value gTimeoutFunc;
static bool enableMethodJit = true;
static bool enableTypeInference = true;
@ -453,7 +454,7 @@ static JSShellContextData *
NewContextData()
{
/* Prevent creation of new contexts after we have been canceled. */
if (gCanceled)
if (gTimedOut)
return NULL;
JSShellContextData *data = (JSShellContextData *)
@ -476,11 +477,28 @@ GetContextData(JSContext *cx)
static JSBool
ShellOperationCallback(JSContext *cx)
{
if (!gCanceled)
if (!gTimedOut)
return true;
JS_ClearPendingException(cx);
return false;
bool result;
if (!gTimeoutFunc.isNull()) {
jsval returnedValue;
if (!JS_CallFunctionValue(cx, NULL, gTimeoutFunc, 0, NULL, &returnedValue))
return false;
if (returnedValue.isBoolean())
result = returnedValue.toBoolean();
else
result = false;
} else {
result = false;
}
if (!result && gExitCode == 0)
gExitCode = EXITCODE_TIMEOUT;
return result;
}
static void
@ -575,7 +593,7 @@ Process(JSContext *cx, JSObject *obj_, const char *filename, bool forceTTY)
JS_ASSERT_IF(!script, gGotError);
if (script && !compileOnly) {
if (!JS_ExecuteScript(cx, obj, script, NULL)) {
if (!gQuitting && !gCanceled)
if (!gQuitting && !gTimedOut)
gExitCode = EXITCODE_RUNTIME_ERROR;
}
int64_t t2 = PRMJ_Now() - t1;
@ -602,7 +620,7 @@ Process(JSContext *cx, JSObject *obj_, const char *filename, bool forceTTY)
size_t len = 0; /* initialize to avoid warnings */
do {
ScheduleWatchdog(cx->runtime, -1);
gCanceled = false;
gTimedOut = false;
errno = 0;
char *line = GetLine(file, startline == lineno ? "js> " : "");
@ -2779,7 +2797,7 @@ Sleep_fn(JSContext *cx, unsigned argc, jsval *vp)
int64_t to_wakeup = PRMJ_Now() + t_ticks;
for (;;) {
PR_WaitCondVar(gSleepWakeup, t_ticks);
if (gCanceled)
if (gTimedOut)
break;
int64_t now = PRMJ_Now();
if (!IsBefore(now, to_wakeup))
@ -2787,7 +2805,7 @@ Sleep_fn(JSContext *cx, unsigned argc, jsval *vp)
t_ticks = to_wakeup - now;
}
PR_Unlock(gWatchdogLock);
return !gCanceled;
return !gTimedOut;
}
static bool
@ -2979,20 +2997,20 @@ ScheduleWatchdog(JSRuntime *rt, double t)
static void
CancelExecution(JSRuntime *rt)
{
gCanceled = true;
if (gExitCode == 0)
gExitCode = EXITCODE_TIMEOUT;
gTimedOut = true;
JS_TriggerOperationCallback(rt);
static const char msg[] = "Script runs for too long, terminating.\n";
if (!gTimeoutFunc.isNull()) {
static const char msg[] = "Script runs for too long, terminating.\n";
#if defined(XP_UNIX) && !defined(JS_THREADSAFE)
/* It is not safe to call fputs from signals. */
/* Dummy assignment avoids GCC warning on "attribute warn_unused_result" */
ssize_t dummy = write(2, msg, sizeof(msg) - 1);
(void)dummy;
/* It is not safe to call fputs from signals. */
/* Dummy assignment avoids GCC warning on "attribute warn_unused_result" */
ssize_t dummy = write(2, msg, sizeof(msg) - 1);
(void)dummy;
#else
fputs(msg, stderr);
fputs(msg, stderr);
#endif
}
}
static JSBool
@ -3019,7 +3037,7 @@ Timeout(JSContext *cx, unsigned argc, jsval *vp)
return true;
}
if (argc > 1) {
if (argc > 2) {
JS_ReportError(cx, "Wrong number of arguments");
return false;
}
@ -3028,6 +3046,15 @@ Timeout(JSContext *cx, unsigned argc, jsval *vp)
if (!JS_ValueToNumber(cx, JS_ARGV(cx, vp)[0], &t))
return false;
if (argc > 1) {
RootedValue value(cx, JS_ARGV(cx, vp)[1]);
if (!value.isObject() || !value.toObject().isFunction()) {
JS_ReportError(cx, "Second argument must be a timeout function");
return false;
}
gTimeoutFunc = value;
}
*vp = JSVAL_VOID;
return SetTimeoutValue(cx, t);
}
@ -3796,9 +3823,10 @@ static JSFunctionSpecWithHelp shell_functions[] = {
" Parses a string, potentially throwing."),
JS_FN_HELP("timeout", Timeout, 1, 0,
"timeout([seconds])",
"timeout([seconds], [func])",
" Get/Set the limit in seconds for the execution time for the current context.\n"
" A negative value (default) means that the execution time is unlimited."),
" A negative value (default) means that the execution time is unlimited.\n"
" If a second argument is provided, it will be invoked when the timer elapses.\n"),
JS_FN_HELP("elapsed", Elapsed, 0, 0,
"elapsed()",
@ -5339,6 +5367,9 @@ main(int argc, char **argv, char **envp)
rt = JS_NewRuntime(32L * 1024L * 1024L, JS_USE_HELPER_THREADS);
if (!rt)
return 1;
gTimeoutFunc = JSVAL_NULL;
if (!JS_AddNamedValueRootRT(rt, &gTimeoutFunc, "gTimeoutFunc"))
return 1;
JS_SetGCParameter(rt, JSGC_MAX_BYTES, 0xffffffff);
#ifdef JSGC_GENERATIONAL