Dear Len, On Tue, Feb 15, 2011 at 10:01:55AM -0600, Kumar Appaiah wrote: > On Tue, Feb 15, 2011 at 10:55:40AM -0500, Len Sorensen wrote: > > After all some people have automake on their system and would try to > > build things that don't like a particular version of automake, so packages > > sometimes conflict. Build-conflicts would be nice to avoid, but at > > least until a better solution is found, it would be better than nothing > > and avoid more bugs being reported. > > No, this is, by no means, the solution. The blame lies squarely with > the patch 06_551238_SMJS-FTBFS.diff, which uses the get_opt_int macro > incorrectly. Build conflicting on auto* is, IMHO, a bad > workaround. Investigating the get_opt_int is the right solution to the > problem.
Based on work already done by the maintainer (which was present on the git repository for the Debian package), I have a patch which seems to work, though I haven't really tested the built package. To try it out, please replace the debian/patches/06_551238_SMJS-FTBFS.diff with the one attached and redo the build. This should build the heartbeat code as well. Please let me know if it works for you. Thanks. Kumar -- Kumar Appaiah
JS_SetBranchCallback has been replaced with JS_SetOperationCallback. This patch is to fix the FTBFS reported in #550868. Patch based on an upstream patch in git f31cf6f9fe805539d50ed55b62b05ce12daf7cb0 from Miciah Dashiel Butler Masters <miciah.mast...@gmail.com> diff --git a/Makefile.config.in b/Makefile.config.in index c463868..bcbebc0 100644 --- a/Makefile.config.in +++ b/Makefile.config.in @@ -117,6 +117,7 @@ CONFIG_DOM = @CONFIG_DOM@ CONFIG_ECMASCRIPT = @CONFIG_ECMASCRIPT@ CONFIG_ECMASCRIPT_SEE = @CONFIG_ECMASCRIPT_SEE@ CONFIG_ECMASCRIPT_SMJS = @CONFIG_ECMASCRIPT_SMJS@ +CONFIG_ECMASCRIPT_SMJS_HEARTBEAT = @CONFIG_ECMASCRIPT_SMJS_HEARTBEAT@ CONFIG_EXMODE = @CONFIG_EXMODE@ CONFIG_FASTMEM = @CONFIG_FASTMEM@ CONFIG_FINGER = @CONFIG_FINGER@ diff --git a/configure b/configure index b8e22ce..350e587 100755 --- a/configure +++ b/configure @@ -709,6 +709,7 @@ SEE_CFLAGS CONFIG_ECMASCRIPT CONFIG_ECMASCRIPT_SEE CONFIG_ECMASCRIPT_SMJS +CONFIG_ECMASCRIPT_SMJS_HEARTBEAT SPIDERMONKEY_LIBS SPIDERMONKEY_CFLAGS CONFIG_SPIDERMONKEY @@ -13394,6 +13395,98 @@ _ACEOF fi done +for ac_func in setitimer +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +{ echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6; } +if { as_var=$as_ac_var; eval "test \"\${$as_var+set}\" = set"; }; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func. + For example, HP-UX 11i <limits.h> declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer <limits.h> to <assert.h> if __STDC__ is defined, since + <limits.h> exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include <limits.h> +#else +# include <assert.h> +#endif + +#undef $ac_func + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char $ac_func (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined __stub_$ac_func || defined __stub___$ac_func +choke me +#endif + +int +main () +{ +return $ac_func (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_link") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && + $as_test_x conftest$ac_exeext; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + eval "$as_ac_var=no" +fi + +rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext conftest.$ac_ext +fi +ac_res=`eval echo '${'$as_ac_var'}'` + { echo "$as_me:$LINENO: result: $ac_res" >&5 +echo "${ECHO_T}$ac_res" >&6; } +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + HAVE_SETITIMER=yes +fi +done for ac_func in cygwin_conv_to_full_win32_path @@ -18397,6 +18490,192 @@ _ACEOF fi done +for ac_func in JS_SetBranchCallback +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +{ echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6; } +if { as_var=$as_ac_var; eval "test \"\${$as_var+set}\" = set"; }; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func. + For example, HP-UX 11i <limits.h> declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer <limits.h> to <assert.h> if __STDC__ is defined, since + <limits.h> exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include <limits.h> +#else +# include <assert.h> +#endif + +#undef $ac_func + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char $ac_func (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined __stub_$ac_func || defined __stub___$ac_func +choke me +#endif + +int +main () +{ +return $ac_func (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_link") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && + $as_test_x conftest$ac_exeext; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + eval "$as_ac_var=no" +fi + +rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext conftest.$ac_ext +fi +ac_res=`eval echo '${'$as_ac_var'}'` + { echo "$as_me:$LINENO: result: $ac_res" >&5 +echo "${ECHO_T}$ac_res" >&6; } +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + +for ac_func in JS_TriggerOperationCallback +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +{ echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6; } +if { as_var=$as_ac_var; eval "test \"\${$as_var+set}\" = set"; }; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func. + For example, HP-UX 11i <limits.h> declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer <limits.h> to <assert.h> if __STDC__ is defined, since + <limits.h> exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include <limits.h> +#else +# include <assert.h> +#endif + +#undef $ac_func + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char $ac_func (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined __stub_$ac_func || defined __stub___$ac_func +choke me +#endif + +int +main () +{ +return $ac_func (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_link") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && + $as_test_x conftest$ac_exeext; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + eval "$as_ac_var=no" +fi + +rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext conftest.$ac_ext +fi +ac_res=`eval echo '${'$as_ac_var'}'` + { echo "$as_me:$LINENO: result: $ac_res" >&5 +echo "${ECHO_T}$ac_res" >&6; } +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + HAVE_JS_TRIGGEROPERATIONCALLBACK=yes +fi +done + fi CFLAGS="$CFLAGS_X"; @@ -18476,6 +18755,22 @@ _ACEOF +if test "x$CONFIG_ECMASCRIPT_SMJS" = xyes && + test "x$HAVE_JS_TRIGGEROPERATIONCALLBACK" = xyes && + test "x$HAVE_SETITIMER" = xyes; then + + CONFIG_ECMASCRIPT_SMJS_HEARTBEAT=yes + ABOUT_CONFIG_ECMASCRIPT_SMJS_HEARTBEAT="ECMAScript heartbeat support" + +cat >>confdefs.h <<\_ACEOF +#define CONFIG_ECMASCRIPT_SMJS_HEARTBEAT 1 +_ACEOF + +else + CONFIG_ECMASCRIPT_SMJS_HEARTBEAT=no +fi + + # =================================================================== # Optional Spidermonkey-based ECMAScript browser scripting diff --git a/configure.in b/configure.in index 4a7143d..08c4bbc 100644 --- a/configure.in +++ b/configure.in @@ -283,6 +283,7 @@ AC_CHECK_FUNCS(snprintf vsnprintf asprintf vasprintf) AC_CHECK_FUNCS(getifaddrs getpwnam inet_pton inet_ntop) AC_CHECK_FUNCS(fflush fsync fseeko ftello sigaction) AC_CHECK_FUNCS(gettimeofday clock_gettime) +AC_CHECK_FUNCS(setitimer, HAVE_SETITIMER=yes) AC_CHECK_FUNCS([cygwin_conv_to_full_win32_path]) @@ -651,6 +652,8 @@ AC_MSG_RESULT($cf_result) CONFIG_SPIDERMONKEY="$cf_result" if test "$cf_result" = "yes"; then AC_CHECK_FUNCS([[JS_ReportAllocationOverflow]]) + AC_CHECK_FUNCS(JS_SetBranchCallback) + AC_CHECK_FUNCS(JS_TriggerOperationCallback, HAVE_JS_TRIGGEROPERATIONCALLBACK=yes) fi EL_RESTORE_FLAGS @@ -665,6 +668,15 @@ EL_CONFIG_DEPENDS(CONFIG_ECMASCRIPT, [CONFIG_ECMASCRIPT_SEE CONFIG_ECMASCRIPT_SM AC_SUBST(CONFIG_ECMASCRIPT_SEE) AC_SUBST(CONFIG_ECMASCRIPT_SMJS) +if test "x$CONFIG_ECMASCRIPT_SMJS" = xyes && + test "x$HAVE_JS_TRIGGEROPERATIONCALLBACK" = xyes && + test "x$HAVE_SETITIMER" = xyes; then + EL_CONFIG(CONFIG_ECMASCRIPT_SMJS_HEARTBEAT, [ECMAScript heartbeat support]) +else + CONFIG_ECMASCRIPT_SMJS_HEARTBEAT=no +fi +AC_SUBST(CONFIG_ECMASCRIPT_SMJS_HEARTBEAT) + # =================================================================== # Optional Spidermonkey-based ECMAScript browser scripting diff --git a/src/ecmascript/ecmascript.h b/src/ecmascript/ecmascript.h index e8d84b5..53d40ae 100644 --- a/src/ecmascript/ecmascript.h +++ b/src/ecmascript/ecmascript.h @@ -32,7 +32,11 @@ struct ecmascript_interpreter { /* The code evaluated by setTimeout() */ struct string code; +#if defined(CONFIG_ECMASCRIPT_SMJS_HEARTBEAT) + struct heartbeat *heartbeat; +#elif defined(HAVE_JS_SETBRANCHCALLBACK) time_t exec_start; +#endif /* This is a cross-rerenderings accumulator of * @document.onload_snippets (see its description for juicy details). diff --git a/src/ecmascript/spidermonkey.c b/src/ecmascript/spidermonkey.c index 78c3bca..7c83cec 100644 --- a/src/ecmascript/spidermonkey.c +++ b/src/ecmascript/spidermonkey.c @@ -25,6 +25,7 @@ #include "ecmascript/spidermonkey.h" #include "ecmascript/spidermonkey/document.h" #include "ecmascript/spidermonkey/form.h" +#include "ecmascript/spidermonkey/heartbeat.h" #include "ecmascript/spidermonkey/location.h" #include "ecmascript/spidermonkey/navigator.h" #include "ecmascript/spidermonkey/unibar.h" @@ -109,6 +110,7 @@ reported: JS_ClearPendingException(ctx); } +#if !defined(CONFIG_ECMASCRIPT_SMJS_HEARTBEAT) && defined(HAVE_JS_SETBRANCHCALLBACK) static JSBool safeguard(JSContext *ctx, JSScript *script) { @@ -132,6 +134,7 @@ setup_safeguard(struct ecmascript_interpreter *interpreter, interpreter->exec_start = time(NULL); JS_SetBranchCallback(ctx, safeguard); } +#endif static void @@ -172,6 +175,9 @@ spidermonkey_get_interpreter(struct ecmascript_interpreter *interpreter) * some kind of bytecode cache. (If we will ever do that.) */ JS_SetOptions(ctx, JSOPTION_VAROBJFIX | JSOPTION_COMPILE_N_GO); JS_SetErrorReporter(ctx, error_reporter); +#if defined(CONFIG_ECMASCRIPT_SMJS_HEARTBEAT) + JS_SetOperationCallback(ctx, heartbeat_callback); +#endif window_obj = JS_NewObject(ctx, (JSClass *) &window_class, NULL, NULL); if (!window_obj) goto release_and_fail; @@ -263,10 +269,17 @@ spidermonkey_eval(struct ecmascript_interpreter *interpreter, assert(interpreter); if (!js_module_init_ok) return; ctx = interpreter->backend_data; +#if defined(CONFIG_ECMASCRIPT_SMJS_HEARTBEAT) + interpreter->heartbeat = add_heartbeat(interpreter); +#elif defined(HAVE_JS_SETBRANCHCALLBACK) setup_safeguard(interpreter, ctx); +#endif interpreter->ret = ret; JS_EvaluateScript(ctx, JS_GetGlobalObject(ctx), code->source, code->length, "", 0, &rval); +#if defined(CONFIG_ECMASCRIPT_SMJS_HEARTBEAT) + done_heartbeat(interpreter->heartbeat); +#endif } @@ -274,17 +287,25 @@ unsigned char * spidermonkey_eval_stringback(struct ecmascript_interpreter *interpreter, struct string *code) { + JSBool ret; JSContext *ctx; jsval rval; assert(interpreter); if (!js_module_init_ok) return NULL; ctx = interpreter->backend_data; - setup_safeguard(interpreter, ctx); interpreter->ret = NULL; - if (JS_EvaluateScript(ctx, JS_GetGlobalObject(ctx), - code->source, code->length, "", 0, &rval) - == JS_FALSE) { +#if defined(CONFIG_ECMASCRIPT_SMJS_HEARTBEAT) + interpreter->heartbeat = add_heartbeat(interpreter); +#elif defined(HAVE_JS_SETBRANCHCALLBACK) + setup_safeguard(interpreter, ctx); +#endif + ret = JS_EvaluateScript(ctx, JS_GetGlobalObject(ctx), + code->source, code->length, "", 0, &rval); +#if defined(CONFIG_ECMASCRIPT_SMJS_HEARTBEAT) + done_heartbeat(interpreter->heartbeat); +#endif + if (ret == JS_FALSE) { return NULL; } if (JSVAL_IS_VOID(rval)) { @@ -308,14 +329,21 @@ spidermonkey_eval_boolback(struct ecmascript_interpreter *interpreter, assert(interpreter); if (!js_module_init_ok) return 0; ctx = interpreter->backend_data; - setup_safeguard(interpreter, ctx); interpreter->ret = NULL; fun = JS_CompileFunction(ctx, NULL, "", 0, NULL, code->source, code->length, "", 0); if (!fun) return -1; +#if defined(CONFIG_ECMASCRIPT_SMJS_HEARTBEAT) + interpreter->heartbeat = add_heartbeat(interpreter); +#elif defined(HAVE_JS_SETBRANCHCALLBACK) + setup_safeguard(interpreter, ctx); +#endif ret = JS_CallFunction(ctx, NULL, fun, 0, NULL, &rval); +#if defined(CONFIG_ECMASCRIPT_SMJS_HEARTBEAT) + done_heartbeat(interpreter->heartbeat); +#endif if (ret == 2) { /* onClick="history.back()" */ return 0; } diff --git a/src/ecmascript/spidermonkey/Makefile b/src/ecmascript/spidermonkey/Makefile index f1c0fef..fd98dc5 100644 --- a/src/ecmascript/spidermonkey/Makefile +++ b/src/ecmascript/spidermonkey/Makefile @@ -2,6 +2,8 @@ top_builddir=../../.. include $(top_builddir)/Makefile.config INCLUDES += $(SPIDERMONKEY_CFLAGS) +OBJS-$(CONFIG_ECMASCRIPT_SMJS_HEARTBEAT) += heartbeat.o + OBJS = document.o form.o location.o navigator.o unibar.o window.o include $(top_srcdir)/Makefile.lib diff --git a/src/ecmascript/spidermonkey/heartbeat.c b/src/ecmascript/spidermonkey/heartbeat.c new file mode 100644 index 0000000..1fb87a6 --- /dev/null +++ b/src/ecmascript/spidermonkey/heartbeat.c @@ -0,0 +1,116 @@ +/* The SpiderMonkey ECMAScript backend heartbeat fuctionality. */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <sys/time.h> /* setitimer(2) */ + +#include "elinks.h" + +#include "ecmascript/spidermonkey/util.h" + +#include "config/options.h" +#include "document/view.h" +#include "ecmascript/ecmascript.h" +#include "ecmascript/spidermonkey.h" +#include "ecmascript/spidermonkey/heartbeat.h" +#include "osdep/signals.h" +#include "session/session.h" +#include "util/lists.h" +#include "util/math.h" /* int_upper_bound */ +#include "util/memory.h" +#include "viewer/text/vs.h" + + + +static INIT_LIST_OF(struct heartbeat, heartbeats); + +static struct itimerval heartbeat_timer = { { 1, 0 }, { 1, 0 } }; + + +/* This callback is installed by JS_SetOperationCallback and triggered + * by JS_TriggerOperationCallback in the heartbeat code below. Returning + * JS_FALSE terminates script execution immediately. */ +JSBool +heartbeat_callback(JSContext *ctx) +{ + return JS_FALSE; +} + +/* Callback for SIGVTALRM. Go through all heartbeats, decrease each + * one's TTL, and call JS_TriggerOperationCallback if a heartbeat's TTL + * goes to 0. */ +static void +check_heartbeats(void *data) +{ + struct heartbeat *hb; + + foreach (hb, heartbeats) { + assert(hb->interpreter); + --hb->ttl; + + if (hb->ttl <= 0) { + if (hb->interpreter->vs + && hb->interpreter->vs->doc_view + && hb->interpreter->vs->doc_view->session + && hb->interpreter->vs->doc_view->session->tab + && hb->interpreter->vs->doc_view->session->tab->term) { + struct session *ses = hb->interpreter->vs->doc_view->session; + struct terminal *term = ses->tab->term; + int max_exec_time = get_opt_int("ecmascript.max_exec_time"); + + ecmascript_timeout_dialog(term, max_exec_time); + } + + JS_TriggerOperationCallback(hb->interpreter->backend_data); + } + } + install_signal_handler(SIGVTALRM, check_heartbeats, NULL, 1); +} + +/* Create a new heartbeat for the given interpreter. */ +struct heartbeat * +add_heartbeat(struct ecmascript_interpreter *interpreter) +{ + struct heartbeat *hb; + assert(interpreter); + + hb = mem_alloc(sizeof(struct heartbeat)); + if (!hb) return NULL; + + hb->ttl = get_opt_int("ecmascript.max_exec_time"); + hb->interpreter = interpreter; + + add_to_list(heartbeats, hb); + + /* Update the heartbeat timer. */ + if (list_is_singleton(*hb)) { + heartbeat_timer.it_value.tv_sec = 1; + setitimer(ITIMER_VIRTUAL, &heartbeat_timer, NULL); + } + + /* We install the handler every call to add_heartbeat instead of only on + * module initialisation because other code may set other handlers for + * the signal. */ + install_signal_handler(SIGVTALRM, check_heartbeats, NULL, 1); + + return hb; +} + +/* Destroy the given heartbeat. */ +void +done_heartbeat(struct heartbeat *hb) +{ + assert(hb->interpreter); + + /* Stop the heartbeat timer if this heartbeat is the only one. */ + if (list_is_singleton(*hb)) { + heartbeat_timer.it_value.tv_sec = 0; + setitimer(ITIMER_VIRTUAL, &heartbeat_timer, NULL); + } + + del_from_list(hb); + hb->interpreter->heartbeat = NULL; + mem_free(hb); +} diff --git a/src/ecmascript/spidermonkey/heartbeat.h b/src/ecmascript/spidermonkey/heartbeat.h new file mode 100644 index 0000000..f7c8b12 --- /dev/null +++ b/src/ecmascript/spidermonkey/heartbeat.h @@ -0,0 +1,24 @@ +#ifndef EL__ECMASCRIPT_SPIDERMONKEY_HEARTBEAT_H +#define EL__ECMASCRIPT_SPIDERMONKEY_HEARTBEAT_H + +#include "ecmascript/spidermonkey/util.h" + +#include "ecmascript/spidermonkey.h" + +struct heartbeat { + LIST_HEAD(struct heartbeat); + + int ttl; /* Time to live. This value is assigned when the + * script begins execution and is decremented every + * second. When it reaches 0, script execution is + * terminated. */ + + struct ecmascript_interpreter *interpreter; +}; + +struct heartbeat *add_heartbeat(struct ecmascript_interpreter *interpreter); +void done_heartbeat(struct heartbeat *hb); + +JSBool heartbeat_callback(JSContext *ctx); + +#endif