]> git.kaiwu.me - njs.git/commitdiff
Moving out setTimeout() and clearTimeout() from njs core.
authorDmitry Volyntsev <xeioex@nginx.com>
Tue, 21 Nov 2023 17:00:52 +0000 (09:00 -0800)
committerDmitry Volyntsev <xeioex@nginx.com>
Tue, 21 Nov 2023 17:00:52 +0000 (09:00 -0800)
This functions are not part of the ECMAScript and should be
implemented by host environment.

auto/sources
external/njs_shell.c
nginx/ngx_http_js_module.c
nginx/ngx_js.c
nginx/ngx_js.h
nginx/ngx_stream_js_module.c
src/njs.h
src/njs_builtin.c
src/njs_main.h
src/test/njs_unit_test.c
test/shell_test.exp

index fa80724d562426c16d6afa8839879900d1c5be57..9f5d9d5ab99621ac8a45468a626ce9fb0d9e4ff1 100644 (file)
@@ -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 \
index 9633c16578577c6f7d73669c4406973ca8079ca5..eaabbb07b7a851af411bba699e8d6314a1ad8eba 100644 (file)
@@ -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(const char *text, int state);
 
 #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(njs_vm_t *vm, njs_value_t *args,
 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_console_t *console)
 {
     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);
 
@@ -648,6 +643,24 @@ njs_console_init(njs_vm_t *vm, njs_console_t *console)
 }
 
 
+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)
 {
@@ -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_value_t *value, njs_int_t ret)
 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 *runtime, const njs_str_t *script)
     }
 
     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 *runtime, const njs_str_t *script)
                 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, njs_value_t *args, njs_uint_t nargs,
 }
 
 
-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 = external;
-    vm = console->vm;
+    console = njs_vm_external_ptr(vm);
 
-    if (delay != 0) {
-        njs_vm_err(vm, "njs_console_set_timer(): async timers unsupported\n");
-        return NULL;
+    if (njs_slow_path(nargs < 2)) {
+        njs_vm_type_error(vm, "too few arguments");
+        return NJS_ERROR;
     }
 
-    ev = njs_mp_alloc(njs_vm_memory_pool(vm), sizeof(njs_ev_t));
-    if (njs_slow_path(ev == NULL)) {
-        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;
+    }
+
+    delay = 0;
+
+    if (!immediate && nargs >= 3
+        && njs_value_is_number(njs_argument(args, 2)))
+    {
+        delay = njs_value_number(njs_argument(args, 2));
     }
 
-    ev->vm_event = vm_event;
+    if (delay != 0) {
+        njs_vm_internal_error(vm, "njs_set_timer(): async timers unsupported");
+        return NJS_ERROR;
+    }
 
-    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);
+    n = immediate ? 2 : 3;
+    nargs = (nargs >= n) ? nargs - n : 0;
 
-    lhq.replace = 0;
-    lhq.value = ev;
-    lhq.proto = &lvlhsh_proto;
-    lhq.pool = njs_vm_memory_pool(vm);
+    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;
+    }
 
-    ret = njs_lvlhsh_insert(&console->events, &lhq);
-    if (njs_slow_path(ret != NJS_OK)) {
-        return NULL;
+    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++;
+
+    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 void
-njs_console_clear_timer(njs_external_ptr_t external, njs_host_event_t event)
+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)
 {
-    njs_vm_t            *vm;
-    njs_ev_t            *ev;
-    njs_int_t           ret;
-    njs_console_t       *console;
-    njs_lvlhsh_query_t  lhq;
+    return njs_set_timer(vm, args, nargs, unused, 0, retval);
+}
 
-    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);
+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);
+}
+
 
-    lhq.proto = &lvlhsh_proto;
-    lhq.pool = njs_vm_memory_pool(vm);
+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_ev_t           ev_lookup, *ev;
+    njs_console_t      *console;
+    njs_rbtree_node_t  *rb;
 
