[njs] Fixed async ctx erasing when a function is called multiple times.

Alexander Borisov alexander.borisov at nginx.com
Thu Sep 2 16:34:57 UTC 2021


details:   https://hg.nginx.org/njs/rev/3a7ffe641a77
branches:  
changeset: 1700:3a7ffe641a77
user:      Alexander Borisov <alexander.borisov at nginx.com>
date:      Thu Sep 02 19:32:27 2021 +0300
description:
Fixed async ctx erasing when a function is called multiple times.

The bug was introduced in 92d10cd761e2.

diffstat:

 src/njs_async.c                  |  46 ++++++++++++++++++++-------------------
 src/njs_function.c               |  12 ----------
 src/njs_value.h                  |   1 +
 src/njs_vmcode.c                 |  14 ++++++++++-
 test/js/async_await_many_call.js |  30 ++++++++++++++++++++++++++
 test/njs_expect_test.exp         |   3 ++
 6 files changed, 70 insertions(+), 36 deletions(-)

diffs (260 lines):

diff -r 4b018107386d -r 3a7ffe641a77 src/njs_async.c
--- a/src/njs_async.c	Wed Sep 01 21:25:10 2021 +0300
+++ b/src/njs_async.c	Thu Sep 02 19:32:27 2021 +0300
@@ -8,33 +8,33 @@
 
 
 static void
