Skip to content

Instantly share code, notes, and snippets.

@xeioex
Created December 7, 2018 18:08
Show Gist options
  • Save xeioex/88e4361649fb4c8d82950cbe499c7a70 to your computer and use it in GitHub Desktop.
Save xeioex/88e4361649fb4c8d82950cbe499c7a70 to your computer and use it in GitHub Desktop.
# HG changeset patch
# User Dmitry Volyntsev <[email protected]>
# Date 1544204674 -10800
# Fri Dec 07 20:44:34 2018 +0300
# Node ID 6b84f91045f6c35810ff619c2434e8e7ffa9d3da
# Parent 0709c3d38212df011229fccb2e7a9e7f23263cce
Fixed handling of immediate posted events.
diff --git a/njs/njs.c b/njs/njs.c
--- a/njs/njs.c
+++ b/njs/njs.c
@@ -10,6 +10,8 @@
static nxt_int_t njs_vm_init(njs_vm_t *vm);
+static nxt_int_t njs_vm_invoke(njs_vm_t *vm, njs_function_t *function,
+ const njs_value_t *args, nxt_uint_t nargs, nxt_bool_t process_events);
static nxt_int_t njs_vm_handle_events(njs_vm_t *vm);
@@ -196,7 +198,7 @@ njs_vm_destroy(njs_vm_t *vm)
njs_event_t *event;
nxt_lvlhsh_each_t lhe;
- if (njs_is_pending_events(vm)) {
+ if (njs_pending_events(vm)) {
nxt_lvlhsh_each_init(&lhe, &njs_event_hash_proto);
for ( ;; ) {
@@ -457,13 +459,13 @@ njs_vm_init(njs_vm_t *vm)
}
-nxt_int_t
-njs_vm_call(njs_vm_t *vm, njs_function_t *function, const njs_value_t *args,
- nxt_uint_t nargs)
+static nxt_int_t
+njs_vm_invoke(njs_vm_t *vm, njs_function_t *function, const njs_value_t *args,
+ nxt_uint_t nargs, nxt_bool_t process_events)
{
u_char *current;
njs_ret_t ret;
- njs_value_t *this;
+ njs_value_t *this, retval;
static const njs_vmcode_stop_t stop[] = {
{ .code = { .operation = njs_vmcode_stop,
@@ -491,11 +493,32 @@ njs_vm_call(njs_vm_t *vm, njs_function_t
vm->current = current;
- if (ret == NJS_STOP) {
- ret = NXT_OK;
+ if (process_events && ret == NJS_STOP) {
+ retval = vm->retval;
+ ret = njs_vm_handle_events(vm);
+
+ if (ret != NJS_ERROR) {
+ vm->retval = retval;
+ }
}
- return ret;
+ switch (ret) {
+ case NJS_STOP:
+ case NXT_AGAIN:
+ return NJS_OK;
+
+ case NXT_ERROR:
+ default:
+ return ret;
+ }
+}
+
+
+nxt_int_t
+njs_vm_call(njs_vm_t *vm, njs_function_t *function, const njs_value_t *args,
+ nxt_uint_t nargs)
+{
+ return njs_vm_invoke(vm, function, args, nargs, 1);
}
@@ -540,7 +563,7 @@ njs_vm_del_event(njs_vm_t *vm, njs_vm_ev
nxt_int_t
njs_vm_pending(njs_vm_t *vm)
{
- return njs_is_pending_events(vm);
+ return njs_pending_events(vm) || njs_posted_events(vm);
}
@@ -575,7 +598,8 @@ njs_vm_post_event(njs_vm_t *vm, njs_vm_e
nxt_int_t
njs_vm_run(njs_vm_t *vm)
{
- nxt_int_t ret;
+ nxt_int_t ret;
+ njs_value_t retval;
if (nxt_slow_path(vm->backtrace != NULL)) {
nxt_array_reset(vm->backtrace);
@@ -584,14 +608,19 @@ njs_vm_run(njs_vm_t *vm)
ret = njs_vmcode_interpreter(vm);
if (ret == NJS_STOP) {
+ retval = vm->retval;
ret = njs_vm_handle_events(vm);
+
+ if (ret != NJS_ERROR) {
+ vm->retval = retval;
+ }
}
switch (ret) {
case NJS_STOP:
+ case NXT_AGAIN:
return NJS_OK;
- case NXT_AGAIN:
case NXT_ERROR:
default:
return ret;
@@ -626,14 +655,14 @@ njs_vm_handle_events(njs_vm_t *vm)
nxt_queue_remove(&ev->link);
}
- ret = njs_vm_call(vm, ev->function, ev->args, ev->nargs);
+ ret = njs_vm_invoke(vm, ev->function, ev->args, ev->nargs, 0);
if (ret == NJS_ERROR) {
return ret;
}
}
- return njs_is_pending_events(vm) ? NJS_AGAIN : NJS_STOP;
+ return njs_pending_events(vm) ? NJS_AGAIN : NJS_STOP;
}
diff --git a/njs/njs_event.h b/njs/njs_event.h
--- a/njs/njs_event.h
+++ b/njs/njs_event.h
@@ -12,7 +12,9 @@
#define NJS_EVENT_DELETE 2
-#define njs_is_pending_events(vm) (!nxt_lvlhsh_is_empty(&(vm)->events_hash))
+#define njs_pending_events(vm) (!nxt_lvlhsh_is_empty(&(vm)->events_hash))
+
+#define njs_posted_events(vm) (!nxt_queue_is_empty(&(vm)->posted_events))
typedef struct {
# HG changeset patch
# User Dmitry Volyntsev <[email protected]>
# Date 1544204675 -10800
# Fri Dec 07 20:44:35 2018 +0300
# Node ID 5aeb70f46d6a285394bb9b6c37e6693b063f3fe6
# Parent 6b84f91045f6c35810ff619c2434e8e7ffa9d3da
Added setImmediate().
In addition, it is possible now to use both setTimeout(fun, 0) and
setImmediate() without timers support from the host environment.
This closes #66 issue on Github.
diff --git a/njs/njs.c b/njs/njs.c
--- a/njs/njs.c
+++ b/njs/njs.c
@@ -524,7 +524,7 @@ njs_vm_call(njs_vm_t *vm, njs_function_t
njs_vm_event_t
njs_vm_add_event(njs_vm_t *vm, njs_function_t *function, nxt_uint_t once,
- njs_host_event_t host_ev, njs_event_destructor destructor)
+ njs_host_event_t host_ev, njs_event_destructor_t destructor)
{
njs_event_t *event;
diff --git a/njs/njs.h b/njs/njs.h
--- a/njs/njs.h
+++ b/njs/njs.h
@@ -125,15 +125,15 @@ typedef void * njs_
typedef void * njs_host_event_t;
typedef void * njs_external_ptr_t;
-typedef njs_host_event_t (*njs_set_timer)(njs_external_ptr_t external,
+typedef njs_host_event_t (*njs_set_timer_t)(njs_external_ptr_t external,
uint64_t delay, njs_vm_event_t vm_event);
-typedef void (*njs_event_destructor)(njs_external_ptr_t external,
+typedef void (*njs_event_destructor_t)(njs_external_ptr_t external,
njs_host_event_t event);
typedef struct {
- njs_set_timer set_timer;
- njs_event_destructor clear_timer;
+ njs_set_timer_t set_timer;
+ njs_event_destructor_t clear_timer;
} njs_vm_ops_t;
@@ -167,7 +167,7 @@ NXT_EXPORT nxt_int_t njs_vm_call(njs_vm_
NXT_EXPORT njs_vm_event_t njs_vm_add_event(njs_vm_t *vm,
njs_function_t *function, nxt_uint_t once, njs_host_event_t host_ev,
- njs_event_destructor destructor);
+ njs_event_destructor_t destructor);
NXT_EXPORT void njs_vm_del_event(njs_vm_t *vm, njs_vm_event_t vm_event);
NXT_EXPORT nxt_int_t njs_vm_pending(njs_vm_t *vm);
NXT_EXPORT nxt_int_t njs_vm_post_event(njs_vm_t *vm, njs_vm_event_t vm_event,
diff --git a/njs/njs_builtin.c b/njs/njs_builtin.c
--- a/njs/njs_builtin.c
+++ b/njs/njs_builtin.c
@@ -111,6 +111,7 @@ const njs_object_init_t *njs_function
&njs_decode_uri_component_function_init,
&njs_require_function_init,
&njs_set_timeout_function_init,
+ &njs_set_immediate_function_init,
&njs_clear_timeout_function_init,
NULL
};
@@ -132,6 +133,8 @@ const njs_function_init_t njs_native_fu
{ njs_module_require, { NJS_SKIP_ARG, NJS_STRING_ARG } },
{ njs_set_timeout,
{ NJS_SKIP_ARG, NJS_FUNCTION_ARG, NJS_NUMBER_ARG } },
+ { njs_set_immediate,
+ { NJS_SKIP_ARG, NJS_FUNCTION_ARG } },
{ njs_clear_timeout, { NJS_SKIP_ARG, NJS_NUMBER_ARG } },
};
diff --git a/njs/njs_event.h b/njs/njs_event.h
--- a/njs/njs_event.h
+++ b/njs/njs_event.h
@@ -18,17 +18,17 @@
typedef struct {
- njs_function_t *function;
- njs_value_t *args;
- nxt_uint_t nargs;
- njs_host_event_t host_event;
- njs_event_destructor destructor;
+ njs_function_t *function;
+ njs_value_t *args;
+ nxt_uint_t nargs;
+ njs_host_event_t host_event;
+ njs_event_destructor_t destructor;
- njs_value_t id;
- nxt_queue_link_t link;
+ njs_value_t id;
+ nxt_queue_link_t link;
- unsigned posted:1;
- unsigned once:1;
+ unsigned posted:1;
+ unsigned once:1;
} njs_event_t;
diff --git a/njs/njs_generator.c b/njs/njs_generator.c
--- a/njs/njs_generator.c
+++ b/njs/njs_generator.c
@@ -387,6 +387,7 @@ njs_generator(njs_vm_t *vm, njs_generato
case NJS_TOKEN_DECODE_URI_COMPONENT:
case NJS_TOKEN_REQUIRE:
case NJS_TOKEN_SET_TIMEOUT:
+ case NJS_TOKEN_SET_IMMEDIATE:
case NJS_TOKEN_CLEAR_TIMEOUT:
return njs_generate_builtin_object(vm, generator, node);
diff --git a/njs/njs_lexer_keyword.c b/njs/njs_lexer_keyword.c
--- a/njs/njs_lexer_keyword.c
+++ b/njs/njs_lexer_keyword.c
@@ -90,6 +90,7 @@ static const njs_keyword_t njs_keywords
{ nxt_string("decodeURIComponent"), NJS_TOKEN_DECODE_URI_COMPONENT, 0 },
{ nxt_string("require"), NJS_TOKEN_REQUIRE, 0 },
{ nxt_string("setTimeout"), NJS_TOKEN_SET_TIMEOUT, 0 },
+ { nxt_string("setImmediate"), NJS_TOKEN_SET_IMMEDIATE, 0 },
{ nxt_string("clearTimeout"), NJS_TOKEN_CLEAR_TIMEOUT, 0 },
/* Reserved words. */
diff --git a/njs/njs_parser.c b/njs/njs_parser.c
--- a/njs/njs_parser.c
+++ b/njs/njs_parser.c
@@ -2045,6 +2045,7 @@ njs_parser_terminal(njs_vm_t *vm, njs_pa
case NJS_TOKEN_DECODE_URI_COMPONENT:
case NJS_TOKEN_REQUIRE:
case NJS_TOKEN_SET_TIMEOUT:
+ case NJS_TOKEN_SET_IMMEDIATE:
case NJS_TOKEN_CLEAR_TIMEOUT:
return njs_parser_builtin_function(vm, parser, node);
diff --git a/njs/njs_parser.h b/njs/njs_parser.h
--- a/njs/njs_parser.h
+++ b/njs/njs_parser.h
@@ -198,6 +198,7 @@ typedef enum {
NJS_TOKEN_DECODE_URI_COMPONENT,
NJS_TOKEN_REQUIRE,
NJS_TOKEN_SET_TIMEOUT,
+ NJS_TOKEN_SET_IMMEDIATE,
NJS_TOKEN_CLEAR_TIMEOUT,
NJS_TOKEN_RESERVED,
diff --git a/njs/njs_time.c b/njs/njs_time.c
--- a/njs/njs_time.c
+++ b/njs/njs_time.c
@@ -10,10 +10,12 @@
#include <stdio.h>
-njs_ret_t
-njs_set_timeout(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
- njs_index_t unused)
+static njs_ret_t
+njs_set_timer(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
+ njs_index_t unused, nxt_bool_t immediate)
{
+ njs_ret_t ret;
+ nxt_uint_t n;
uint64_t delay;
njs_event_t *event;
njs_vm_ops_t *ops;
@@ -28,26 +30,29 @@ njs_set_timeout(njs_vm_t *vm, njs_value_
return NJS_ERROR;
}
+ delay = 0;
+
+ if (!immediate && nargs >= 3 && njs_is_number(&args[2])) {
+ delay = args[2].data.u.number;
+ }
+
ops = vm->options.ops;
- if (nxt_slow_path(ops == NULL)) {
+
+ if (nxt_slow_path(ops == NULL && delay != 0)) {
njs_internal_error(vm, "not supported by host environment");
return NJS_ERROR;
}
- delay = 0;
-
- if (nargs >= 3 && njs_is_number(&args[2])) {
- delay = args[2].data.u.number;
- }
-
event = nxt_mem_cache_alloc(vm->mem_cache_pool, sizeof(njs_event_t));
if (nxt_slow_path(event == NULL)) {
goto memory_error;
}
- event->destructor = ops->clear_timer;
+ n = immediate ? 2 : 3;
+
+ event->destructor = (ops != NULL) ? ops->clear_timer : NULL;
event->function = args[1].data.u.function;
- event->nargs = (nargs >= 3) ? nargs - 3 : 0;
+ event->nargs = (nargs >= n) ? nargs - n : 0;
event->once = 1;
event->posted = 0;
@@ -58,16 +63,28 @@ njs_set_timeout(njs_vm_t *vm, njs_value_
goto memory_error;
}
- memcpy(event->args, &args[3], sizeof(njs_value_t) * event->nargs);
+ memcpy(event->args, &args[n], sizeof(njs_value_t) * event->nargs);
}
- event->host_event = ops->set_timer(vm->external, delay, event);
- if (event->host_event == NULL) {
- njs_internal_error(vm, "set_timer() failed");
+ if (delay != 0) {
+ event->host_event = ops->set_timer(vm->external, delay, event);
+ if (nxt_slow_path(event->host_event == NULL)) {
+ njs_internal_error(vm, "set_timer() failed");
+ return NJS_ERROR;
+ }
+ }
+
+ ret = njs_add_event(vm, event);
+ if (nxt_slow_path(ret != NXT_OK)) {
return NJS_ERROR;
}
- return njs_add_event(vm, event);
+ if (delay == 0) {
+ event->posted = 1;
+ nxt_queue_insert_tail(&vm->posted_events, &event->link);
+ }
+
+ return NJS_OK;
memory_error:
@@ -78,6 +95,22 @@ memory_error:
njs_ret_t
+njs_set_timeout(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
+ njs_index_t unused)
+{
+ return njs_set_timer(vm, args, nargs, unused, 0);
+}
+
+
+njs_ret_t
+njs_set_immediate(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
+ njs_index_t unused)
+{
+ return njs_set_timer(vm, args, nargs, unused, 1);
+}
+
+
+njs_ret_t
njs_clear_timeout(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
njs_index_t unused)
{
@@ -116,6 +149,12 @@ const njs_object_init_t njs_set_timeout
0,
};
+const njs_object_init_t njs_set_immediate_function_init = {
+ nxt_string("setImmediate"),
+ NULL,
+ 0,
+};
+
const njs_object_init_t njs_clear_timeout_function_init = {
nxt_string("clearTimeout"),
diff --git a/njs/njs_time.h b/njs/njs_time.h
--- a/njs/njs_time.h
+++ b/njs/njs_time.h
@@ -10,11 +10,14 @@
njs_ret_t njs_set_timeout(njs_vm_t *vm, njs_value_t *args,
nxt_uint_t nargs, njs_index_t unused);
+njs_ret_t njs_set_immediate(njs_vm_t *vm, njs_value_t *args,
+ nxt_uint_t nargs, njs_index_t unused);
njs_ret_t njs_clear_timeout(njs_vm_t *vm, njs_value_t *args,
nxt_uint_t nargs, njs_index_t unused);
extern const njs_object_init_t njs_set_timeout_function_init;
+extern const njs_object_init_t njs_set_immediate_function_init;
extern const njs_object_init_t njs_clear_timeout_function_init;
#endif /* _NJS_TIMEOUT_H_INCLUDED_ */
diff --git a/njs/njs_vm.h b/njs/njs_vm.h
--- a/njs/njs_vm.h
+++ b/njs/njs_vm.h
@@ -930,6 +930,7 @@ enum njs_function_e {
NJS_FUNCTION_STRING_DECODE_URI_COMPONENT,
NJS_FUNCTION_REQUIRE,
NJS_FUNCTION_SET_TIMEOUT,
+ NJS_FUNCTION_SET_IMMEDIATE,
NJS_FUNCTION_CLEAR_TIMEOUT,
#define NJS_FUNCTION_MAX (NJS_FUNCTION_CLEAR_TIMEOUT + 1)
};
diff --git a/njs/test/njs_interactive_test.c b/njs/test/njs_interactive_test.c
--- a/njs/test/njs_interactive_test.c
+++ b/njs/test/njs_interactive_test.c
@@ -110,6 +110,50 @@ static njs_interactive_test_t njs_test[
"function(){}()" ENTER),
nxt_string("SyntaxError: Unexpected token \"(\" in 1") },
+ /* Immediate events */
+
+ { nxt_string("var a = 0; "
+ "setTimeout(function (x) {a = x}, 0, 'a'); a" ENTER),
+ nxt_string("0") },
+
+ { nxt_string("var a = 0; "
+ "setTimeout(function (x) {a = x}, 0, 'a')" ENTER
+ "a" ENTER),
+ nxt_string("a") },
+
+ { nxt_string("var a = 0; "
+ "setImmediate(function (x) {a = x}, 'a')" ENTER
+ "a" ENTER),
+ nxt_string("a") },
+
+ { nxt_string("var a = 0; "
+ "setTimeout(function (x) {"
+ " setTimeout(function (y) {a = y}, 0, x)"
+ "}, 0, 5); a" ENTER
+ "a" ENTER),
+ nxt_string("5") },
+
+ { nxt_string("var a = 0; "
+ "setImmediate(function (x) {"
+ " setImmediate(function (y) {a = y}, x)"
+ "}, 5); a" ENTER
+ "a" ENTER),
+ nxt_string("5") },
+
+ { nxt_string("var a = 0; "
+ "setTimeout(function (x) {a = x}, 0, 'a'); a" ENTER),
+ nxt_string("0") },
+
+ { nxt_string("var i = 0; "
+ "(function x() { if (i < 10) setImmediate(x); i++; })()" ENTER
+ "i" ENTER),
+ nxt_string("11") },
+
+ { nxt_string("var a = 0; "
+ "var t = setImmediate(function() {a = 1}); clearTimeout(t)" ENTER
+ "a" ENTER),
+ nxt_string("0") },
+
/* Backtraces */
{ nxt_string("function ff(o) {return o.a.a}" ENTER
diff --git a/njs/test/njs_unit_test.c b/njs/test/njs_unit_test.c
--- a/njs/test/njs_unit_test.c
+++ b/njs/test/njs_unit_test.c
@@ -10635,8 +10635,11 @@ static njs_unit_test_t njs_test[] =
{ nxt_string("setTimeout()"),
nxt_string("TypeError: too few arguments") },
- { nxt_string("setTimeout(function(){})"),
- nxt_string("InternalError: not supported by host environment") },
+ { nxt_string("var a = 0; setTimeout(function () {a = 1;}); a"),
+ nxt_string("0") },
+
+ { nxt_string("var a = 0; setTimeout(function () {setImmediate(function () {a = 1})}); a"),
+ nxt_string("0") },
{ nxt_string("setTimeout(function(){}, 12)"),
nxt_string("InternalError: not supported by host environment") },
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment