[njs] setTimeout() and clearTimeout() methods.
Dmitry Volyntsev
xeioex at nginx.com
Wed Mar 21 14:33:27 UTC 2018
details: http://hg.nginx.org/njs/rev/6d599ae5b35b
branches:
changeset: 463:6d599ae5b35b
user: Dmitry Volyntsev <xeioex at nginx.com>
date: Wed Mar 21 17:33:12 2018 +0300
description:
setTimeout() and clearTimeout() methods.
Public methods are introduced to create and post async events for
a VM instance. njs_vm_add_event() creates an async event for the VM
to wait for. njs_vm_post_event() notifies the VM that the event
occurred. If async events were added njs_vm_run() returns NJS_AGAIN
until there are no remaining pending events.
diffstat:
Makefile | 27 ++++
nginx/ngx_http_js_module.c | 249 ++++++++++++++++++++++++++++++++++++++-
nginx/ngx_stream_js_module.c | 38 +++++-
njs/njs_builtin.c | 8 +-
njs/njs_event.c | 121 +++++++++++++++++++
njs/njs_event.h | 39 ++++++
njs/njs_generator.c | 2 +
njs/njs_lexer_keyword.c | 2 +
njs/njs_parser.c | 2 +
njs/njs_parser.h | 2 +
njs/njs_time.c | 141 ++++++++++++++++++++++
njs/njs_time.h | 20 +++
njs/njs_vm.h | 14 +-
njs/njscript.c | 89 ++++++++++++++-
njs/njscript.h | 46 +++++++-
njs/test/njs_interactive_test.c | 5 +
njs/test/njs_unit_test.c | 19 +++
17 files changed, 804 insertions(+), 20 deletions(-)
diffs (truncated from 1264 to 1000 lines):
diff -r e8c08e05d18c -r 6d599ae5b35b Makefile
--- a/Makefile Thu Mar 15 15:15:25 2018 +0300
+++ b/Makefile Wed Mar 21 17:33:12 2018 +0300
@@ -20,7 +20,9 @@ NXT_BUILDDIR = build
$(NXT_BUILDDIR)/njs_date.o \
$(NXT_BUILDDIR)/njs_error.o \
$(NXT_BUILDDIR)/njs_math.o \
+ $(NXT_BUILDDIR)/njs_time.o \
$(NXT_BUILDDIR)/njs_module.o \
+ $(NXT_BUILDDIR)/njs_event.o \
$(NXT_BUILDDIR)/njs_fs.o \
$(NXT_BUILDDIR)/njs_extern.o \
$(NXT_BUILDDIR)/njs_variable.o \
@@ -56,7 +58,9 @@ NXT_BUILDDIR = build
$(NXT_BUILDDIR)/njs_date.o \
$(NXT_BUILDDIR)/njs_error.o \
$(NXT_BUILDDIR)/njs_math.o \
+ $(NXT_BUILDDIR)/njs_time.o \
$(NXT_BUILDDIR)/njs_module.o \
+ $(NXT_BUILDDIR)/njs_event.o \
$(NXT_BUILDDIR)/njs_fs.o \
$(NXT_BUILDDIR)/njs_extern.o \
$(NXT_BUILDDIR)/njs_variable.o \
@@ -301,6 +305,18 @@ dist:
-I$(NXT_LIB) -Injs \
njs/njs_math.c
+$(NXT_BUILDDIR)/njs_time.o: \
+ $(NXT_BUILDDIR)/libnxt.a \
+ njs/njscript.h \
+ njs/njs_vm.h \
+ njs/njs_object.h \
+ njs/njs_time.h \
+ njs/njs_time.c \
+
+ $(NXT_CC) -c -o $(NXT_BUILDDIR)/njs_time.o $(NXT_CFLAGS) \
+ -I$(NXT_LIB) -Injs \
+ njs/njs_time.c
+
$(NXT_BUILDDIR)/njs_module.o: \
$(NXT_BUILDDIR)/libnxt.a \
njs/njscript.h \
@@ -312,6 +328,17 @@ dist:
-I$(NXT_LIB) -Injs \
njs/njs_module.c
+$(NXT_BUILDDIR)/njs_event.o: \
+ $(NXT_BUILDDIR)/libnxt.a \
+ njs/njscript.h \
+ njs/njs_vm.h \
+ njs/njs_event.h \
+ njs/njs_event.c \
+
+ $(NXT_CC) -c -o $(NXT_BUILDDIR)/njs_event.o $(NXT_CFLAGS) \
+ -I$(NXT_LIB) -Injs \
+ njs/njs_event.c
+
$(NXT_BUILDDIR)/njs_fs.o: \
$(NXT_BUILDDIR)/libnxt.a \
njs/njscript.h \
diff -r e8c08e05d18c -r 6d599ae5b35b nginx/ngx_http_js_module.c
--- a/nginx/ngx_http_js_module.c Thu Mar 15 15:15:25 2018 +0300
+++ b/nginx/ngx_http_js_module.c Wed Mar 21 17:33:12 2018 +0300
@@ -29,6 +29,7 @@ typedef struct {
typedef struct {
njs_vm_t *vm;
+ ngx_log_t *log;
njs_opaque_value_t args[2];
} ngx_http_js_ctx_t;
@@ -39,10 +40,21 @@ typedef struct {
} ngx_http_js_table_entry_t;
-static ngx_int_t ngx_http_js_handler(ngx_http_request_t *r);
+typedef struct {
+ ngx_http_request_t *request;
+ njs_vm_event_t vm_event;
+ void *unused;
+ ngx_int_t ident;
+} ngx_http_js_event_t;
+
+
+static ngx_int_t ngx_http_js_content_handler(ngx_http_request_t *r);
+static void ngx_http_js_content_event_handler(ngx_http_request_t *r);
+static void ngx_http_js_content_write_event_handler(ngx_http_request_t *r);
static ngx_int_t ngx_http_js_variable(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
static ngx_int_t ngx_http_js_init_vm(ngx_http_request_t *r);
+static void ngx_http_js_cleanup_ctx(void *data);
static void ngx_http_js_cleanup_vm(void *data);
static njs_ret_t ngx_http_js_ext_get_string(njs_vm_t *vm, njs_value_t *value,
@@ -94,6 +106,14 @@ static njs_ret_t ngx_http_js_ext_next_ar
static njs_ret_t ngx_http_js_ext_get_variable(njs_vm_t *vm, njs_value_t *value,
void *obj, uintptr_t data);
+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 void ngx_http_js_handle_event(ngx_http_request_t *r,
+ njs_vm_event_t vm_event);
+
static char *ngx_http_js_include(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
static char *ngx_http_js_set(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
@@ -378,8 +398,33 @@ static njs_external_t ngx_http_js_exter
};
+static njs_vm_ops_t ngx_http_js_ops = {
+ ngx_http_js_set_timer,
+ ngx_http_js_clear_timer
+};
+
+
static ngx_int_t
-ngx_http_js_handler(ngx_http_request_t *r)
+ngx_http_js_content_handler(ngx_http_request_t *r)
+{
+ ngx_int_t rc;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http js content handler");
+
+ rc = ngx_http_read_client_request_body(r,
+ ngx_http_js_content_event_handler);
+
+ if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
+ return rc;
+ }
+
+ return NGX_DONE;
+}
+
+
+static void
+ngx_http_js_content_event_handler(ngx_http_request_t *r)
{
ngx_int_t rc;
nxt_str_t name, exception;
@@ -387,10 +432,14 @@ ngx_http_js_handler(ngx_http_request_t *
ngx_http_js_ctx_t *ctx;
ngx_http_js_loc_conf_t *jlcf;
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http js content event handler");
+
rc = ngx_http_js_init_vm(r);
if (rc == NGX_ERROR || rc == NGX_DECLINED) {
- return rc;
+ ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return;
}
jlcf = ngx_http_get_module_loc_conf(r, ngx_http_js_module);
@@ -407,7 +456,8 @@ ngx_http_js_handler(ngx_http_request_t *
if (func == NULL) {
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"js function \"%V\" not found", &jlcf->content);
- return NGX_DECLINED;
+ ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return;
}
if (njs_vm_call(ctx->vm, func, ctx->args, 2) != NJS_OK) {
@@ -416,10 +466,66 @@ ngx_http_js_handler(ngx_http_request_t *
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"js exception: %*s", exception.length, exception.start);
- return NGX_ERROR;
+ ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return;
+ }
+
+ if (njs_vm_pending(ctx->vm)) {
+ r->write_event_handler = ngx_http_js_content_write_event_handler;
+ return;
}
- return NGX_OK;
+ ngx_http_finalize_request(r, NGX_OK);
+}
+
+
+static void
+ngx_http_js_content_write_event_handler(ngx_http_request_t *r)
+{
+ ngx_event_t *wev;
+ ngx_connection_t *c;
+ ngx_http_js_ctx_t *ctx;
+ ngx_http_core_loc_conf_t *clcf;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http js content write event handler");
+
+ ctx = ngx_http_get_module_ctx(r, ngx_http_js_module);
+
+ if (!njs_vm_pending(ctx->vm)) {
+ ngx_http_finalize_request(r, NGX_OK);
+ return;
+ }
+
+ c = r->connection;
+ wev = c->write;
+
+ if (wev->timedout) {
+ ngx_connection_error(c, NGX_ETIMEDOUT, "client timed out");
+ ngx_http_finalize_request(r, NGX_HTTP_REQUEST_TIME_OUT);
+ return;
+ }
+
+ if (ngx_http_output_filter(r, NULL) == NGX_ERROR) {
+ ngx_http_finalize_request(r, NGX_ERROR);
+ return;
+ }
+
+ clcf = ngx_http_get_module_loc_conf(r->main, ngx_http_core_module);
+
+ if (ngx_handle_write_event(wev, clcf->send_lowat) != NGX_OK) {
+ ngx_http_finalize_request(r, NGX_ERROR);
+ return;
+ }
+
+ if (!wev->delayed) {
+ if (wev->active && !wev->ready) {
+ ngx_add_timer(wev, clcf->send_timeout);
+
+ } else if (wev->timer_set) {
+ ngx_del_timer(wev);
+ }
+ }
}
@@ -430,6 +536,7 @@ ngx_http_js_variable(ngx_http_request_t
ngx_str_t *fname = (ngx_str_t *) data;
ngx_int_t rc;
+ nxt_int_t pending;
nxt_str_t name, value, exception;
njs_function_t *func;
ngx_http_js_ctx_t *ctx;
@@ -461,6 +568,8 @@ ngx_http_js_variable(ngx_http_request_t
return NGX_OK;
}
+ pending = njs_vm_pending(ctx->vm);
+
if (njs_vm_call(ctx->vm, func, ctx->args, 2) != NJS_OK) {
njs_vm_retval_to_ext_string(ctx->vm, &exception);
@@ -475,6 +584,12 @@ ngx_http_js_variable(ngx_http_request_t
return NGX_ERROR;
}
+ if (!pending && njs_vm_pending(ctx->vm)) {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "async operation inside \"%V\" variable handler", fname);
+ return NGX_ERROR;
+ }
+
v->len = value.length;
v->valid = 1;
v->no_cacheable = 0;
@@ -489,6 +604,7 @@ static ngx_int_t
ngx_http_js_init_vm(ngx_http_request_t *r)
{
nxt_int_t rc;
+ nxt_str_t exception;
ngx_http_js_ctx_t *ctx;
ngx_pool_cleanup_t *cln;
ngx_http_js_loc_conf_t *jlcf;
@@ -523,10 +639,17 @@ ngx_http_js_init_vm(ngx_http_request_t *
return NGX_ERROR;
}
- cln->handler = ngx_http_js_cleanup_vm;
- cln->data = ctx->vm;
+ ctx->log = r->connection->log;
+
+ cln->handler = ngx_http_js_cleanup_ctx;
+ cln->data = ctx;
- if (njs_vm_run(ctx->vm) != NJS_OK) {
+ if (njs_vm_run(ctx->vm) == NJS_ERROR) {
+ njs_vm_retval_to_ext_string(ctx->vm, &exception);
+
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "js exception: %*s", exception.length, exception.start);
+
return NGX_ERROR;
}
@@ -545,6 +668,19 @@ ngx_http_js_init_vm(ngx_http_request_t *
static void
+ngx_http_js_cleanup_ctx(void *data)
+{
+ ngx_http_js_ctx_t *ctx = data;
+
+ if (njs_vm_pending(ctx->vm)) {
+ ngx_log_error(NGX_LOG_ERR, ctx->log, 0, "pending events");
+ }
+
+ njs_vm_destroy(ctx->vm);
+}
+
+
+static void
ngx_http_js_cleanup_vm(void *data)
{
njs_vm_t *vm = data;
@@ -1157,6 +1293,98 @@ ngx_http_js_ext_get_variable(njs_vm_t *v
}
+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_connection_t *c;
+ 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;
+
+ c = r->connection;
+
+ ngx_http_js_handle_event(r, js_event->vm_event);
+
+ ngx_http_run_posted_requests(c);
+}
+
+
+static void
+ngx_http_js_handle_event(ngx_http_request_t *r, njs_vm_event_t vm_event)
+{
+ njs_ret_t rc;
+ nxt_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);
+
+ rc = njs_vm_run(ctx->vm);
+
+ if (rc == NJS_ERROR) {
+ njs_vm_retval_to_ext_string(ctx->vm, &exception);
+
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "js exception: %*s", exception.length, exception.start);
+
+ ngx_http_finalize_request(r, NGX_ERROR);
+ }
+
+ if (rc == NJS_OK) {
+ ngx_http_post_request(r, NULL);
+ }
+}
+
+
static char *
ngx_http_js_include(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
@@ -1235,6 +1463,7 @@ ngx_http_js_include(ngx_conf_t *cf, ngx_
ngx_memzero(&options, sizeof(njs_vm_opt_t));
options.backtrace = 1;
+ options.ops = &ngx_http_js_ops;
jlcf->vm = njs_vm_create(&options);
if (jlcf->vm == NULL) {
@@ -1339,7 +1568,7 @@ ngx_http_js_content(ngx_conf_t *cf, ngx_
jlcf->content = value[1];
clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
- clcf->handler = ngx_http_js_handler;
+ clcf->handler = ngx_http_js_content_handler;
return NGX_CONF_OK;
}
diff -r e8c08e05d18c -r 6d599ae5b35b nginx/ngx_stream_js_module.c
--- a/nginx/ngx_stream_js_module.c Thu Mar 15 15:15:25 2018 +0300
+++ b/nginx/ngx_stream_js_module.c Wed Mar 21 17:33:12 2018 +0300
@@ -30,6 +30,7 @@ typedef struct {
typedef struct {
njs_vm_t *vm;
+ ngx_log_t *log;
njs_opaque_value_t arg;
ngx_buf_t *buf;
ngx_chain_t *free;
@@ -49,6 +50,7 @@ static ngx_int_t ngx_stream_js_body_filt
static ngx_int_t ngx_stream_js_variable(ngx_stream_session_t *s,
ngx_stream_variable_value_t *v, uintptr_t data);
static ngx_int_t ngx_stream_js_init_vm(ngx_stream_session_t *s);
+static void ngx_stream_js_cleanup_ctx(void *data);
static void ngx_stream_js_cleanup_vm(void *data);
static njs_ret_t ngx_stream_js_ext_get_remote_address(njs_vm_t *vm,
@@ -526,6 +528,7 @@ ngx_stream_js_variable(ngx_stream_sessio
ngx_str_t *fname = (ngx_str_t *) data;
ngx_int_t rc;
+ nxt_int_t pending;
nxt_str_t name, value, exception;
njs_function_t *func;
ngx_stream_js_ctx_t *ctx;
@@ -557,6 +560,8 @@ ngx_stream_js_variable(ngx_stream_sessio
return NGX_OK;
}
+ pending = njs_vm_pending(ctx->vm);
+
if (njs_vm_call(ctx->vm, func, &ctx->arg, 1) != NJS_OK) {
njs_vm_retval_to_ext_string(ctx->vm, &exception);
@@ -571,6 +576,12 @@ ngx_stream_js_variable(ngx_stream_sessio
return NGX_ERROR;
}
+ if (!pending && njs_vm_pending(ctx->vm)) {
+ ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
+ "async operation inside \"%V\" variable handler", fname);
+ return NGX_ERROR;
+ }
+
v->len = value.length;
v->valid = 1;
v->no_cacheable = 0;
@@ -585,6 +596,7 @@ static ngx_int_t
ngx_stream_js_init_vm(ngx_stream_session_t *s)
{
nxt_int_t rc;
+ nxt_str_t exception;
ngx_pool_cleanup_t *cln;
ngx_stream_js_ctx_t *ctx;
ngx_stream_js_srv_conf_t *jscf;
@@ -619,10 +631,17 @@ ngx_stream_js_init_vm(ngx_stream_session
return NGX_ERROR;
}
- cln->handler = ngx_stream_js_cleanup_vm;
- cln->data = ctx->vm;
+ ctx->log = s->connection->log;
+
+ cln->handler = ngx_stream_js_cleanup_ctx;
+ cln->data = ctx;
- if (njs_vm_run(ctx->vm) != NJS_OK) {
+ if (njs_vm_run(ctx->vm) == NJS_ERROR) {
+ njs_vm_retval_to_ext_string(ctx->vm, &exception);
+
+ ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
+ "js exception: %*s", exception.length, exception.start);
+
return NGX_ERROR;
}
@@ -636,6 +655,19 @@ ngx_stream_js_init_vm(ngx_stream_session
static void
+ngx_stream_js_cleanup_ctx(void *data)
+{
+ ngx_stream_js_ctx_t *ctx = data;
+
+ if (njs_vm_pending(ctx->vm)) {
+ ngx_log_error(NGX_LOG_ERR, ctx->log, 0, "pending events");
+ }
+
+ njs_vm_destroy(ctx->vm);
+}
+
+
+static void
ngx_stream_js_cleanup_vm(void *data)
{
njs_vm_t *vm = data;
diff -r e8c08e05d18c -r 6d599ae5b35b njs/njs_builtin.c
--- a/njs/njs_builtin.c Thu Mar 15 15:15:25 2018 +0300
+++ b/njs/njs_builtin.c Wed Mar 21 17:33:12 2018 +0300
@@ -30,6 +30,7 @@
#include <njs_date.h>
#include <njs_error.h>
#include <njs_math.h>
+#include <njs_time.h>
#include <njs_module.h>
#include <njs_fs.h>
#include <string.h>
@@ -113,7 +114,9 @@ const njs_object_init_t *njs_function
&njs_encode_uri_component_function_init,
&njs_decode_uri_function_init,
&njs_decode_uri_component_function_init,
- &njs_require_function_init
+ &njs_require_function_init,
+ &njs_set_timeout_function_init,
+ &njs_clear_timeout_function_init
};
@@ -131,6 +134,9 @@ const njs_function_init_t njs_native_fu
{ njs_string_decode_uri, { NJS_SKIP_ARG, NJS_STRING_ARG } },
{ njs_string_decode_uri_component, { NJS_SKIP_ARG, NJS_STRING_ARG } },
{ njs_module_require, { NJS_SKIP_ARG, NJS_STRING_ARG } },
+ { njs_set_timeout,
+ { NJS_SKIP_ARG, NJS_FUNCTION_ARG, NJS_NUMBER_ARG } },
+ { njs_clear_timeout, { NJS_SKIP_ARG, NJS_NUMBER_ARG } },
};
diff -r e8c08e05d18c -r 6d599ae5b35b njs/njs_event.c
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/njs/njs_event.c Wed Mar 21 17:33:12 2018 +0300
@@ -0,0 +1,121 @@
+
+/*
+ * Copyright (C) Dmitry Volyntsev
+ * Copyright (C) NGINX, Inc.
+ */
+
+#include <nxt_auto_config.h>
+#include <nxt_types.h>
+#include <nxt_clang.h>
+#include <nxt_string.h>
+#include <nxt_stub.h>
+#include <nxt_array.h>
+#include <nxt_djb_hash.h>
+#include <nxt_lvlhsh.h>
+#include <nxt_random.h>
+#include <nxt_mem_cache_pool.h>
+#include <njscript.h>
+#include <njs_vm.h>
+#include <njs_number.h>
+#include <njs_string.h>
+#include <njs_object.h>
+#include <njs_array.h>
+#include <njs_function.h>
+#include <njs_error.h>
+#include <njs_event.h>
+#include <njs_time.h>
+#include <string.h>
+#include <stdio.h>
+
+
+static nxt_int_t njs_event_hash_test(nxt_lvlhsh_query_t *lhq, void *data);
+
+
+const nxt_lvlhsh_proto_t njs_event_hash_proto
+ nxt_aligned(64) =
+{
+ NXT_LVLHSH_DEFAULT,
+ 0,
+ njs_event_hash_test,
+ njs_lvlhsh_alloc,
+ njs_lvlhsh_free,
+};
+
+
+static nxt_int_t
+njs_event_hash_test(nxt_lvlhsh_query_t *lhq, void *data)
+{
+ nxt_str_t id;
+ njs_event_t *event;
+
+ event = data;
+
+ njs_string_get(&event->id, &id);
+
+ if (nxt_strstr_eq(&lhq->key, &id)) {
+ return NXT_OK;
+ }
+
+ return NXT_DECLINED;
+}
+
+
+nxt_int_t
+njs_add_event(njs_vm_t *vm, njs_event_t *event)
+{
+ size_t size;
+ nxt_int_t ret;
+ nxt_lvlhsh_query_t lhq;
+
+ size = snprintf((char *) njs_string_short_start(&event->id),
+ NJS_STRING_SHORT, "%u", vm->event_id++);
+ njs_string_short_set(&event->id, size, size);
+
+ njs_string_get(&event->id, &lhq.key);
+ lhq.key_hash = nxt_djb_hash(lhq.key.start, lhq.key.length);
+ lhq.value = event;
+ lhq.proto = &njs_event_hash_proto;
+ lhq.pool = vm->mem_cache_pool;
+
+ ret = nxt_lvlhsh_insert(&vm->events_hash, &lhq);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ njs_exception_internal_error(vm, "Failed to add event with id: %s",
+ njs_string_short_start(&event->id));
+
+ njs_del_event(vm, event, NJS_EVENT_RELEASE | NJS_EVENT_DELETE);
+ return NJS_ERROR;
+ }
+
+ njs_value_number_set(&vm->retval, vm->event_id - 1);
+
+ return NJS_OK;
+}
+
+
+void
+njs_del_event(njs_vm_t *vm, njs_event_t *ev, nxt_uint_t action)
+{
+ nxt_lvlhsh_query_t lhq;
+
+ if (action & NJS_EVENT_RELEASE) {
+ if (ev->destructor != NULL && ev->host_event != NULL) {
+ ev->destructor(vm->external, ev->host_event);
+ }
+
+ ev->host_event = NULL;
+ }
+
+ if (action & NJS_EVENT_DELETE) {
+ njs_string_get(&ev->id, &lhq.key);
+ lhq.key_hash = nxt_djb_hash(lhq.key.start, lhq.key.length);
+ lhq.proto = &njs_event_hash_proto;
+ lhq.pool = vm->mem_cache_pool;
+
+ if (ev->posted) {
+ ev->posted = 0;
+ nxt_queue_remove(&ev->link);
+ }
+
+ (void) nxt_lvlhsh_delete(&vm->events_hash, &lhq);
+ }
+}
diff -r e8c08e05d18c -r 6d599ae5b35b njs/njs_event.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/njs/njs_event.h Wed Mar 21 17:33:12 2018 +0300
@@ -0,0 +1,39 @@
+
+/*
+ * Copyright (C) Dmitry Volyntsev
+ * Copyright (C) NGINX, Inc.
+ */
+
+#ifndef _NJS_EVENT_H_INCLUDED_
+#define _NJS_EVENT_H_INCLUDED_
+
+
+#define NJS_EVENT_RELEASE 1
+#define NJS_EVENT_DELETE 2
+
+
+#define njs_is_pending_events(vm) (!nxt_lvlhsh_is_empty(&(vm)->events_hash))
+
+
+typedef struct {
+ njs_function_t *function;
+ njs_opaque_value_t *args;
+ nxt_uint_t nargs;
+ njs_host_event_t host_event;
+ njs_event_destructor destructor;
+
+ njs_value_t id;
+ nxt_queue_link_t link;
+
+ unsigned posted:1;
+} njs_event_t;
+
+
+nxt_int_t njs_add_event(njs_vm_t *vm, njs_event_t *event);
+void njs_del_event(njs_vm_t *vm, njs_event_t *event, nxt_uint_t action);
+
+
+extern const nxt_lvlhsh_proto_t njs_event_hash_proto;
+
+
+#endif /* _NJS_EVENT_H_INCLUDED_ */
diff -r e8c08e05d18c -r 6d599ae5b35b njs/njs_generator.c
--- a/njs/njs_generator.c Thu Mar 15 15:15:25 2018 +0300
+++ b/njs/njs_generator.c Wed Mar 21 17:33:12 2018 +0300
@@ -320,6 +320,8 @@ njs_generator(njs_vm_t *vm, njs_parser_t
case NJS_TOKEN_DECODE_URI:
case NJS_TOKEN_DECODE_URI_COMPONENT:
case NJS_TOKEN_REQUIRE:
+ case NJS_TOKEN_SET_TIMEOUT:
+ case NJS_TOKEN_CLEAR_TIMEOUT:
return njs_generate_builtin_object(vm, parser, node);
case NJS_TOKEN_FUNCTION:
diff -r e8c08e05d18c -r 6d599ae5b35b njs/njs_lexer_keyword.c
--- a/njs/njs_lexer_keyword.c Thu Mar 15 15:15:25 2018 +0300
+++ b/njs/njs_lexer_keyword.c Wed Mar 21 17:33:12 2018 +0300
@@ -102,6 +102,8 @@ static const njs_keyword_t njs_keywords
{ nxt_string("decodeURI"), NJS_TOKEN_DECODE_URI, 0 },
{ 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("clearTimeout"), NJS_TOKEN_CLEAR_TIMEOUT, 0 },
/* Reserved words. */
diff -r e8c08e05d18c -r 6d599ae5b35b njs/njs_parser.c
--- a/njs/njs_parser.c Thu Mar 15 15:15:25 2018 +0300
+++ b/njs/njs_parser.c Wed Mar 21 17:33:12 2018 +0300
@@ -2072,6 +2072,8 @@ njs_parser_terminal(njs_vm_t *vm, njs_pa
case NJS_TOKEN_DECODE_URI:
case NJS_TOKEN_DECODE_URI_COMPONENT:
case NJS_TOKEN_REQUIRE:
+ case NJS_TOKEN_SET_TIMEOUT:
+ case NJS_TOKEN_CLEAR_TIMEOUT:
return njs_parser_builtin_function(vm, parser, node);
default:
diff -r e8c08e05d18c -r 6d599ae5b35b njs/njs_parser.h
--- a/njs/njs_parser.h Thu Mar 15 15:15:25 2018 +0300
+++ b/njs/njs_parser.h Wed Mar 21 17:33:12 2018 +0300
@@ -199,6 +199,8 @@ typedef enum {
NJS_TOKEN_DECODE_URI,
NJS_TOKEN_DECODE_URI_COMPONENT,
NJS_TOKEN_REQUIRE,
+ NJS_TOKEN_SET_TIMEOUT,
+ NJS_TOKEN_CLEAR_TIMEOUT,
NJS_TOKEN_RESERVED,
} njs_token_t;
diff -r e8c08e05d18c -r 6d599ae5b35b njs/njs_time.c
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/njs/njs_time.c Wed Mar 21 17:33:12 2018 +0300
@@ -0,0 +1,141 @@
+
+/*
+ * Copyright (C) Dmitry Volyntsev
+ * Copyright (C) NGINX, Inc.
+ */
+
+#include <nxt_auto_config.h>
+#include <nxt_types.h>
+#include <nxt_clang.h>
+#include <nxt_string.h>
+#include <nxt_stub.h>
+#include <nxt_array.h>
+#include <nxt_djb_hash.h>
+#include <nxt_lvlhsh.h>
+#include <nxt_random.h>
+#include <nxt_mem_cache_pool.h>
+#include <njscript.h>
+#include <njs_vm.h>
+#include <njs_number.h>
+#include <njs_string.h>
+#include <njs_object.h>
+#include <njs_array.h>
+#include <njs_function.h>
+#include <njs_error.h>
+#include <njs_event.h>
+#include <njs_time.h>
+#include <string.h>
+#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)
+{
+ uint64_t delay;
+ njs_event_t *event;
+ njs_vm_ops_t *ops;
+
+ if (nxt_slow_path(nargs < 2)) {
+ njs_exception_type_error(vm, "too few arguments", NULL);
+ return NJS_ERROR;
+ }
+
+ if (nxt_slow_path(!njs_is_function(&args[1]))) {
+ njs_exception_type_error(vm, "first arg must be a function", NULL);
+ return NJS_ERROR;
+ }
+
+ ops = vm->ops;
+ if (nxt_slow_path(ops == NULL)) {
+ njs_exception_internal_error(vm, "not supported by host environment",
+ NULL);
+ 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;
+ event->function = args[1].data.u.function;
+ event->nargs = (nargs >= 3) ? nargs - 3 : 0;
+ event->posted = 0;
+
+ if (event->nargs != 0) {
+ event->args = nxt_mem_cache_alloc(vm->mem_cache_pool,
+ sizeof(njs_value_t) * event->nargs);
+ if (nxt_slow_path(event->args == NULL)) {
+ goto memory_error;
+ }
+
+ memcpy(event->args, &args[3], sizeof(njs_value_t) * event->nargs);
+ }
+
+ event->host_event = ops->set_timer(vm->external, delay, event);
+ if (event->host_event == NULL) {
+ njs_exception_internal_error(vm, "set_timer() failed", NULL);
+ return NJS_ERROR;
+ }
+
+ return njs_add_event(vm, event);
+
+memory_error:
+
+ njs_exception_memory_error(vm);
+ return NJS_ERROR;
+}
+
+
+njs_ret_t
+njs_clear_timeout(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
+ njs_index_t unused)
+{
+ u_char buf[16];
+ njs_ret_t ret;
+ njs_event_t *event;
+ nxt_lvlhsh_query_t lhq;
+
+ if (nxt_fast_path(nargs < 2) || !njs_is_number(&args[1])) {
+ vm->retval = njs_string_void;
+ return NJS_OK;
+ }
+
+ lhq.key.start = buf;
+ lhq.key.length = snprintf((char *) buf, sizeof(buf) - 1, "%u",
+ (unsigned) args[1].data.u.number);
+ lhq.key_hash = nxt_djb_hash(lhq.key.start, lhq.key.length);
+ lhq.proto = &njs_event_hash_proto;
+ lhq.pool = vm->mem_cache_pool;
+
+ ret = nxt_lvlhsh_find(&vm->events_hash, &lhq);
+ if (ret == NXT_OK) {
+ event = lhq.value;
+ njs_del_event(vm, event, NJS_EVENT_RELEASE | NJS_EVENT_DELETE);
+ }
+
+ vm->retval = njs_string_void;
+
+ return NJS_OK;
+}
+
+
+const njs_object_init_t njs_set_timeout_function_init = {
+ nxt_string("setTimeout"),
+ NULL,
+ 0,
+};
+
+
+const njs_object_init_t njs_clear_timeout_function_init = {
+ nxt_string("clearTimeout"),
+ NULL,
+ 0,
+};
diff -r e8c08e05d18c -r 6d599ae5b35b njs/njs_time.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/njs/njs_time.h Wed Mar 21 17:33:12 2018 +0300
@@ -0,0 +1,20 @@
+
+/*
+ * Copyright (C) Dmitry Volyntsev
+ * Copyright (C) NGINX, Inc.
+ */
+
+#ifndef _NJS_TIMEOUT_H_INCLUDED_
+#define _NJS_TIMEOUT_H_INCLUDED_
+
+
+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_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_clear_timeout_function_init;
+
+#endif /* _NJS_TIMEOUT_H_INCLUDED_ */
diff -r e8c08e05d18c -r 6d599ae5b35b njs/njs_vm.h
--- a/njs/njs_vm.h Thu Mar 15 15:15:25 2018 +0300
+++ b/njs/njs_vm.h Wed Mar 21 17:33:12 2018 +0300
@@ -881,7 +881,9 @@ enum njs_function_e {
NJS_FUNCTION_STRING_DECODE_URI,
NJS_FUNCTION_STRING_DECODE_URI_COMPONENT,
NJS_FUNCTION_REQUIRE,
-#define NJS_FUNCTION_MAX (NJS_FUNCTION_REQUIRE + 1)
+ NJS_FUNCTION_SET_TIMEOUT,
+ NJS_FUNCTION_CLEAR_TIMEOUT,
+#define NJS_FUNCTION_MAX (NJS_FUNCTION_CLEAR_TIMEOUT + 1)
};
@@ -960,12 +962,12 @@ struct njs_vm_s {
njs_value_t *scopes[NJS_SCOPES];
- void *external;
+ njs_external_ptr_t external;
njs_native_frame_t *top_frame;
njs_frame_t *active_frame;
- nxt_array_t *external_objects; /* of void * */
+ nxt_array_t *external_objects; /* of njs_external_ptr_t */
nxt_lvlhsh_t externals_hash;
nxt_lvlhsh_t external_prototypes_hash;
@@ -974,6 +976,12 @@ struct njs_vm_s {
nxt_lvlhsh_t values_hash;
nxt_lvlhsh_t modules_hash;
+ uint32_t event_id;
+ nxt_lvlhsh_t events_hash;
+ nxt_queue_t posted_events;
+
+ njs_vm_ops_t *ops;
+
/*
* The prototypes and constructors arrays must be together because
* they are copied from njs_vm_shared_t by single memcpy()
diff -r e8c08e05d18c -r 6d599ae5b35b njs/njscript.c
--- a/njs/njscript.c Thu Mar 15 15:15:25 2018 +0300
+++ b/njs/njscript.c Wed Mar 21 17:33:12 2018 +0300
@@ -20,13 +20,17 @@
#include <njs_string.h>
#include <njs_object.h>
#include <njs_function.h>
+#include <njs_error.h>
#include <njs_variable.h>
More information about the nginx-devel
mailing list