-    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_external_ptr_t external,
 }
 
 
-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;
-}
-
-
-static void *
-lvlhsh_pool_alloc(void *pool, size_t size)
-{
-    return njs_mp_align(pool, NJS_MAX_ALIGNMENT, size);
-}
-
+    if (ev1->id > ev2->id) {
+        return 1;
+    }
 
-static void
-lvlhsh_pool_free(void *pool, void *p, size_t size)
-{
-    njs_mp_free(pool, p);
+    return 0;
 }
index 5500cc04078f10c04ecadd21db076d77d7147da8..3d27f7fb22599887a8c8795a8bf02c9a8139ae6c 100644 (file)
@@ -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;
@@ -77,14 +77,6 @@ typedef struct {
 } ngx_http_js_cb_t;
 
 
-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)
@@ -264,11 +256,6 @@ static njs_int_t ngx_http_js_location(njs_vm_t *vm, ngx_http_request_t *r,
     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_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 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_periodic_session[] = {
 
 
 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(ngx_http_request_t *r)
 
     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_request_t *r)
     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_t *r, ngx_chain_t *in)
                 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_request_t *r, ngx_http_variable_value_t *v,
 
     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 *r, njs_int_t proto_id)
             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(ngx_http_request_t *r)
 
     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_request_t *r, ngx_int_t rc)
     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));
+                   ngx_vm_pending(ctx));
 
-    if (r->count > 1 || (rc == NGX_OK && njs_vm_pending(ctx->vm))) {
+    if (r->count > 1 || (rc == NGX_OK && ngx_vm_pending(ctx))) {
         return;
     }
 
@@ -4386,65 +4377,6 @@ ngx_http_js_periodic_init(ngx_js_periodic_t *periodic)
 }
 
 
-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_request_t *r, njs_vm_event_t vm_event,
 
         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_request_t *r, njs_vm_event_t vm_event,
 }
 
 
+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)
 {
index 4c0f8c29a666062c3a0104711491dd2db891401a..eb4094ca4df4547004fa5ca39522f498cf2ca316 100644 (file)
@@ -43,6 +43,12 @@ static njs_int_t ngx_js_ext_console_time(njs_vm_t *vm, njs_value_t *args,
     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 *fname, ngx_log_t *log,
     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 *fname, ngx_log_t *log,
         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;
 }
 
 
@@ -430,6 +443,78 @@ ngx_js_string(njs_vm_t *vm, njs_value_t *value, njs_str_t *str)
 }
 
 
+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)
 {
@@ -437,6 +522,10 @@ ngx_js_core_init(njs_vm_t *vm)
     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);
+
+    njs_value_number_set(retval, event->fd);
+
+    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 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)
+{
+    ngx_js_ctx_t       *ctx;
+    ngx_js_event_t      event_lookup, *event;
+    njs_rbtree_node_t  *rb;
+
+    if (nargs < 2 || !njs_value_is_number(njs_argument(args, 1))) {
+        njs_value_undefined_set(retval);
+        return NJS_OK;
+    }
+
+    ctx = ngx_external_ctx(vm, njs_vm_external_ptr(vm));
+    event_lookup.fd = njs_value_number(njs_argument(args, 1));
+
+    rb = njs_rbtree_find(&ctx->waiting_events, &event_lookup.node);
+    if (njs_slow_path(rb == NULL)) {
+        njs_vm_internal_error(vm, "failed to find timer");
+        return NJS_ERROR;
+    }
+
+    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);
+
+    njs_value_undefined_set(retval);
+
+    return NJS_OK;
+}
+
+
 void
 ngx_js_logger(njs_vm_t *vm, njs_external_ptr_t external, njs_log_level_t level,
     const u_char *start, size_t length)
index e7653ece9c63f3e507192d6b4cd94835325e94a5..418fccb162911e64805a77e2c7e3d159bf4d9ba7 100644 (file)
@@ -12,7 +12,9 @@
 
 #include <ngx_config.h>
 #include <ngx_core.h>
