Dukky: Add refcounting to threads

In order to cope with threads which manage to navigate entirely
while executing (sadly possible) we need to handle the possibility
that a thread is destroyed by the browser but still needs to live
until it returns from whatever exec it was doing at the time.

Signed-off-by: Daniel Silverstone <dsilvers@digital-scurf.org>
This commit is contained in:
Daniel Silverstone 2020-03-21 20:49:40 +00:00
parent 3c4652c1c3
commit 80e7ac7345
No known key found for this signature in database
GPG Key ID: C30DF439F2987D74

View File

@ -59,6 +59,7 @@
struct jsheap { struct jsheap {
duk_context *ctx; /**< duktape base context */ duk_context *ctx; /**< duktape base context */
duk_uarridx_t next_thread; /**< monotonic thread counter */ duk_uarridx_t next_thread; /**< monotonic thread counter */
unsigned int live_threads; /**< number of live threads */
uint64_t exec_start_time; uint64_t exec_start_time;
}; };
@ -66,6 +67,8 @@ struct jsheap {
* dukky javascript thread * dukky javascript thread
*/ */
struct jsthread { struct jsthread {
bool pending_destroy; /**< Whether this thread is pending destruction */
unsigned int in_use; /**< The number of times this thread is in use */
jsheap *heap; /**< The heap this thread belongs to */ jsheap *heap; /**< The heap this thread belongs to */
duk_context *ctx; /**< The duktape thread context */ duk_context *ctx; /**< The duktape thread context */
duk_uarridx_t thread_idx; /**< The thread number */ duk_uarridx_t thread_idx; /**< The thread number */
@ -621,6 +624,7 @@ js_newheap(int timeout, jsheap **heap)
/* exported interface documented in js.h */ /* exported interface documented in js.h */
void js_destroyheap(jsheap *heap) void js_destroyheap(jsheap *heap)
{ {
assert(heap->live_threads == 0);
NSLOG(dukky, DEBUG, "Destroying duktape javascript context"); NSLOG(dukky, DEBUG, "Destroying duktape javascript context");
duk_destroy_heap(heap->ctx); duk_destroy_heap(heap->ctx);
free(heap); free(heap);
@ -652,6 +656,7 @@ nserror js_newthread(jsheap *heap, void *win_priv, void *doc_priv, jsthread **th
ret->ctx = duk_require_context(heap->ctx, -1); ret->ctx = duk_require_context(heap->ctx, -1);
ret->thread_idx = heap->next_thread++; ret->thread_idx = heap->next_thread++;
duk_put_prop_index(heap->ctx, -2, ret->thread_idx); duk_put_prop_index(heap->ctx, -2, ret->thread_idx);
heap->live_threads++;
duk_pop(heap->ctx); /* ... */ duk_pop(heap->ctx); /* ... */
duk_push_int(CTX, 0); duk_push_int(CTX, 0);
duk_push_int(CTX, 1); duk_push_int(CTX, 1);
@ -738,11 +743,16 @@ nserror js_newthread(jsheap *heap, void *win_priv, void *doc_priv, jsthread **th
#undef CTX #undef CTX
#define CTX (thread->ctx) #define CTX (thread->ctx)
/**
/* exported interface documented in js.h */ * Destroy a Duktape thread
void js_destroythread(jsthread *thread) */
static void dukky_destroythread(jsthread *thread)
{ {
jsheap *heap = thread->heap; jsheap *heap = thread->heap;
assert(thread->in_use == 0);
assert(thread->pending_destroy = true);
/* Closing down the extant thread */ /* Closing down the extant thread */
NSLOG(dukky, DEBUG, "Closing down extant thread %p in heap %p", thread, heap); NSLOG(dukky, DEBUG, "Closing down extant thread %p in heap %p", thread, heap);
duk_get_global_string(CTX, MAGIC(closedownThread)); duk_get_global_string(CTX, MAGIC(closedownThread));
@ -759,6 +769,33 @@ void js_destroythread(jsthread *thread)
/* Finally give the heap a chance to clean up */ /* Finally give the heap a chance to clean up */
duk_gc(heap->ctx, 0); duk_gc(heap->ctx, 0);
duk_gc(heap->ctx, DUK_GC_COMPACT); duk_gc(heap->ctx, DUK_GC_COMPACT);
heap->live_threads--;
}
/* exported interface documented in js.h */
void js_destroythread(jsthread *thread)
{
thread->pending_destroy = true;
if (thread->in_use == 0) {
dukky_destroythread(thread);
}
}
static void dukky_enter_thread(jsthread *thread)
{
assert(thread != NULL);
thread->in_use++;
}
static void dukky_leave_thread(jsthread *thread)
{
assert(thread != NULL);
assert(thread->in_use > 0);
thread->in_use--;
if (thread->in_use == 0 && thread->pending_destroy == true) {
dukky_destroythread(thread);
}
} }
duk_bool_t dukky_check_timeout(void *udata) duk_bool_t dukky_check_timeout(void *udata)
@ -845,12 +882,15 @@ void dukky_log_stack_frame(duk_context *ctx, const char * reason)
bool bool
js_exec(jsthread *thread, const uint8_t *txt, size_t txtlen, const char *name) js_exec(jsthread *thread, const uint8_t *txt, size_t txtlen, const char *name)
{ {
bool ret = false;
assert(thread); assert(thread);
if (txt == NULL || txtlen == 0) { if (txt == NULL || txtlen == 0) {
return false; return false;
} }
dukky_enter_thread(thread);
duk_set_top(CTX, 0); duk_set_top(CTX, 0);
NSLOG(dukky, DEEPDEBUG, "Running %"PRIsizet" bytes from %s", txtlen, name); NSLOG(dukky, DEEPDEBUG, "Running %"PRIsizet" bytes from %s", txtlen, name);
/* NSLOG(dukky, DEEPDEBUG, "\n%s\n", txt); */ /* NSLOG(dukky, DEEPDEBUG, "\n%s\n", txt); */
@ -877,11 +917,14 @@ js_exec(jsthread *thread, const uint8_t *txt, size_t txtlen, const char *name)
if (duk_get_top(CTX) == 0) duk_push_boolean(CTX, false); if (duk_get_top(CTX) == 0) duk_push_boolean(CTX, false);
NSLOG(dukky, DEEPDEBUG, "Returning %s", NSLOG(dukky, DEEPDEBUG, "Returning %s",
duk_get_boolean(CTX, 0) ? "true" : "false"); duk_get_boolean(CTX, 0) ? "true" : "false");
return duk_get_boolean(CTX, 0); ret = duk_get_boolean(CTX, 0);
goto out;
handle_error: handle_error:
dukky_dump_error(CTX); dukky_dump_error(CTX);
return false; out:
dukky_leave_thread(thread);
return ret;
} }
static const char* dukky_event_proto(dom_event *evt) static const char* dukky_event_proto(dom_event *evt)
@ -1406,6 +1449,8 @@ void js_handle_new_element(jsthread *thread, struct dom_element *node)
if (exc != DOM_NO_ERR) return; if (exc != DOM_NO_ERR) return;
if (map == NULL) return; if (map == NULL) return;
dukky_enter_thread(thread);
exc = dom_namednodemap_get_length(map, &siz); exc = dom_namednodemap_get_length(map, &siz);
if (exc != DOM_NO_ERR) goto out; if (exc != DOM_NO_ERR) goto out;
@ -1455,11 +1500,14 @@ out:
dom_node_unref(attr); dom_node_unref(attr);
dom_namednodemap_unref(map); dom_namednodemap_unref(map);
dukky_leave_thread(thread);
} }
void js_event_cleanup(jsthread *thread, struct dom_event *evt) void js_event_cleanup(jsthread *thread, struct dom_event *evt)
{ {
assert(thread); assert(thread);
dukky_enter_thread(thread);
/* ... */ /* ... */
duk_get_global_string(CTX, EVENT_MAGIC); duk_get_global_string(CTX, EVENT_MAGIC);
/* ... EVENT_MAP */ /* ... EVENT_MAP */
@ -1469,6 +1517,7 @@ void js_event_cleanup(jsthread *thread, struct dom_event *evt)
/* ... EVENT_MAP */ /* ... EVENT_MAP */
duk_pop(CTX); duk_pop(CTX);
/* ... */ /* ... */
dukky_leave_thread(thread);
} }
bool js_fire_event(jsthread *thread, const char *type, struct dom_document *doc, struct dom_node *target) bool js_fire_event(jsthread *thread, const char *type, struct dom_document *doc, struct dom_node *target)
@ -1507,6 +1556,7 @@ bool js_fire_event(jsthread *thread, const char *type, struct dom_document *doc,
dom_event_unref(evt); dom_event_unref(evt);
return true; return true;
} }
dukky_enter_thread(thread);
/* ... */ /* ... */
duk_get_global_string(CTX, HANDLER_MAGIC); duk_get_global_string(CTX, HANDLER_MAGIC);
/* ... handlers */ /* ... handlers */
@ -1523,6 +1573,7 @@ bool js_fire_event(jsthread *thread, const char *type, struct dom_document *doc,
exc = dom_html_document_get_body(doc, &body); exc = dom_html_document_get_body(doc, &body);
if (exc != DOM_NO_ERR) { if (exc != DOM_NO_ERR) {
dom_event_unref(evt); dom_event_unref(evt);
dukky_leave_thread(thread);
return true; return true;
} }
dukky_push_node(CTX, (struct dom_node *)body); dukky_push_node(CTX, (struct dom_node *)body);
@ -1533,6 +1584,7 @@ bool js_fire_event(jsthread *thread, const char *type, struct dom_document *doc,
dom_node_unref(body); dom_node_unref(body);
/* ... handlers */ /* ... handlers */
duk_pop(CTX); duk_pop(CTX);
dukky_leave_thread(thread);
return true; return true;
} }
/* Unref the body, we don't need it any more */ /* Unref the body, we don't need it any more */
@ -1574,6 +1626,7 @@ bool js_fire_event(jsthread *thread, const char *type, struct dom_document *doc,
/* ... */ /* ... */
js_event_cleanup(thread, evt); js_event_cleanup(thread, evt);
dom_event_unref(evt); dom_event_unref(evt);
dukky_leave_thread(thread);
return true; return true;
} }
/* ... result */ /* ... result */
@ -1581,5 +1634,6 @@ bool js_fire_event(jsthread *thread, const char *type, struct dom_document *doc,
/* ... */ /* ... */
js_event_cleanup(thread, evt); js_event_cleanup(thread, evt);
dom_event_unref(evt); dom_event_unref(evt);
dukky_leave_thread(thread);
return true; return true;
} }