[njs] Refactored asynchronous events.

Dmitry Volyntsev xeioex at nginx.com
Tue Dec 5 16:55:02 UTC 2023


details:   https://hg.nginx.org/njs/rev/bc80bcb3102c
branches:  
changeset: 2245:bc80bcb3102c
user:      Dmitry Volyntsev <xeioex at nginx.com>
date:      Tue Dec 05 08:54:18 2023 -0800
description:
Refactored asynchronous events.

To align njs with other JS engines, async events are removed from njs
core. The following functions were removed: njs_vm_add_event(),
njs_vm_del_event(), njs_vm_waiting(). Instead the host is expected
to manage async events by itself.

In addition, the posted events are renamed to jobs, to better align with
the ECMA specs. The following methods are removed: njs_vm_run().
Instead, the host is expected to call njs_vm_execute_pending_job() in a
loop to execute pending jobs. The following functions were added:
njs_vm_enqueue_job().

diffstat:

 auto/sources                    |    1 -
 external/njs_fs_module.c        |   17 +--
 external/njs_shell.c            |   16 ++-
 external/njs_webcrypto_module.c |    8 +-
 nginx/ngx_http_js_module.c      |   99 +++++++-----------
 nginx/ngx_js.c                  |  107 ++++++++++++--------
 nginx/ngx_js.h                  |   33 ++++-
 nginx/ngx_js_fetch.c            |   59 ++++++----
 nginx/ngx_stream_js_module.c    |  133 +++++++++----------------
 src/njs.h                       |   59 +----------
 src/njs_event.c                 |   95 ------------------
 src/njs_event.h                 |   24 ----
 src/njs_promise.c               |   39 +------
 src/njs_vm.c                    |  208 ++++++++++-----------------------------
 src/njs_vm.h                    |    4 +-
 src/test/njs_externals_test.c   |   35 ++++--
 src/test/njs_externals_test.h   |    2 +-
 src/test/njs_unit_test.c        |   24 ++-
 18 files changed, 329 insertions(+), 634 deletions(-)

diffs (truncated from 1774 to 1000 lines):

