/* The QuickJS ECMAScript backend heartbeat fuctionality. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include /* setitimer(2) */ #include #include "elinks.h" #include "config/options.h" #include "document/view.h" #include "ecmascript/ecmascript.h" #include "ecmascript/quickjs.h" #include "ecmascript/quickjs/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_SetInterruptHandler. * Returning 1 terminates script execution immediately. */ int js_heartbeat_callback(JSRuntime *rt, void *opaque) { struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)opaque; if (!interpreter || !interpreter->heartbeat || interpreter->heartbeat->ttl > 0) { return 0; } return 1; } /* Callback for SIGVTALRM. Go through all heartbeats, decrease each * one's TTL, and call JS_RequestInterruptCallback 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", ses); ecmascript_timeout_dialog(term, max_exec_time); } } } 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 session *ses; struct heartbeat *hb; assert(interpreter); if (!interpreter->vs || !interpreter->vs->doc_view) ses = NULL; else ses = interpreter->vs->doc_view->session; hb = (struct heartbeat *)mem_alloc(sizeof(struct heartbeat)); if (!hb) return NULL; hb->ttl = get_opt_int("ecmascript.max_exec_time", ses); 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) { if (!hb) return; /* add_heartbeat returned NULL */ 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); }