diff --git a/src/httpd.js.c b/src/httpd.js.c index 6962c99c..8f6536ec 100644 --- a/src/httpd.js.c +++ b/src/httpd.js.c @@ -63,6 +63,8 @@ static int _object_to_headers(JSContext* context, JSValue object, const char** h } headers[count * 2 + 0] = JS_ToCString(context, key); headers[count * 2 + 1] = JS_ToCString(context, key_value); + JS_FreeValue(context, key); + JS_FreeValue(context, key_value); } for (uint32_t i = 0; i < plen; ++i) { @@ -315,11 +317,18 @@ static JSValue _httpd_websocket_upgrade(JSContext* context, JSValueConst this_va headers[headers_count * 2 + 1] = key; headers_count++; } - headers_count += _object_to_headers(context, argv[1], headers + headers_count * 2, tf_countof(headers) - headers_count * 2); + int js_headers_count = _object_to_headers(context, argv[1], headers + headers_count * 2, tf_countof(headers) - headers_count * 2); + headers_count += js_headers_count; tf_http_request_websocket_upgrade(request); tf_http_respond(request, 101, headers, headers_count, NULL, 0); + for (int i = headers_count - js_headers_count; i < headers_count * 2; i++) + { + JS_FreeCString(context, headers[i * 2 + 0]); + JS_FreeCString(context, headers[i * 2 + 1]); + } + request->on_message = _httpd_message_callback; request->on_close = _httpd_websocket_close_callback; request->context = context; diff --git a/src/ssb.js.c b/src/ssb.js.c index 5efcc7a5..6e0e5c19 100644 --- a/src/ssb.js.c +++ b/src/ssb.js.c @@ -856,6 +856,7 @@ static void _tf_ssb_sqlAsync_after_work(uv_work_t* work, int status) JS_FreeValue(context, sql_work->promise[0]); JS_FreeValue(context, sql_work->promise[1]); JS_FreeValue(context, sql_work->callback); + JS_FreeCString(context, sql_work->query); _tf_ssb_sqlAsync_destroy(sql_work); tf_trace_end(trace); } diff --git a/src/task.c b/src/task.c index d5135d24..c8ec1b99 100644 --- a/src/task.c +++ b/src/task.c @@ -83,6 +83,17 @@ typedef struct _hitch_t uint64_t duration_ns; } hitch_t; +typedef struct _timeout_t timeout_t; + +typedef struct _timeout_t +{ + timeout_t* previous; + timeout_t* next; + uv_timer_t _timer; + tf_task_t* _task; + JSValue _callback; +} timeout_t; + typedef struct _tf_task_t { taskid_t _nextTask; @@ -145,6 +156,8 @@ typedef struct _tf_task_t promise_stack_t* _promise_stacks; int _promise_stack_count; + timeout_t* timeouts; + hitch_t hitches[32]; } tf_task_t; @@ -185,6 +198,7 @@ static JSValue _tf_task_get_parent(JSContext* context, JSValueConst this_val, in static JSValue _tf_task_exit(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv); static JSValue _tf_task_getStats(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv); static JSValue _tf_task_getFile(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv); +static JSValue _tf_task_setTimeout(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv); static promise_t* _tf_task_find_promise(tf_task_t* task, promiseid_t id); static void _tf_task_sendPromiseResolve(tf_task_t* from, tf_taskstub_t* to, promiseid_t promise, JSValue result); @@ -197,6 +211,8 @@ static void _tf_task_release_export(tf_taskstub_t* stub, exportid_t exportId); static bool _tf_task_run_jobs(tf_task_t* task); static void _tf_task_run_jobs_idle(uv_idle_t* idle); static void _tf_task_run_jobs_prepare(uv_prepare_t* prepare); +static void _timeout_unlink(tf_task_t* task, timeout_t* timeout); +static void _timeout_closed(uv_handle_t* handle); typedef struct _import_record_t { @@ -1762,6 +1778,7 @@ void tf_task_activate(tf_task_t* task) JS_SetPropertyStr(context, global, "version", JS_NewCFunction(context, _tf_task_version, "version", 0)); JS_SetPropertyStr(context, global, "platform", JS_NewCFunction(context, _tf_task_platform, "platform", 0)); JS_SetPropertyStr(context, global, "getFile", JS_NewCFunction(context, _tf_task_getFile, "getFile", 1)); + JS_SetPropertyStr(context, global, "setTimeout", JS_NewCFunction(context, _tf_task_setTimeout, "setTimeout", 2)); JS_FreeValue(context, global); } @@ -1834,6 +1851,15 @@ void tf_task_destroy(tf_task_t* task) JS_FreeValue(task->_context, task->_loadedFiles); + while (task->timeouts) + { + timeout_t* timeout = task->timeouts; + JS_FreeValue(task->_context, timeout->_callback); + timeout->_callback = JS_UNDEFINED; + _timeout_unlink(task, timeout); + uv_close((uv_handle_t*)&timeout->_timer, _timeout_closed); + } + if (task->_ssb) { tf_ssb_destroy(task->_ssb); @@ -1906,7 +1932,12 @@ void tf_task_destroy(tf_task_t* task) } tf_free(task->_promise_stacks); tf_free((void*)task->_path); + bool was_trusted = task->_trusted; tf_free(task); + if (was_trusted) + { + tf_printf("Goodbye."); + } } JSValue tf_task_add_import(tf_task_t* task, taskid_t stub_id, exportid_t export_id) @@ -2015,3 +2046,90 @@ const char* tf_task_get_name(tf_task_t* task) { return task->_scriptName; } + +static void _timeout_link(tf_task_t* task, timeout_t* timeout) +{ + assert(!timeout->previous); + assert(!timeout->next); + timeout->previous = task->timeouts ? task->timeouts->previous : timeout; + timeout->next = task->timeouts ? task->timeouts: timeout; + if (task->timeouts) + { + task->timeouts->previous = timeout; + if (task->timeouts->next == task->timeouts) + { + task->timeouts->next = timeout; + } + } + task->timeouts = timeout; +} + +static void _timeout_unlink(tf_task_t* task, timeout_t* timeout) +{ + assert(timeout->previous); + assert(timeout->next); + if (timeout->next == timeout && timeout->previous == timeout) + { + task->timeouts = NULL; + } + else + { + timeout->next->previous = timeout->previous; + timeout->previous->next = timeout->next; + if (task->timeouts == timeout) + { + task->timeouts = timeout->next; + } + } +} + +static void _timeout_closed(uv_handle_t* handle) +{ + timeout_t* timeout = handle->data; + tf_free(timeout); +} + +static void _util_timeoutCallback(uv_timer_t* handle) +{ + timeout_t* timeout = handle->data; + tf_trace_begin(tf_task_get_trace(timeout->_task), "_util_timeoutCallback"); + JSContext* context = tf_task_get_context(timeout->_task); + JSValue result = JS_Call( + context, + timeout->_callback, + JS_NULL, + 0, + NULL); + tf_util_report_error(context, result); + JS_FreeValue(context, result); + JS_FreeValue(context, timeout->_callback); + tf_trace_end(tf_task_get_trace(timeout->_task)); + _timeout_unlink(timeout->_task, timeout); + uv_close((uv_handle_t*)handle, _timeout_closed); +} + +static JSValue _tf_task_setTimeout(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) +{ + tf_task_t* task = JS_GetContextOpaque(context); + + timeout_t* timeout = tf_malloc(sizeof(timeout_t)); + *timeout = (timeout_t) + { + ._task = task, + ._callback = JS_DupValue(context, argv[0]), + ._timer = { .data = timeout }, + }; + _timeout_link(task, timeout); + + uv_timer_init(tf_task_get_loop(task), &timeout->_timer); + + int64_t duration; + JS_ToInt64(context, &duration, argv[1]); + if (uv_timer_start(&timeout->_timer, _util_timeoutCallback, duration, 0) != 0) + { + JS_FreeValue(context, timeout->_callback); + _timeout_unlink(task, timeout); + tf_free(timeout); + } + return JS_NULL; +} diff --git a/src/util.js.c b/src/util.js.c index 52b27c4e..520072e5 100644 --- a/src/util.js.c +++ b/src/util.js.c @@ -244,60 +244,6 @@ bool tf_util_report_error(JSContext* context, JSValue value) return is_error; } -typedef struct _timeout_t { - uv_timer_t _timer; - tf_task_t* _task; - JSValue _callback; -} timeout_t; - -static void _handle_closed(uv_handle_t* handle) -{ - timeout_t* timeout = handle->data; - tf_free(timeout); -} - -static void _util_timeoutCallback(uv_timer_t* handle) -{ - timeout_t* timeout = handle->data; - tf_trace_begin(tf_task_get_trace(timeout->_task), "_util_timeoutCallback"); - JSContext* context = tf_task_get_context(timeout->_task); - JSValue result = JS_Call( - context, - timeout->_callback, - JS_NULL, - 0, - NULL); - tf_util_report_error(context, result); - JS_FreeValue(context, result); - JS_FreeValue(context, timeout->_callback); - tf_trace_end(tf_task_get_trace(timeout->_task)); - uv_close((uv_handle_t*)handle, _handle_closed); -} - -static JSValue _util_setTimeout(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) -{ - tf_task_t* task = JS_GetContextOpaque(context); - - timeout_t* timeout = tf_malloc(sizeof(timeout_t)); - *timeout = (timeout_t) - { - ._task = task, - ._callback = JS_DupValue(context, argv[0]), - ._timer = { .data = timeout }, - }; - - uv_timer_init(tf_task_get_loop(task), &timeout->_timer); - - int64_t duration; - JS_ToInt64(context, &duration, argv[1]); - if (uv_timer_start(&timeout->_timer, _util_timeoutCallback, duration, 0) != 0) - { - JS_FreeValue(context, timeout->_callback); - tf_free(timeout); - } - return JS_NULL; -} - static JSValue _util_parseHttpRequest(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) { JSValue result = JS_UNDEFINED; @@ -491,7 +437,6 @@ void tf_util_register(JSContext* context) JS_SetPropertyStr(context, global, "bip39Words", JS_NewCFunction(context, _util_bip39_words, "bip39Words", 1)); JS_SetPropertyStr(context, global, "bip39Bytes", JS_NewCFunction(context, _util_bip39_bytes, "bip39Bytes", 1)); JS_SetPropertyStr(context, global, "print", JS_NewCFunction(context, _util_print, "print", 1)); - JS_SetPropertyStr(context, global, "setTimeout", JS_NewCFunction(context, _util_setTimeout, "setTimeout", 2)); JS_SetPropertyStr(context, global, "parseHttpRequest", JS_NewCFunction(context, _util_parseHttpRequest, "parseHttpRequest", 2)); JS_SetPropertyStr(context, global, "parseHttpResponse", JS_NewCFunction(context, _util_parseHttpResponse, "parseHttpResponse", 2)); JS_SetPropertyStr(context, global, "sha1Digest", JS_NewCFunction(context, _util_sha1_digest, "sha1Digest", 1));