[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