+#include <ngx_event.h>
 #include <njs.h>
+#include <njs_rbtree.h>
 #include "ngx_js_fetch.h"
 #include "ngx_js_shared_dict.h"
 
 #define ngx_js_buffer_type(btype) ((btype) & ~NGX_JS_DEPRECATED)
 
 
+typedef struct ngx_js_event_s ngx_js_event_t;
+typedef struct ngx_js_dict_s  ngx_js_dict_t;
+typedef struct ngx_js_ctx_s  ngx_js_ctx_t;
+
+
 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 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,
@@ -43,10 +51,10 @@ typedef ngx_flag_t (*ngx_external_flag_pt)(njs_vm_t *vm,
 typedef ngx_flag_t (*ngx_external_size_pt)(njs_vm_t *vm,
     njs_external_ptr_t e);
 typedef ngx_ssl_t *(*ngx_external_ssl_pt)(njs_vm_t *vm, njs_external_ptr_t e);
+typedef ngx_js_ctx_t *(*ngx_js_external_ctx_pt)(njs_vm_t *vm,
+    njs_external_ptr_t e);
 
 
-typedef struct ngx_js_dict_s  ngx_js_dict_t;
-
 typedef struct {
     ngx_str_t              name;
     ngx_str_t              path;
@@ -55,6 +63,19 @@ typedef struct {
 } ngx_js_named_path_t;
 
 
+struct ngx_js_event_s {
+    njs_vm_t            *vm;
+    njs_function_t      *function;
+    njs_value_t         *args;
+    ngx_socket_t         fd;
+    NJS_RBTREE_NODE     (node);
+    njs_uint_t           nargs;
+    void               (*destructor)(njs_external_ptr_t external,
+                                     ngx_js_event_t *event);
+    ngx_event_t          ev;
+};
+
+
 #define NGX_JS_COMMON_MAIN_CONF                                               \
     ngx_js_dict_t         *dicts;                                             \
     ngx_array_t           *periodics                                          \
@@ -89,6 +110,12 @@ typedef struct {
 #endif
 
 
+#define NGX_JS_COMMON_CTX                                                     \
+    njs_vm_t              *vm;                                                \
+    njs_rbtree_t           waiting_events;                                    \
+    ngx_socket_t           event_id
+
+
 typedef struct {
     NGX_JS_COMMON_MAIN_CONF;
 } ngx_js_main_conf_t;
@@ -99,6 +126,11 @@ typedef struct {
 } ngx_js_loc_conf_t;
 
 
+struct ngx_js_ctx_s {
+    NGX_JS_COMMON_CTX;
+};
+
+
 #define ngx_external_connection(vm, e)                                        \
     (*((ngx_connection_t **) ((u_char *) (e) + njs_vm_meta(vm, 0))))
 #define ngx_external_pool(vm, e)                                              \
@@ -122,6 +154,10 @@ typedef struct {
 #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)
 
 
 #define ngx_js_prop(vm, type, value, start, len)                              \
@@ -129,6 +165,12 @@ typedef struct {
                              : njs_vm_value_buffer_set(vm, value, start, len))
 
 
+#define ngx_vm_pending(ctx)                                                   \
+    (njs_vm_pending((ctx)->vm) || !njs_rbtree_is_empty(&(ctx)->waiting_events))
+
+
+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,
     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,
index e13f6f7fe6a37beab64d5640d3d7478e96eca7be..bb19f811ef3e45ecc386bdadc9e24fe3d767344d 100644 (file)
@@ -48,7 +48,7 @@ typedef struct {
 
 
 typedef struct {
-    njs_vm_t               *vm;
+    NGX_JS_COMMON_CTX;
     njs_opaque_value_t      retval;
     njs_opaque_value_t      args[3];
     ngx_buf_t              *buf;
@@ -67,14 +67,6 @@ typedef struct {
 } ngx_stream_js_ctx_t;
 
 
-typedef struct {
-    ngx_stream_session_t  *session;
-    njs_vm_event_t         vm_event;
-    void                  *unused;
-    ngx_int_t              ident;
-} ngx_stream_js_event_t;
-
-
 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,
@@ -121,11 +113,6 @@ static njs_int_t ngx_stream_js_periodic_variables(njs_vm_t *vm,
     njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval,
     njs_value_t *retval);
 
-static njs_host_event_t ngx_stream_js_set_timer(njs_external_ptr_t external,
-    uint64_t delay, njs_vm_event_t vm_event);
-static void ngx_stream_js_clear_timer(njs_external_ptr_t external,
-    njs_host_event_t event);
-static void ngx_stream_js_timer_handler(ngx_event_t *ev);
 static ngx_pool_t *ngx_stream_js_pool(njs_vm_t *vm, ngx_stream_session_t *s);
 static ngx_resolver_t *ngx_stream_js_resolver(njs_vm_t *vm,
     ngx_stream_session_t *s);
@@ -138,6 +125,8 @@ 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 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);
 static void ngx_stream_js_periodic_event_handler(ngx_event_t *ev);
@@ -616,8 +605,6 @@ static njs_external_t  ngx_stream_js_ext_session_flags[] = {
 
 
 static njs_vm_ops_t ngx_stream_js_ops = {
-    ngx_stream_js_set_timer,
-    ngx_stream_js_clear_timer,
     NULL,
     ngx_js_logger,
 };
@@ -635,6 +622,8 @@ static uintptr_t ngx_stream_js_uptr[] = {
     (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,
 };
 
 
@@ -762,7 +751,7 @@ ngx_stream_js_phase_handler(ngx_stream_session_t *s, ngx_str_t *name)
         return NGX_ERROR;
     }
 
-    if (njs_vm_pending(ctx->vm)) {
+    if (ngx_vm_pending(ctx)) {
         ctx->in_progress = 1;
         rc = ctx->events[NGX_JS_EVENT_UPLOAD].ev ? NGX_AGAIN : NGX_DONE;
 
@@ -933,7 +922,7 @@ ngx_stream_js_variable_set(ngx_stream_session_t *s,
 
     ctx = ngx_stream_get_module_ctx(s, ngx_stream_js_module);
 
-    pending = njs_vm_pending(ctx->vm);
+    pending = ngx_vm_pending(ctx);
 
     rc = ngx_js_invoke(ctx->vm, fname, s->connection->log, &ctx->args[0], 1,
                        &ctx->retval);
@@ -1016,6 +1005,8 @@ ngx_stream_js_init_vm(ngx_stream_session_t *s, njs_int_t proto_id)
             return NGX_ERROR;
         }
 
+        ngx_js_ctx_init((ngx_js_ctx_t *) ctx);
+
         njs_value_invalid_set(njs_value_arg(&ctx->retval));
 
         ngx_stream_set_ctx(s, ctx, ngx_stream_js_module);
@@ -1106,14 +1097,14 @@ ngx_stream_js_cleanup(void *data)
 
     ngx_stream_js_drop_events(ctx);
 
-    if (njs_vm_pending(ctx->vm)) {
+    if (ngx_vm_pending(ctx)) {
         ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, "pending events");
     }
 
     ngx_log_debug1(NGX_LOG_DEBUG_STREAM, s->connection->log, 0,
                    "stream js vm destroy: %p", ctx->vm);
 
-    njs_vm_destroy(ctx->vm);
+    ngx_js_ctx_destroy((ngx_js_ctx_t *) ctx);
 }
 
 
@@ -1672,65 +1663,6 @@ ngx_stream_js_periodic_variables(njs_vm_t *vm, njs_object_prop_t *prop,
 }
 
 
-static njs_host_event_t
-ngx_stream_js_set_timer(njs_external_ptr_t external, uint64_t delay,
-    njs_vm_event_t vm_event)
-{
-    ngx_event_t            *ev;
-    ngx_stream_session_t   *s;
-    ngx_stream_js_event_t  *js_event;
-
-    s = (ngx_stream_session_t *) external;
-
-    ev = ngx_pcalloc(s->connection->pool, sizeof(ngx_event_t));
-    if (ev == NULL) {
-        return NULL;
-    }
-
-    js_event = ngx_palloc(s->connection->pool, sizeof(ngx_stream_js_event_t));
-    if (js_event == NULL) {
-        return NULL;
-    }
-
-    js_event->session = s;
-    js_event->vm_event = vm_event;
-    js_event->ident = s->connection->fd;
-
-    ev->data = js_event;
-    ev->log = s->connection->log;
-    ev->handler = ngx_stream_js_timer_handler;
-
-    ngx_add_timer(ev, delay);
-
-    return ev;
-}
-
-
-static void
-ngx_stream_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_stream_js_timer_handler(ngx_event_t *ev)
-{
-    ngx_stream_session_t   *s;
-    ngx_stream_js_event_t  *js_event;
-
-    js_event = (ngx_stream_js_event_t *) ev->data;
-
-    s = js_event->session;
-
-    ngx_stream_js_handle_event(s, js_event->vm_event, NULL, 0);
-}
-
-
 static ngx_pool_t *
 ngx_stream_js_pool(njs_vm_t *vm, ngx_stream_session_t *s)
 {
@@ -1816,13 +1748,26 @@ ngx_stream_js_handle_event(ngx_stream_session_t *s, njs_vm_event_t vm_event,
 
         ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
                       "js exception: %V", &exception);
+    }
+
+    ngx_stream_js_event_finalize(s, rc);
+}
+
+
+static void
+ngx_stream_js_event_finalize(ngx_stream_session_t *s, njs_int_t rc)
+{
+    ngx_log_debug1(NGX_LOG_DEBUG_STREAM, s->connection->log, 0,
+                   "http js event finalize rc: %i", (ngx_int_t) rc);
 
+    if (rc == NJS_ERROR) {
         if (s->health_check) {
             ngx_stream_js_periodic_finalize(s, NGX_ERROR);
             return;
         }
 
         ngx_stream_finalize_session(s, NGX_STREAM_INTERNAL_SERVER_ERROR);
+        return;
     }
 
     if (rc == NJS_OK) {
@@ -1831,6 +1776,13 @@ ngx_stream_js_handle_event(ngx_stream_session_t *s, njs_vm_event_t vm_event,
 }
 
 
+static ngx_js_ctx_t *
+ngx_stream_js_ctx(njs_vm_t *vm, ngx_stream_session_t *s)
+{
+    return ngx_stream_get_module_ctx(s, ngx_stream_js_module);
+}
+
+
 static njs_int_t
 ngx_js_stream_init(njs_vm_t *vm)
 {
@@ -2022,7 +1974,7 @@ ngx_stream_js_periodic_event_handler(ngx_event_t *ev)
 
     ctx = ngx_stream_get_module_ctx(s, ngx_stream_js_module);
 
-    if (!njs_vm_pending(ctx->vm)) {
+    if (!ngx_vm_pending(ctx)) {
         ngx_stream_js_periodic_finalize(s, NGX_OK);
         return;
     }
@@ -2039,9 +1991,9 @@ ngx_stream_js_periodic_finalize(ngx_stream_session_t *s, ngx_int_t rc)
     ngx_log_debug4(NGX_LOG_DEBUG_STREAM, s->connection->log, 0,
                    "stream js periodic finalize: \"%V\" rc: %i c: %i "
                    "pending: %i", &ctx->periodic->method, rc, s->received,
-                   njs_vm_pending(ctx->vm));
+                   ngx_vm_pending(ctx));
 
-    if (s->received > 1 || (rc == NGX_OK && njs_vm_pending(ctx->vm))) {
+    if (s->received > 1 || (rc == NGX_OK && ngx_vm_pending(ctx))) {
         return;
     }
 
index a68c4fc6d4a16a7e8c890a1b49360f6fc7fc2d89..319924c81c80c8d5a783434b8fd6ecc497e7165e 100644 (file)
--- a/src/njs.h
+++ b/src/njs.h
@@ -240,8 +240,6 @@ typedef void (*njs_logger_t)(njs_vm_t *vm, njs_external_ptr_t external,
 
 
 typedef struct {
-    njs_set_timer_t                 set_timer;
-    njs_event_destructor_t          clear_timer;
     njs_module_loader_t             module_loader;
     njs_logger_t                    logger;
 } njs_vm_ops_t;
index 355e82cfdc8fd4b4d410520a1702e1b04b05136e..f0d7e72d7e021c954a57019247d6bc7362a99071 100644 (file)
@@ -1259,12 +1259,6 @@ static const njs_object_prop_t  njs_global_this_object_properties[] =
 
     NJS_DECLARE_PROP_NATIVE("eval", njs_eval_function, 1, 0),
 
-    NJS_DECLARE_PROP_NATIVE("setTimeout", njs_set_timeout, 2, 0),
-
-    NJS_DECLARE_PROP_NATIVE("setImmediate", njs_set_immediate, 4, 0),
-
-    NJS_DECLARE_PROP_NATIVE("clearTimeout", njs_clear_timeout, 1, 0),
-
     NJS_DECLARE_PROP_NATIVE("require", njs_module_require, 1, 0),
 
     /* Global objects. */
index 25d7d31e471db8619a543732a4e5647bfbef3997..dc764c684e1cd44be0dfd5acc29b6c5e101f3782 100644 (file)
@@ -81,7 +81,6 @@
 #include <njs_encoding.h>
 #include <njs_buffer.h>
 
-#include <njs_timer.h>
 #include <njs_module.h>
 
 #include <njs_event.h>
index 951f24d938bf335259e289788f5d1c555ef00b7b..e9ff3a6bd0e363ebf07d0cee7f7f6b1f0dbe8d6a 100644 (file)
@@ -18947,25 +18947,6 @@ static njs_unit_test_t  njs_test[] =
     { njs_str("require.hasOwnProperty('length')"),
       njs_str("true") },
 
-    /* setTimeout(). */
-
-    { njs_str("setTimeout()"),
-      njs_str("TypeError: too few arguments") },
-
-    { njs_str("setTimeout(function(){})"),
-      njs_str("InternalError: not supported by host environment") },
-
-    { njs_str("setTimeout(function(){}, 12)"),
-      njs_str("InternalError: not supported by host environment") },
-
-    /* clearTimeout(). */
-
-    { njs_str("clearTimeout()"),
-      njs_str("undefined") },
-
-    { njs_str("clearTimeout(123)"),
-      njs_str("undefined") },
-
     /* Trick: number to boolean. */
 
     { njs_str("var a = 0; !!a"),
@@ -23368,11 +23349,6 @@ static njs_unit_test_t  njs_backtraces_test[] =
               "    at require (native)\n"
               "    at main (:1)\n") },
 
-    { njs_str("setTimeout()"),
-      njs_str("TypeError: too few arguments\n"
-              "    at setTimeout (native)\n"
-              "    at main (:1)\n") },
-
     { njs_str("require('crypto').createHash('sha')"),
       njs_str("TypeError: not supported algorithm: \"sha\"\n"
               "    at crypto.createHash (native)\n"
index eee76c34e7431bb5a36bb94999492d63177ed917..a8c17dd2b52fc0c129f2d69e56a550c9cf3e168a 100644 (file)
@@ -420,7 +420,7 @@ njs_test {
 
 njs_test {
     {"setTimeout(function () {}, 1, 'a')\r\n"
-     "njs_console_set_timer(): async timers unsupported"}
+     "InternalError: njs_set_timer(): async timers unsupported"}
 }
 
 njs_test {