-njs_async_context_free(njs_vm_t *vm, njs_native_frame_t *frame);
+njs_async_context_free(njs_vm_t *vm, njs_async_ctx_t *ctx);
 
 
 njs_int_t
 njs_async_function_frame_invoke(njs_vm_t *vm, njs_value_t *retval)
 {
-    njs_int_t           ret;
-    njs_value_t         ctor;
-    njs_async_ctx_t     *ctx;
-    njs_native_frame_t  *frame;
+    njs_int_t                 ret;
+    njs_value_t               ctor;
+    njs_native_frame_t        *frame;
+    njs_promise_capability_t  *capability;
 
     frame = vm->top_frame;
     frame->retval = retval;
 
-    ctx = frame->function->context;
-
     njs_set_function(&ctor, &vm->constructors[NJS_OBJ_TYPE_PROMISE]);
 
-    ctx->capability = njs_promise_new_capability(vm, &ctor);
-    if (njs_slow_path(ctx->capability == NULL)) {
+    capability = njs_promise_new_capability(vm, &ctor);
+    if (njs_slow_path(capability == NULL)) {
         return NJS_ERROR;
     }
 
+    frame->function->context = capability;
+
     ret = njs_function_lambda_call(vm);
 
     if (ret == NJS_OK) {
-        ret = njs_function_call(vm, njs_function(&ctx->capability->resolve),
+        ret = njs_function_call(vm, njs_function(&capability->resolve),
                                 &njs_value_undefined, retval, 1, &vm->retval);
 
     } else if (ret == NJS_ERROR) {
@@ -42,12 +42,12 @@ njs_async_function_frame_invoke(njs_vm_t
             return NJS_ERROR;
         }
 
-        ret = njs_function_call(vm, njs_function(&ctx->capability->reject),
+        ret = njs_function_call(vm, njs_function(&capability->reject),
                                 &njs_value_undefined, &vm->retval, 1,
                                 &vm->retval);
     }
 
-    *retval = ctx->capability->promise;
+    *retval = capability->promise;
 
     return ret;
 }
@@ -60,6 +60,7 @@ njs_await_fulfilled(njs_vm_t *vm, njs_va
     njs_int_t           ret;
     njs_value_t         **cur_local, **cur_closures, **cur_temp, *value;
     njs_frame_t         *frame;
+    njs_function_t      *function;
     njs_async_ctx_t     *ctx;
     njs_native_frame_t  *top, *async;
 
@@ -71,6 +72,7 @@ njs_await_fulfilled(njs_vm_t *vm, njs_va
     }
 
     async = ctx->await;
+    function = async->function;
 
     cur_local = vm->levels[NJS_LEVEL_LOCAL];
     cur_closures = vm->levels[NJS_LEVEL_CLOSURE];
@@ -90,8 +92,14 @@ njs_await_fulfilled(njs_vm_t *vm, njs_va
 
     vm->top_frame->retval = &vm->retval;
 
+    function->context = ctx->capability;
+    function->await = ctx;
+
     ret = njs_vmcode_interpreter(vm, ctx->pc);
 
+    function->context = NULL;
+    function->await = NULL;
+
     vm->levels[NJS_LEVEL_LOCAL] = cur_local;
     vm->levels[NJS_LEVEL_CLOSURE] = cur_closures;
     vm->levels[NJS_LEVEL_TEMP] = cur_temp;
@@ -103,7 +111,7 @@ njs_await_fulfilled(njs_vm_t *vm, njs_va
         ret = njs_function_call(vm, njs_function(&ctx->capability->resolve),
                             &njs_value_undefined, &vm->retval, 1, &vm->retval);
 
-        njs_async_context_free(vm, vm->top_frame);
+        njs_async_context_free(vm, ctx);
 
     } else if (ret == NJS_ERROR) {
         if (njs_is_memory_error(vm, &vm->retval)) {
@@ -122,7 +130,7 @@ failed:
     (void) njs_function_call(vm, njs_function(&ctx->capability->reject),
                              &njs_value_undefined, value, 1, &vm->retval);
 
-    njs_async_context_free(vm, vm->top_frame);
+    njs_async_context_free(vm, ctx);
 
     return NJS_ERROR;
 }
@@ -143,7 +151,7 @@ njs_await_rejected(njs_vm_t *vm, njs_val
         (void) njs_function_call(vm, njs_function(&ctx->capability->reject),
                                  &njs_value_undefined, value, 1, &vm->retval);
 
-        njs_async_context_free(vm, vm->top_frame);
+        njs_async_context_free(vm, ctx);
 
         return NJS_ERROR;
     }
@@ -155,16 +163,10 @@ njs_await_rejected(njs_vm_t *vm, njs_val
 
 
 static void
-njs_async_context_free(njs_vm_t *vm, njs_native_frame_t *frame)
+njs_async_context_free(njs_vm_t *vm, njs_async_ctx_t *ctx)
 {
-    njs_async_ctx_t  *ctx;
-
-    ctx = frame->function->context;
-
     njs_mp_free(vm->mem_pool, ctx->capability);
     njs_mp_free(vm->mem_pool, ctx);
-
-    frame->function->context = NULL;
 }
 
 
diff -r 4b018107386d -r 3a7ffe641a77 src/njs_function.c
--- a/src/njs_function.c	Wed Sep 01 21:25:10 2021 +0300
+++ b/src/njs_function.c	Thu Sep 02 19:32:27 2021 +0300
@@ -427,7 +427,6 @@ njs_function_lambda_frame(njs_vm_t *vm, 
     njs_value_t            *value, *bound, **new, **temp;
     njs_frame_t            *frame;
     njs_function_t         *target;
-    njs_async_ctx_t        *ctx;
     njs_native_frame_t     *native_frame;
     njs_function_lambda_t  *lambda;
 
@@ -454,17 +453,6 @@ njs_function_lambda_frame(njs_vm_t *vm, 
         lambda = target->u.lambda;
     }
 
-    if (njs_function_object_type(vm, target) == NJS_OBJ_TYPE_ASYNC_FUNCTION) {
-        ctx = njs_mp_alloc(vm->mem_pool, sizeof(njs_async_ctx_t));
-        if (njs_slow_path(ctx == NULL)) {
-            njs_memory_error(vm);
-            return NJS_ERROR;
-        }
-
-        ctx->await = NULL;
-        target->context = ctx;
-    }
-
     args_count = function->args_offset + njs_max(nargs, lambda->nargs);
     value_count = args_count + njs_max(args_count, lambda->nlocal);
 
diff -r 4b018107386d -r 3a7ffe641a77 src/njs_value.h
--- a/src/njs_value.h	Wed Sep 01 21:25:10 2021 +0300
+++ b/src/njs_value.h	Thu Sep 02 19:32:27 2021 +0300
@@ -284,6 +284,7 @@ struct njs_function_s {
     } u;
 
     void                              *context;
+    void                              *await;
 
     njs_value_t                       *bound;
 };
diff -r 4b018107386d -r 3a7ffe641a77 src/njs_vmcode.c
--- a/src/njs_vmcode.c	Wed Sep 01 21:25:10 2021 +0300
+++ b/src/njs_vmcode.c	Thu Sep 02 19:32:27 2021 +0300
@@ -1827,7 +1827,6 @@ njs_vmcode_await(njs_vm_t *vm, njs_vmcod
     njs_native_frame_t  *active;
 
     active = &vm->active_frame->native;
-    ctx = active->function->context;
 
     value = njs_scope_valid_value(vm, await->retval);
     if (njs_slow_path(value == NULL)) {
@@ -1841,7 +1840,15 @@ njs_vmcode_await(njs_vm_t *vm, njs_vmcod
         return NJS_ERROR;
     }
 
-    if (ctx->await == NULL) {
+    ctx = active->function->await;
+
+    if (ctx == NULL) {
+        ctx = njs_mp_alloc(vm->mem_pool, sizeof(njs_async_ctx_t));
+        if (njs_slow_path(ctx == NULL)) {
+            njs_memory_error(vm);
+            return NJS_ERROR;
+        }
+
         size = njs_function_frame_size(active);
 
         fulfilled = njs_promise_create_function(vm, size);
@@ -1850,6 +1857,9 @@ njs_vmcode_await(njs_vm_t *vm, njs_vmcod
         }
 
         ctx->await = fulfilled->context;
+        ctx->capability = active->function->context;
+
+        active->function->context = NULL;
 
         ret = njs_function_frame_save(vm, ctx->await, NULL);
         if (njs_slow_path(ret != NJS_OK)) {
diff -r 4b018107386d -r 3a7ffe641a77 test/js/async_await_many_call.js
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/js/async_await_many_call.js	Thu Sep 02 19:32:27 2021 +0300
@@ -0,0 +1,30 @@
+async function test(name) {
+    let k1, k2;
+
+    switch (name) {
+        case "First":
+            k1 = await Promise.resolve("SUN");
+            k2 = await Promise.resolve("MOON");
+            break;
+
+        case "Second":
+            k1 = await Promise.resolve("CAT");
+            k2 = await Promise.resolve("MOUSE");
+            break;
+
+        case "Third":
+            k1 = await Promise.resolve("MAN");
+            k2 = await Promise.resolve("WOMAN");
+            break;
+
+        default:
+            break;
+    }
+
+    return `${name}: ${k1} ${k2}`;
+};
+
+Promise.all(['First', 'Second', 'Third'].map(v => test(v)))
+.then(results => {
+    console.log(results)
+})
diff -r 4b018107386d -r 3a7ffe641a77 test/njs_expect_test.exp
--- a/test/njs_expect_test.exp	Wed Sep 01 21:25:10 2021 +0300
+++ b/test/njs_expect_test.exp	Thu Sep 02 19:32:27 2021 +0300
@@ -1173,3 +1173,6 @@ end"
 
 njs_run {"./test/js/async_await_try_resolve.js"} \
 "key: resolve"
+
+njs_run {"./test/js/async_await_many_call.js"} \
+"\\\['First: SUN MOON','Second: CAT MOUSE','Third: MAN WOMAN']"


More information about the nginx-devel mailing list