diff -r 439ea33e531c -r bc80bcb3102c auto/sources
--- a/auto/sources	Wed Nov 29 20:46:36 2023 -0800
+++ b/auto/sources	Tue Dec 05 08:54:18 2023 -0800
@@ -34,7 +34,6 @@ NJS_LIB_SRCS=" \
    src/njs_generator.c \
    src/njs_disassembler.c \
    src/njs_module.c \
-   src/njs_event.c \
    src/njs_extern.c \
    src/njs_boolean.c \
    src/njs_number.c \
diff -r 439ea33e531c -r bc80bcb3102c external/njs_fs_module.c
--- a/external/njs_fs_module.c	Wed Nov 29 20:46:36 2023 -0800
+++ b/external/njs_fs_module.c	Tue Dec 05 08:54:18 2023 -0800
@@ -3264,7 +3264,6 @@ njs_fs_result(njs_vm_t *vm, njs_opaque_v
     const njs_value_t *callback, njs_uint_t nargs, njs_value_t *retval)
 {
     njs_int_t           ret;
-    njs_vm_event_t      vm_event;
     njs_function_t      *cb;
     njs_opaque_value_t  promise, callbacks[2], arguments[2];
 
@@ -3290,16 +3289,11 @@ njs_fs_result(njs_vm_t *vm, njs_opaque_v
             return NJS_ERROR;
         }
 
-        vm_event = njs_vm_add_event(vm, cb, 1, NULL, NULL);
-        if (njs_slow_path(vm_event == NULL)) {
-            return NJS_ERROR;
-        }
-
         njs_value_assign(&arguments[0],
                          &callbacks[njs_value_is_error(njs_value_arg(result))]);
         njs_value_assign(&arguments[1], result);
 
-        ret = njs_vm_post_event(vm, vm_event, njs_value_arg(&arguments), 2);
+        ret = njs_vm_enqueue_job(vm, cb, njs_value_arg(&arguments), 2);
         if (njs_slow_path(ret == NJS_ERROR)) {
             return NJS_ERROR;
         }
@@ -3318,13 +3312,8 @@ njs_fs_result(njs_vm_t *vm, njs_opaque_v
             njs_value_assign(&arguments[1], result);
         }
 
-        vm_event = njs_vm_add_event(vm, njs_value_function(callback), 1, NULL,
-                                    NULL);
-        if (njs_slow_path(vm_event == NULL)) {
-            return NJS_ERROR;
-        }
-
-        ret = njs_vm_post_event(vm, vm_event, njs_value_arg(&arguments), 2);
+        ret = njs_vm_enqueue_job(vm, njs_value_function(callback),
+                                 njs_value_arg(&arguments), 2);
         if (njs_slow_path(ret == NJS_ERROR)) {
             return NJS_ERROR;
         }
diff -r 439ea33e531c -r bc80bcb3102c external/njs_shell.c
--- a/external/njs_shell.c	Wed Nov 29 20:46:36 2023 -0800
+++ b/external/njs_shell.c	Tue Dec 05 08:54:18 2023 -0800
@@ -1096,12 +1096,18 @@ njs_process_script(njs_vm_t *vm, void *r
     }
 
     for ( ;; ) {
-        ret = njs_vm_run(vm);
-        if (ret == NJS_ERROR) {
-            njs_process_output(vm, njs_value_arg(&retval), ret);
+        for ( ;; ) {
+            ret = njs_vm_execute_pending_job(vm);
+            if (ret <= NJS_OK) {
+                if (ret == NJS_ERROR || njs_vm_unhandled_rejection(vm)) {
+                    njs_process_output(vm, NULL, ret);
 
-            if (!njs_vm_options(vm)->interactive) {
-                return NJS_ERROR;
+                    if (!njs_vm_options(vm)->interactive) {
+                        return NJS_ERROR;
+                    }
+                }
+
+                break;
             }
         }
 
diff -r 439ea33e531c -r bc80bcb3102c external/njs_webcrypto_module.c
--- a/external/njs_webcrypto_module.c	Wed Nov 29 20:46:36 2023 -0800
+++ b/external/njs_webcrypto_module.c	Tue Dec 05 08:54:18 2023 -0800
@@ -4771,7 +4771,6 @@ njs_webcrypto_result(njs_vm_t *vm, njs_o
 {
     njs_int_t           ret;
     njs_function_t      *callback;
-    njs_vm_event_t      vm_event;
     njs_opaque_value_t  promise, arguments[2];
 
     ret = njs_vm_promise_create(vm, njs_value_arg(&promise),
@@ -4785,11 +4784,6 @@ njs_webcrypto_result(njs_vm_t *vm, njs_o
         goto error;
     }
 
-    vm_event = njs_vm_add_event(vm, callback, 1, NULL, NULL);
-    if (vm_event == NULL) {
-        goto error;
-    }
-
     njs_value_assign(&arguments[0], &arguments[(rc != NJS_OK)]);
 
     if (rc != NJS_OK) {
@@ -4799,7 +4793,7 @@ njs_webcrypto_result(njs_vm_t *vm, njs_o
         njs_value_assign(&arguments[1], result);
     }
 
-    ret = njs_vm_post_event(vm, vm_event, njs_value_arg(&arguments), 2);
+    ret = njs_vm_enqueue_job(vm, callback, njs_value_arg(&arguments), 2);
     if (ret == NJS_ERROR) {
         goto error;
     }
diff -r 439ea33e531c -r bc80bcb3102c nginx/ngx_http_js_module.c
--- a/nginx/ngx_http_js_module.c	Wed Nov 29 20:46:36 2023 -0800
+++ b/nginx/ngx_http_js_module.c	Tue Dec 05 08:54:18 2023 -0800
@@ -266,9 +266,7 @@ static ngx_msec_t ngx_http_js_fetch_time
 static size_t ngx_http_js_buffer_size(njs_vm_t *vm, ngx_http_request_t *r);
 static size_t ngx_http_js_max_response_buffer_size(njs_vm_t *vm,
     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 void ngx_http_js_event_finalize(ngx_http_request_t *r, ngx_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);
@@ -852,14 +850,13 @@ static uintptr_t ngx_http_js_uptr[] = {
     (uintptr_t) ngx_http_js_pool,
     (uintptr_t) ngx_http_js_resolver,
     (uintptr_t) ngx_http_js_resolver_timeout,
-    (uintptr_t) ngx_http_js_handle_vm_event,
+    (uintptr_t) ngx_http_js_event_finalize,
     (uintptr_t) ngx_http_js_ssl,
     (uintptr_t) ngx_http_js_ssl_verify,
     (uintptr_t) ngx_http_js_fetch_timeout,
     (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,
 };
 
@@ -950,8 +947,8 @@ ngx_http_js_content_event_handler(ngx_ht
 
     ctx->status = NGX_HTTP_INTERNAL_SERVER_ERROR;
 
-    rc = ngx_js_call(ctx->vm, &jlcf->content, r->connection->log,
-                     &ctx->request, 1);
+    rc = ngx_js_name_call(ctx->vm, &jlcf->content, r->connection->log,
+                          &ctx->request, 1);
 
     if (rc == NGX_ERROR) {
         ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
@@ -1081,8 +1078,8 @@ ngx_http_js_header_filter(ngx_http_reque
     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                    "http js header call \"%V\"", &jlcf->header_filter);
 
-    rc = ngx_js_call(ctx->vm, &jlcf->header_filter, r->connection->log,
-                     &ctx->request, 1);
+    rc = ngx_js_name_call(ctx->vm, &jlcf->header_filter, r->connection->log,
+                          &ctx->request, 1);
 
     if (rc == NGX_ERROR) {
         return NGX_ERROR;
@@ -1184,8 +1181,8 @@ ngx_http_js_body_filter(ngx_http_request
             ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
                            "http js body call \"%V\"", &jlcf->body_filter);
 
-            rc = ngx_js_call(ctx->vm, &jlcf->body_filter, c->log, &arguments[0],
-                             3);
+            rc = ngx_js_name_call(ctx->vm, &jlcf->body_filter, c->log,
+                                  &arguments[0], 3);
 
             if (rc == NGX_ERROR) {
                 return NGX_ERROR;
@@ -1260,8 +1257,8 @@ ngx_http_js_variable_set(ngx_http_reques
 
     pending = ngx_vm_pending(ctx);
 
-    rc = ngx_js_invoke(ctx->vm, fname, r->connection->log, &ctx->request, 1,
-                       &ctx->retval);
+    rc = ngx_js_name_invoke(ctx->vm, fname, r->connection->log, &ctx->request,
+                            1, &ctx->retval);
 
     if (rc == NGX_ERROR) {
         v->not_found = 1;
@@ -3389,7 +3386,7 @@ ngx_http_js_subrequest(ngx_http_request_
 {
     ngx_int_t                    flags;
     ngx_str_t                    uri, args;
-    njs_vm_event_t               vm_event;
+    ngx_js_event_t              *event;
     ngx_http_js_ctx_t           *ctx;
     ngx_http_post_subrequest_t  *ps;
 
@@ -3404,20 +3401,25 @@ ngx_http_js_subrequest(ngx_http_request_
             return NJS_ERROR;
         }
 
-        vm_event = njs_vm_add_event(ctx->vm, callback, 1, NULL, NULL);
-        if (vm_event == NULL) {
-            njs_vm_error(ctx->vm, "internal error");
+        event = njs_mp_zalloc(njs_vm_memory_pool(ctx->vm),
+                              sizeof(ngx_js_event_t));
+        if (njs_slow_path(event == NULL)) {
+            njs_vm_memory_error(ctx->vm);
             return NJS_ERROR;
         }
 
+        event->vm = ctx->vm;
+        event->function = callback;
+        event->fd = ctx->event_id++;
+
         ps->handler = ngx_http_js_subrequest_done;
-        ps->data = vm_event;
+        ps->data = event;
 
         flags |= NGX_HTTP_SUBREQUEST_IN_MEMORY;
 
     } else {
         ps = NULL;
-        vm_event = NULL;
+        event = NULL;
     }
 
     uri.len = uri_arg->length;
@@ -3429,14 +3431,14 @@ ngx_http_js_subrequest(ngx_http_request_
     if (ngx_http_subrequest(r, &uri, args.len ? &args : NULL, sr, ps, flags)
         != NGX_OK)
     {
-        if (vm_event != NULL) {
-            njs_vm_del_event(ctx->vm, vm_event);
-        }
-
         njs_vm_error(ctx->vm, "subrequest creation failed");
         return NJS_ERROR;
     }
 
+    if (event != NULL) {
+        ngx_js_add_event(ctx, event);
+    }
+
     return NJS_OK;
 }
 
@@ -3444,7 +3446,7 @@ ngx_http_js_subrequest(ngx_http_request_
 static ngx_int_t
 ngx_http_js_subrequest_done(ngx_http_request_t *r, void *data, ngx_int_t rc)
 {
-    njs_vm_event_t  vm_event = data;
+    ngx_js_event_t  *event = data;
 
     njs_int_t            ret;
     ngx_http_js_ctx_t   *ctx;
@@ -3493,7 +3495,11 @@ ngx_http_js_subrequest_done(ngx_http_req
         return NGX_ERROR;
     }
 
-    ngx_http_js_handle_vm_event(r->parent, vm_event, njs_value_arg(&reply), 1);
+    rc = ngx_js_call(ctx->vm, event->function, njs_value_arg(&reply), 1);
+
+    ngx_js_del_event(ctx, event);
+
+    ngx_http_js_event_finalize(r->parent, rc);
 
     return NGX_OK;
 }
@@ -4248,8 +4254,8 @@ ngx_http_js_periodic_handler(ngx_event_t
 
     r->count++;
 
-    rc = ngx_js_invoke(ctx->vm, &periodic->method, &periodic->log,
-                       &ctx->request, 1, &ctx->retval);
+    rc = ngx_js_name_invoke(ctx->vm, &periodic->method, &periodic->log,
+                            &ctx->request, 1, &ctx->retval);
 
     if (rc == NGX_AGAIN) {
         rc = NGX_OK;
@@ -4440,41 +4446,12 @@ ngx_http_js_max_response_buffer_size(njs
 
 
 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)
-{
-    njs_int_t           rc;
-    ngx_str_t           exception;
-    ngx_http_js_ctx_t  *ctx;
-
-    ctx = ngx_http_get_module_ctx(r, ngx_http_js_module);
-
-    njs_vm_post_event(ctx->vm, vm_event, args, nargs);
-
-    rc = njs_vm_run(ctx->vm);
-
-    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
-                   "http js post event handler rc: %i event: %p",
-                   (ngx_int_t) rc, vm_event);
-
-    if (rc == NJS_ERROR) {
-        ngx_js_exception(ctx->vm, &exception);
-
-        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_http_js_event_finalize(ngx_http_request_t *r, ngx_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) {
+                   "http js event finalize rc: %i", rc);
+
+    if (rc == NGX_ERROR) {
         if (r->health_check) {
             ngx_http_js_periodic_finalize(r, NGX_ERROR);
             return;
@@ -4484,7 +4461,7 @@ ngx_http_js_event_finalize(ngx_http_requ
         return;
     }
 
-    if (rc == NJS_OK) {
+    if (rc == NGX_OK) {
         ngx_http_post_request(r, NULL);
     }
 
diff -r 439ea33e531c -r bc80bcb3102c nginx/ngx_js.c
--- a/nginx/ngx_js.c	Wed Nov 29 20:46:36 2023 -0800
+++ b/nginx/ngx_js.c	Tue Dec 05 08:54:18 2023 -0800
@@ -334,17 +334,57 @@ static njs_int_t      ngx_js_console_pro
 
 
 ngx_int_t
-ngx_js_call(njs_vm_t *vm, ngx_str_t *fname, ngx_log_t *log,
+ngx_js_call(njs_vm_t *vm, njs_function_t *func, njs_value_t *args,
+    njs_uint_t nargs)
+{
+    njs_int_t          ret;
+    ngx_str_t          exception;
+    ngx_connection_t  *c;
+
+    ret = njs_vm_call(vm, func, args, nargs);
+    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);
+        return NGX_ERROR;
+    }
+
+    for ( ;; ) {
+        ret = njs_vm_execute_pending_job(vm);
+        if (ret <= NJS_OK) {
+            c = ngx_external_connection(vm, njs_vm_external_ptr(vm));
+
+            if (ret == NJS_ERROR || njs_vm_unhandled_rejection(vm)) {
+                ngx_js_exception(vm, &exception);
+
+                ngx_log_error(NGX_LOG_ERR, c->log, 0,
+                              "js job exception: %V", &exception);
+                return NGX_ERROR;
+            }
+
+            break;
+        }
+    }
+
+    return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_js_name_call(njs_vm_t *vm, ngx_str_t *fname, ngx_log_t *log,
     njs_opaque_value_t *args, njs_uint_t nargs)
 {
     njs_opaque_value_t  unused;
 
-    return ngx_js_invoke(vm, fname, log, args, nargs, &unused);
+    return ngx_js_name_invoke(vm, fname, log, args, nargs, &unused);
 }
 
 
 ngx_int_t
-ngx_js_invoke(njs_vm_t *vm, ngx_str_t *fname, ngx_log_t *log,
+ngx_js_name_invoke(njs_vm_t *vm, ngx_str_t *fname, ngx_log_t *log,
     njs_opaque_value_t *args, njs_uint_t nargs, njs_opaque_value_t *retval)
 {
     njs_int_t        ret;
@@ -374,18 +414,19 @@ ngx_js_invoke(njs_vm_t *vm, ngx_str_t *f
         return NGX_ERROR;
     }
 
-    ret = njs_vm_run(vm);
-    if (ret == NJS_ERROR) {
-        ngx_js_exception(vm, &exception);
+    for ( ;; ) {
+        ret = njs_vm_execute_pending_job(vm);
+        if (ret <= NJS_OK) {
+            if (ret == NJS_ERROR || njs_vm_unhandled_rejection(vm)) {
+                ngx_js_exception(vm, &exception);
 
-        ngx_log_error(NGX_LOG_ERR, log, 0,
-                      "js exception: %V", &exception);
+                ngx_log_error(NGX_LOG_ERR, log, 0,
+                              "js job exception: %V", &exception);
+                return NGX_ERROR;
+            }
 
-        return NGX_ERROR;
-    }
-
-    if (ret == NJS_AGAIN) {
-        return NGX_AGAIN;
+            break;
+        }
     }
 
     ctx = ngx_external_ctx(vm, njs_vm_external_ptr(vm));
@@ -966,34 +1007,21 @@ 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_vm_t        *vm;
+    ngx_int_t        rc;
+    ngx_js_ctx_t    *ctx;
+    ngx_js_event_t  *event;
 
     event = (ngx_js_event_t *) ((u_char *) ev - offsetof(ngx_js_event_t, ev));
 
     vm = event->vm;
 
-    ret = njs_vm_call(vm, event->function, event->args, event->nargs);
-
-    external = njs_vm_external_ptr(vm);
-    ctx = ngx_external_ctx(vm, external);
-    njs_rbtree_delete(&ctx->waiting_events, &event->node);
+    rc = ngx_js_call(vm, event->function, event->args, event->nargs);
 
-    if (ret == NJS_ERROR) {
-        ngx_js_exception(vm, &exception);
-
-        c = ngx_external_connection(vm, njs_vm_external_ptr(vm));
+    ctx = ngx_external_ctx(vm, njs_vm_external_ptr(vm));
+    ngx_js_del_event(ctx, event);
 
-        ngx_log_error(NGX_LOG_ERR, c->log, 0,
-                      "js exception: %V", &exception);
-    }
-
-    ngx_external_event_finalize(vm)(external, ret);
+    ngx_external_event_finalize(vm)(njs_vm_external_ptr(vm), rc);
 }
 
 
@@ -1065,7 +1093,7 @@ njs_set_timer(njs_vm_t *vm, njs_value_t 
                sizeof(njs_opaque_value_t) * event->nargs);
     }
 
-    njs_rbtree_insert(&ctx->waiting_events, &event->node);
+    ngx_js_add_event(ctx, event);
 
     ngx_add_timer(&event->ev, delay);
 
@@ -1113,14 +1141,9 @@ njs_clear_timeout(njs_vm_t *vm, njs_valu
         return NJS_ERROR;
     }
 
-    event = (ngx_js_event_t *) ((u_char *) rb
-                                - offsetof(ngx_js_event_t, node));
+    event = (ngx_js_event_t *) ((u_char *) rb - offsetof(ngx_js_event_t, node));
 
-    if (event->ev.timer_set) {
-        ngx_del_timer(&event->ev);
-    }
-
-    njs_rbtree_delete(&ctx->waiting_events, (njs_rbtree_part_t *) rb);
+    ngx_js_del_event(ctx, event);
 
     njs_value_undefined_set(retval);
 
diff -r 439ea33e531c -r bc80bcb3102c nginx/ngx_js.h
--- a/nginx/ngx_js.h	Wed Nov 29 20:46:36 2023 -0800
+++ b/nginx/ngx_js.h	Tue Dec 05 08:54:18 2023 -0800
@@ -39,9 +39,7 @@ typedef struct ngx_js_ctx_s  ngx_js_ctx_
 
 
 typedef ngx_pool_t *(*ngx_external_pool_pt)(njs_vm_t *vm, njs_external_ptr_t e);
-typedef void (*ngx_js_event_handler_pt)(njs_external_ptr_t e,
-    njs_vm_event_t vm_event, njs_value_t *args, njs_uint_t nargs);
-typedef void (*ngx_js_event_finalize_pt)(njs_external_ptr_t e, njs_int_t rc);
+typedef void (*ngx_js_event_finalize_pt)(njs_external_ptr_t e, ngx_int_t rc);
 typedef ngx_resolver_t *(*ngx_external_resolver_pt)(njs_vm_t *vm,
     njs_external_ptr_t e);
 typedef ngx_msec_t (*ngx_external_timeout_pt)(njs_vm_t *vm,
@@ -73,6 +71,7 @@ struct ngx_js_event_s {
     void               (*destructor)(njs_external_ptr_t external,
                                      ngx_js_event_t *event);
     ngx_event_t          ev;
+    void                *data;
 };
 
 
@@ -116,6 +115,20 @@ struct ngx_js_event_s {
     ngx_socket_t           event_id
 
 
+#define ngx_js_add_event(ctx, event)                                          \
+    njs_rbtree_insert(&(ctx)->waiting_events, &(event)->node)
+
+
+#define ngx_js_del_event(ctx, event)                                          \
+    do {                                                                      \
+        if ((event)->destructor) {                                            \
+            (event)->destructor(njs_vm_external_ptr((event)->vm), event);     \
+        }                                                                     \
+                                                                              \
+        njs_rbtree_delete(&(ctx)->waiting_events, &(event)->node);            \
+    } while (0)
+
+
 typedef struct {
     NGX_JS_COMMON_MAIN_CONF;
 } ngx_js_main_conf_t;
@@ -139,8 +152,8 @@ struct ngx_js_ctx_s {
     ((ngx_external_resolver_pt) njs_vm_meta(vm, 2))(vm, e)
 #define ngx_external_resolver_timeout(vm, e)                                  \
     ((ngx_external_timeout_pt) njs_vm_meta(vm, 3))(vm, e)
-#define ngx_external_event_handler(vm, e)                                     \
-    ((ngx_js_event_handler_pt) njs_vm_meta(vm, 4))
+#define ngx_external_event_finalize(vm) \
+    ((ngx_js_event_finalize_pt) njs_vm_meta(vm, 4))
 #define ngx_external_ssl(vm, e)                                               \
     ((ngx_external_ssl_pt) njs_vm_meta(vm, 5))(vm, e)
 #define ngx_external_ssl_verify(vm, e)                                        \
@@ -154,10 +167,8 @@ struct ngx_js_ctx_s {
 #define NGX_JS_MAIN_CONF_INDEX  10
 #define ngx_main_conf(vm)                                                     \
 	((ngx_js_main_conf_t *) njs_vm_meta(vm, NGX_JS_MAIN_CONF_INDEX))
-#define ngx_external_event_finalize(vm) \
-    ((ngx_js_event_finalize_pt) njs_vm_meta(vm, 11))
 #define ngx_external_ctx(vm, e) \
-    ((ngx_js_external_ctx_pt) njs_vm_meta(vm, 12))(vm, e)
+    ((ngx_js_external_ctx_pt) njs_vm_meta(vm, 11))(vm, e)
 
 
 #define ngx_js_prop(vm, type, value, start, len)                              \
@@ -171,9 +182,11 @@ struct ngx_js_ctx_s {
 
 void ngx_js_ctx_init(ngx_js_ctx_t *ctx);
 void ngx_js_ctx_destroy(ngx_js_ctx_t *ctx);
-ngx_int_t ngx_js_call(njs_vm_t *vm, ngx_str_t *fname, ngx_log_t *log,
+ngx_int_t ngx_js_call(njs_vm_t *vm, njs_function_t *func, njs_value_t *args,
+    njs_uint_t nargs);
+ngx_int_t ngx_js_name_call(njs_vm_t *vm, ngx_str_t *fname, ngx_log_t *log,
     njs_opaque_value_t *args, njs_uint_t nargs);
-ngx_int_t ngx_js_invoke(njs_vm_t *vm, ngx_str_t *fname, ngx_log_t *log,
+ngx_int_t ngx_js_name_invoke(njs_vm_t *vm, ngx_str_t *fname, ngx_log_t *log,
     njs_opaque_value_t *args, njs_uint_t nargs, njs_opaque_value_t *retval);
 ngx_int_t ngx_js_exception(njs_vm_t *vm, ngx_str_t *s);
 
diff -r 439ea33e531c -r bc80bcb3102c nginx/ngx_js_fetch.c
--- a/nginx/ngx_js_fetch.c	Wed Nov 29 20:46:36 2023 -0800
+++ b/nginx/ngx_js_fetch.c	Tue Dec 05 08:54:18 2023 -0800
@@ -116,9 +116,7 @@ struct ngx_js_http_s {
     ngx_pool_t                    *pool;
 
     njs_vm_t                      *vm;
-    njs_external_ptr_t             external;
-    njs_vm_event_t                 vm_event;
-    ngx_js_event_handler_pt        event_handler;
+    ngx_js_event_t                *event;
 
     ngx_resolver_ctx_t            *ctx;
     ngx_addr_t                     addr;
@@ -177,7 +175,7 @@ static njs_int_t ngx_js_headers_fill(njs
 static ngx_js_http_t *ngx_js_http_alloc(njs_vm_t *vm, ngx_pool_t *pool,
     ngx_log_t *log);
 static void njs_js_http_destructor(njs_external_ptr_t external,
-    njs_host_event_t host);
+    ngx_js_event_t *event);
 static void ngx_js_resolve_handler(ngx_resolver_ctx_t *ctx);
 static njs_int_t ngx_js_fetch_promissified_result(njs_vm_t *vm,
     njs_value_t *result, njs_int_t rc, njs_value_t *retval);
@@ -695,9 +693,6 @@ ngx_js_ext_fetch(njs_vm_t *vm, njs_value
         return NJS_ERROR;
     }
 
-    http->external = external;
-    http->event_handler = ngx_external_event_handler(vm, external);
-
     ret = ngx_js_request_constructor(vm, &request, &u, external, args, nargs);
     if (ret != NJS_OK) {
         goto fail;
@@ -1273,8 +1268,9 @@ static ngx_js_http_t *
 ngx_js_http_alloc(njs_vm_t *vm, ngx_pool_t *pool, ngx_log_t *log)
 {
     njs_int_t        ret;
+    ngx_js_ctx_t    *ctx;
     ngx_js_http_t   *http;
-    njs_vm_event_t   vm_event;
+    ngx_js_event_t  *event;
     njs_function_t  *callback;
 
     http = ngx_pcalloc(pool, sizeof(ngx_js_http_t));
@@ -1301,12 +1297,22 @@ ngx_js_http_alloc(njs_vm_t *vm, ngx_pool
         goto failed;
     }
 
-    vm_event = njs_vm_add_event(vm, callback, 1, http, njs_js_http_destructor);
-    if (vm_event == NULL) {
+    event = njs_mp_zalloc(njs_vm_memory_pool(vm), sizeof(ngx_js_event_t));
+    if (njs_slow_path(event == NULL)) {
         goto failed;
     }
 
-    http->vm_event = vm_event;
+    ctx = ngx_external_ctx(vm, njs_vm_external_ptr(vm));
+
+    event->vm = vm;
+    event->function = callback;
+    event->destructor = njs_js_http_destructor;
+    event->fd = ctx->event_id++;
+    event->data = http;
+
+    ngx_js_add_event(ctx, event);
+
+    http->event = event;
 
     ngx_log_debug1(NGX_LOG_DEBUG_EVENT, log, 0, "js fetch alloc:%p", http);
 
@@ -1428,11 +1434,11 @@ ngx_js_http_close_connection(ngx_connect
 
 
 static void
-njs_js_http_destructor(njs_external_ptr_t external, njs_host_event_t host)
+njs_js_http_destructor(njs_external_ptr_t external, ngx_js_event_t *event)
 {
     ngx_js_http_t  *http;
 
-    http = host;
+    http = event->data;
 
     ngx_log_debug1(NGX_LOG_DEBUG_EVENT, http->log, 0, "js fetch destructor:%p",
                    http);
@@ -1455,7 +1461,6 @@ ngx_js_fetch_promissified_result(njs_vm_
 {
     njs_int_t            ret;
     njs_function_t      *callback;
-    njs_vm_event_t       vm_event;
     njs_opaque_value_t   promise, arguments[2];
 
     ret = njs_vm_promise_create(vm, njs_value_arg(&promise),
@@ -1469,11 +1474,6 @@ ngx_js_fetch_promissified_result(njs_vm_
         goto error;
     }
 
-    vm_event = njs_vm_add_event(vm, callback, 1, NULL, NULL);
-    if (vm_event == NULL) {
-        goto error;
-    }
-
     njs_value_assign(&arguments[0], &arguments[(rc != NJS_OK)]);
 
     if (rc != NJS_OK) {
@@ -1483,7 +1483,7 @@ ngx_js_fetch_promissified_result(njs_vm_
         njs_value_assign(&arguments[1], result);
     }
 
-    ret = njs_vm_post_event(vm, vm_event, njs_value_arg(&arguments), 2);
+    ret = njs_vm_enqueue_job(vm, callback, njs_value_arg(&arguments), 2);
     if (ret == NJS_ERROR) {
         goto error;
     }
@@ -1504,7 +1504,10 @@ static void
 ngx_js_http_fetch_done(ngx_js_http_t *http, njs_opaque_value_t *retval,
     njs_int_t rc)
 {
-    njs_opaque_value_t  arguments[2], *action;
+    njs_vm_t            *vm;
+    ngx_js_ctx_t        *ctx;
+    ngx_js_event_t      *event;
+    njs_opaque_value_t   arguments[2], *action;
 
     ngx_log_debug2(NGX_LOG_DEBUG_EVENT, http->log, 0,
                    "js fetch done http:%p rc:%i", http, (ngx_int_t) rc);
@@ -1514,12 +1517,20 @@ ngx_js_http_fetch_done(ngx_js_http_t *ht
         http->peer.connection = NULL;
     }
 
-    if (http->vm_event != NULL) {
+    if (http->event != NULL) {
         action = &http->promise_callbacks[(rc != NJS_OK)];
         njs_value_assign(&arguments[0], action);
         njs_value_assign(&arguments[1], retval);
-        http->event_handler(http->external, http->vm_event,
-                            njs_value_arg(&arguments), 2);
+
+        vm = http->vm;
+        event = http->event;
+
+        rc = ngx_js_call(vm, event->function, njs_value_arg(&arguments), 2);
+
+        ctx = ngx_external_ctx(vm,  njs_vm_external_ptr(vm));
+        ngx_js_del_event(ctx, event);
+
+        ngx_external_event_finalize(vm)(njs_vm_external_ptr(vm), rc);
     }
 }
 
diff -r 439ea33e531c -r bc80bcb3102c nginx/ngx_stream_js_module.c
--- a/nginx/ngx_stream_js_module.c	Wed Nov 29 20:46:36 2023 -0800
+++ b/nginx/ngx_stream_js_module.c	Tue Dec 05 08:54:18 2023 -0800
@@ -22,7 +22,7 @@ typedef struct {
 
 
 typedef struct {
-    njs_vm_event_t          ev;
+    njs_function_t         *function;
     ngx_uint_t              data_type;
 } ngx_stream_js_ev_t;
 
@@ -67,6 +67,12 @@ typedef struct {
 } ngx_stream_js_ctx_t;
 
 
+#define ngx_stream_pending(ctx)                                               \
+    (ngx_vm_pending(ctx)                                                      \
+     || (ctx)->events[NGX_JS_EVENT_UPLOAD].function != NULL                   \
+     || (ctx)->events[NGX_JS_EVENT_DOWNLOAD].function != NULL)
+
+
 static ngx_int_t ngx_stream_js_access_handler(ngx_stream_session_t *s);
 static ngx_int_t ngx_stream_js_preread_handler(ngx_stream_session_t *s);
 static ngx_int_t ngx_stream_js_phase_handler(ngx_stream_session_t *s,
@@ -86,7 +92,7 @@ static void ngx_stream_js_cleanup(void *
 static njs_int_t ngx_stream_js_run_event(ngx_stream_session_t *s,
     ngx_stream_js_ctx_t *ctx, ngx_stream_js_ev_t *event,
     ngx_uint_t from_upstream);
-static njs_vm_event_t *ngx_stream_js_event(ngx_stream_session_t *s,
+static njs_function_t **ngx_stream_js_event(ngx_stream_session_t *s,
     njs_str_t *event);
 
 static njs_int_t ngx_stream_js_ext_get_remote_address(njs_vm_t *vm,
@@ -123,9 +129,7 @@ static ngx_msec_t ngx_stream_js_fetch_ti
 static size_t ngx_stream_js_buffer_size(njs_vm_t *vm, ngx_stream_session_t *s);
 static size_t ngx_stream_js_max_response_buffer_size(njs_vm_t *vm,
     ngx_stream_session_t *s);
-static void ngx_stream_js_handle_event(ngx_stream_session_t *s,
-    njs_vm_event_t vm_event, njs_value_t *args, njs_uint_t nargs);
-static void ngx_stream_js_event_finalize(ngx_stream_session_t *s, njs_int_t rc);
+static void ngx_stream_js_event_finalize(ngx_stream_session_t *s, ngx_int_t rc);
 static ngx_js_ctx_t *ngx_stream_js_ctx(njs_vm_t *vm, ngx_stream_session_t *s);
 
 static void ngx_stream_js_periodic_handler(ngx_event_t *ev);
@@ -615,14 +619,13 @@ static uintptr_t ngx_stream_js_uptr[] = 
     (uintptr_t) ngx_stream_js_pool,
     (uintptr_t) ngx_stream_js_resolver,
     (uintptr_t) ngx_stream_js_resolver_timeout,
-    (uintptr_t) ngx_stream_js_handle_event,
+    (uintptr_t) ngx_stream_js_event_finalize,
     (uintptr_t) ngx_stream_js_ssl,
     (uintptr_t) ngx_stream_js_ssl_verify,
     (uintptr_t) ngx_stream_js_fetch_timeout,
     (uintptr_t) ngx_stream_js_buffer_size,
     (uintptr_t) ngx_stream_js_max_response_buffer_size,
     (uintptr_t) 0 /* main_conf ptr */,
-    (uintptr_t) ngx_stream_js_event_finalize,
     (uintptr_t) ngx_stream_js_ctx,
 };
 
@@ -734,7 +737,7 @@ ngx_stream_js_phase_handler(ngx_stream_s
         ngx_log_debug1(NGX_LOG_DEBUG_STREAM, c->log, 0,
                        "stream js phase call \"%V\"", name);
 
-        rc = ngx_js_call(ctx->vm, name, c->log, &ctx->args[0], 1);
+        rc = ngx_js_name_call(ctx->vm, name, c->log, &ctx->args[0], 1);
 
         if (rc == NGX_ERROR) {
             return rc;
@@ -751,9 +754,9 @@ ngx_stream_js_phase_handler(ngx_stream_s
         return NGX_ERROR;
     }
 
-    if (ngx_vm_pending(ctx)) {
+    if (ngx_stream_pending(ctx)) {
         ctx->in_progress = 1;
-        rc = ctx->events[NGX_JS_EVENT_UPLOAD].ev ? NGX_AGAIN : NGX_DONE;
+        rc = ctx->events[NGX_JS_EVENT_UPLOAD].function ? NGX_AGAIN : NGX_DONE;
 
     } else {
         ctx->in_progress = 0;
@@ -811,7 +814,7 @@ ngx_stream_js_body_filter(ngx_stream_ses
         ngx_log_debug1(NGX_LOG_DEBUG_STREAM, c->log, 0,
                        "stream js filter call \"%V\"" , &jscf->filter);
 
-        rc = ngx_js_call(ctx->vm, &jscf->filter, c->log, &ctx->args[0], 1);
+        rc = ngx_js_name_call(ctx->vm, &jscf->filter, c->log, &ctx->args[0], 1);
 
         if (rc == NGX_ERROR) {
             return rc;
@@ -827,7 +830,7 @@ ngx_stream_js_body_filter(ngx_stream_ses
 
         event = ngx_stream_event(from_upstream);
 
-        if (event->ev != NULL) {
+        if (event->function != NULL) {
             ret = ngx_stream_js_run_event(s, ctx, event, from_upstream);
             if (ret != NJS_OK) {
                 ngx_js_exception(ctx->vm, &exception);
@@ -922,10 +925,10 @@ ngx_stream_js_variable_set(ngx_stream_se
 
     ctx = ngx_stream_get_module_ctx(s, ngx_stream_js_module);
 
-    pending = ngx_vm_pending(ctx);
-
-    rc = ngx_js_invoke(ctx->vm, fname, s->connection->log, &ctx->args[0], 1,
-                       &ctx->retval);
+    pending = ngx_stream_pending(ctx);
+
+    rc = ngx_js_name_invoke(ctx->vm, fname, s->connection->log, &ctx->args[0],
+                            1, &ctx->retval);
 
     if (rc == NGX_ERROR) {
         v->not_found = 1;
@@ -1078,9 +1081,8 @@ ngx_stream_js_drop_events(ngx_stream_js_
     ngx_uint_t  i;
 
     for (i = 0; i < NGX_JS_EVENT_MAX; i++) {
-        if (ctx->events[i].ev != NULL) {
-            njs_vm_del_event(ctx->vm, ctx->events[i].ev);
-            ctx->events[i].ev = NULL;
+        if (ctx->events[i].function != NULL) {
+            ctx->events[i].function = NULL;
         }
     }
 }
@@ -1119,7 +1121,7 @@ ngx_stream_js_run_event(ngx_stream_sessi
     uintptr_t          flags;
     ngx_connection_t  *c;
 
-    if (event->ev == NULL) {
+    if (event->function == NULL) {
         return NJS_OK;
     }
 
@@ -1152,18 +1154,12 @@ ngx_stream_js_run_event(ngx_stream_sessi
         return NGX_ERROR;
     }
 
-    njs_vm_post_event(ctx->vm, event->ev, njs_value_arg(&ctx->args[1]), 2);
-
-    ret = njs_vm_run(ctx->vm);
-    if (ret == NJS_ERROR) {
-        return ret;
-    }
-
-    return NJS_OK;
+    return ngx_js_call(ctx->vm, event->function, njs_value_arg(&ctx->args[1]),
+                       2);
 }
 
 
-static njs_vm_event_t *
+static njs_function_t **
 ngx_stream_js_event(ngx_stream_session_t *s, njs_str_t *event)
 {
     ngx_uint_t            i, n, type;
@@ -1231,7 +1227,7 @@ ngx_stream_js_event(ngx_stream_session_t
         }
     }
 
-    return &ctx->events[events[i].id].ev;
+    return &ctx->events[events[i].id].function;
 }
 
 
@@ -1317,10 +1313,10 @@ static njs_int_t
 ngx_stream_js_ext_on(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
     njs_index_t unused, njs_value_t *retval)
 {
-    njs_str_t              name;
-    njs_value_t           *callback;
-    njs_vm_event_t        *event;
-    ngx_stream_session_t  *s;
+    njs_str_t               name;
+    njs_value_t            *callback;
+    njs_function_t        **cb;
+    ngx_stream_session_t   *s;
 
     s = njs_vm_external(vm, ngx_stream_js_session_proto_id,
                         njs_argument(args, 0));
@@ -1340,21 +1336,17 @@ ngx_stream_js_ext_on(njs_vm_t *vm, njs_v
         return NJS_ERROR;
     }
 
-    event = ngx_stream_js_event(s, &name);
-    if (event == NULL) {
+    cb = ngx_stream_js_event(s, &name);
+    if (cb == NULL) {
         return NJS_ERROR;
     }
 
-    if (*event != NULL) {
+    if (*cb != NULL) {
         njs_vm_error(vm, "event handler \"%V\" is already set", &name);
         return NJS_ERROR;
     }
 
-    *event = njs_vm_add_event(vm, njs_value_function(callback), 0, NULL, NULL);
-    if (*event == NULL) {
-        njs_vm_error(vm, "internal error");
-        return NJS_ERROR;
-    }
+    *cb = njs_value_function(callback);
 
     njs_value_undefined_set(retval);
 
@@ -1366,9 +1358,9 @@ static njs_int_t
 ngx_stream_js_ext_off(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
     njs_index_t unused, njs_value_t *retval)
 {
-    njs_str_t              name;
-    njs_vm_event_t        *event;
-    ngx_stream_session_t  *s;
+    njs_str_t               name;
+    njs_function_t        **callback;
+    ngx_stream_session_t   *s;
 
     s = njs_vm_external(vm, ngx_stream_js_session_proto_id,
                         njs_argument(args, 0));
@@ -1382,14 +1374,12 @@ ngx_stream_js_ext_off(njs_vm_t *vm, njs_
         return NJS_ERROR;
     }
 
-    event = ngx_stream_js_event(s, &name);
-    if (event == NULL) {
+    callback = ngx_stream_js_event(s, &name);
+    if (callback == NULL) {
         return NJS_ERROR;
     }
 
-    njs_vm_del_event(vm, *event);
-
-    *event = NULL;
+    *callback = NULL;
 
     njs_value_undefined_set(retval);
 
@@ -1726,41 +1716,12 @@ ngx_stream_js_max_response_buffer_size(n
 
 
 static void
-ngx_stream_js_handle_event(ngx_stream_session_t *s, njs_vm_event_t vm_event,
-    njs_value_t *args, njs_uint_t nargs)
-{
-    njs_int_t            rc;
-    ngx_str_t            exception;
-    ngx_stream_js_ctx_t  *ctx;
-
-    ctx = ngx_stream_get_module_ctx(s, ngx_stream_js_module);
-
-    njs_vm_post_event(ctx->vm, vm_event, args, nargs);
-
-    rc = njs_vm_run(ctx->vm);
-
-    ngx_log_debug2(NGX_LOG_DEBUG_STREAM, s->connection->log, 0,
-                   "stream js post event handler rc: %i event: %p",
-                   (ngx_int_t) rc, vm_event);
-
-    if (rc == NJS_ERROR) {
-        ngx_js_exception(ctx->vm, &exception);
-
-        ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
-                      "js exception: %V", &exception);
-    }
-
-    ngx_stream_js_event_finalize(s, rc);
-}
-
-
-static void


More information about the nginx-devel mailing list