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 ngx_uint_t ngx_js_dict_evict(ngx_js_dict_t *dict, ngx_uint_t count);
static njs_int_t ngx_js_dict_shared_error_name(njs_vm_t *vm,
njs_object_prop_t *prop, uint32_t unused, njs_value_t *value,
{
void *p;
- dict->shpool->log_nomem = !dict->evict;
+ if (!dict->evict) {
+ return ngx_slab_alloc_locked(dict->shpool, n);
+ }
+
+ dict->shpool->log_nomem = 0;
p = ngx_slab_alloc_locked(dict->shpool, n);
+
+ while (p == NULL) {
+ if (ngx_js_dict_evict(dict, 16) == 0) {
+ break;
+ }
+
+ p = ngx_slab_alloc_locked(dict->shpool, n);
+ }
+
dict->shpool->log_nomem = 1;
- if (p == NULL && dict->evict) {
- ngx_js_dict_evict(dict, 16);
+ if (p == NULL) {
p = ngx_slab_alloc_locked(dict->shpool, n);
}
}
-static void
-ngx_js_dict_evict(ngx_js_dict_t *dict, ngx_int_t count)
+static ngx_uint_t
+ngx_js_dict_evict(ngx_js_dict_t *dict, ngx_uint_t count)
{
+ ngx_uint_t evicted;
ngx_rbtree_t *rbtree;
ngx_rbtree_node_t *rn, *next;
ngx_js_dict_node_t *node;
rbtree = &dict->sh->rbtree_expire;
if (rbtree->root == rbtree->sentinel) {
- return;
+ return 0;
}
+ evicted = 0;
+
for (rn = ngx_rbtree_min(rbtree->root, rbtree->sentinel);
rn != NULL;
rn = next)
{
if (count-- == 0) {
- return;
+ return evicted;
}
node = (ngx_js_dict_node_t *)
ngx_rbtree_delete(&dict->sh->rbtree, (ngx_rbtree_node_t *) node);
ngx_js_dict_node_free(dict, node);
+
+ evicted++;
}
+
+ return evicted;
}
location /no_self_evict {
js_content test.no_self_evict;
}
+
+ location /cross_slab_class {
+ js_content test.cross_slab_class;
+ }
}
}
r.return(200, 'FILLED:' + elems);
}
- export default { stress, no_self_evict };
+ function cross_slab_class(r) {
+ var dict = ngx.shared.stress;
+ var v = 'x'.repeat(16);
+
+ /* Calibrate: find how many tiny entries fit. */
+ dict.clear();
+ dict.set('probe', v);
+
+ var elems = 0;
+ while (dict.has('probe')) {
+ dict.set('s_' + elems++, v);
+ }
+
+ /* Rebuild at exact capacity with tiny entries. */
+ dict.clear();
+ for (var i = 0; i < elems; i++) {
+ dict.set('s_' + i, v);
+ }
+
+ /* Check that the zone can evict enough entries to fit a big one. */
+
+ dict.set('big', 'y'.repeat(2048));
+
+ r.return(200, 'FILLED:' + elems);
+ }
+
+ export default { stress, no_self_evict, cross_slab_class };
EOF
$t->try_run('no js_shared_dict_zone');
-$t->plan(6);
+$t->plan(7);
###############################################################################
unlike($t->read_file('error.log'), qr/is already free/,
'evict update: no double-free in error log');
+like(http_get('/cross_slab_class'), qr/FILLED:/,
+ 'evict cross slab class: large alloc after small entries');
+
###############################################################################