mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
bug 479252 - avoiding watchdog ticks when idle in jsshell. r=gal
This commit is contained in:
parent
7fe73224c5
commit
fe9a728583
@ -5336,6 +5336,17 @@ JS_TriggerOperationCallback(JSContext *cx)
|
|||||||
JS_ATOMIC_SET(&cx->operationCallbackFlag, 1);
|
JS_ATOMIC_SET(&cx->operationCallbackFlag, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
JS_PUBLIC_API(void)
|
||||||
|
JS_TriggerAllOperationCallbacks(JSRuntime *rt)
|
||||||
|
{
|
||||||
|
JSContext *acx, *iter;
|
||||||
|
JS_LOCK_GC(rt);
|
||||||
|
iter = NULL;
|
||||||
|
while ((acx = js_ContextIterator(rt, JS_FALSE, &iter)))
|
||||||
|
JS_TriggerOperationCallback(acx);
|
||||||
|
JS_UNLOCK_GC(rt);
|
||||||
|
}
|
||||||
|
|
||||||
JS_PUBLIC_API(JSBool)
|
JS_PUBLIC_API(JSBool)
|
||||||
JS_IsRunning(JSContext *cx)
|
JS_IsRunning(JSContext *cx)
|
||||||
{
|
{
|
||||||
|
@ -2281,6 +2281,9 @@ JS_GetOperationCallback(JSContext *cx);
|
|||||||
extern JS_PUBLIC_API(void)
|
extern JS_PUBLIC_API(void)
|
||||||
JS_TriggerOperationCallback(JSContext *cx);
|
JS_TriggerOperationCallback(JSContext *cx);
|
||||||
|
|
||||||
|
extern JS_PUBLIC_API(void)
|
||||||
|
JS_TriggerAllOperationCallbacks(JSRuntime *rt);
|
||||||
|
|
||||||
extern JS_PUBLIC_API(JSBool)
|
extern JS_PUBLIC_API(JSBool)
|
||||||
JS_IsRunning(JSContext *cx);
|
JS_IsRunning(JSContext *cx);
|
||||||
|
|
||||||
|
@ -43,6 +43,7 @@
|
|||||||
*/
|
*/
|
||||||
#include "jsstddef.h"
|
#include "jsstddef.h"
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
#include <math.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
@ -98,7 +99,8 @@
|
|||||||
typedef enum JSShellExitCode {
|
typedef enum JSShellExitCode {
|
||||||
EXITCODE_RUNTIME_ERROR = 3,
|
EXITCODE_RUNTIME_ERROR = 3,
|
||||||
EXITCODE_FILE_NOT_FOUND = 4,
|
EXITCODE_FILE_NOT_FOUND = 4,
|
||||||
EXITCODE_OUT_OF_MEMORY = 5
|
EXITCODE_OUT_OF_MEMORY = 5,
|
||||||
|
EXITCODE_TIMEOUT = 6
|
||||||
} JSShellExitCode;
|
} JSShellExitCode;
|
||||||
|
|
||||||
size_t gStackChunkSize = 8192;
|
size_t gStackChunkSize = 8192;
|
||||||
@ -109,26 +111,41 @@ static jsuword gStackBase;
|
|||||||
|
|
||||||
static size_t gScriptStackQuota = JS_DEFAULT_SCRIPT_STACK_QUOTA;
|
static size_t gScriptStackQuota = JS_DEFAULT_SCRIPT_STACK_QUOTA;
|
||||||
|
|
||||||
static uint32 gOperationLimit = 0;
|
/*
|
||||||
|
* Limit the timeout to 30 minutes to prevent an overflow on platfoms
|
||||||
|
* that represent the time internally in microseconds using 32-bit int.
|
||||||
|
*/
|
||||||
|
static jsdouble MAX_TIMEOUT_INTERVAL = 1800.0;
|
||||||
|
static jsdouble gTimeoutInterval = -1.0;
|
||||||
|
static volatile bool gCanceled = false;
|
||||||
|
|
||||||
static JSBool
|
static JSBool
|
||||||
SetTimeoutValue(JSContext *cx, jsdouble t);
|
SetTimeoutValue(JSContext *cx, jsdouble t);
|
||||||
|
|
||||||
static double
|
static bool
|
||||||
GetTimeoutValue(JSContext *cx);
|
InitWatchdog(JSRuntime *rt);
|
||||||
|
|
||||||
static void
|
static void
|
||||||
StopWatchdog(JSRuntime *rt);
|
KillWatchdog();
|
||||||
|
|
||||||
static JSBool
|
static bool
|
||||||
StartWatchdog(JSRuntime *rt);
|
ScheduleWatchdog(JSRuntime *rt, jsdouble t);
|
||||||
|
|
||||||
|
static void
|
||||||
|
CancelExecution(JSRuntime *rt);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Watchdog thread state.
|
* Watchdog thread state.
|
||||||
*/
|
*/
|
||||||
#ifdef JS_THREADSAFE
|
#ifdef JS_THREADSAFE
|
||||||
|
|
||||||
|
static PRLock *gWatchdogLock = NULL;
|
||||||
static PRCondVar *gWatchdogWakeup = NULL;
|
static PRCondVar *gWatchdogWakeup = NULL;
|
||||||
static PRThread *gWatchdogThread = NULL;
|
static PRThread *gWatchdogThread = NULL;
|
||||||
|
static bool gWatchdogHasTimeout = false;
|
||||||
|
static PRIntervalTime gWatchdogTimeout = 0;
|
||||||
|
|
||||||
|
static PRCondVar *gSleepWakeup = NULL;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Holding the gcLock already guarantees that the context list is locked when
|
* Holding the gcLock already guarantees that the context list is locked when
|
||||||
@ -141,20 +158,19 @@ static PRThread *gWatchdogThread = NULL;
|
|||||||
JS_END_MACRO
|
JS_END_MACRO
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
static JSRuntime *gRuntime = NULL;
|
static JSRuntime *gRuntime = NULL;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Since signal handlers can't block, we must disable them before manipulating
|
* Since signal handlers can't block, we must disable them before manipulating
|
||||||
* the context list.
|
* the context list.
|
||||||
*/
|
*/
|
||||||
|
#define WITH_LOCKED_CONTEXT_LIST(x) \
|
||||||
#define WITH_LOCKED_CONTEXT_LIST(x) \
|
JS_BEGIN_MACRO \
|
||||||
JS_BEGIN_MACRO \
|
ScheduleWatchdog(gRuntime, -1); \
|
||||||
StopWatchdog(gRuntime); \
|
x; \
|
||||||
x; \
|
ScheduleWatchdog(gRuntime, gTimeoutInterval); \
|
||||||
StartWatchdog(gRuntime); \
|
|
||||||
JS_END_MACRO
|
JS_END_MACRO
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
int gExitCode = 0;
|
int gExitCode = 0;
|
||||||
@ -263,6 +279,10 @@ struct JSShellContextData {
|
|||||||
static JSShellContextData *
|
static JSShellContextData *
|
||||||
NewContextData()
|
NewContextData()
|
||||||
{
|
{
|
||||||
|
/* Prevent creation of new contexts after we have been canceled. */
|
||||||
|
if (gCanceled)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
JSShellContextData *data = (JSShellContextData *)
|
JSShellContextData *data = (JSShellContextData *)
|
||||||
calloc(sizeof(JSShellContextData), 1);
|
calloc(sizeof(JSShellContextData), 1);
|
||||||
if (!data)
|
if (!data)
|
||||||
@ -283,12 +303,7 @@ GetContextData(JSContext *cx)
|
|||||||
static JSBool
|
static JSBool
|
||||||
ShellOperationCallback(JSContext *cx)
|
ShellOperationCallback(JSContext *cx)
|
||||||
{
|
{
|
||||||
JSShellContextData *data;
|
return !gCanceled;
|
||||||
if ((data = GetContextData(cx)) != NULL) {
|
|
||||||
/* If we spent too much time in this script, abort it. */
|
|
||||||
return !gOperationLimit || (uint32(js_IntervalNow() - data->startTime) < gOperationLimit);
|
|
||||||
}
|
|
||||||
return JS_TRUE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -383,8 +398,6 @@ Process(JSContext *cx, JSObject *obj, char *filename, JSBool forceTTY)
|
|||||||
buffer = NULL;
|
buffer = NULL;
|
||||||
size = 0; /* assign here to avoid warnings */
|
size = 0; /* assign here to avoid warnings */
|
||||||
do {
|
do {
|
||||||
size_t len = 0; /* initialize to avoid warnings */
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Accumulate lines until we get a 'compilable unit' - one that either
|
* Accumulate lines until we get a 'compilable unit' - one that either
|
||||||
* generates an error (before running out of source) or that compiles
|
* generates an error (before running out of source) or that compiles
|
||||||
@ -392,7 +405,13 @@ Process(JSContext *cx, JSObject *obj, char *filename, JSBool forceTTY)
|
|||||||
* coincides with the end of a line.
|
* coincides with the end of a line.
|
||||||
*/
|
*/
|
||||||
startline = lineno;
|
startline = lineno;
|
||||||
|
size_t len = 0; /* initialize to avoid warnings */
|
||||||
do {
|
do {
|
||||||
|
ScheduleWatchdog(cx->runtime, -1);
|
||||||
|
#ifdef JS_THREADSAFE
|
||||||
|
jsrefcount rc = JS_SuspendRequest(cx);
|
||||||
|
#endif
|
||||||
|
gCanceled = false;
|
||||||
errno = 0;
|
errno = 0;
|
||||||
char *line = GetLine(file, startline == lineno ? "js> " : "");
|
char *line = GetLine(file, startline == lineno ? "js> " : "");
|
||||||
if (!line) {
|
if (!line) {
|
||||||
@ -432,6 +451,13 @@ Process(JSContext *cx, JSObject *obj, char *filename, JSBool forceTTY)
|
|||||||
free(line);
|
free(line);
|
||||||
}
|
}
|
||||||
lineno++;
|
lineno++;
|
||||||
|
#ifdef JS_THREADSAFE
|
||||||
|
JS_ResumeRequest(cx, rc);
|
||||||
|
#endif
|
||||||
|
if (!ScheduleWatchdog(cx->runtime, gTimeoutInterval)) {
|
||||||
|
hitEOF = JS_TRUE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
} while (!JS_BufferIsCompilableUnit(cx, obj, buffer, len));
|
} while (!JS_BufferIsCompilableUnit(cx, obj, buffer, len));
|
||||||
|
|
||||||
if (hitEOF && !buffer)
|
if (hitEOF && !buffer)
|
||||||
@ -693,6 +719,8 @@ extern void js_InitJITStatsClass(JSContext *cx, JSObject *glob);
|
|||||||
return usage();
|
return usage();
|
||||||
|
|
||||||
Process(cx, obj, argv[i], JS_FALSE);
|
Process(cx, obj, argv[i], JS_FALSE);
|
||||||
|
if (gExitCode != 0)
|
||||||
|
return gExitCode;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* XXX: js -f foo.js should interpret foo.js and then
|
* XXX: js -f foo.js should interpret foo.js and then
|
||||||
@ -2808,27 +2836,59 @@ ShapeOf(JSContext *cx, uintN argc, jsval *vp)
|
|||||||
|
|
||||||
#ifdef JS_THREADSAFE
|
#ifdef JS_THREADSAFE
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check that t1 comes strictly before t2. The function correctly deals with
|
||||||
|
* PRIntervalTime wrap-around between t2 and t1 assuming that t2 and t1 stays
|
||||||
|
* within INT32_MAX from each other. We use MAX_TIMEOUT_INTERVAL to enforce
|
||||||
|
* this restriction.
|
||||||
|
*/
|
||||||
|
static bool
|
||||||
|
IsBefore(PRIntervalTime t1, PRIntervalTime t2)
|
||||||
|
{
|
||||||
|
return int32(t1 - t2) < 0;
|
||||||
|
}
|
||||||
|
|
||||||
static JSBool
|
static JSBool
|
||||||
Sleep_fn(JSContext *cx, uintN argc, jsval *vp)
|
Sleep_fn(JSContext *cx, uintN argc, jsval *vp)
|
||||||
{
|
{
|
||||||
jsdouble t_secs;
|
PRIntervalTime t_ticks;
|
||||||
PRUint32 t_ticks;
|
|
||||||
jsrefcount rc;
|
|
||||||
|
|
||||||
if (!JS_ValueToNumber(cx, argc == 0 ? JSVAL_VOID : vp[2], &t_secs))
|
if (argc == 0) {
|
||||||
return JS_FALSE;
|
t_ticks = 0;
|
||||||
|
} else {
|
||||||
|
jsdouble t_secs;
|
||||||
|
|
||||||
if (t_secs < 0 || JSDOUBLE_IS_NaN(t_secs))
|
if (!JS_ValueToNumber(cx, argc == 0 ? JSVAL_VOID : vp[2], &t_secs))
|
||||||
t_secs = 0;
|
return JS_FALSE;
|
||||||
|
|
||||||
rc = JS_SuspendRequest(cx);
|
/* NB: The next condition also filter out NaNs. */
|
||||||
t_ticks = (PRUint32)(PR_TicksPerSecond() * t_secs);
|
if (!(t_secs <= MAX_TIMEOUT_INTERVAL)) {
|
||||||
if (PR_Sleep(t_ticks) == PR_SUCCESS)
|
JS_ReportError(cx, "Excessive sleep interval");
|
||||||
*vp = JSVAL_TRUE;
|
return JS_FALSE;
|
||||||
else
|
}
|
||||||
*vp = JSVAL_FALSE;
|
t_ticks = (t_secs <= 0.0)
|
||||||
JS_ResumeRequest(cx, rc);
|
? 0
|
||||||
return JS_TRUE;
|
: PRIntervalTime(PR_TicksPerSecond() * t_secs);
|
||||||
|
}
|
||||||
|
if (t_ticks == 0) {
|
||||||
|
JS_YieldRequest(cx);
|
||||||
|
} else {
|
||||||
|
jsrefcount rc = JS_SuspendRequest(cx);
|
||||||
|
PR_Lock(gWatchdogLock);
|
||||||
|
PRIntervalTime to_wakeup = PR_IntervalNow() + t_ticks;
|
||||||
|
for (;;) {
|
||||||
|
PR_WaitCondVar(gSleepWakeup, t_ticks);
|
||||||
|
if (gCanceled)
|
||||||
|
break;
|
||||||
|
PRIntervalTime now = PR_IntervalNow();
|
||||||
|
if (!IsBefore(now, to_wakeup))
|
||||||
|
break;
|
||||||
|
t_ticks = to_wakeup - now;
|
||||||
|
}
|
||||||
|
PR_Unlock(gWatchdogLock);
|
||||||
|
JS_ResumeRequest(cx, rc);
|
||||||
|
}
|
||||||
|
return !gCanceled;
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef struct ScatterThreadData ScatterThreadData;
|
typedef struct ScatterThreadData ScatterThreadData;
|
||||||
@ -3065,165 +3125,222 @@ fail:
|
|||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
InitWatchdog(JSRuntime *rt)
|
||||||
|
{
|
||||||
|
JS_ASSERT(!gWatchdogThread);
|
||||||
|
gWatchdogLock = PR_NewLock();
|
||||||
|
if (gWatchdogLock) {
|
||||||
|
gWatchdogWakeup = PR_NewCondVar(gWatchdogLock);
|
||||||
|
if (gWatchdogWakeup) {
|
||||||
|
gSleepWakeup = PR_NewCondVar(gWatchdogLock);
|
||||||
|
if (gSleepWakeup)
|
||||||
|
return true;
|
||||||
|
PR_DestroyCondVar(gWatchdogWakeup);
|
||||||
|
}
|
||||||
|
PR_DestroyLock(gWatchdogLock);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
KillWatchdog()
|
||||||
|
{
|
||||||
|
PRThread *thread;
|
||||||
|
|
||||||
|
PR_Lock(gWatchdogLock);
|
||||||
|
thread = gWatchdogThread;
|
||||||
|
if (thread) {
|
||||||
|
/*
|
||||||
|
* The watchdog thread is running, tell it to terminate waking it up
|
||||||
|
* if necessary.
|
||||||
|
*/
|
||||||
|
gWatchdogThread = NULL;
|
||||||
|
PR_NotifyCondVar(gWatchdogWakeup);
|
||||||
|
}
|
||||||
|
PR_Unlock(gWatchdogLock);
|
||||||
|
if (thread)
|
||||||
|
PR_JoinThread(thread);
|
||||||
|
PR_DestroyCondVar(gSleepWakeup);
|
||||||
|
PR_DestroyCondVar(gWatchdogWakeup);
|
||||||
|
PR_DestroyLock(gWatchdogLock);
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
WatchdogMain(void *arg)
|
WatchdogMain(void *arg)
|
||||||
{
|
{
|
||||||
JSRuntime *rt = (JSRuntime *) arg;
|
JSRuntime *rt = (JSRuntime *) arg;
|
||||||
|
|
||||||
JS_LOCK_GC(rt);
|
PR_Lock(gWatchdogLock);
|
||||||
while (gWatchdogThread) {
|
while (gWatchdogThread) {
|
||||||
JSContext *acx = NULL;
|
PRIntervalTime now = PR_IntervalNow();
|
||||||
|
if (gWatchdogHasTimeout && !IsBefore(now, gWatchdogTimeout)) {
|
||||||
while ((acx = js_NextActiveContext(rt, acx)))
|
/*
|
||||||
JS_TriggerOperationCallback(acx);
|
* The timeout has just expired. Trigger the operation callback
|
||||||
|
* outside the lock.
|
||||||
|
*/
|
||||||
|
gWatchdogHasTimeout = false;
|
||||||
|
PR_Unlock(gWatchdogLock);
|
||||||
|
CancelExecution(rt);
|
||||||
|
PR_Lock(gWatchdogLock);
|
||||||
|
|
||||||
|
/* Wake up any threads doing sleep. */
|
||||||
|
PR_NotifyAllCondVar(gSleepWakeup);
|
||||||
|
} else {
|
||||||
|
PRIntervalTime sleepDuration = gWatchdogHasTimeout
|
||||||
|
? gWatchdogTimeout - now
|
||||||
|
: PR_INTERVAL_NO_TIMEOUT;
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
PRStatus status =
|
PRStatus status =
|
||||||
#endif
|
#endif
|
||||||
/* Trigger the operation callbacks every second. */
|
PR_WaitCondVar(gWatchdogWakeup, sleepDuration);
|
||||||
PR_WaitCondVar(gWatchdogWakeup, PR_SecondsToInterval(1));
|
JS_ASSERT(status == PR_SUCCESS);
|
||||||
JS_ASSERT(status == PR_SUCCESS);
|
}
|
||||||
}
|
}
|
||||||
/* Wake up the main thread waiting for the watchdog to terminate. */
|
PR_Unlock(gWatchdogLock);
|
||||||
PR_NotifyCondVar(gWatchdogWakeup);
|
|
||||||
JS_UNLOCK_GC(rt);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static JSBool
|
static bool
|
||||||
StartWatchdog(JSRuntime *rt)
|
ScheduleWatchdog(JSRuntime *rt, jsdouble t)
|
||||||
{
|
{
|
||||||
if (gWatchdogThread || !gOperationLimit)
|
if (t <= 0) {
|
||||||
return JS_TRUE;
|
PR_Lock(gWatchdogLock);
|
||||||
|
gWatchdogHasTimeout = false;
|
||||||
|
PR_Unlock(gWatchdogLock);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
JS_LOCK_GC(rt);
|
PRIntervalTime interval = PRIntervalTime(ceil(t * PR_TicksPerSecond()));
|
||||||
gWatchdogThread = PR_CreateThread(PR_USER_THREAD,
|
PRIntervalTime timeout = PR_IntervalNow() + interval;
|
||||||
WatchdogMain,
|
PR_Lock(gWatchdogLock);
|
||||||
rt,
|
|
||||||
PR_PRIORITY_NORMAL,
|
|
||||||
PR_LOCAL_THREAD,
|
|
||||||
PR_UNJOINABLE_THREAD,
|
|
||||||
0);
|
|
||||||
if (!gWatchdogThread) {
|
if (!gWatchdogThread) {
|
||||||
JS_UNLOCK_GC(rt);
|
JS_ASSERT(!gWatchdogHasTimeout);
|
||||||
return JS_FALSE;
|
gWatchdogThread = PR_CreateThread(PR_USER_THREAD,
|
||||||
|
WatchdogMain,
|
||||||
|
rt,
|
||||||
|
PR_PRIORITY_NORMAL,
|
||||||
|
PR_LOCAL_THREAD,
|
||||||
|
PR_JOINABLE_THREAD,
|
||||||
|
0);
|
||||||
|
if (!gWatchdogThread) {
|
||||||
|
PR_Unlock(gWatchdogLock);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if (!gWatchdogHasTimeout || IsBefore(timeout, gWatchdogTimeout)) {
|
||||||
|
PR_NotifyCondVar(gWatchdogWakeup);
|
||||||
}
|
}
|
||||||
JS_UNLOCK_GC(rt);
|
gWatchdogHasTimeout = true;
|
||||||
return JS_TRUE;
|
gWatchdogTimeout = timeout;
|
||||||
|
PR_Unlock(gWatchdogLock);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
#else /* !JS_THREADSAFE */
|
||||||
StopWatchdog(JSRuntime *rt)
|
|
||||||
{
|
|
||||||
JS_LOCK_GC(rt);
|
|
||||||
if (gWatchdogThread) {
|
|
||||||
/*
|
|
||||||
* The watchdog thread is running, tell it to terminate waking it up
|
|
||||||
* if necessary and wait until it signals that it done.
|
|
||||||
*/
|
|
||||||
gWatchdogThread = NULL;
|
|
||||||
PR_NotifyCondVar(gWatchdogWakeup);
|
|
||||||
PR_WaitCondVar(gWatchdogWakeup, PR_INTERVAL_NO_TIMEOUT);
|
|
||||||
}
|
|
||||||
JS_UNLOCK_GC(rt);
|
|
||||||
JS_DESTROY_CONDVAR(gWatchdogWakeup);
|
|
||||||
}
|
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
static void
|
|
||||||
WatchdogHandler(int sig)
|
|
||||||
{
|
|
||||||
JSRuntime *rt = gRuntime;
|
|
||||||
JSContext *acx = NULL;
|
|
||||||
|
|
||||||
while ((acx = js_NextActiveContext(rt, acx)))
|
|
||||||
JS_TriggerOperationCallback(acx);
|
|
||||||
|
|
||||||
#ifndef XP_WIN
|
|
||||||
alarm(1);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef XP_WIN
|
#ifdef XP_WIN
|
||||||
static HANDLE gTimerHandle = 0;
|
static HANDLE gTimerHandle = 0;
|
||||||
|
|
||||||
VOID CALLBACK TimerCallback(PVOID lpParameter, BOOLEAN TimerOrWaitFired)
|
VOID CALLBACK
|
||||||
|
TimerCallback(PVOID lpParameter, BOOLEAN TimerOrWaitFired)
|
||||||
{
|
{
|
||||||
WatchdogHandler(0);
|
CancelExecution(rt);
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
static JSBool
|
|
||||||
StartWatchdog(JSRuntime *rt)
|
|
||||||
{
|
|
||||||
if (!gOperationLimit)
|
|
||||||
return JS_TRUE;
|
|
||||||
|
|
||||||
#ifdef XP_WIN
|
|
||||||
JS_ASSERT(gTimerHandle == 0);
|
|
||||||
if (!CreateTimerQueueTimer(&gTimerHandle,
|
|
||||||
NULL,
|
|
||||||
(WAITORTIMERCALLBACK)TimerCallback,
|
|
||||||
NULL,
|
|
||||||
1000,
|
|
||||||
1000,
|
|
||||||
WT_EXECUTEINTIMERTHREAD))
|
|
||||||
return JS_FALSE;
|
|
||||||
#else
|
#else
|
||||||
signal(SIGALRM, WatchdogHandler); /* set the Alarm signal capture */
|
|
||||||
alarm(1);
|
static void
|
||||||
|
AlarmHandler(int sig)
|
||||||
|
{
|
||||||
|
CancelExecution(gRuntime);
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return JS_TRUE;
|
static bool
|
||||||
|
InitWatchdog(JSRuntime *rt)
|
||||||
|
{
|
||||||
|
gRuntime = rt;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
StopWatchdog(JSRuntime *rt)
|
KillWatchdog()
|
||||||
{
|
{
|
||||||
#ifdef XP_WIN
|
ScheduleWatchdog(gRuntime, -1);
|
||||||
DeleteTimerQueueTimer(NULL, gTimerHandle, NULL);
|
|
||||||
gTimerHandle = 0;
|
|
||||||
#else
|
|
||||||
alarm(0);
|
|
||||||
signal(SIGALRM, NULL);
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* JS_THREADSAFE */
|
static bool
|
||||||
|
ScheduleWatchdog(JSRuntime *rt, jsdouble t)
|
||||||
|
{
|
||||||
|
#ifdef XP_WIN
|
||||||
|
if (gTimerHandle) {
|
||||||
|
DeleteTimerQueueTimer(NULL, gTimerHandle, NULL);
|
||||||
|
gTimerHandle = 0;
|
||||||
|
}
|
||||||
|
if (t > 0 &&
|
||||||
|
!CreateTimerQueueTimer(&gTimerHandle,
|
||||||
|
NULL,
|
||||||
|
(WAITORTIMERCALLBACK)TimerCallback,
|
||||||
|
rt,
|
||||||
|
DWORD(ceil(t * 1000.0)),
|
||||||
|
0,
|
||||||
|
WT_EXECUTEINTIMERTHREAD | WT_EXECUTEONLYONCE)) {
|
||||||
|
gTimerHandle = 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
/* FIXME: use setitimer when available for sub-second resolution. */
|
||||||
|
if (t <= 0) {
|
||||||
|
alarm(0);
|
||||||
|
signal(SIGALRM, NULL);
|
||||||
|
} else {
|
||||||
|
signal(SIGALRM, AlarmHandler); /* set the Alarm signal capture */
|
||||||
|
alarm(ceil(t));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* !JS_THREADSAFE */
|
||||||
|
|
||||||
|
static void
|
||||||
|
CancelExecution(JSRuntime *rt)
|
||||||
|
{
|
||||||
|
gCanceled = true;
|
||||||
|
if (gExitCode == 0)
|
||||||
|
gExitCode = EXITCODE_TIMEOUT;
|
||||||
|
JS_TriggerAllOperationCallbacks(rt);
|
||||||
|
|
||||||
|
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. */
|
||||||
|
write(2, msg, sizeof(msg) - 1);
|
||||||
|
#else
|
||||||
|
fputs(msg, stderr);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
static JSBool
|
static JSBool
|
||||||
SetTimeoutValue(JSContext *cx, jsdouble t)
|
SetTimeoutValue(JSContext *cx, jsdouble t)
|
||||||
{
|
{
|
||||||
/* NB: The next condition also filter out NaNs. */
|
/* NB: The next condition also filter out NaNs. */
|
||||||
if (!(t <= 3600.0)) {
|
if (!(t <= MAX_TIMEOUT_INTERVAL)) {
|
||||||
JS_ReportError(cx, "Excessive timeout value");
|
JS_ReportError(cx, "Excessive timeout value");
|
||||||
return JS_FALSE;
|
return JS_FALSE;
|
||||||
}
|
}
|
||||||
|
gTimeoutInterval = t;
|
||||||
gOperationLimit = (t > 0) ? JSInt64(t*1000) : 0;
|
if (!ScheduleWatchdog(cx->runtime, t)) {
|
||||||
|
JS_ReportError(cx, "Failed to create the watchdog");
|
||||||
if (!StartWatchdog(cx->runtime)) {
|
|
||||||
JS_ReportError(cx, "failed to create the watchdog");
|
|
||||||
return JS_FALSE;
|
return JS_FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
return JS_TRUE;
|
return JS_TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static double
|
|
||||||
GetTimeoutValue(JSContext *cx)
|
|
||||||
{
|
|
||||||
if (!gOperationLimit)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
return gOperationLimit/PRMJ_USEC_PER_MSEC;
|
|
||||||
}
|
|
||||||
|
|
||||||
static JSBool
|
static JSBool
|
||||||
Timeout(JSContext *cx, uintN argc, jsval *vp)
|
Timeout(JSContext *cx, uintN argc, jsval *vp)
|
||||||
{
|
{
|
||||||
if (argc == 0)
|
if (argc == 0)
|
||||||
return JS_NewNumberValue(cx, GetTimeoutValue(cx), vp);
|
return JS_NewNumberValue(cx, gTimeoutInterval, vp);
|
||||||
|
|
||||||
if (argc > 1) {
|
if (argc > 1) {
|
||||||
JS_ReportError(cx, "Wrong number of arguments");
|
JS_ReportError(cx, "Wrong number of arguments");
|
||||||
@ -4377,13 +4494,8 @@ main(int argc, char **argv, char **envp)
|
|||||||
if (!rt)
|
if (!rt)
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
#ifdef JS_THREADSAFE
|
if (!InitWatchdog(rt))
|
||||||
gWatchdogWakeup = JS_NEW_CONDVAR(rt->gcLock);
|
|
||||||
if (!gWatchdogWakeup)
|
|
||||||
return 1;
|
return 1;
|
||||||
#else
|
|
||||||
gRuntime = rt;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
JS_SetContextCallback(rt, ContextCallback);
|
JS_SetContextCallback(rt, ContextCallback);
|
||||||
|
|
||||||
@ -4498,7 +4610,7 @@ main(int argc, char **argv, char **envp)
|
|||||||
JS_DestroyContext(cx)
|
JS_DestroyContext(cx)
|
||||||
);
|
);
|
||||||
|
|
||||||
StopWatchdog(rt);
|
KillWatchdog();
|
||||||
|
|
||||||
JS_DestroyRuntime(rt);
|
JS_DestroyRuntime(rt);
|
||||||
JS_ShutDown();
|
JS_ShutDown();
|
||||||
|
Loading…
Reference in New Issue
Block a user