details: https://hg.nginx.org/njs/rev/dffdf7c50dfc branches: changeset: 2238:dffdf7c50dfc user: Dmitry Volyntsev <xei...@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); _______________________________________________ nginx-devel mailing list nginx-devel@nginx.org https://mailman.nginx.org/mailman/listinfo/nginx-devel