[njs] Moving out setTimeout() and clearTimeout() from njs core.
Dmitry Volyntsev
xeioex at nginx.com
Tue Nov 21 17:04:30 UTC 2023
details: https://hg.nginx.org/njs/rev/dffdf7c50dfc
branches:
changeset: 2238:dffdf7c50dfc
user: Dmitry Volyntsev <xeioex at nginx.com>
date: Tue Nov 21 09:00:52 2023 -0800
description:
Moving out setTimeout() and clearTimeout() from njs core.
This functions are not part of the ECMAScript and should be
implemented by host environment.
diffstat:
auto/sources | 1 -
external/njs_shell.c | 314 ++++++++++++++++++++++++++----------------
nginx/ngx_http_js_module.c | 123 +++++-----------
nginx/ngx_js.c | 273 +++++++++++++++++++++++++++++++++++++-
nginx/ngx_js.h | 46 ++++++-
nginx/ngx_stream_js_module.c | 120 ++++-----------
src/njs.h | 2 -
src/njs_builtin.c | 6 -
src/njs_main.h | 1 -
src/test/njs_unit_test.c | 24 ---
test/shell_test.exp | 2 +-
11 files changed, 580 insertions(+), 332 deletions(-)
diffs (truncated from 1505 to 1000 lines):
diff -r 896e7e271382 -r dffdf7c50dfc auto/sources
--- a/auto/sources Tue Nov 21 08:57:09 2023 -0800
+++ b/auto/sources Tue Nov 21 09:00:52 2023 -0800
@@ -33,7 +33,6 @@ NJS_LIB_SRCS=" \
src/njs_scope.c \
src/njs_generator.c \
src/njs_disassembler.c \
- src/njs_timer.c \
src/njs_module.c \
src/njs_event.c \
src/njs_extern.c \
diff -r 896e7e271382 -r dffdf7c50dfc external/njs_shell.c
--- a/external/njs_shell.c Tue Nov 21 08:57:09 2023 -0800
+++ b/external/njs_shell.c Tue Nov 21 09:00:52 2023 -0800
@@ -11,8 +11,6 @@
#include <njs_arr.h>
#include <njs_queue.h>
#include <njs_rbtree.h>
-#include <njs_flathsh.h>
-#include <njs_djb_hash.h>
#if (!defined NJS_FUZZER_TARGET && defined NJS_HAVE_READLINE)
@@ -70,7 +68,12 @@ typedef struct {
typedef struct {
- njs_vm_event_t vm_event;
+ NJS_RBTREE_NODE (node);
+ njs_function_t *function;
+ njs_value_t *args;
+ njs_uint_t nargs;
+ uint32_t id;
+
njs_queue_link_t link;
} njs_ev_t;
@@ -85,7 +88,8 @@ typedef struct {
typedef struct {
njs_vm_t *vm;
- njs_lvlhsh_t events; /* njs_ev_t * */
+ uint32_t event_id;
+ njs_rbtree_t events; /* njs_ev_t * */
njs_queue_t posted_events;
njs_queue_t labels;
@@ -120,6 +124,12 @@ static char *njs_completion_generator(co
#endif
+static njs_int_t njs_set_timeout(njs_vm_t *vm, njs_value_t *args,
+ njs_uint_t nargs, njs_index_t unused, njs_value_t *retval);
+static njs_int_t njs_set_immediate(njs_vm_t *vm, njs_value_t *args,
+ njs_uint_t nargs, njs_index_t unused, njs_value_t *retval);
+static njs_int_t njs_clear_timeout(njs_vm_t *vm, njs_value_t *args,
+ njs_uint_t nargs, njs_index_t unused, njs_value_t *retval);
static njs_int_t njs_ext_console_log(njs_vm_t *vm, njs_value_t *args,
njs_uint_t nargs, njs_index_t magic, njs_value_t *retval);
static njs_int_t njs_ext_console_time(njs_vm_t *vm, njs_value_t *args,
@@ -127,17 +137,11 @@ static njs_int_t njs_ext_console_time(nj
static njs_int_t njs_ext_console_time_end(njs_vm_t *vm, njs_value_t *args,
njs_uint_t nargs, njs_index_t unused, njs_value_t *retval);
-static njs_host_event_t njs_console_set_timer(njs_external_ptr_t external,
- uint64_t delay, njs_vm_event_t vm_event);
-
-static void njs_console_clear_timer(njs_external_ptr_t external,
- njs_host_event_t event);
static void njs_console_log(njs_vm_t *vm, njs_external_ptr_t external,
njs_log_level_t level, const u_char *start, size_t length);
-static njs_int_t lvlhsh_key_test(njs_lvlhsh_query_t *lhq, void *data);
-static void *lvlhsh_pool_alloc(void *pool, size_t size);
-static void lvlhsh_pool_free(void *pool, void *p, size_t size);
+static intptr_t njs_event_rbtree_compare(njs_rbtree_node_t *node1,
+ njs_rbtree_node_t *node2);
njs_int_t njs_array_buffer_detach(njs_vm_t *vm, njs_value_t *args,
njs_uint_t nargs, njs_index_t unused, njs_value_t *retval);
@@ -264,17 +268,7 @@ static njs_external_t njs_ext_262[] = {
};
-static const njs_lvlhsh_proto_t lvlhsh_proto njs_aligned(64) = {
- NJS_LVLHSH_LARGE_SLAB,
- lvlhsh_key_test,
- lvlhsh_pool_alloc,
- lvlhsh_pool_free,
-};
-
-
static njs_vm_ops_t njs_console_ops = {
- njs_console_set_timer,
- njs_console_clear_timer,
NULL,
njs_console_log,
};
@@ -635,7 +629,8 @@ njs_console_init(njs_vm_t *vm, njs_conso
{
console->vm = vm;
- njs_lvlhsh_init(&console->events);
+ console->event_id = 0;
+ njs_rbtree_init(&console->events, njs_event_rbtree_compare);
njs_queue_init(&console->posted_events);
njs_queue_init(&console->labels);
@@ -649,6 +644,24 @@ njs_console_init(njs_vm_t *vm, njs_conso
static njs_int_t
+njs_function_bind(njs_vm_t *vm, const njs_str_t *name,
+ njs_function_native_t native, njs_bool_t ctor)
+{
+ njs_function_t *f;
+ njs_opaque_value_t value;
+
+ f = njs_vm_function_alloc(vm, native, 1, ctor);
+ if (f == NULL) {
+ return NJS_ERROR;
+ }
+
+ njs_value_function_set(njs_value_arg(&value), f);
+
+ return njs_vm_bind(vm, name, njs_value_arg(&value), 1);
+}
+
+
+static njs_int_t
njs_externals_init(njs_vm_t *vm)
{
njs_int_t ret, proto_id;
@@ -659,6 +672,9 @@ njs_externals_init(njs_vm_t *vm)
static const njs_str_t dollar_262 = njs_str("$262");
static const njs_str_t print_name = njs_str("print");
static const njs_str_t console_log = njs_str("console.log");
+ static const njs_str_t set_timeout = njs_str("setTimeout");
+ static const njs_str_t set_immediate = njs_str("setImmediate");
+ static const njs_str_t clear_timeout = njs_str("clearTimeout");
console = njs_vm_options(vm)->external;
@@ -690,8 +706,18 @@ njs_externals_init(njs_vm_t *vm)
return NJS_ERROR;
}
- ret = njs_console_init(vm, console);
- if (njs_slow_path(ret != NJS_OK)) {
+ ret = njs_function_bind(vm, &set_timeout, njs_set_timeout, 0);
+ if (ret != NJS_OK) {
+ return NJS_ERROR;
+ }
+
+ ret = njs_function_bind(vm, &set_immediate, njs_set_immediate, 0);
+ if (ret != NJS_OK) {
+ return NJS_ERROR;
+ }
+
+ ret = njs_function_bind(vm, &clear_timeout, njs_clear_timeout, 0);
+ if (ret != NJS_OK) {
return NJS_ERROR;
}
@@ -712,6 +738,11 @@ njs_externals_init(njs_vm_t *vm)
return NJS_ERROR;
}
+ ret = njs_console_init(vm, console);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return NJS_ERROR;
+ }
+
return NJS_OK;
}
@@ -829,10 +860,13 @@ njs_console_output(njs_vm_t *vm, njs_val
static njs_int_t
njs_process_events(void *runtime)
{
- njs_ev_t *ev;
- njs_queue_t *events;
- njs_console_t *console;
- njs_queue_link_t *link;
+ njs_ev_t *ev;
+ njs_vm_t *vm;
+ njs_int_t ret;
+ njs_queue_t *events;
+ njs_console_t *console;
+ njs_queue_link_t *link;
+ njs_opaque_value_t retval;
if (runtime == NULL) {
njs_stderror("njs_process_events(): no runtime\n");
@@ -840,6 +874,7 @@ njs_process_events(void *runtime)
}
console = runtime;
+ vm = console->vm;
events = &console->posted_events;
@@ -856,10 +891,24 @@ njs_process_events(void *runtime)
ev->link.prev = NULL;
ev->link.next = NULL;
- njs_vm_post_event(console->vm, ev->vm_event, NULL, 0);
+ njs_rbtree_delete(&console->events, &ev->node);
+
+ ret = njs_vm_invoke(vm, ev->function, ev->args, ev->nargs,
+ njs_value_arg(&retval));
+ if (ret == NJS_ERROR) {
+ njs_process_output(vm, njs_value_arg(&retval), ret);
+
+ if (!njs_vm_options(vm)->interactive) {
+ return NJS_ERROR;
+ }
+ }
}
- return NJS_OK;
+ if (!njs_rbtree_is_empty(&console->events)) {
+ return NJS_AGAIN;
+ }
+
+ return njs_vm_pending(vm) ? NJS_AGAIN: NJS_OK;
}
@@ -1047,28 +1096,7 @@ njs_process_script(njs_vm_t *vm, void *r
}
for ( ;; ) {
- if (!njs_vm_pending(vm) && !njs_vm_unhandled_rejection(vm)) {
- ret = NJS_OK;
- break;
- }
-
- ret = njs_process_events(runtime);
- if (njs_slow_path(ret != NJS_OK)) {
- njs_stderror("njs_process_events() failed\n");
- ret = NJS_ERROR;
- break;
- }
-
- if (njs_vm_waiting(vm) && !njs_vm_posted(vm)) {
- /*TODO: async events. */
-
- njs_stderror("njs_process_script(): async events unsupported\n");
- ret = NJS_ERROR;
- break;
- }
-
ret = njs_vm_run(vm);
-
if (ret == NJS_ERROR) {
njs_process_output(vm, njs_value_arg(&retval), ret);
@@ -1076,6 +1104,15 @@ njs_process_script(njs_vm_t *vm, void *r
return NJS_ERROR;
}
}
+
+ ret = njs_process_events(runtime);
+ if (njs_slow_path(ret == NJS_ERROR)) {
+ break;
+ }
+
+ if (ret == NJS_OK) {
+ break;
+ }
}
return ret;
@@ -1534,81 +1571,119 @@ njs_ext_console_time_end(njs_vm_t *vm, n
}
-static njs_host_event_t
-njs_console_set_timer(njs_external_ptr_t external, uint64_t delay,
- njs_vm_event_t vm_event)
+static njs_int_t
+njs_set_timer(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
+ njs_index_t unused, njs_bool_t immediate, njs_value_t *retval)
{
- njs_ev_t *ev;
- njs_vm_t *vm;
- njs_int_t ret;
- njs_console_t *console;
- njs_lvlhsh_query_t lhq;
+ njs_ev_t *ev;
+ uint64_t delay;
+ njs_uint_t n;
+ njs_console_t *console;
+
+ console = njs_vm_external_ptr(vm);
- console = external;
- vm = console->vm;
+ if (njs_slow_path(nargs < 2)) {
+ njs_vm_type_error(vm, "too few arguments");
+ return NJS_ERROR;
+ }
- if (delay != 0) {
- njs_vm_err(vm, "njs_console_set_timer(): async timers unsupported\n");
- return NULL;
+ if (njs_slow_path(!njs_value_is_function(njs_argument(args, 1)))) {
+ njs_vm_type_error(vm, "first arg must be a function");
+ return NJS_ERROR;
}
- ev = njs_mp_alloc(njs_vm_memory_pool(vm), sizeof(njs_ev_t));
- if (njs_slow_path(ev == NULL)) {
- return NULL;
+ delay = 0;
+
+ if (!immediate && nargs >= 3
+ && njs_value_is_number(njs_argument(args, 2)))
+ {
+ delay = njs_value_number(njs_argument(args, 2));
+ }
+
+ if (delay != 0) {
+ njs_vm_internal_error(vm, "njs_set_timer(): async timers unsupported");
+ return NJS_ERROR;
}
- ev->vm_event = vm_event;
+ n = immediate ? 2 : 3;
+ nargs = (nargs >= n) ? nargs - n : 0;
- lhq.key.start = (u_char *) &ev->vm_event;
- lhq.key.length = sizeof(njs_vm_event_t);
- lhq.key_hash = njs_djb_hash(lhq.key.start, lhq.key.length);
+ ev = njs_mp_alloc(njs_vm_memory_pool(vm),
+ sizeof(njs_ev_t) + sizeof(njs_opaque_value_t) * nargs);
+ if (njs_slow_path(ev == NULL)) {
+ njs_vm_memory_error(vm);
+ return NJS_ERROR;
+ }
- lhq.replace = 0;
- lhq.value = ev;
- lhq.proto = &lvlhsh_proto;
- lhq.pool = njs_vm_memory_pool(vm);
+ ev->function = njs_value_function(njs_argument(args, 1));
+ ev->nargs = nargs;
+ ev->args = (njs_value_t *) ((u_char *) ev + sizeof(njs_ev_t));
+ ev->id = console->event_id++;
- ret = njs_lvlhsh_insert(&console->events, &lhq);
- if (njs_slow_path(ret != NJS_OK)) {
- return NULL;
+ if (ev->nargs != 0) {
+ memcpy(ev->args, njs_argument(args, n),
+ sizeof(njs_opaque_value_t) * ev->nargs);
}
+ njs_rbtree_insert(&console->events, &ev->node);
+
njs_queue_insert_tail(&console->posted_events, &ev->link);
- return (njs_host_event_t) ev;
+ njs_value_number_set(retval, ev->id);
+
+ return NJS_OK;
+}
+
+
+static njs_int_t
+njs_set_timeout(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
+ njs_index_t unused, njs_value_t *retval)
+{
+ return njs_set_timer(vm, args, nargs, unused, 0, retval);
+}
+
+
+static njs_int_t
+njs_set_immediate(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
+ njs_index_t unused, njs_value_t *retval)
+{
+ return njs_set_timer(vm, args, nargs, unused, 1, retval);
}
-static void
-njs_console_clear_timer(njs_external_ptr_t external, njs_host_event_t event)
+static njs_int_t
+njs_clear_timeout(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
+ njs_index_t unused, njs_value_t *retval)
{
- njs_vm_t *vm;
- njs_ev_t *ev;
- njs_int_t ret;
- njs_console_t *console;
- njs_lvlhsh_query_t lhq;
+ njs_ev_t ev_lookup, *ev;
+ njs_console_t *console;
+ njs_rbtree_node_t *rb;
- ev = event;
- console = external;
- vm = console->vm;
-
- lhq.key.start = (u_char *) &ev->vm_event;
- lhq.key.length = sizeof(njs_vm_event_t);
- lhq.key_hash = njs_djb_hash(lhq.key.start, lhq.key.length);
-
- lhq.proto = &lvlhsh_proto;
- lhq.pool = njs_vm_memory_pool(vm);
-
- if (ev->link.prev != NULL) {
- njs_queue_remove(&ev->link);
+ if (nargs < 2 || !njs_value_is_number(njs_argument(args, 1))) {
+ njs_value_undefined_set(retval);
+ return NJS_OK;
}
- ret = njs_lvlhsh_delete(&console->events, &lhq);
- if (ret != NJS_OK) {
- njs_vm_err(vm, "njs_lvlhsh_delete() failed\n");
+ console = njs_vm_external_ptr(vm);
+
+ ev_lookup.id = njs_value_number(njs_argument(args, 1));
+
+ rb = njs_rbtree_find(&console->events, &ev_lookup.node);
+ if (njs_slow_path(rb == NULL)) {
+ njs_vm_internal_error(vm, "failed to find timer");
+ return NJS_ERROR;
}
- njs_mp_free(njs_vm_memory_pool(vm), ev);
+ njs_rbtree_delete(&console->events, (njs_rbtree_part_t *) rb);
+
+ ev = (njs_ev_t *) rb;
+ njs_queue_remove(&ev->link);
+ ev->link.prev = NULL;
+ ev->link.next = NULL;
+
+ njs_value_undefined_set(retval);
+
+ return NJS_OK;
}
@@ -1630,30 +1705,21 @@ njs_console_log(njs_vm_t *vm, njs_extern
}
-static njs_int_t
-lvlhsh_key_test(njs_lvlhsh_query_t *lhq, void *data)
+static intptr_t
+njs_event_rbtree_compare(njs_rbtree_node_t *node1, njs_rbtree_node_t *node2)
{
- njs_ev_t *ev;
+ njs_ev_t *ev1, *ev2;
- ev = data;
+ ev1 = (njs_ev_t *) node1;
+ ev2 = (njs_ev_t *) node2;
- if (memcmp(&ev->vm_event, lhq->key.start, sizeof(njs_vm_event_t)) == 0) {
- return NJS_OK;
+ if (ev1->id < ev2->id) {
+ return -1;
}
- return NJS_DECLINED;
-}
-
+ if (ev1->id > ev2->id) {
+ return 1;
+ }
-static void *
-lvlhsh_pool_alloc(void *pool, size_t size)
-{
- return njs_mp_align(pool, NJS_MAX_ALIGNMENT, size);
+ return 0;
}
-
-
-static void
-lvlhsh_pool_free(void *pool, void *p, size_t size)
-{
- njs_mp_free(pool, p);
-}
diff -r 896e7e271382 -r dffdf7c50dfc nginx/ngx_http_js_module.c
--- a/nginx/ngx_http_js_module.c Tue Nov 21 08:57:09 2023 -0800
+++ b/nginx/ngx_http_js_module.c Tue Nov 21 09:00:52 2023 -0800
@@ -49,7 +49,7 @@ typedef struct {
typedef struct {
- njs_vm_t *vm;
+ NGX_JS_COMMON_CTX;
ngx_log_t *log;
ngx_uint_t done;
ngx_int_t status;
@@ -78,14 +78,6 @@ typedef struct {
typedef struct {
- ngx_http_request_t *request;
- njs_vm_event_t vm_event;
- void *unused;
- ngx_int_t ident;
-} ngx_http_js_event_t;
-
-
-typedef struct {
njs_str_t name;
#if defined(nginx_version) && (nginx_version >= 1023000)
unsigned flags;
@@ -264,11 +256,6 @@ static njs_int_t ngx_http_js_location(nj
unsigned flags, njs_str_t *name, njs_value_t *setval,
njs_value_t *retval);
-static njs_host_event_t ngx_http_js_set_timer(njs_external_ptr_t external,
- uint64_t delay, njs_vm_event_t vm_event);
-static void ngx_http_js_clear_timer(njs_external_ptr_t external,
- njs_host_event_t event);
-static void ngx_http_js_timer_handler(ngx_event_t *ev);
static ngx_pool_t *ngx_http_js_pool(njs_vm_t *vm, ngx_http_request_t *r);
static ngx_resolver_t *ngx_http_js_resolver(njs_vm_t *vm,
ngx_http_request_t *r);
@@ -281,6 +268,8 @@ static size_t ngx_http_js_max_response_b
ngx_http_request_t *r);
static void ngx_http_js_handle_vm_event(ngx_http_request_t *r,
njs_vm_event_t vm_event, njs_value_t *args, njs_uint_t nargs);
+static void ngx_http_js_event_finalize(ngx_http_request_t *r, njs_int_t rc);
+static ngx_js_ctx_t *ngx_http_js_ctx(njs_vm_t *vm, ngx_http_request_t *r);
static void ngx_http_js_periodic_handler(ngx_event_t *ev);
static void ngx_http_js_periodic_write_event_handler(ngx_http_request_t *r);
@@ -853,8 +842,6 @@ static njs_external_t ngx_http_js_ext_p
static njs_vm_ops_t ngx_http_js_ops = {
- ngx_http_js_set_timer,
- ngx_http_js_clear_timer,
NULL,
ngx_js_logger,
};
@@ -872,6 +859,8 @@ static uintptr_t ngx_http_js_uptr[] = {
(uintptr_t) ngx_http_js_buffer_size,
(uintptr_t) ngx_http_js_max_response_buffer_size,
(uintptr_t) 0 /* main_conf ptr */,
+ (uintptr_t) ngx_http_js_event_finalize,
+ (uintptr_t) ngx_http_js_ctx,
};
@@ -991,7 +980,7 @@ ngx_http_js_content_write_event_handler(
ctx = ngx_http_get_module_ctx(r, ngx_http_js_module);
- if (!njs_vm_pending(ctx->vm)) {
+ if (!ngx_vm_pending(ctx)) {
ngx_http_js_content_finalize(r, ctx);
return;
}
@@ -1087,7 +1076,7 @@ ngx_http_js_header_filter(ngx_http_reque
ctx = ngx_http_get_module_ctx(r, ngx_http_js_module);
ctx->filter = 1;
- pending = njs_vm_pending(ctx->vm);
+ pending = ngx_vm_pending(ctx);
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http js header call \"%V\"", &jlcf->header_filter);
@@ -1190,7 +1179,7 @@ ngx_http_js_body_filter(ngx_http_request
return ret;
}
- pending = njs_vm_pending(ctx->vm);
+ pending = ngx_vm_pending(ctx);
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
"http js body call \"%V\"", &jlcf->body_filter);
@@ -1269,7 +1258,7 @@ ngx_http_js_variable_set(ngx_http_reques
ctx = ngx_http_get_module_ctx(r, ngx_http_js_module);
- pending = njs_vm_pending(ctx->vm);
+ pending = ngx_vm_pending(ctx);
rc = ngx_js_invoke(ctx->vm, fname, r->connection->log, &ctx->request, 1,
&ctx->retval);
@@ -1352,6 +1341,8 @@ ngx_http_js_init_vm(ngx_http_request_t *
return NGX_ERROR;
}
+ ngx_js_ctx_init((ngx_js_ctx_t *) ctx);
+
njs_value_invalid_set(njs_value_arg(&ctx->retval));
ngx_http_set_ctx(r, ctx, ngx_http_js_module);
@@ -1424,14 +1415,14 @@ ngx_http_js_cleanup_ctx(void *data)
{
ngx_http_js_ctx_t *ctx = data;
- if (njs_vm_pending(ctx->vm)) {
+ if (ngx_vm_pending(ctx)) {
ngx_log_error(NGX_LOG_ERR, ctx->log, 0, "pending events");
}
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->log, 0, "http js vm destroy: %p",
ctx->vm);
- njs_vm_destroy(ctx->vm);
+ ngx_js_ctx_destroy((ngx_js_ctx_t *) ctx);
}
@@ -4280,7 +4271,7 @@ ngx_http_js_periodic_write_event_handler
ctx = ngx_http_get_module_ctx(r, ngx_http_js_module);
- if (!njs_vm_pending(ctx->vm)) {
+ if (!ngx_vm_pending(ctx)) {
ngx_http_js_periodic_finalize(r, NGX_OK);
return;
}
@@ -4317,9 +4308,9 @@ ngx_http_js_periodic_finalize(ngx_http_r
ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http js periodic finalize: \"%V\" rc: %i c: %i pending: %i",
&ctx->periodic->method, rc, r->count,
- njs_vm_pending(ctx->vm));
-
- if (r->count > 1 || (rc == NGX_OK && njs_vm_pending(ctx->vm))) {
+ ngx_vm_pending(ctx));
+
+ if (r->count > 1 || (rc == NGX_OK && ngx_vm_pending(ctx))) {
return;
}
@@ -4386,65 +4377,6 @@ ngx_http_js_periodic_init(ngx_js_periodi
}
-static njs_host_event_t
-ngx_http_js_set_timer(njs_external_ptr_t external, uint64_t delay,
- njs_vm_event_t vm_event)
-{
- ngx_event_t *ev;
- ngx_http_request_t *r;
- ngx_http_js_event_t *js_event;
-
- r = (ngx_http_request_t *) external;
-
- ev = ngx_pcalloc(r->pool, sizeof(ngx_event_t));
- if (ev == NULL) {
- return NULL;
- }
-
- js_event = ngx_palloc(r->pool, sizeof(ngx_http_js_event_t));
- if (js_event == NULL) {
- return NULL;
- }
-
- js_event->request = r;
- js_event->vm_event = vm_event;
- js_event->ident = r->connection->fd;
-
- ev->data = js_event;
- ev->log = r->connection->log;
- ev->handler = ngx_http_js_timer_handler;
-
- ngx_add_timer(ev, delay);
-
- return ev;
-}
-
-
-static void
-ngx_http_js_clear_timer(njs_external_ptr_t external, njs_host_event_t event)
-{
- ngx_event_t *ev = event;
-
- if (ev->timer_set) {
- ngx_del_timer(ev);
- }
-}
-
-
-static void
-ngx_http_js_timer_handler(ngx_event_t *ev)
-{
- ngx_http_request_t *r;
- ngx_http_js_event_t *js_event;
-
- js_event = (ngx_http_js_event_t *) ev->data;
-
- r = js_event->request;
-
- ngx_http_js_handle_vm_event(r, js_event->vm_event, NULL, 0);
-}
-
-
static ngx_pool_t *
ngx_http_js_pool(njs_vm_t *vm, ngx_http_request_t *r)
{
@@ -4530,7 +4462,19 @@ ngx_http_js_handle_vm_event(ngx_http_req
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"js exception: %V", &exception);
-
+ }
+
+ ngx_http_js_event_finalize(r, rc);
+}
+
+
+static void
+ngx_http_js_event_finalize(ngx_http_request_t *r, njs_int_t rc)
+{
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http js event finalize rc: %i", (ngx_int_t) rc);
+
+ if (rc == NJS_ERROR) {
if (r->health_check) {
ngx_http_js_periodic_finalize(r, NGX_ERROR);
return;
@@ -4548,6 +4492,13 @@ ngx_http_js_handle_vm_event(ngx_http_req
}
+static ngx_js_ctx_t *
+ngx_http_js_ctx(njs_vm_t *vm, ngx_http_request_t *r)
+{
+ return ngx_http_get_module_ctx(r, ngx_http_js_module);
+}
+
+
static njs_int_t
ngx_js_http_init(njs_vm_t *vm)
{
diff -r 896e7e271382 -r dffdf7c50dfc nginx/ngx_js.c
--- a/nginx/ngx_js.c Tue Nov 21 08:57:09 2023 -0800
+++ b/nginx/ngx_js.c Tue Nov 21 09:00:52 2023 -0800
@@ -43,6 +43,12 @@ static njs_int_t ngx_js_ext_console_time
njs_uint_t nargs, njs_index_t unused, njs_value_t *retval);
static njs_int_t ngx_js_ext_console_time_end(njs_vm_t *vm, njs_value_t *args,
njs_uint_t nargs, njs_index_t unused, njs_value_t *retval);
+static njs_int_t njs_set_timeout(njs_vm_t *vm, njs_value_t *args,
+ njs_uint_t nargs, njs_index_t unused, njs_value_t *retval);
+static njs_int_t njs_set_immediate(njs_vm_t *vm, njs_value_t *args,
+ njs_uint_t nargs, njs_index_t unused, njs_value_t *retval);
+static njs_int_t njs_clear_timeout(njs_vm_t *vm, njs_value_t *args,
+ njs_uint_t nargs, njs_index_t unused, njs_value_t *retval);
static void ngx_js_cleanup_vm(void *data);
static njs_int_t ngx_js_core_init(njs_vm_t *vm);
@@ -344,6 +350,7 @@ ngx_js_invoke(njs_vm_t *vm, ngx_str_t *f
njs_int_t ret;
njs_str_t name;
ngx_str_t exception;
+ ngx_js_ctx_t *ctx;
njs_function_t *func;
name.start = fname->data;
@@ -377,7 +384,13 @@ ngx_js_invoke(njs_vm_t *vm, ngx_str_t *f
return NGX_ERROR;
}
- return (ret == NJS_AGAIN) ? NGX_AGAIN : NGX_OK;
+ if (ret == NJS_AGAIN) {
+ return NGX_AGAIN;
+ }
+
+ ctx = ngx_external_ctx(vm, njs_vm_external_ptr(vm));
+
+ return njs_rbtree_is_empty(&ctx->waiting_events) ? NGX_OK : NGX_AGAIN;
}
@@ -431,12 +444,88 @@ ngx_js_string(njs_vm_t *vm, njs_value_t
static njs_int_t
+njs_function_bind(njs_vm_t *vm, const njs_str_t *name,
+ njs_function_native_t native, njs_bool_t ctor)
+{
+ njs_function_t *f;
+ njs_opaque_value_t value;
+
+ f = njs_vm_function_alloc(vm, native, 1, ctor);
+ if (f == NULL) {
+ return NJS_ERROR;
+ }
+
+ njs_value_function_set(njs_value_arg(&value), f);
+
+ return njs_vm_bind(vm, name, njs_value_arg(&value), 1);
+}
+
+
+
+static intptr_t
+ngx_js_event_rbtree_compare(njs_rbtree_node_t *node1, njs_rbtree_node_t *node2)
+{
+ ngx_js_event_t *ev1, *ev2;
+
+ ev1 = (ngx_js_event_t *) ((u_char *) node1
+ - offsetof(ngx_js_event_t, node));
+ ev2 = (ngx_js_event_t *) ((u_char *) node2
+ - offsetof(ngx_js_event_t, node));
+
+ if (ev1->fd < ev2->fd) {
+ return -1;
+ }
+
+ if (ev1->fd > ev2->fd) {
+ return 1;
+ }
+
+ return 0;
+}
+
+
+void
+ngx_js_ctx_init(ngx_js_ctx_t *ctx)
+{
+ ctx->event_id = 0;
+ njs_rbtree_init(&ctx->waiting_events, ngx_js_event_rbtree_compare);
+}
+
+
+void
+ngx_js_ctx_destroy(ngx_js_ctx_t *ctx)
+{
+ ngx_js_event_t *event;
+ njs_rbtree_node_t *node;
+
+ node = njs_rbtree_min(&ctx->waiting_events);
+
+ while (njs_rbtree_is_there_successor(&ctx->waiting_events, node)) {
+ event = (ngx_js_event_t *) ((u_char *) node
+ - offsetof(ngx_js_event_t, node));
+
+ if (event->destructor != NULL) {
+ event->destructor(njs_vm_external_ptr(event->vm), event);
+ }
+
+ node = njs_rbtree_node_successor(&ctx->waiting_events, node);
+ }
+
+ njs_vm_destroy(ctx->vm);
+}
+
+
+static njs_int_t
ngx_js_core_init(njs_vm_t *vm)
{
njs_int_t ret, proto_id;
njs_str_t name;
njs_opaque_value_t value;
+ static const njs_str_t set_timeout = njs_str("setTimeout");
+ static const njs_str_t set_immediate = njs_str("setImmediate");
+ static const njs_str_t clear_timeout = njs_str("clearTimeout");
+
proto_id = njs_vm_external_prototype(vm, ngx_js_ext_core,
njs_nitems(ngx_js_ext_core));
if (proto_id < 0) {
@@ -476,6 +565,21 @@ ngx_js_core_init(njs_vm_t *vm)
return NJS_ERROR;
}
+ ret = njs_function_bind(vm, &set_timeout, njs_set_timeout, 1);
+ if (ret != NJS_OK) {
+ return NJS_ERROR;
+ }
+
+ ret = njs_function_bind(vm, &set_immediate, njs_set_immediate, 1);
+ if (ret != NJS_OK) {
+ return NJS_ERROR;
+ }
+
+ ret = njs_function_bind(vm, &clear_timeout, njs_clear_timeout, 1);
+ if (ret != NJS_OK) {
+ return NJS_ERROR;
+ }
+
return NJS_OK;
}
@@ -859,6 +963,173 @@ not_found:
}
+static void
+ngx_js_timer_handler(ngx_event_t *ev)
+{
+ njs_vm_t *vm;
+ njs_int_t ret;
+ ngx_str_t exception;
+ ngx_js_ctx_t *ctx;
+ ngx_js_event_t *event;
+ ngx_connection_t *c;
+ njs_external_ptr_t external;
+ njs_opaque_value_t retval;
+
+ event = (ngx_js_event_t *) ((u_char *) ev - offsetof(ngx_js_event_t, ev));
+
+ vm = event->vm;
+
+ ret = njs_vm_invoke(vm, event->function, event->args, event->nargs,
+ njs_value_arg(&retval));
+
+ external = njs_vm_external_ptr(vm);
+ ctx = ngx_external_ctx(vm, external);
+ njs_rbtree_delete(&ctx->waiting_events, &event->node);
+
+ if (ret == NJS_ERROR) {
+ ngx_js_exception(vm, &exception);
+
+ c = ngx_external_connection(vm, njs_vm_external_ptr(vm));
+
+ ngx_log_error(NGX_LOG_ERR, c->log, 0,
+ "js exception: %V", &exception);
+ }
+
+ ngx_external_event_finalize(vm)(external, ret);
+}
+
+
+static void
+ngx_js_clear_timer(njs_external_ptr_t external, ngx_js_event_t *event)
+{
+ if (event->ev.timer_set) {
+ ngx_del_timer(&event->ev);
+ }
+}
+
+
+static njs_int_t
+njs_set_timer(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
+ njs_index_t unused, njs_bool_t immediate, njs_value_t *retval)
+{
+ uint64_t delay;
+ njs_uint_t n;
+ ngx_js_ctx_t *ctx;
+ ngx_js_event_t *event;
+ ngx_connection_t *c;
+
+ if (njs_slow_path(nargs < 2)) {
+ njs_vm_type_error(vm, "too few arguments");
+ return NJS_ERROR;
+ }
+
+ if (njs_slow_path(!njs_value_is_function(njs_argument(args, 1)))) {
+ njs_vm_type_error(vm, "first arg must be a function");
+ return NJS_ERROR;
+ }
+
+ delay = 0;
+
+ if (!immediate && nargs >= 3
+ && njs_value_is_number(njs_argument(args, 2)))
+ {
+ delay = njs_value_number(njs_argument(args, 2));
+ }
+
+ n = immediate ? 2 : 3;
+ nargs = (nargs >= n) ? nargs - n : 0;
+
+ event = njs_mp_zalloc(njs_vm_memory_pool(vm),
+ sizeof(ngx_js_event_t)
+ + sizeof(njs_opaque_value_t) * nargs);
+ if (njs_slow_path(event == NULL)) {
+ njs_vm_memory_error(vm);
+ return NJS_ERROR;
+ }
+
+ event->vm = vm;
+ event->function = njs_value_function(njs_argument(args, 1));
+ event->nargs = nargs;
+ event->args = (njs_value_t *) ((u_char *) event + sizeof(ngx_js_event_t));
+ event->destructor = ngx_js_clear_timer;
+
+ ctx = ngx_external_ctx(vm, njs_vm_external_ptr(vm));
+ event->fd = ctx->event_id++;
+
+ c = ngx_external_connection(vm, njs_vm_external_ptr(vm));
+
+ event->ev.log = c->log;
+ event->ev.data = event;
+ event->ev.handler = ngx_js_timer_handler;
+
+ if (event->nargs != 0) {
+ memcpy(event->args, njs_argument(args, n),
+ sizeof(njs_opaque_value_t) * event->nargs);
+ }
+
+ njs_rbtree_insert(&ctx->waiting_events, &event->node);
+
+ ngx_add_timer(&event->ev, delay);
More information about the nginx-devel
mailing list