details: https://hg.nginx.org/njs/rev/ff7eb3c4bf76 branches: changeset: 2176:ff7eb3c4bf76 user: Dmitry Volyntsev <xei...@nginx.com> date: Mon Jul 03 13:32:41 2023 -0700 description: Modules: introduced js_shared_dict_zone directive.
The directive allows to declare a dictionary that is shared among the working processes. A dictionary expects strings as keys. It stores string or number as values. The value type is declared using type= argument of the directive. The default type is string. example.conf: # Declares a shared dictionary of strings of size 1 Mb that # removes key-value after 60 seconds of inactivity. js_shared_dict_zone zone=foo:1M timeout=60s; # Declares a shared dictionary of strings of size 512Kb that # forcibly remove oldest key-value pairs when memory is not enough. js_shared_dict_zone zone=bar:512K timeout=30s evict; # Declares a permanent number shared dictionary of size 32Kb. js_shared_dict_zone zone=num:32k type=number; example.js: function get(r) { r.return(200, ngx.shared.foo.get(r.args.key)); } function set(r) { r.return(200, ngx.shared.foo.set(r.args.key, r.args.value)); } function delete(r) { r.return(200, ngx.shared.bar.delete(r.args.key)); } function increment(r) { r.return(200, ngx.shared.num.incr(r.args.key, 2)); } In collaboration with Artem S. Povalyukhin, Jakub Jirutka and 洪志道 (Hong Zhi Dao). This closes #437 issue on Github. diffstat: nginx/config | 6 +- nginx/ngx_http_js_module.c | 48 +- nginx/ngx_js.c | 34 + nginx/ngx_js.h | 18 + nginx/ngx_js_shared_dict.c | 1586 +++++++++++++++++++++++++++++++++++++++ nginx/ngx_js_shared_dict.h | 19 + nginx/ngx_stream_js_module.c | 48 +- nginx/t/js_shared_dict.t | 299 +++++++ nginx/t/stream_js_shared_dict.t | 188 ++++ ts/ngx_core.d.ts | 150 +++ 10 files changed, 2390 insertions(+), 6 deletions(-) diffs (truncated from 2634 to 1000 lines): diff -r 167f75576f49 -r ff7eb3c4bf76 nginx/config --- a/nginx/config Mon Jul 03 12:49:00 2023 -0700 +++ b/nginx/config Mon Jul 03 13:32:41 2023 -0700 @@ -5,10 +5,12 @@ NJS_LIBXSLT=${NJS_LIBXSLT:-YES} NJS_ZLIB=${NJS_ZLIB:-YES} NJS_DEPS="$ngx_addon_dir/ngx_js.h \ - $ngx_addon_dir/ngx_js_fetch.h" + $ngx_addon_dir/ngx_js_fetch.h \ + $ngx_addon_dir/ngx_js_shared_dict.h" NJS_SRCS="$ngx_addon_dir/ngx_js.c \ $ngx_addon_dir/ngx_js_fetch.c \ - $ngx_addon_dir/ngx_js_regex.c" + $ngx_addon_dir/ngx_js_regex.c \ + $ngx_addon_dir/ngx_js_shared_dict.c" NJS_OPENSSL_LIB= NJS_XSLT_LIB= diff -r 167f75576f49 -r ff7eb3c4bf76 nginx/ngx_http_js_module.c --- a/nginx/ngx_http_js_module.c Mon Jul 03 12:49:00 2023 -0700 +++ b/nginx/ngx_http_js_module.c Mon Jul 03 13:32:41 2023 -0700 @@ -252,10 +252,13 @@ static char *ngx_http_js_set(ngx_conf_t static char *ngx_http_js_var(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static char *ngx_http_js_content(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +static char *ngx_http_js_shared_dict_zone(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); static char *ngx_http_js_body_filter_set(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static ngx_int_t ngx_http_js_init_conf_vm(ngx_conf_t *cf, ngx_js_loc_conf_t *conf); +static void *ngx_http_js_create_main_conf(ngx_conf_t *cf); static void *ngx_http_js_create_loc_conf(ngx_conf_t *cf); static char *ngx_http_js_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child); @@ -393,6 +396,13 @@ static ngx_command_t ngx_http_js_comman #endif + { ngx_string("js_shared_dict_zone"), + NGX_HTTP_MAIN_CONF|NGX_CONF_1MORE, + ngx_http_js_shared_dict_zone, + 0, + 0, + NULL }, + ngx_null_command }; @@ -401,7 +411,7 @@ static ngx_http_module_t ngx_http_js_mo NULL, /* preconfiguration */ ngx_http_js_init, /* postconfiguration */ - NULL, /* create main configuration */ + ngx_http_js_create_main_conf, /* create main configuration */ NULL, /* init main configuration */ NULL, /* create server configuration */ @@ -775,6 +785,7 @@ static uintptr_t ngx_http_js_uptr[] = { (uintptr_t) ngx_http_js_fetch_timeout, (uintptr_t) ngx_http_js_buffer_size, (uintptr_t) ngx_http_js_max_response_buffer_size, + (uintptr_t) 0 /* main_conf ptr */, }; @@ -798,6 +809,7 @@ njs_module_t *njs_http_js_addon_modules[ */ &ngx_js_ngx_module, &ngx_js_fetch_module, + &ngx_js_shared_dict_module, #ifdef NJS_HAVE_OPENSSL &njs_webcrypto_module, #endif @@ -4149,10 +4161,14 @@ ngx_js_http_init(njs_vm_t *vm) static ngx_int_t ngx_http_js_init_conf_vm(ngx_conf_t *cf, ngx_js_loc_conf_t *conf) { - njs_vm_opt_t options; + njs_vm_opt_t options; + ngx_js_main_conf_t *jmcf; njs_vm_opt_init(&options); + jmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_js_module); + ngx_http_js_uptr[NGX_JS_MAIN_CONF_INDEX] = (uintptr_t) jmcf; + options.backtrace = 1; options.unhandled_rejection = NJS_VM_OPT_UNHANDLED_REJECTION_THROW; options.ops = &ngx_http_js_ops; @@ -4293,6 +4309,14 @@ ngx_http_js_content(ngx_conf_t *cf, ngx_ static char * +ngx_http_js_shared_dict_zone(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf) +{ + return ngx_js_shared_dict_zone(cf, cmd, conf, &ngx_http_js_module); +} + + +static char * ngx_http_js_body_filter_set(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { ngx_http_js_loc_conf_t *jlcf = conf; @@ -4331,6 +4355,26 @@ ngx_http_js_body_filter_set(ngx_conf_t * static void * +ngx_http_js_create_main_conf(ngx_conf_t *cf) +{ + ngx_js_main_conf_t *jmcf; + + jmcf = ngx_pcalloc(cf->pool, sizeof(ngx_js_main_conf_t)); + if (jmcf == NULL) { + return NULL; + } + + /* + * set by ngx_pcalloc(): + * + * jmcf->dicts = NULL; + */ + + return jmcf; +} + + +static void * ngx_http_js_create_loc_conf(ngx_conf_t *cf) { ngx_http_js_loc_conf_t *conf = diff -r 167f75576f49 -r ff7eb3c4bf76 nginx/ngx_js.c --- a/nginx/ngx_js.c Mon Jul 03 12:49:00 2023 -0700 +++ b/nginx/ngx_js.c Mon Jul 03 13:32:41 2023 -0700 @@ -32,6 +32,28 @@ static void ngx_js_cleanup_vm(void *data static njs_int_t ngx_js_core_init(njs_vm_t *vm); +static njs_external_t ngx_js_ext_global_shared[] = { + + { + .flags = NJS_EXTERN_PROPERTY | NJS_EXTERN_SYMBOL, + .name.symbol = NJS_SYMBOL_TO_STRING_TAG, + .u.property = { + .value = "GlobalShared", + } + }, + + { + .flags = NJS_EXTERN_SELF, + .u.object = { + .enumerable = 1, + .prop_handler = njs_js_ext_global_shared_prop, + .keys = njs_js_ext_global_shared_keys, + } + }, + +}; + + static njs_external_t ngx_js_ext_core[] = { { @@ -113,6 +135,18 @@ static njs_external_t ngx_js_ext_core[] }, { + .flags = NJS_EXTERN_OBJECT, + .name.string = njs_str("shared"), + .enumerable = 1, + .writable = 1, + .u.object = { + .enumerable = 1, + .properties = ngx_js_ext_global_shared, + .nproperties = njs_nitems(ngx_js_ext_global_shared), + } + }, + + { .flags = NJS_EXTERN_PROPERTY, .name.string = njs_str("prefix"), .enumerable = 1, diff -r 167f75576f49 -r ff7eb3c4bf76 nginx/ngx_js.h --- a/nginx/ngx_js.h Mon Jul 03 12:49:00 2023 -0700 +++ b/nginx/ngx_js.h Mon Jul 03 13:32:41 2023 -0700 @@ -14,6 +14,7 @@ #include <ngx_core.h> #include <njs.h> #include "ngx_js_fetch.h" +#include "ngx_js_shared_dict.h" #define NGX_JS_UNSET 0 @@ -43,6 +44,9 @@ typedef ngx_flag_t (*ngx_external_size_p njs_external_ptr_t e); typedef ngx_ssl_t *(*ngx_external_ssl_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; @@ -51,6 +55,10 @@ typedef struct { } ngx_js_named_path_t; +#define NGX_JS_COMMON_MAIN_CONF \ + ngx_js_dict_t *dicts \ + + #define _NGX_JS_COMMON_LOC_CONF \ njs_vm_t *vm; \ ngx_array_t *imports; \ @@ -81,6 +89,11 @@ typedef struct { typedef struct { + NGX_JS_COMMON_MAIN_CONF; +} ngx_js_main_conf_t; + + +typedef struct { NGX_JS_COMMON_LOC_CONF; } ngx_js_loc_conf_t; @@ -105,6 +118,9 @@ typedef struct { ((ngx_external_size_pt) njs_vm_meta(vm, 8))(vm, e) #define ngx_external_max_response_buffer_size(vm, e) \ ((ngx_external_size_pt) njs_vm_meta(vm, 9))(vm, e) +#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_js_prop(vm, type, value, start, len) \ @@ -134,6 +150,8 @@ ngx_int_t ngx_js_init_conf_vm(ngx_conf_t ngx_js_loc_conf_t *ngx_js_create_conf(ngx_conf_t *cf, size_t size); char * ngx_js_merge_conf(ngx_conf_t *cf, void *parent, void *child, ngx_int_t (*init_vm)(ngx_conf_t *cf, ngx_js_loc_conf_t *conf)); +char *ngx_js_shared_dict_zone(ngx_conf_t *cf, ngx_command_t *cmd, void *conf, + void *tag); njs_int_t ngx_js_ext_string(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval); diff -r 167f75576f49 -r ff7eb3c4bf76 nginx/ngx_js_shared_dict.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nginx/ngx_js_shared_dict.c Mon Jul 03 13:32:41 2023 -0700 @@ -0,0 +1,1586 @@ + +/* + * Copyright (C) Dmitry Volyntsev + * Copyright (C) NGINX, Inc. + */ + + +#include <ngx_config.h> +#include <ngx_core.h> +#include "ngx_js.h" +#include "ngx_js_shared_dict.h" + + +typedef struct { + ngx_rbtree_t rbtree; + ngx_rbtree_node_t sentinel; + ngx_atomic_t rwlock; + + ngx_rbtree_t rbtree_expire; + ngx_rbtree_node_t sentinel_expire; +} ngx_js_dict_sh_t; + + +typedef struct { + ngx_str_node_t sn; + ngx_rbtree_node_t expire; + union { + ngx_str_t value; + double number; + } u; +} ngx_js_dict_node_t; + + +struct ngx_js_dict_s { + ngx_shm_zone_t *shm_zone; + ngx_js_dict_sh_t *sh; + ngx_slab_pool_t *shpool; + + ngx_msec_t timeout; + ngx_flag_t evict; +#define NGX_JS_DICT_TYPE_STRING 0 +#define NGX_JS_DICT_TYPE_NUMBER 1 + ngx_uint_t type; + + ngx_js_dict_t *next; +}; + + +static njs_int_t njs_js_ext_shared_dict_capacity(njs_vm_t *vm, + njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, + njs_value_t *retval); +static njs_int_t njs_js_ext_shared_dict_clear(njs_vm_t *vm, njs_value_t *args, + njs_uint_t nargs, njs_index_t flags, njs_value_t *retval); +static njs_int_t njs_js_ext_shared_dict_delete(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_js_ext_shared_dict_free_space(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_js_ext_shared_dict_get(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_js_ext_shared_dict_has(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_js_ext_shared_dict_keys(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_js_ext_shared_dict_incr(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_js_ext_shared_dict_name(njs_vm_t *vm, + njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, + njs_value_t *retval); +static njs_int_t njs_js_ext_shared_dict_pop(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_js_ext_shared_dict_set(njs_vm_t *vm, njs_value_t *args, + njs_uint_t nargs, njs_index_t flags, njs_value_t *retval); +static njs_int_t njs_js_ext_shared_dict_size(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_js_ext_shared_dict_type(njs_vm_t *vm, + njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, + njs_value_t *retval); +static ngx_js_dict_node_t *ngx_js_dict_lookup(ngx_js_dict_t *dict, + njs_str_t *key); + +#define NGX_JS_DICT_FLAG_MUST_EXIST 1 +#define NGX_JS_DICT_FLAG_MUST_NOT_EXIST 2 + +static ngx_int_t ngx_js_dict_set(njs_vm_t *vm, ngx_js_dict_t *dict, + njs_str_t *key, njs_value_t *value, unsigned flags); +static ngx_int_t ngx_js_dict_add(ngx_js_dict_t *dict, njs_str_t *key, + njs_value_t *value, ngx_msec_t now); +static ngx_int_t ngx_js_dict_update(ngx_js_dict_t *dict, + ngx_js_dict_node_t *node, njs_value_t *value, ngx_msec_t now); +static ngx_int_t ngx_js_dict_get(njs_vm_t *vm, ngx_js_dict_t *dict, + njs_str_t *key, njs_value_t *retval); +static ngx_int_t ngx_js_dict_incr(ngx_js_dict_t *dict, njs_str_t *key, + njs_value_t *delta, njs_value_t *init, double *value); +static ngx_int_t ngx_js_dict_delete(njs_vm_t *vm, ngx_js_dict_t *dict, + njs_str_t *key, njs_value_t *retval); +static ngx_int_t ngx_js_dict_copy_value_locked(njs_vm_t *vm, + ngx_js_dict_t *dict, ngx_js_dict_node_t *node, njs_value_t *retval); + +static void ngx_js_dict_expire(ngx_js_dict_t *dict, ngx_msec_t now); +static void ngx_js_dict_evict(ngx_js_dict_t *dict, ngx_int_t count); + +static njs_int_t ngx_js_dict_shared_error_name(njs_vm_t *vm, + njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, + njs_value_t *retval); + +static ngx_int_t ngx_js_dict_init_zone(ngx_shm_zone_t *shm_zone, void *data); +static njs_int_t ngx_js_shared_dict_preinit(njs_vm_t *vm); +static njs_int_t ngx_js_shared_dict_init(njs_vm_t *vm); + + +static njs_external_t ngx_js_ext_shared_dict[] = { + + { + .flags = NJS_EXTERN_PROPERTY | NJS_EXTERN_SYMBOL, + .name.symbol = NJS_SYMBOL_TO_STRING_TAG, + .u.property = { + .value = "SharedDict", + } + }, + + { + .flags = NJS_EXTERN_METHOD, + .name.string = njs_str("add"), + .writable = 1, + .configurable = 1, + .enumerable = 1, + .u.method = { + .native = njs_js_ext_shared_dict_set, + .magic8 = NGX_JS_DICT_FLAG_MUST_NOT_EXIST, + } + }, + + { + .flags = NJS_EXTERN_PROPERTY, + .name.string = njs_str("capacity"), + .enumerable = 1, + .u.property = { + .handler = njs_js_ext_shared_dict_capacity, + } + }, + + { + .flags = NJS_EXTERN_METHOD, + .name.string = njs_str("clear"), + .writable = 1, + .configurable = 1, + .enumerable = 1, + .u.method = { + .native = njs_js_ext_shared_dict_clear, + } + }, + + { + .flags = NJS_EXTERN_METHOD, + .name.string = njs_str("freeSpace"), + .writable = 1, + .configurable = 1, + .enumerable = 1, + .u.method = { + .native = njs_js_ext_shared_dict_free_space, + } + }, + + { + .flags = NJS_EXTERN_METHOD, + .name.string = njs_str("delete"), + .writable = 1, + .configurable = 1, + .enumerable = 1, + .u.method = { + .native = njs_js_ext_shared_dict_delete, + } + }, + + { + .flags = NJS_EXTERN_METHOD, + .name.string = njs_str("incr"), + .writable = 1, + .configurable = 1, + .enumerable = 1, + .u.method = { + .native = njs_js_ext_shared_dict_incr, + } + }, + + { + .flags = NJS_EXTERN_METHOD, + .name.string = njs_str("get"), + .writable = 1, + .configurable = 1, + .enumerable = 1, + .u.method = { + .native = njs_js_ext_shared_dict_get, + } + }, + + { + .flags = NJS_EXTERN_METHOD, + .name.string = njs_str("has"), + .writable = 1, + .configurable = 1, + .enumerable = 1, + .u.method = { + .native = njs_js_ext_shared_dict_has, + } + }, + + { + .flags = NJS_EXTERN_METHOD, + .name.string = njs_str("keys"), + .writable = 1, + .configurable = 1, + .enumerable = 1, + .u.method = { + .native = njs_js_ext_shared_dict_keys, + } + }, + + { + .flags = NJS_EXTERN_PROPERTY, + .name.string = njs_str("name"), + .enumerable = 1, + .u.property = { + .handler = njs_js_ext_shared_dict_name, + } + }, + + { + .flags = NJS_EXTERN_METHOD, + .name.string = njs_str("pop"), + .writable = 1, + .configurable = 1, + .enumerable = 1, + .u.method = { + .native = njs_js_ext_shared_dict_pop, + } + }, + + { + .flags = NJS_EXTERN_METHOD, + .name.string = njs_str("replace"), + .writable = 1, + .configurable = 1, + .enumerable = 1, + .u.method = { + .native = njs_js_ext_shared_dict_set, + .magic8 = NGX_JS_DICT_FLAG_MUST_EXIST, + } + }, + + { + .flags = NJS_EXTERN_METHOD, + .name.string = njs_str("set"), + .writable = 1, + .configurable = 1, + .enumerable = 1, + .u.method = { + .native = njs_js_ext_shared_dict_set, + } + }, + + { + .flags = NJS_EXTERN_METHOD, + .name.string = njs_str("size"), + .writable = 1, + .configurable = 1, + .enumerable = 1, + .u.method = { + .native = njs_js_ext_shared_dict_size, + } + }, + + { + .flags = NJS_EXTERN_PROPERTY, + .name.string = njs_str("type"), + .enumerable = 1, + .u.property = { + .handler = njs_js_ext_shared_dict_type, + } + }, + +}; + + +static njs_external_t ngx_js_ext_error_ctor_props[] = { + + { + .flags = NJS_EXTERN_PROPERTY, + .name.string = njs_str("name"), + .enumerable = 1, + .u.property = { + .handler = ngx_js_dict_shared_error_name, + } + }, + + { + .flags = NJS_EXTERN_PROPERTY, + .name.string = njs_str("prototype"), + .enumerable = 1, + .u.property = { + .handler = njs_object_prototype_create, + } + }, + +}; + + +static njs_external_t ngx_js_ext_error_proto_props[] = { + + { + .flags = NJS_EXTERN_PROPERTY, + .name.string = njs_str("name"), + .enumerable = 1, + .u.property = { + .handler = ngx_js_dict_shared_error_name, + } + }, + + { + .flags = NJS_EXTERN_PROPERTY, + .name.string = njs_str("prototype"), + .enumerable = 1, + .u.property = { + .handler = njs_object_prototype_create, + } + }, + +}; + + +static njs_int_t ngx_js_shared_dict_proto_id; +static njs_int_t ngx_js_shared_dict_error_id; + + +njs_module_t ngx_js_shared_dict_module = { + .name = njs_str("shared_dict"), + .preinit = ngx_js_shared_dict_preinit, + .init = ngx_js_shared_dict_init, +}; + + +njs_int_t +njs_js_ext_global_shared_prop(njs_vm_t *vm, njs_object_prop_t *prop, + njs_value_t *value, njs_value_t *setval, njs_value_t *retval) +{ + njs_int_t ret; + njs_str_t name; + ngx_js_dict_t *dict; + ngx_shm_zone_t *shm_zone; + ngx_js_main_conf_t *conf; + + ret = njs_vm_prop_name(vm, prop, &name); + if (ret != NJS_OK) { + return NJS_ERROR; + } + + conf = ngx_main_conf(vm); + + for (dict = conf->dicts; dict != NULL; dict = dict->next) { + shm_zone = dict->shm_zone; + + if (shm_zone->shm.name.len == name.length + && ngx_strncmp(shm_zone->shm.name.data, name.start, + name.length) + == 0) + { + ret = njs_vm_external_create(vm, retval, + ngx_js_shared_dict_proto_id, + shm_zone, 0); + if (ret != NJS_OK) { + njs_vm_internal_error(vm, "sharedDict creation failed"); + return NJS_ERROR; + } + + return NJS_OK; + } + } + + njs_value_null_set(retval); + + return NJS_DECLINED; +} + + +njs_int_t +njs_js_ext_global_shared_keys(njs_vm_t *vm, njs_value_t *unused, + njs_value_t *keys) +{ + njs_int_t rc; + njs_value_t *value; + ngx_js_dict_t *dict; + ngx_shm_zone_t *shm_zone; + ngx_js_main_conf_t *conf; + + conf = ngx_main_conf(vm); + + rc = njs_vm_array_alloc(vm, keys, 4); + if (rc != NJS_OK) { + return NJS_ERROR; + } + + for (dict = conf->dicts; dict != NULL; dict = dict->next) { + shm_zone = dict->shm_zone; + + value = njs_vm_array_push(vm, keys); + if (value == NULL) { + return NJS_ERROR; + } + + rc = njs_vm_value_string_set(vm, value, shm_zone->shm.name.data, + shm_zone->shm.name.len); + if (rc != NJS_OK) { + return NJS_ERROR; + } + } + + return NJS_OK; +} + + +static njs_int_t +njs_js_ext_shared_dict_capacity(njs_vm_t *vm, njs_object_prop_t *prop, + njs_value_t *value, njs_value_t *setval, njs_value_t *retval) +{ + ngx_shm_zone_t *shm_zone; + + shm_zone = njs_vm_external(vm, ngx_js_shared_dict_proto_id, value); + if (shm_zone == NULL) { + njs_value_undefined_set(retval); + return NJS_DECLINED; + } + + njs_value_number_set(retval, shm_zone->shm.size); + + return NJS_OK; +} + + +static njs_int_t +njs_js_ext_shared_dict_clear(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, + njs_index_t unused, njs_value_t *retval) +{ + ngx_js_dict_t *dict; + ngx_shm_zone_t *shm_zone; + + shm_zone = njs_vm_external(vm, ngx_js_shared_dict_proto_id, + njs_argument(args, 0)); + if (shm_zone == NULL) { + njs_vm_type_error(vm, "\"this\" is not a shared dict"); + return NJS_ERROR; + } + + dict = shm_zone->data; + + ngx_rwlock_wlock(&dict->sh->rwlock); + + ngx_js_dict_evict(dict, 0x7fffffff /* INT_MAX */); + + ngx_rwlock_unlock(&dict->sh->rwlock); + + njs_value_undefined_set(retval); + + return NJS_OK; +} + + +static njs_int_t +njs_js_ext_shared_dict_free_space(njs_vm_t *vm, njs_value_t *args, + njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) +{ + size_t bytes; + ngx_js_dict_t *dict; + ngx_shm_zone_t *shm_zone; + + shm_zone = njs_vm_external(vm, ngx_js_shared_dict_proto_id, + njs_argument(args, 0)); + if (shm_zone == NULL) { + njs_vm_type_error(vm, "\"this\" is not a shared dict"); + return NJS_ERROR; + } + + dict = shm_zone->data; + + ngx_rwlock_rlock(&dict->sh->rwlock); + bytes = dict->shpool->pfree * ngx_pagesize; + ngx_rwlock_unlock(&dict->sh->rwlock); + + njs_value_number_set(retval, bytes); + + return NJS_OK; +} + + +static njs_int_t +njs_js_ext_shared_dict_delete(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, + njs_index_t unused, njs_value_t *retval) +{ + ngx_int_t rc; + njs_str_t key; + ngx_shm_zone_t *shm_zone; + + shm_zone = njs_vm_external(vm, ngx_js_shared_dict_proto_id, + njs_argument(args, 0)); + if (shm_zone == NULL) { + njs_vm_type_error(vm, "\"this\" is not a shared dict"); + return NJS_ERROR; + } + + if (ngx_js_string(vm, njs_arg(args, nargs, 1), &key) != NGX_OK) { + return NJS_ERROR; + } + + rc = ngx_js_dict_delete(vm, shm_zone->data, &key, NULL); + + njs_value_boolean_set(retval, rc == NGX_OK); + + return NJS_OK; +} + + +static njs_int_t +njs_js_ext_shared_dict_get(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, + njs_index_t unused, njs_value_t *retval) +{ + ngx_int_t rc; + njs_str_t key; + ngx_shm_zone_t *shm_zone; + + shm_zone = njs_vm_external(vm, ngx_js_shared_dict_proto_id, + njs_argument(args, 0)); + if (shm_zone == NULL) { + njs_vm_type_error(vm, "\"this\" is not a shared dict"); + return NJS_ERROR; + } + + if (ngx_js_string(vm, njs_arg(args, nargs, 1), &key) != NGX_OK) { + return NJS_ERROR; + } + + rc = ngx_js_dict_get(vm, shm_zone->data, &key, retval); + if (njs_slow_path(rc == NGX_ERROR)) { + njs_vm_error(vm, "failed to get value from shared dict"); + return NJS_ERROR; + } + + return NJS_OK; +} + + +static njs_int_t +njs_js_ext_shared_dict_has(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, + njs_index_t unused, njs_value_t *retval) +{ + njs_str_t key; + ngx_msec_t now; + ngx_time_t *tp; + ngx_js_dict_t *dict; + ngx_shm_zone_t *shm_zone; + ngx_js_dict_node_t *node; + + shm_zone = njs_vm_external(vm, ngx_js_shared_dict_proto_id, + njs_argument(args, 0)); + if (shm_zone == NULL) { + njs_vm_type_error(vm, "\"this\" is not a shared dict"); + return NJS_ERROR; + } + + if (ngx_js_string(vm, njs_arg(args, nargs, 1), &key) != NGX_OK) { + return NJS_ERROR; + } + + dict = shm_zone->data; + + ngx_rwlock_rlock(&dict->sh->rwlock); + + node = ngx_js_dict_lookup(dict, &key); + + if (node != NULL && dict->timeout) { + tp = ngx_timeofday(); + now = tp->sec * 1000 + tp->msec; + + if (now >= node->expire.key) { + node = NULL; + } + } + + ngx_rwlock_unlock(&dict->sh->rwlock); + + njs_value_boolean_set(retval, node != NULL); + + return NJS_OK; +} + + +static njs_int_t +njs_js_ext_shared_dict_keys(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, + njs_index_t unused, njs_value_t *retval) +{ + njs_int_t rc; + ngx_int_t max_count; + njs_value_t *value; + ngx_rbtree_t *rbtree; + ngx_js_dict_t *dict; + ngx_shm_zone_t *shm_zone; + ngx_rbtree_node_t *rn; + ngx_js_dict_node_t *node; + + shm_zone = njs_vm_external(vm, ngx_js_shared_dict_proto_id, + njs_argument(args, 0)); + if (shm_zone == NULL) { + njs_vm_type_error(vm, "\"this\" is not a shared dict"); + return NJS_ERROR; + } + + dict = shm_zone->data; + + max_count = 1024; + + if (nargs > 1) { + if (ngx_js_integer(vm, njs_arg(args, nargs, 1), &max_count) != NGX_OK) { + return NJS_ERROR; + } + } + + rc = njs_vm_array_alloc(vm, retval, 8); + if (rc != NJS_OK) { + return NJS_ERROR; + } + + ngx_rwlock_rlock(&dict->sh->rwlock); + + rbtree = &dict->sh->rbtree; + + if (rbtree->root == rbtree->sentinel) { + goto done; + } + + for (rn = ngx_rbtree_min(rbtree->root, rbtree->sentinel); + rn != NULL; + rn = ngx_rbtree_next(rbtree, rn)) + { + if (max_count-- == 0) { + break; + } + + node = (ngx_js_dict_node_t *) rn; + + value = njs_vm_array_push(vm, retval); + if (value == NULL) { + goto fail; + } + + rc = njs_vm_value_string_set(vm, value, node->sn.str.data, + node->sn.str.len); + if (rc != NJS_OK) { + goto fail; + } + } + +done: + + ngx_rwlock_unlock(&dict->sh->rwlock); + + return NJS_OK; + +fail: + + ngx_rwlock_unlock(&dict->sh->rwlock); + + return NJS_ERROR; +} + + +static njs_int_t +njs_js_ext_shared_dict_incr(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, + njs_index_t unused, njs_value_t *retval) +{ + double value; + ngx_int_t rc; + njs_str_t key; + njs_value_t *delta, *init; + ngx_js_dict_t *dict; + ngx_shm_zone_t *shm_zone; + njs_opaque_value_t lvalue; + + shm_zone = njs_vm_external(vm, ngx_js_shared_dict_proto_id, + njs_argument(args, 0)); + if (shm_zone == NULL) { + njs_vm_type_error(vm, "\"this\" is not a shared dict"); + return NJS_ERROR; + } + + dict = shm_zone->data; + + if (dict->type != NGX_JS_DICT_TYPE_NUMBER) { + njs_vm_type_error(vm, "shared dict is not a number dict"); + return NJS_ERROR; + } + + if (ngx_js_string(vm, njs_arg(args, nargs, 1), &key) != NGX_OK) { + return NJS_ERROR; + } + + delta = njs_arg(args, nargs, 2); + if (!njs_value_is_number(delta)) { + njs_vm_type_error(vm, "delta is not a number"); + return NJS_ERROR; + } + + init = njs_lvalue_arg(njs_value_arg(&lvalue), args, nargs, 3); + if (!njs_value_is_number(init) && !njs_value_is_undefined(init)) { + njs_vm_type_error(vm, "init value is not a number"); + return NJS_ERROR; + } + + if (njs_value_is_undefined(init)) { + njs_value_number_set(init, 0); + } + + rc = ngx_js_dict_incr(shm_zone->data, &key, delta, init, &value); + if (rc == NGX_ERROR) { + njs_vm_error(vm, "failed to increment value in shared dict"); + return NJS_ERROR; + } + + njs_value_number_set(retval, value); + + return NJS_OK; +} + + +static njs_int_t +njs_js_ext_shared_dict_name(njs_vm_t *vm, njs_object_prop_t *prop, + njs_value_t *value, njs_value_t *setval, njs_value_t *retval) +{ + ngx_shm_zone_t *shm_zone; + + shm_zone = njs_vm_external(vm, ngx_js_shared_dict_proto_id, value); + if (shm_zone == NULL) { + njs_value_undefined_set(retval); + return NJS_DECLINED; + } + + return njs_vm_value_string_set(vm, retval, shm_zone->shm.name.data, + shm_zone->shm.name.len); +} + + +static njs_int_t _______________________________________________ nginx-devel mailing list nginx-devel@nginx.org https://mailman.nginx.org/mailman/listinfo/nginx-devel