Line data Source code
1 :
2 : /*
3 : * Copyright (C) Yichun Zhang (agentzh)
4 : */
5 :
6 :
7 : #ifndef DDEBUG
8 : #define DDEBUG 0
9 : #endif
10 : #include "ddebug.h"
11 :
12 :
13 : #include "ngx_http_lua_shdict.h"
14 : #include "ngx_http_lua_util.h"
15 : #include "ngx_http_lua_api.h"
16 :
17 :
18 : static int ngx_http_lua_shdict_set(lua_State *L);
19 : static int ngx_http_lua_shdict_safe_set(lua_State *L);
20 : static int ngx_http_lua_shdict_get(lua_State *L);
21 : static int ngx_http_lua_shdict_get_stale(lua_State *L);
22 : static int ngx_http_lua_shdict_get_helper(lua_State *L, int get_stale);
23 : static int ngx_http_lua_shdict_expire(ngx_http_lua_shdict_ctx_t *ctx,
24 : ngx_uint_t n);
25 : static ngx_int_t ngx_http_lua_shdict_lookup(ngx_shm_zone_t *shm_zone,
26 : ngx_uint_t hash, u_char *kdata, size_t klen,
27 : ngx_http_lua_shdict_node_t **sdp);
28 : static int ngx_http_lua_shdict_set_helper(lua_State *L, int flags);
29 : static int ngx_http_lua_shdict_add(lua_State *L);
30 : static int ngx_http_lua_shdict_safe_add(lua_State *L);
31 : static int ngx_http_lua_shdict_replace(lua_State *L);
32 : static int ngx_http_lua_shdict_incr(lua_State *L);
33 : static int ngx_http_lua_shdict_delete(lua_State *L);
34 : static int ngx_http_lua_shdict_flush_all(lua_State *L);
35 : static int ngx_http_lua_shdict_flush_expired(lua_State *L);
36 : static int ngx_http_lua_shdict_get_keys(lua_State *L);
37 : static int ngx_http_lua_shdict_lpush(lua_State *L);
38 : static int ngx_http_lua_shdict_rpush(lua_State *L);
39 : static int ngx_http_lua_shdict_push_helper(lua_State *L, int flags);
40 : static int ngx_http_lua_shdict_lpop(lua_State *L);
41 : static int ngx_http_lua_shdict_rpop(lua_State *L);
42 : static int ngx_http_lua_shdict_pop_helper(lua_State *L, int flags);
43 : static int ngx_http_lua_shdict_llen(lua_State *L);
44 :
45 :
46 : static ngx_inline ngx_shm_zone_t *ngx_http_lua_shdict_get_zone(lua_State *L,
47 : int index);
48 :
49 :
50 : #define NGX_HTTP_LUA_SHDICT_ADD 0x0001
51 : #define NGX_HTTP_LUA_SHDICT_REPLACE 0x0002
52 : #define NGX_HTTP_LUA_SHDICT_SAFE_STORE 0x0004
53 :
54 :
55 : #define NGX_HTTP_LUA_SHDICT_LEFT 0x0001
56 : #define NGX_HTTP_LUA_SHDICT_RIGHT 0x0002
57 :
58 :
59 : enum {
60 : SHDICT_USERDATA_INDEX = 1,
61 : };
62 :
63 :
64 : enum {
65 : SHDICT_TNIL = 0, /* same as LUA_TNIL */
66 : SHDICT_TBOOLEAN = 1, /* same as LUA_TBOOLEAN */
67 : SHDICT_TNUMBER = 3, /* same as LUA_TNUMBER */
68 : SHDICT_TSTRING = 4, /* same as LUA_TSTRING */
69 : SHDICT_TLIST = 5,
70 : };
71 :
72 :
73 : static ngx_inline ngx_queue_t *
74 0 : ngx_http_lua_shdict_get_list_head(ngx_http_lua_shdict_node_t *sd, size_t len)
75 : {
76 0 : return (ngx_queue_t *) ngx_align_ptr(((u_char *) &sd->data + len),
77 : NGX_ALIGNMENT);
78 : }
79 :
80 :
81 : ngx_int_t
82 0 : ngx_http_lua_shdict_init_zone(ngx_shm_zone_t *shm_zone, void *data)
83 : {
84 0 : ngx_http_lua_shdict_ctx_t *octx = data;
85 :
86 : size_t len;
87 : ngx_http_lua_shdict_ctx_t *ctx;
88 :
89 : dd("init zone");
90 :
91 0 : ctx = shm_zone->data;
92 :
93 0 : if (octx) {
94 0 : ctx->sh = octx->sh;
95 0 : ctx->shpool = octx->shpool;
96 :
97 0 : return NGX_OK;
98 : }
99 :
100 0 : ctx->shpool = (ngx_slab_pool_t *) shm_zone->shm.addr;
101 :
102 0 : if (shm_zone->shm.exists) {
103 0 : ctx->sh = ctx->shpool->data;
104 :
105 0 : return NGX_OK;
106 : }
107 :
108 0 : ctx->sh = ngx_slab_alloc(ctx->shpool, sizeof(ngx_http_lua_shdict_shctx_t));
109 0 : if (ctx->sh == NULL) {
110 0 : return NGX_ERROR;
111 : }
112 :
113 0 : ctx->shpool->data = ctx->sh;
114 :
115 0 : ngx_rbtree_init(&ctx->sh->rbtree, &ctx->sh->sentinel,
116 : ngx_http_lua_shdict_rbtree_insert_value);
117 :
118 0 : ngx_queue_init(&ctx->sh->lru_queue);
119 :
120 0 : len = sizeof(" in lua_shared_dict zone \"\"") + shm_zone->shm.name.len;
121 :
122 0 : ctx->shpool->log_ctx = ngx_slab_alloc(ctx->shpool, len);
123 0 : if (ctx->shpool->log_ctx == NULL) {
124 0 : return NGX_ERROR;
125 : }
126 :
127 0 : ngx_sprintf(ctx->shpool->log_ctx, " in lua_shared_dict zone \"%V\"%Z",
128 : &shm_zone->shm.name);
129 :
130 : #if defined(nginx_version) && nginx_version >= 1005013
131 0 : ctx->shpool->log_nomem = 0;
132 : #endif
133 :
134 0 : return NGX_OK;
135 : }
136 :
137 :
138 : void
139 0 : ngx_http_lua_shdict_rbtree_insert_value(ngx_rbtree_node_t *temp,
140 : ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel)
141 : {
142 : ngx_rbtree_node_t **p;
143 : ngx_http_lua_shdict_node_t *sdn, *sdnt;
144 :
145 : for ( ;; ) {
146 :
147 0 : if (node->key < temp->key) {
148 :
149 0 : p = &temp->left;
150 :
151 0 : } else if (node->key > temp->key) {
152 :
153 0 : p = &temp->right;
154 :
155 : } else { /* node->key == temp->key */
156 :
157 0 : sdn = (ngx_http_lua_shdict_node_t *) &node->color;
158 0 : sdnt = (ngx_http_lua_shdict_node_t *) &temp->color;
159 :
160 0 : p = ngx_memn2cmp(sdn->data, sdnt->data, sdn->key_len,
161 0 : sdnt->key_len) < 0 ? &temp->left : &temp->right;
162 : }
163 :
164 0 : if (*p == sentinel) {
165 0 : break;
166 : }
167 :
168 0 : temp = *p;
169 : }
170 :
171 0 : *p = node;
172 0 : node->parent = temp;
173 0 : node->left = sentinel;
174 0 : node->right = sentinel;
175 0 : ngx_rbt_red(node);
176 0 : }
177 :
178 :
179 : static ngx_int_t
180 0 : ngx_http_lua_shdict_lookup(ngx_shm_zone_t *shm_zone, ngx_uint_t hash,
181 : u_char *kdata, size_t klen, ngx_http_lua_shdict_node_t **sdp)
182 : {
183 : ngx_int_t rc;
184 : ngx_time_t *tp;
185 : uint64_t now;
186 : int64_t ms;
187 : ngx_rbtree_node_t *node, *sentinel;
188 : ngx_http_lua_shdict_ctx_t *ctx;
189 : ngx_http_lua_shdict_node_t *sd;
190 :
191 0 : ctx = shm_zone->data;
192 :
193 0 : node = ctx->sh->rbtree.root;
194 0 : sentinel = ctx->sh->rbtree.sentinel;
195 :
196 0 : while (node != sentinel) {
197 :
198 0 : if (hash < node->key) {
199 0 : node = node->left;
200 0 : continue;
201 : }
202 :
203 0 : if (hash > node->key) {
204 0 : node = node->right;
205 0 : continue;
206 : }
207 :
208 : /* hash == node->key */
209 :
210 0 : sd = (ngx_http_lua_shdict_node_t *) &node->color;
211 :
212 0 : rc = ngx_memn2cmp(kdata, sd->data, klen, (size_t) sd->key_len);
213 :
214 0 : if (rc == 0) {
215 0 : ngx_queue_remove(&sd->queue);
216 0 : ngx_queue_insert_head(&ctx->sh->lru_queue, &sd->queue);
217 :
218 0 : *sdp = sd;
219 :
220 : dd("node expires: %lld", (long long) sd->expires);
221 :
222 0 : if (sd->expires != 0) {
223 0 : tp = ngx_timeofday();
224 :
225 0 : now = (uint64_t) tp->sec * 1000 + tp->msec;
226 0 : ms = sd->expires - now;
227 :
228 : dd("time to live: %lld", (long long) ms);
229 :
230 0 : if (ms < 0) {
231 : dd("node already expired");
232 0 : return NGX_DONE;
233 : }
234 : }
235 :
236 0 : return NGX_OK;
237 : }
238 :
239 0 : node = (rc < 0) ? node->left : node->right;
240 : }
241 :
242 0 : *sdp = NULL;
243 :
244 0 : return NGX_DECLINED;
245 : }
246 :
247 :
248 : static int
249 0 : ngx_http_lua_shdict_expire(ngx_http_lua_shdict_ctx_t *ctx, ngx_uint_t n)
250 : {
251 : ngx_time_t *tp;
252 : uint64_t now;
253 : ngx_queue_t *q, *list_queue, *lq;
254 : int64_t ms;
255 : ngx_rbtree_node_t *node;
256 : ngx_http_lua_shdict_node_t *sd;
257 0 : int freed = 0;
258 : ngx_http_lua_shdict_list_node_t *lnode;
259 :
260 0 : tp = ngx_timeofday();
261 :
262 0 : now = (uint64_t) tp->sec * 1000 + tp->msec;
263 :
264 : /*
265 : * n == 1 deletes one or two expired entries
266 : * n == 0 deletes oldest entry by force
267 : * and one or two zero rate entries
268 : */
269 :
270 0 : while (n < 3) {
271 :
272 0 : if (ngx_queue_empty(&ctx->sh->lru_queue)) {
273 0 : return freed;
274 : }
275 :
276 0 : q = ngx_queue_last(&ctx->sh->lru_queue);
277 :
278 0 : sd = ngx_queue_data(q, ngx_http_lua_shdict_node_t, queue);
279 :
280 0 : if (n++ != 0) {
281 :
282 0 : if (sd->expires == 0) {
283 0 : return freed;
284 : }
285 :
286 0 : ms = sd->expires - now;
287 0 : if (ms > 0) {
288 0 : return freed;
289 : }
290 : }
291 :
292 0 : if (sd->value_type == SHDICT_TLIST) {
293 0 : list_queue = ngx_http_lua_shdict_get_list_head(sd, sd->key_len);
294 :
295 0 : for (lq = ngx_queue_head(list_queue);
296 : lq != ngx_queue_sentinel(list_queue);
297 0 : lq = ngx_queue_next(lq))
298 : {
299 0 : lnode = ngx_queue_data(lq, ngx_http_lua_shdict_list_node_t,
300 : queue);
301 :
302 0 : ngx_slab_free_locked(ctx->shpool, lnode);
303 : }
304 : }
305 :
306 0 : ngx_queue_remove(q);
307 :
308 0 : node = (ngx_rbtree_node_t *)
309 : ((u_char *) sd - offsetof(ngx_rbtree_node_t, color));
310 :
311 0 : ngx_rbtree_delete(&ctx->sh->rbtree, node);
312 :
313 0 : ngx_slab_free_locked(ctx->shpool, node);
314 :
315 0 : freed++;
316 : }
317 :
318 0 : return freed;
319 : }
320 :
321 :
322 : void
323 18 : ngx_http_lua_inject_shdict_api(ngx_http_lua_main_conf_t *lmcf, lua_State *L)
324 : {
325 : ngx_http_lua_shdict_ctx_t *ctx;
326 : ngx_uint_t i;
327 : ngx_shm_zone_t **zone;
328 :
329 18 : if (lmcf->shdict_zones != NULL) {
330 0 : lua_createtable(L, 0, lmcf->shdict_zones->nelts /* nrec */);
331 : /* ngx.shared */
332 :
333 0 : lua_createtable(L, 0 /* narr */, 18 /* nrec */); /* shared mt */
334 :
335 0 : lua_pushcfunction(L, ngx_http_lua_shdict_get);
336 0 : lua_setfield(L, -2, "get");
337 :
338 0 : lua_pushcfunction(L, ngx_http_lua_shdict_get_stale);
339 0 : lua_setfield(L, -2, "get_stale");
340 :
341 0 : lua_pushcfunction(L, ngx_http_lua_shdict_set);
342 0 : lua_setfield(L, -2, "set");
343 :
344 0 : lua_pushcfunction(L, ngx_http_lua_shdict_safe_set);
345 0 : lua_setfield(L, -2, "safe_set");
346 :
347 0 : lua_pushcfunction(L, ngx_http_lua_shdict_add);
348 0 : lua_setfield(L, -2, "add");
349 :
350 0 : lua_pushcfunction(L, ngx_http_lua_shdict_safe_add);
351 0 : lua_setfield(L, -2, "safe_add");
352 :
353 0 : lua_pushcfunction(L, ngx_http_lua_shdict_replace);
354 0 : lua_setfield(L, -2, "replace");
355 :
356 0 : lua_pushcfunction(L, ngx_http_lua_shdict_incr);
357 0 : lua_setfield(L, -2, "incr");
358 :
359 0 : lua_pushcfunction(L, ngx_http_lua_shdict_delete);
360 0 : lua_setfield(L, -2, "delete");
361 :
362 0 : lua_pushcfunction(L, ngx_http_lua_shdict_lpush);
363 0 : lua_setfield(L, -2, "lpush");
364 :
365 0 : lua_pushcfunction(L, ngx_http_lua_shdict_rpush);
366 0 : lua_setfield(L, -2, "rpush");
367 :
368 0 : lua_pushcfunction(L, ngx_http_lua_shdict_lpop);
369 0 : lua_setfield(L, -2, "lpop");
370 :
371 0 : lua_pushcfunction(L, ngx_http_lua_shdict_rpop);
372 0 : lua_setfield(L, -2, "rpop");
373 :
374 0 : lua_pushcfunction(L, ngx_http_lua_shdict_llen);
375 0 : lua_setfield(L, -2, "llen");
376 :
377 0 : lua_pushcfunction(L, ngx_http_lua_shdict_flush_all);
378 0 : lua_setfield(L, -2, "flush_all");
379 :
380 0 : lua_pushcfunction(L, ngx_http_lua_shdict_flush_expired);
381 0 : lua_setfield(L, -2, "flush_expired");
382 :
383 0 : lua_pushcfunction(L, ngx_http_lua_shdict_get_keys);
384 0 : lua_setfield(L, -2, "get_keys");
385 :
386 0 : lua_pushvalue(L, -1); /* shared mt mt */
387 0 : lua_setfield(L, -2, "__index"); /* shared mt */
388 :
389 0 : zone = lmcf->shdict_zones->elts;
390 :
391 0 : for (i = 0; i < lmcf->shdict_zones->nelts; i++) {
392 0 : ctx = zone[i]->data;
393 :
394 0 : lua_pushlstring(L, (char *) ctx->name.data, ctx->name.len);
395 : /* shared mt key */
396 :
397 0 : lua_createtable(L, 1 /* narr */, 0 /* nrec */);
398 : /* table of zone[i] */
399 0 : lua_pushlightuserdata(L, zone[i]); /* shared mt key ud */
400 0 : lua_rawseti(L, -2, SHDICT_USERDATA_INDEX); /* {zone[i]} */
401 0 : lua_pushvalue(L, -3); /* shared mt key ud mt */
402 0 : lua_setmetatable(L, -2); /* shared mt key ud */
403 0 : lua_rawset(L, -4); /* shared mt */
404 : }
405 :
406 0 : lua_pop(L, 1); /* shared */
407 :
408 : } else {
409 18 : lua_newtable(L); /* ngx.shared */
410 : }
411 :
412 18 : lua_setfield(L, -2, "shared");
413 18 : }
414 :
415 :
416 : static int
417 0 : ngx_http_lua_shdict_get(lua_State *L)
418 : {
419 0 : return ngx_http_lua_shdict_get_helper(L, 0 /* stale */);
420 : }
421 :
422 :
423 : static int
424 0 : ngx_http_lua_shdict_get_stale(lua_State *L)
425 : {
426 0 : return ngx_http_lua_shdict_get_helper(L, 1 /* stale */);
427 : }
428 :
429 :
430 : static ngx_inline ngx_shm_zone_t *
431 0 : ngx_http_lua_shdict_get_zone(lua_State *L, int index)
432 : {
433 : ngx_shm_zone_t *zone;
434 :
435 0 : lua_rawgeti(L, index, SHDICT_USERDATA_INDEX);
436 0 : zone = lua_touserdata(L, -1);
437 0 : lua_pop(L, 1);
438 :
439 0 : return zone;
440 : }
441 :
442 :
443 : static int
444 0 : ngx_http_lua_shdict_get_helper(lua_State *L, int get_stale)
445 : {
446 : int n;
447 : ngx_str_t name;
448 : ngx_str_t key;
449 : uint32_t hash;
450 : ngx_int_t rc;
451 : ngx_http_lua_shdict_ctx_t *ctx;
452 : ngx_http_lua_shdict_node_t *sd;
453 : ngx_str_t value;
454 : int value_type;
455 : double num;
456 : u_char c;
457 : ngx_shm_zone_t *zone;
458 0 : uint32_t user_flags = 0;
459 :
460 0 : n = lua_gettop(L);
461 :
462 0 : if (n != 2) {
463 0 : return luaL_error(L, "expecting exactly two arguments, "
464 : "but only seen %d", n);
465 : }
466 :
467 0 : if (lua_type(L, 1) != LUA_TTABLE) {
468 0 : return luaL_error(L, "bad \"zone\" argument");
469 : }
470 :
471 0 : zone = ngx_http_lua_shdict_get_zone(L, 1);
472 0 : if (zone == NULL) {
473 0 : return luaL_error(L, "bad \"zone\" argument");
474 : }
475 :
476 0 : ctx = zone->data;
477 0 : name = ctx->name;
478 :
479 0 : if (lua_isnil(L, 2)) {
480 0 : lua_pushnil(L);
481 0 : lua_pushliteral(L, "nil key");
482 0 : return 2;
483 : }
484 :
485 0 : key.data = (u_char *) luaL_checklstring(L, 2, &key.len);
486 :
487 0 : if (key.len == 0) {
488 0 : lua_pushnil(L);
489 0 : lua_pushliteral(L, "empty key");
490 0 : return 2;
491 : }
492 :
493 0 : if (key.len > 65535) {
494 0 : lua_pushnil(L);
495 0 : lua_pushliteral(L, "key too long");
496 0 : return 2;
497 : }
498 :
499 0 : hash = ngx_crc32_short(key.data, key.len);
500 :
501 : #if (NGX_DEBUG)
502 0 : ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ctx->log, 0,
503 : "fetching key \"%V\" in shared dict \"%V\"", &key, &name);
504 : #endif /* NGX_DEBUG */
505 :
506 0 : ngx_shmtx_lock(&ctx->shpool->mutex);
507 :
508 : #if 1
509 0 : if (!get_stale) {
510 0 : ngx_http_lua_shdict_expire(ctx, 1);
511 : }
512 : #endif
513 :
514 0 : rc = ngx_http_lua_shdict_lookup(zone, hash, key.data, key.len, &sd);
515 :
516 : dd("shdict lookup returns %d", (int) rc);
517 :
518 0 : if (rc == NGX_DECLINED || (rc == NGX_DONE && !get_stale)) {
519 0 : ngx_shmtx_unlock(&ctx->shpool->mutex);
520 0 : lua_pushnil(L);
521 0 : return 1;
522 : }
523 :
524 : /* rc == NGX_OK || (rc == NGX_DONE && get_stale) */
525 :
526 0 : value_type = sd->value_type;
527 :
528 : dd("data: %p", sd->data);
529 : dd("key len: %d", (int) sd->key_len);
530 :
531 0 : value.data = sd->data + sd->key_len;
532 0 : value.len = (size_t) sd->value_len;
533 :
534 0 : switch (value_type) {
535 :
536 0 : case SHDICT_TSTRING:
537 :
538 0 : lua_pushlstring(L, (char *) value.data, value.len);
539 0 : break;
540 :
541 0 : case SHDICT_TNUMBER:
542 :
543 0 : if (value.len != sizeof(double)) {
544 :
545 0 : ngx_shmtx_unlock(&ctx->shpool->mutex);
546 :
547 0 : return luaL_error(L, "bad lua number value size found for key %s "
548 : "in shared_dict %s: %lu", key.data, name.data,
549 0 : (unsigned long) value.len);
550 : }
551 :
552 0 : ngx_memcpy(&num, value.data, sizeof(double));
553 :
554 0 : lua_pushnumber(L, num);
555 0 : break;
556 :
557 0 : case SHDICT_TBOOLEAN:
558 :
559 0 : if (value.len != sizeof(u_char)) {
560 :
561 0 : ngx_shmtx_unlock(&ctx->shpool->mutex);
562 :
563 0 : return luaL_error(L, "bad lua boolean value size found for key %s "
564 : "in shared_dict %s: %lu", key.data, name.data,
565 0 : (unsigned long) value.len);
566 : }
567 :
568 0 : c = *value.data;
569 :
570 0 : lua_pushboolean(L, c ? 1 : 0);
571 0 : break;
572 :
573 0 : case SHDICT_TLIST:
574 :
575 0 : ngx_shmtx_unlock(&ctx->shpool->mutex);
576 :
577 0 : lua_pushnil(L);
578 0 : lua_pushliteral(L, "value is a list");
579 0 : return 2;
580 :
581 0 : default:
582 :
583 0 : ngx_shmtx_unlock(&ctx->shpool->mutex);
584 :
585 0 : return luaL_error(L, "bad value type found for key %s in "
586 : "shared_dict %s: %d", key.data, name.data,
587 : value_type);
588 : }
589 :
590 0 : user_flags = sd->user_flags;
591 :
592 0 : ngx_shmtx_unlock(&ctx->shpool->mutex);
593 :
594 0 : if (get_stale) {
595 :
596 : /* always return value, flags, stale */
597 :
598 0 : if (user_flags) {
599 0 : lua_pushinteger(L, (lua_Integer) user_flags);
600 :
601 : } else {
602 0 : lua_pushnil(L);
603 : }
604 :
605 0 : lua_pushboolean(L, rc == NGX_DONE);
606 0 : return 3;
607 : }
608 :
609 0 : if (user_flags) {
610 0 : lua_pushinteger(L, (lua_Integer) user_flags);
611 0 : return 2;
612 : }
613 :
614 0 : return 1;
615 : }
616 :
617 :
618 : static int
619 0 : ngx_http_lua_shdict_delete(lua_State *L)
620 : {
621 : int n;
622 :
623 0 : n = lua_gettop(L);
624 :
625 0 : if (n != 2) {
626 0 : return luaL_error(L, "expecting 2 arguments, "
627 : "but only seen %d", n);
628 : }
629 :
630 0 : lua_pushnil(L);
631 :
632 0 : return ngx_http_lua_shdict_set_helper(L, 0);
633 : }
634 :
635 :
636 : static int
637 0 : ngx_http_lua_shdict_flush_all(lua_State *L)
638 : {
639 : ngx_queue_t *q;
640 : ngx_http_lua_shdict_node_t *sd;
641 : int n;
642 : ngx_http_lua_shdict_ctx_t *ctx;
643 : ngx_shm_zone_t *zone;
644 :
645 0 : n = lua_gettop(L);
646 :
647 0 : if (n != 1) {
648 0 : return luaL_error(L, "expecting 1 argument, but seen %d", n);
649 : }
650 :
651 0 : luaL_checktype(L, 1, LUA_TTABLE);
652 :
653 0 : zone = ngx_http_lua_shdict_get_zone(L, 1);
654 0 : if (zone == NULL) {
655 0 : return luaL_error(L, "bad user data for the ngx_shm_zone_t pointer");
656 : }
657 :
658 0 : ctx = zone->data;
659 :
660 0 : ngx_shmtx_lock(&ctx->shpool->mutex);
661 :
662 0 : for (q = ngx_queue_head(&ctx->sh->lru_queue);
663 0 : q != ngx_queue_sentinel(&ctx->sh->lru_queue);
664 0 : q = ngx_queue_next(q))
665 : {
666 0 : sd = ngx_queue_data(q, ngx_http_lua_shdict_node_t, queue);
667 0 : sd->expires = 1;
668 : }
669 :
670 0 : ngx_http_lua_shdict_expire(ctx, 0);
671 :
672 0 : ngx_shmtx_unlock(&ctx->shpool->mutex);
673 :
674 0 : return 0;
675 : }
676 :
677 :
678 : static int
679 0 : ngx_http_lua_shdict_flush_expired(lua_State *L)
680 : {
681 : ngx_queue_t *q, *prev, *list_queue, *lq;
682 : ngx_http_lua_shdict_node_t *sd;
683 : ngx_http_lua_shdict_ctx_t *ctx;
684 : ngx_shm_zone_t *zone;
685 : ngx_time_t *tp;
686 0 : int freed = 0;
687 0 : int attempts = 0;
688 : ngx_rbtree_node_t *node;
689 : uint64_t now;
690 : int n;
691 : ngx_http_lua_shdict_list_node_t *lnode;
692 :
693 0 : n = lua_gettop(L);
694 :
695 0 : if (n != 1 && n != 2) {
696 0 : return luaL_error(L, "expecting 1 or 2 argument(s), but saw %d", n);
697 : }
698 :
699 0 : luaL_checktype(L, 1, LUA_TTABLE);
700 :
701 0 : zone = ngx_http_lua_shdict_get_zone(L, 1);
702 0 : if (zone == NULL) {
703 0 : return luaL_error(L, "bad user data for the ngx_shm_zone_t pointer");
704 : }
705 :
706 0 : if (n == 2) {
707 0 : attempts = luaL_checkint(L, 2);
708 : }
709 :
710 0 : ctx = zone->data;
711 :
712 0 : ngx_shmtx_lock(&ctx->shpool->mutex);
713 :
714 0 : if (ngx_queue_empty(&ctx->sh->lru_queue)) {
715 0 : ngx_shmtx_unlock(&ctx->shpool->mutex);
716 0 : lua_pushnumber(L, 0);
717 0 : return 1;
718 : }
719 :
720 0 : tp = ngx_timeofday();
721 :
722 0 : now = (uint64_t) tp->sec * 1000 + tp->msec;
723 :
724 0 : q = ngx_queue_last(&ctx->sh->lru_queue);
725 :
726 0 : while (q != ngx_queue_sentinel(&ctx->sh->lru_queue)) {
727 0 : prev = ngx_queue_prev(q);
728 :
729 0 : sd = ngx_queue_data(q, ngx_http_lua_shdict_node_t, queue);
730 :
731 0 : if (sd->expires != 0 && sd->expires <= now) {
732 :
733 0 : if (sd->value_type == SHDICT_TLIST) {
734 0 : list_queue = ngx_http_lua_shdict_get_list_head(sd, sd->key_len);
735 :
736 0 : for (lq = ngx_queue_head(list_queue);
737 : lq != ngx_queue_sentinel(list_queue);
738 0 : lq = ngx_queue_next(lq))
739 : {
740 0 : lnode = ngx_queue_data(lq, ngx_http_lua_shdict_list_node_t,
741 : queue);
742 :
743 0 : ngx_slab_free_locked(ctx->shpool, lnode);
744 : }
745 : }
746 :
747 0 : ngx_queue_remove(q);
748 :
749 0 : node = (ngx_rbtree_node_t *)
750 : ((u_char *) sd - offsetof(ngx_rbtree_node_t, color));
751 :
752 0 : ngx_rbtree_delete(&ctx->sh->rbtree, node);
753 0 : ngx_slab_free_locked(ctx->shpool, node);
754 0 : freed++;
755 :
756 0 : if (attempts && freed == attempts) {
757 0 : break;
758 : }
759 : }
760 :
761 0 : q = prev;
762 : }
763 :
764 0 : ngx_shmtx_unlock(&ctx->shpool->mutex);
765 :
766 0 : lua_pushnumber(L, freed);
767 0 : return 1;
768 : }
769 :
770 :
771 : /*
772 : * This trades CPU for memory. This is potentially slow. O(2n)
773 : */
774 :
775 : static int
776 0 : ngx_http_lua_shdict_get_keys(lua_State *L)
777 : {
778 : ngx_queue_t *q, *prev;
779 : ngx_http_lua_shdict_node_t *sd;
780 : ngx_http_lua_shdict_ctx_t *ctx;
781 : ngx_shm_zone_t *zone;
782 : ngx_time_t *tp;
783 0 : int total = 0;
784 0 : int attempts = 1024;
785 : uint64_t now;
786 : int n;
787 :
788 0 : n = lua_gettop(L);
789 :
790 0 : if (n != 1 && n != 2) {
791 0 : return luaL_error(L, "expecting 1 or 2 argument(s), "
792 : "but saw %d", n);
793 : }
794 :
795 0 : luaL_checktype(L, 1, LUA_TTABLE);
796 :
797 0 : zone = ngx_http_lua_shdict_get_zone(L, 1);
798 0 : if (zone == NULL) {
799 0 : return luaL_error(L, "bad user data for the ngx_shm_zone_t pointer");
800 : }
801 :
802 0 : if (n == 2) {
803 0 : attempts = luaL_checkint(L, 2);
804 : }
805 :
806 0 : ctx = zone->data;
807 :
808 0 : ngx_shmtx_lock(&ctx->shpool->mutex);
809 :
810 0 : if (ngx_queue_empty(&ctx->sh->lru_queue)) {
811 0 : ngx_shmtx_unlock(&ctx->shpool->mutex);
812 0 : lua_createtable(L, 0, 0);
813 0 : return 1;
814 : }
815 :
816 0 : tp = ngx_timeofday();
817 :
818 0 : now = (uint64_t) tp->sec * 1000 + tp->msec;
819 :
820 : /* first run through: get total number of elements we need to allocate */
821 :
822 0 : q = ngx_queue_last(&ctx->sh->lru_queue);
823 :
824 0 : while (q != ngx_queue_sentinel(&ctx->sh->lru_queue)) {
825 0 : prev = ngx_queue_prev(q);
826 :
827 0 : sd = ngx_queue_data(q, ngx_http_lua_shdict_node_t, queue);
828 :
829 0 : if (sd->expires == 0 || sd->expires > now) {
830 0 : total++;
831 0 : if (attempts && total == attempts) {
832 0 : break;
833 : }
834 : }
835 :
836 0 : q = prev;
837 : }
838 :
839 0 : lua_createtable(L, total, 0);
840 :
841 : /* second run through: add keys to table */
842 :
843 0 : total = 0;
844 0 : q = ngx_queue_last(&ctx->sh->lru_queue);
845 :
846 0 : while (q != ngx_queue_sentinel(&ctx->sh->lru_queue)) {
847 0 : prev = ngx_queue_prev(q);
848 :
849 0 : sd = ngx_queue_data(q, ngx_http_lua_shdict_node_t, queue);
850 :
851 0 : if (sd->expires == 0 || sd->expires > now) {
852 0 : lua_pushlstring(L, (char *) sd->data, sd->key_len);
853 0 : lua_rawseti(L, -2, ++total);
854 0 : if (attempts && total == attempts) {
855 0 : break;
856 : }
857 : }
858 :
859 0 : q = prev;
860 : }
861 :
862 0 : ngx_shmtx_unlock(&ctx->shpool->mutex);
863 :
864 : /* table is at top of stack */
865 0 : return 1;
866 : }
867 :
868 :
869 : static int
870 0 : ngx_http_lua_shdict_add(lua_State *L)
871 : {
872 0 : return ngx_http_lua_shdict_set_helper(L, NGX_HTTP_LUA_SHDICT_ADD);
873 : }
874 :
875 :
876 : static int
877 0 : ngx_http_lua_shdict_safe_add(lua_State *L)
878 : {
879 0 : return ngx_http_lua_shdict_set_helper(L, NGX_HTTP_LUA_SHDICT_ADD
880 : |NGX_HTTP_LUA_SHDICT_SAFE_STORE);
881 : }
882 :
883 :
884 : static int
885 0 : ngx_http_lua_shdict_replace(lua_State *L)
886 : {
887 0 : return ngx_http_lua_shdict_set_helper(L, NGX_HTTP_LUA_SHDICT_REPLACE);
888 : }
889 :
890 :
891 : static int
892 0 : ngx_http_lua_shdict_set(lua_State *L)
893 : {
894 0 : return ngx_http_lua_shdict_set_helper(L, 0);
895 : }
896 :
897 :
898 : static int
899 0 : ngx_http_lua_shdict_safe_set(lua_State *L)
900 : {
901 0 : return ngx_http_lua_shdict_set_helper(L, NGX_HTTP_LUA_SHDICT_SAFE_STORE);
902 : }
903 :
904 :
905 : static int
906 0 : ngx_http_lua_shdict_set_helper(lua_State *L, int flags)
907 : {
908 : int i, n;
909 : ngx_str_t key;
910 : uint32_t hash;
911 : ngx_int_t rc;
912 : ngx_http_lua_shdict_ctx_t *ctx;
913 : ngx_http_lua_shdict_node_t *sd;
914 : ngx_str_t value;
915 : int value_type;
916 : double num;
917 : u_char c;
918 0 : lua_Number exptime = 0;
919 : u_char *p;
920 : ngx_rbtree_node_t *node;
921 : ngx_time_t *tp;
922 : ngx_shm_zone_t *zone;
923 0 : int forcible = 0;
924 : /* indicates whether to foricibly override other
925 : * valid entries */
926 0 : int32_t user_flags = 0;
927 : ngx_queue_t *queue, *q;
928 :
929 0 : n = lua_gettop(L);
930 :
931 0 : if (n != 3 && n != 4 && n != 5) {
932 0 : return luaL_error(L, "expecting 3, 4 or 5 arguments, "
933 : "but only seen %d", n);
934 : }
935 :
936 0 : if (lua_type(L, 1) != LUA_TTABLE) {
937 0 : return luaL_error(L, "bad \"zone\" argument");
938 : }
939 :
940 0 : zone = ngx_http_lua_shdict_get_zone(L, 1);
941 0 : if (zone == NULL) {
942 0 : return luaL_error(L, "bad \"zone\" argument");
943 : }
944 :
945 0 : ctx = zone->data;
946 :
947 0 : if (lua_isnil(L, 2)) {
948 0 : lua_pushnil(L);
949 0 : lua_pushliteral(L, "nil key");
950 0 : return 2;
951 : }
952 :
953 0 : key.data = (u_char *) luaL_checklstring(L, 2, &key.len);
954 :
955 0 : if (key.len == 0) {
956 0 : lua_pushnil(L);
957 0 : lua_pushliteral(L, "empty key");
958 0 : return 2;
959 : }
960 :
961 0 : if (key.len > 65535) {
962 0 : lua_pushnil(L);
963 0 : lua_pushliteral(L, "key too long");
964 0 : return 2;
965 : }
966 :
967 0 : hash = ngx_crc32_short(key.data, key.len);
968 :
969 0 : value_type = lua_type(L, 3);
970 :
971 0 : switch (value_type) {
972 :
973 0 : case SHDICT_TSTRING:
974 0 : value.data = (u_char *) lua_tolstring(L, 3, &value.len);
975 0 : break;
976 :
977 0 : case SHDICT_TNUMBER:
978 0 : value.len = sizeof(double);
979 0 : num = lua_tonumber(L, 3);
980 0 : value.data = (u_char *) #
981 0 : break;
982 :
983 0 : case SHDICT_TBOOLEAN:
984 0 : value.len = sizeof(u_char);
985 0 : c = lua_toboolean(L, 3) ? 1 : 0;
986 0 : value.data = &c;
987 0 : break;
988 :
989 0 : case LUA_TNIL:
990 0 : if (flags & (NGX_HTTP_LUA_SHDICT_ADD|NGX_HTTP_LUA_SHDICT_REPLACE)) {
991 0 : lua_pushnil(L);
992 0 : lua_pushliteral(L, "attempt to add or replace nil values");
993 0 : return 2;
994 : }
995 :
996 0 : ngx_str_null(&value);
997 0 : break;
998 :
999 0 : default:
1000 0 : lua_pushnil(L);
1001 0 : lua_pushliteral(L, "bad value type");
1002 0 : return 2;
1003 : }
1004 :
1005 0 : if (n >= 4) {
1006 0 : exptime = luaL_checknumber(L, 4);
1007 0 : if (exptime < 0) {
1008 0 : return luaL_error(L, "bad \"exptime\" argument");
1009 : }
1010 : }
1011 :
1012 0 : if (n == 5) {
1013 0 : user_flags = (uint32_t) luaL_checkinteger(L, 5);
1014 : }
1015 :
1016 0 : ngx_shmtx_lock(&ctx->shpool->mutex);
1017 :
1018 : #if 1
1019 0 : ngx_http_lua_shdict_expire(ctx, 1);
1020 : #endif
1021 :
1022 0 : rc = ngx_http_lua_shdict_lookup(zone, hash, key.data, key.len, &sd);
1023 :
1024 : dd("shdict lookup returned %d", (int) rc);
1025 :
1026 0 : if (flags & NGX_HTTP_LUA_SHDICT_REPLACE) {
1027 :
1028 0 : if (rc == NGX_DECLINED || rc == NGX_DONE) {
1029 0 : ngx_shmtx_unlock(&ctx->shpool->mutex);
1030 :
1031 0 : lua_pushboolean(L, 0);
1032 0 : lua_pushliteral(L, "not found");
1033 0 : lua_pushboolean(L, forcible);
1034 0 : return 3;
1035 : }
1036 :
1037 : /* rc == NGX_OK */
1038 :
1039 0 : goto replace;
1040 : }
1041 :
1042 0 : if (flags & NGX_HTTP_LUA_SHDICT_ADD) {
1043 :
1044 0 : if (rc == NGX_OK) {
1045 0 : ngx_shmtx_unlock(&ctx->shpool->mutex);
1046 :
1047 0 : lua_pushboolean(L, 0);
1048 0 : lua_pushliteral(L, "exists");
1049 0 : lua_pushboolean(L, forcible);
1050 0 : return 3;
1051 : }
1052 :
1053 0 : if (rc == NGX_DONE) {
1054 : /* exists but expired */
1055 :
1056 : dd("go to replace");
1057 0 : goto replace;
1058 : }
1059 :
1060 : /* rc == NGX_DECLINED */
1061 :
1062 : dd("go to insert");
1063 0 : goto insert;
1064 : }
1065 :
1066 0 : if (rc == NGX_OK || rc == NGX_DONE) {
1067 :
1068 0 : if (value_type == LUA_TNIL) {
1069 0 : goto remove;
1070 : }
1071 :
1072 0 : replace:
1073 :
1074 0 : if (value.data
1075 0 : && value.len == (size_t) sd->value_len
1076 0 : && sd->value_type != SHDICT_TLIST)
1077 : {
1078 :
1079 0 : ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ctx->log, 0,
1080 : "lua shared dict set: found old entry and value "
1081 : "size matched, reusing it");
1082 :
1083 0 : ngx_queue_remove(&sd->queue);
1084 0 : ngx_queue_insert_head(&ctx->sh->lru_queue, &sd->queue);
1085 :
1086 0 : sd->key_len = (u_short) key.len;
1087 :
1088 0 : if (exptime > 0) {
1089 0 : tp = ngx_timeofday();
1090 0 : sd->expires = (uint64_t) tp->sec * 1000 + tp->msec
1091 0 : + (uint64_t) (exptime * 1000);
1092 :
1093 : } else {
1094 0 : sd->expires = 0;
1095 : }
1096 :
1097 0 : sd->user_flags = user_flags;
1098 :
1099 0 : sd->value_len = (uint32_t) value.len;
1100 :
1101 : dd("setting value type to %d", value_type);
1102 :
1103 0 : sd->value_type = (uint8_t) value_type;
1104 :
1105 0 : p = ngx_copy(sd->data, key.data, key.len);
1106 0 : ngx_memcpy(p, value.data, value.len);
1107 :
1108 0 : ngx_shmtx_unlock(&ctx->shpool->mutex);
1109 :
1110 0 : lua_pushboolean(L, 1);
1111 0 : lua_pushnil(L);
1112 0 : lua_pushboolean(L, forcible);
1113 0 : return 3;
1114 : }
1115 :
1116 0 : ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ctx->log, 0,
1117 : "lua shared dict set: found old entry but value size "
1118 : "NOT matched, removing it first");
1119 :
1120 0 : remove:
1121 :
1122 0 : if (sd->value_type == SHDICT_TLIST) {
1123 0 : queue = ngx_http_lua_shdict_get_list_head(sd, key.len);
1124 :
1125 0 : for (q = ngx_queue_head(queue);
1126 : q != ngx_queue_sentinel(queue);
1127 0 : q = ngx_queue_next(q))
1128 : {
1129 0 : p = (u_char *) ngx_queue_data(q,
1130 : ngx_http_lua_shdict_list_node_t,
1131 : queue);
1132 :
1133 0 : ngx_slab_free_locked(ctx->shpool, p);
1134 : }
1135 : }
1136 :
1137 0 : ngx_queue_remove(&sd->queue);
1138 :
1139 0 : node = (ngx_rbtree_node_t *)
1140 0 : ((u_char *) sd - offsetof(ngx_rbtree_node_t, color));
1141 :
1142 0 : ngx_rbtree_delete(&ctx->sh->rbtree, node);
1143 :
1144 0 : ngx_slab_free_locked(ctx->shpool, node);
1145 :
1146 : }
1147 :
1148 0 : insert:
1149 :
1150 : /* rc == NGX_DECLINED or value size unmatch */
1151 :
1152 0 : if (value.data == NULL) {
1153 0 : ngx_shmtx_unlock(&ctx->shpool->mutex);
1154 :
1155 0 : lua_pushboolean(L, 1);
1156 0 : lua_pushnil(L);
1157 0 : lua_pushboolean(L, 0);
1158 0 : return 3;
1159 : }
1160 :
1161 0 : ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ctx->log, 0,
1162 : "lua shared dict set: creating a new entry");
1163 :
1164 0 : n = offsetof(ngx_rbtree_node_t, color)
1165 : + offsetof(ngx_http_lua_shdict_node_t, data)
1166 0 : + key.len
1167 0 : + value.len;
1168 :
1169 : dd("overhead = %d", (int) (offsetof(ngx_rbtree_node_t, color)
1170 : + offsetof(ngx_http_lua_shdict_node_t, data)));
1171 :
1172 0 : node = ngx_slab_alloc_locked(ctx->shpool, n);
1173 :
1174 0 : if (node == NULL) {
1175 :
1176 0 : if (flags & NGX_HTTP_LUA_SHDICT_SAFE_STORE) {
1177 0 : ngx_shmtx_unlock(&ctx->shpool->mutex);
1178 :
1179 0 : lua_pushboolean(L, 0);
1180 0 : lua_pushliteral(L, "no memory");
1181 0 : return 2;
1182 : }
1183 :
1184 0 : ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->log, 0,
1185 : "lua shared dict set: overriding non-expired items "
1186 : "due to memory shortage for entry \"%V\"", &key);
1187 :
1188 0 : for (i = 0; i < 30; i++) {
1189 0 : if (ngx_http_lua_shdict_expire(ctx, 0) == 0) {
1190 0 : break;
1191 : }
1192 :
1193 0 : forcible = 1;
1194 :
1195 0 : node = ngx_slab_alloc_locked(ctx->shpool, n);
1196 0 : if (node != NULL) {
1197 0 : goto allocated;
1198 : }
1199 : }
1200 :
1201 0 : ngx_shmtx_unlock(&ctx->shpool->mutex);
1202 :
1203 0 : lua_pushboolean(L, 0);
1204 0 : lua_pushliteral(L, "no memory");
1205 0 : lua_pushboolean(L, forcible);
1206 0 : return 3;
1207 : }
1208 :
1209 0 : allocated:
1210 :
1211 0 : sd = (ngx_http_lua_shdict_node_t *) &node->color;
1212 :
1213 0 : node->key = hash;
1214 0 : sd->key_len = (u_short) key.len;
1215 :
1216 0 : if (exptime > 0) {
1217 0 : tp = ngx_timeofday();
1218 0 : sd->expires = (uint64_t) tp->sec * 1000 + tp->msec
1219 0 : + (uint64_t) (exptime * 1000);
1220 :
1221 : } else {
1222 0 : sd->expires = 0;
1223 : }
1224 :
1225 0 : sd->user_flags = user_flags;
1226 :
1227 0 : sd->value_len = (uint32_t) value.len;
1228 :
1229 : dd("setting value type to %d", value_type);
1230 :
1231 0 : sd->value_type = (uint8_t) value_type;
1232 :
1233 0 : p = ngx_copy(sd->data, key.data, key.len);
1234 0 : ngx_memcpy(p, value.data, value.len);
1235 :
1236 0 : ngx_rbtree_insert(&ctx->sh->rbtree, node);
1237 :
1238 0 : ngx_queue_insert_head(&ctx->sh->lru_queue, &sd->queue);
1239 :
1240 0 : ngx_shmtx_unlock(&ctx->shpool->mutex);
1241 :
1242 0 : lua_pushboolean(L, 1);
1243 0 : lua_pushnil(L);
1244 0 : lua_pushboolean(L, forcible);
1245 0 : return 3;
1246 : }
1247 :
1248 :
1249 : static int
1250 0 : ngx_http_lua_shdict_incr(lua_State *L)
1251 : {
1252 : int i, n;
1253 : ngx_str_t key;
1254 : uint32_t hash;
1255 : ngx_int_t rc;
1256 : ngx_http_lua_shdict_ctx_t *ctx;
1257 : ngx_http_lua_shdict_node_t *sd;
1258 : double num;
1259 0 : double init = 0;
1260 : u_char *p;
1261 : ngx_shm_zone_t *zone;
1262 : double value;
1263 : ngx_rbtree_node_t *node;
1264 : /* indicates whether to foricibly override other
1265 : * valid entries */
1266 0 : int forcible = 0;
1267 : ngx_queue_t *queue, *q;
1268 :
1269 0 : n = lua_gettop(L);
1270 :
1271 0 : if (n != 3 && n != 4) {
1272 0 : return luaL_error(L, "expecting 3 or 4 arguments, but only seen %d", n);
1273 : }
1274 :
1275 0 : if (lua_type(L, 1) != LUA_TTABLE) {
1276 0 : return luaL_error(L, "bad \"zone\" argument");
1277 : }
1278 :
1279 0 : zone = ngx_http_lua_shdict_get_zone(L, 1);
1280 0 : if (zone == NULL) {
1281 0 : return luaL_error(L, "bad user data for the ngx_shm_zone_t pointer");
1282 : }
1283 :
1284 0 : ctx = zone->data;
1285 :
1286 0 : if (lua_isnil(L, 2)) {
1287 0 : lua_pushnil(L);
1288 0 : lua_pushliteral(L, "nil key");
1289 0 : return 2;
1290 : }
1291 :
1292 0 : key.data = (u_char *) luaL_checklstring(L, 2, &key.len);
1293 :
1294 0 : if (key.len == 0) {
1295 0 : lua_pushnil(L);
1296 0 : lua_pushliteral(L, "empty key");
1297 0 : return 2;
1298 : }
1299 :
1300 0 : if (key.len > 65535) {
1301 0 : lua_pushnil(L);
1302 0 : lua_pushliteral(L, "key too long");
1303 0 : return 2;
1304 : }
1305 :
1306 0 : hash = ngx_crc32_short(key.data, key.len);
1307 :
1308 0 : value = luaL_checknumber(L, 3);
1309 :
1310 0 : if (n == 4) {
1311 0 : init = luaL_checknumber(L, 4);
1312 : }
1313 :
1314 : dd("looking up key %.*s in shared dict %.*s", (int) key.len, key.data,
1315 : (int) ctx->name.len, ctx->name.data);
1316 :
1317 0 : ngx_shmtx_lock(&ctx->shpool->mutex);
1318 :
1319 : #if 1
1320 0 : ngx_http_lua_shdict_expire(ctx, 1);
1321 : #endif
1322 :
1323 0 : rc = ngx_http_lua_shdict_lookup(zone, hash, key.data, key.len, &sd);
1324 :
1325 : dd("shdict lookup returned %d", (int) rc);
1326 :
1327 0 : if (rc == NGX_DECLINED || rc == NGX_DONE) {
1328 :
1329 0 : if (n == 3) {
1330 0 : ngx_shmtx_unlock(&ctx->shpool->mutex);
1331 :
1332 0 : lua_pushnil(L);
1333 0 : lua_pushliteral(L, "not found");
1334 0 : return 2;
1335 : }
1336 :
1337 : /* add value */
1338 0 : num = value + init;
1339 :
1340 0 : if (rc == NGX_DONE) {
1341 :
1342 : /* found an expired item */
1343 :
1344 0 : if ((size_t) sd->value_len == sizeof(double)
1345 0 : && sd->value_type != SHDICT_TLIST)
1346 : {
1347 0 : ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ctx->log, 0,
1348 : "lua shared dict incr: found old entry and "
1349 : "value size matched, reusing it");
1350 :
1351 0 : ngx_queue_remove(&sd->queue);
1352 0 : ngx_queue_insert_head(&ctx->sh->lru_queue, &sd->queue);
1353 :
1354 : dd("go to setvalue");
1355 0 : goto setvalue;
1356 : }
1357 :
1358 : dd("go to remove");
1359 0 : goto remove;
1360 : }
1361 :
1362 : dd("go to insert");
1363 0 : goto insert;
1364 : }
1365 :
1366 : /* rc == NGX_OK */
1367 :
1368 0 : if (sd->value_type != SHDICT_TNUMBER || sd->value_len != sizeof(double)) {
1369 0 : ngx_shmtx_unlock(&ctx->shpool->mutex);
1370 :
1371 0 : lua_pushnil(L);
1372 0 : lua_pushliteral(L, "not a number");
1373 0 : return 2;
1374 : }
1375 :
1376 0 : ngx_queue_remove(&sd->queue);
1377 0 : ngx_queue_insert_head(&ctx->sh->lru_queue, &sd->queue);
1378 :
1379 : dd("setting value type to %d", (int) sd->value_type);
1380 :
1381 0 : p = sd->data + key.len;
1382 :
1383 0 : ngx_memcpy(&num, p, sizeof(double));
1384 0 : num += value;
1385 :
1386 0 : ngx_memcpy(p, (double *) &num, sizeof(double));
1387 :
1388 0 : ngx_shmtx_unlock(&ctx->shpool->mutex);
1389 :
1390 0 : lua_pushnumber(L, num);
1391 0 : lua_pushnil(L);
1392 0 : return 2;
1393 :
1394 0 : remove:
1395 :
1396 0 : ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ctx->log, 0,
1397 : "lua shared dict incr: found old entry but value size "
1398 : "NOT matched, removing it first");
1399 :
1400 0 : if (sd->value_type == SHDICT_TLIST) {
1401 0 : queue = ngx_http_lua_shdict_get_list_head(sd, key.len);
1402 :
1403 0 : for (q = ngx_queue_head(queue);
1404 : q != ngx_queue_sentinel(queue);
1405 0 : q = ngx_queue_next(q))
1406 : {
1407 0 : p = (u_char *) ngx_queue_data(q,
1408 : ngx_http_lua_shdict_list_node_t,
1409 : queue);
1410 :
1411 0 : ngx_slab_free_locked(ctx->shpool, p);
1412 : }
1413 : }
1414 :
1415 0 : ngx_queue_remove(&sd->queue);
1416 :
1417 0 : node = (ngx_rbtree_node_t *)
1418 0 : ((u_char *) sd - offsetof(ngx_rbtree_node_t, color));
1419 :
1420 0 : ngx_rbtree_delete(&ctx->sh->rbtree, node);
1421 :
1422 0 : ngx_slab_free_locked(ctx->shpool, node);
1423 :
1424 0 : insert:
1425 :
1426 0 : ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ctx->log, 0,
1427 : "lua shared dict incr: creating a new entry");
1428 :
1429 0 : n = offsetof(ngx_rbtree_node_t, color)
1430 : + offsetof(ngx_http_lua_shdict_node_t, data)
1431 0 : + key.len
1432 0 : + sizeof(double);
1433 :
1434 0 : node = ngx_slab_alloc_locked(ctx->shpool, n);
1435 :
1436 0 : if (node == NULL) {
1437 :
1438 0 : ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->log, 0,
1439 : "lua shared dict incr: overriding non-expired items "
1440 : "due to memory shortage for entry \"%V\"", &key);
1441 :
1442 0 : for (i = 0; i < 30; i++) {
1443 0 : if (ngx_http_lua_shdict_expire(ctx, 0) == 0) {
1444 0 : break;
1445 : }
1446 :
1447 0 : forcible = 1;
1448 :
1449 0 : node = ngx_slab_alloc_locked(ctx->shpool, n);
1450 0 : if (node != NULL) {
1451 0 : goto allocated;
1452 : }
1453 : }
1454 :
1455 0 : ngx_shmtx_unlock(&ctx->shpool->mutex);
1456 :
1457 0 : lua_pushboolean(L, 0);
1458 0 : lua_pushliteral(L, "no memory");
1459 0 : lua_pushboolean(L, forcible);
1460 0 : return 3;
1461 : }
1462 :
1463 0 : allocated:
1464 :
1465 0 : sd = (ngx_http_lua_shdict_node_t *) &node->color;
1466 :
1467 0 : node->key = hash;
1468 :
1469 0 : sd->key_len = (u_short) key.len;
1470 :
1471 0 : sd->value_len = (uint32_t) sizeof(double);
1472 :
1473 0 : ngx_rbtree_insert(&ctx->sh->rbtree, node);
1474 :
1475 0 : ngx_queue_insert_head(&ctx->sh->lru_queue, &sd->queue);
1476 :
1477 0 : setvalue:
1478 :
1479 0 : sd->user_flags = 0;
1480 :
1481 0 : sd->expires = 0;
1482 :
1483 : dd("setting value type to %d", LUA_TNUMBER);
1484 :
1485 0 : sd->value_type = (uint8_t) LUA_TNUMBER;
1486 :
1487 0 : p = ngx_copy(sd->data, key.data, key.len);
1488 0 : ngx_memcpy(p, (double *) &num, sizeof(double));
1489 :
1490 0 : ngx_shmtx_unlock(&ctx->shpool->mutex);
1491 :
1492 0 : lua_pushnumber(L, num);
1493 0 : lua_pushnil(L);
1494 0 : lua_pushboolean(L, forcible);
1495 0 : return 3;
1496 : }
1497 :
1498 :
1499 : ngx_int_t
1500 0 : ngx_http_lua_shared_dict_get(ngx_shm_zone_t *zone, u_char *key_data,
1501 : size_t key_len, ngx_http_lua_value_t *value)
1502 : {
1503 : u_char *data;
1504 : size_t len;
1505 : uint32_t hash;
1506 : ngx_int_t rc;
1507 : ngx_http_lua_shdict_ctx_t *ctx;
1508 : ngx_http_lua_shdict_node_t *sd;
1509 :
1510 0 : if (zone == NULL) {
1511 0 : return NGX_ERROR;
1512 : }
1513 :
1514 0 : hash = ngx_crc32_short(key_data, key_len);
1515 :
1516 0 : ctx = zone->data;
1517 :
1518 0 : ngx_shmtx_lock(&ctx->shpool->mutex);
1519 :
1520 0 : rc = ngx_http_lua_shdict_lookup(zone, hash, key_data, key_len, &sd);
1521 :
1522 : dd("shdict lookup returned %d", (int) rc);
1523 :
1524 0 : if (rc == NGX_DECLINED || rc == NGX_DONE) {
1525 0 : ngx_shmtx_unlock(&ctx->shpool->mutex);
1526 :
1527 0 : return rc;
1528 : }
1529 :
1530 : /* rc == NGX_OK */
1531 :
1532 0 : value->type = sd->value_type;
1533 :
1534 : dd("type: %d", (int) value->type);
1535 :
1536 0 : data = sd->data + sd->key_len;
1537 0 : len = (size_t) sd->value_len;
1538 :
1539 0 : switch (value->type) {
1540 :
1541 0 : case SHDICT_TSTRING:
1542 :
1543 0 : if (value->value.s.data == NULL || value->value.s.len == 0) {
1544 0 : ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, "no string buffer "
1545 : "initialized");
1546 0 : ngx_shmtx_unlock(&ctx->shpool->mutex);
1547 0 : return NGX_ERROR;
1548 : }
1549 :
1550 0 : if (len > value->value.s.len) {
1551 0 : len = value->value.s.len;
1552 :
1553 : } else {
1554 0 : value->value.s.len = len;
1555 : }
1556 :
1557 0 : ngx_memcpy(value->value.s.data, data, len);
1558 0 : break;
1559 :
1560 0 : case SHDICT_TNUMBER:
1561 :
1562 0 : if (len != sizeof(double)) {
1563 0 : ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, "bad lua number "
1564 : "value size found for key %*s: %lu", key_len,
1565 : key_data, (unsigned long) len);
1566 :
1567 0 : ngx_shmtx_unlock(&ctx->shpool->mutex);
1568 0 : return NGX_ERROR;
1569 : }
1570 :
1571 0 : ngx_memcpy(&value->value.b, data, len);
1572 0 : break;
1573 :
1574 0 : case SHDICT_TBOOLEAN:
1575 :
1576 0 : if (len != sizeof(u_char)) {
1577 0 : ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, "bad lua boolean "
1578 : "value size found for key %*s: %lu", key_len,
1579 : key_data, (unsigned long) len);
1580 :
1581 0 : ngx_shmtx_unlock(&ctx->shpool->mutex);
1582 0 : return NGX_ERROR;
1583 : }
1584 :
1585 0 : value->value.b = *data;
1586 0 : break;
1587 :
1588 0 : default:
1589 0 : ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, "bad lua value type "
1590 : "found for key %*s: %d", key_len, key_data,
1591 : (int) value->type);
1592 :
1593 0 : ngx_shmtx_unlock(&ctx->shpool->mutex);
1594 0 : return NGX_ERROR;
1595 : }
1596 :
1597 0 : ngx_shmtx_unlock(&ctx->shpool->mutex);
1598 0 : return NGX_OK;
1599 : }
1600 :
1601 :
1602 : static int
1603 0 : ngx_http_lua_shdict_lpush(lua_State *L)
1604 : {
1605 0 : return ngx_http_lua_shdict_push_helper(L, NGX_HTTP_LUA_SHDICT_LEFT);
1606 : }
1607 :
1608 :
1609 : static int
1610 0 : ngx_http_lua_shdict_rpush(lua_State *L)
1611 : {
1612 0 : return ngx_http_lua_shdict_push_helper(L, NGX_HTTP_LUA_SHDICT_RIGHT);
1613 : }
1614 :
1615 :
1616 : static int
1617 0 : ngx_http_lua_shdict_push_helper(lua_State *L, int flags)
1618 : {
1619 : int n;
1620 : ngx_str_t key;
1621 : uint32_t hash;
1622 : ngx_int_t rc;
1623 : ngx_http_lua_shdict_ctx_t *ctx;
1624 : ngx_http_lua_shdict_node_t *sd;
1625 : ngx_str_t value;
1626 : int value_type;
1627 : double num;
1628 : ngx_rbtree_node_t *node;
1629 : ngx_shm_zone_t *zone;
1630 : ngx_queue_t *queue, *q;
1631 : ngx_http_lua_shdict_list_node_t *lnode;
1632 :
1633 0 : n = lua_gettop(L);
1634 :
1635 0 : if (n != 3) {
1636 0 : return luaL_error(L, "expecting 3 arguments, "
1637 : "but only seen %d", n);
1638 : }
1639 :
1640 0 : if (lua_type(L, 1) != LUA_TTABLE) {
1641 0 : return luaL_error(L, "bad \"zone\" argument");
1642 : }
1643 :
1644 0 : zone = ngx_http_lua_shdict_get_zone(L, 1);
1645 0 : if (zone == NULL) {
1646 0 : return luaL_error(L, "bad \"zone\" argument");
1647 : }
1648 :
1649 0 : ctx = zone->data;
1650 :
1651 0 : if (lua_isnil(L, 2)) {
1652 0 : lua_pushnil(L);
1653 0 : lua_pushliteral(L, "nil key");
1654 0 : return 2;
1655 : }
1656 :
1657 0 : key.data = (u_char *) luaL_checklstring(L, 2, &key.len);
1658 :
1659 0 : if (key.len == 0) {
1660 0 : lua_pushnil(L);
1661 0 : lua_pushliteral(L, "empty key");
1662 0 : return 2;
1663 : }
1664 :
1665 0 : if (key.len > 65535) {
1666 0 : lua_pushnil(L);
1667 0 : lua_pushliteral(L, "key too long");
1668 0 : return 2;
1669 : }
1670 :
1671 0 : hash = ngx_crc32_short(key.data, key.len);
1672 :
1673 0 : value_type = lua_type(L, 3);
1674 :
1675 0 : switch (value_type) {
1676 :
1677 0 : case SHDICT_TSTRING:
1678 0 : value.data = (u_char *) lua_tolstring(L, 3, &value.len);
1679 0 : break;
1680 :
1681 0 : case SHDICT_TNUMBER:
1682 0 : value.len = sizeof(double);
1683 0 : num = lua_tonumber(L, 3);
1684 0 : value.data = (u_char *) #
1685 0 : break;
1686 :
1687 0 : default:
1688 0 : lua_pushnil(L);
1689 0 : lua_pushliteral(L, "bad value type");
1690 0 : return 2;
1691 : }
1692 :
1693 0 : ngx_shmtx_lock(&ctx->shpool->mutex);
1694 :
1695 : #if 1
1696 0 : ngx_http_lua_shdict_expire(ctx, 1);
1697 : #endif
1698 :
1699 0 : rc = ngx_http_lua_shdict_lookup(zone, hash, key.data, key.len, &sd);
1700 :
1701 : dd("shdict lookup returned %d", (int) rc);
1702 :
1703 : /* exists but expired */
1704 :
1705 0 : if (rc == NGX_DONE) {
1706 :
1707 0 : if (sd->value_type != SHDICT_TLIST) {
1708 : /* TODO: reuse when length matched */
1709 :
1710 0 : ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ctx->log, 0,
1711 : "lua shared dict push: found old entry and value "
1712 : "type not matched, remove it first");
1713 :
1714 0 : ngx_queue_remove(&sd->queue);
1715 :
1716 0 : node = (ngx_rbtree_node_t *)
1717 0 : ((u_char *) sd - offsetof(ngx_rbtree_node_t, color));
1718 :
1719 0 : ngx_rbtree_delete(&ctx->sh->rbtree, node);
1720 :
1721 0 : ngx_slab_free_locked(ctx->shpool, node);
1722 :
1723 : dd("go to init_list");
1724 0 : goto init_list;
1725 : }
1726 :
1727 0 : ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ctx->log, 0,
1728 : "lua shared dict push: found old entry and value "
1729 : "type matched, reusing it");
1730 :
1731 0 : sd->expires = 0;
1732 :
1733 : /* free list nodes */
1734 :
1735 0 : queue = ngx_http_lua_shdict_get_list_head(sd, key.len);
1736 :
1737 0 : for (q = ngx_queue_head(queue);
1738 : q != ngx_queue_sentinel(queue);
1739 0 : q = ngx_queue_next(q))
1740 : {
1741 : /* TODO: reuse matched size list node */
1742 0 : lnode = ngx_queue_data(q, ngx_http_lua_shdict_list_node_t, queue);
1743 0 : ngx_slab_free_locked(ctx->shpool, lnode);
1744 : }
1745 :
1746 0 : ngx_queue_init(queue);
1747 :
1748 0 : ngx_queue_remove(&sd->queue);
1749 0 : ngx_queue_insert_head(&ctx->sh->lru_queue, &sd->queue);
1750 :
1751 : dd("go to push_node");
1752 0 : goto push_node;
1753 : }
1754 :
1755 : /* exists and not expired */
1756 :
1757 0 : if (rc == NGX_OK) {
1758 :
1759 0 : if (sd->value_type != SHDICT_TLIST) {
1760 0 : ngx_shmtx_unlock(&ctx->shpool->mutex);
1761 :
1762 0 : lua_pushnil(L);
1763 0 : lua_pushliteral(L, "value not a list");
1764 0 : return 2;
1765 : }
1766 :
1767 0 : queue = ngx_http_lua_shdict_get_list_head(sd, key.len);
1768 :
1769 0 : ngx_queue_remove(&sd->queue);
1770 0 : ngx_queue_insert_head(&ctx->sh->lru_queue, &sd->queue);
1771 :
1772 : dd("go to push_node");
1773 0 : goto push_node;
1774 : }
1775 :
1776 : /* rc == NGX_DECLINED, not found */
1777 :
1778 0 : init_list:
1779 :
1780 0 : ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ctx->log, 0,
1781 : "lua shared dict list: creating a new entry");
1782 :
1783 : /* NOTICE: we assume the begin point aligned in slab, be careful */
1784 0 : n = offsetof(ngx_rbtree_node_t, color)
1785 : + offsetof(ngx_http_lua_shdict_node_t, data)
1786 0 : + key.len
1787 0 : + sizeof(ngx_queue_t);
1788 :
1789 : dd("length before aligned: %d", n);
1790 :
1791 0 : n = (int) (uintptr_t) ngx_align_ptr(n, NGX_ALIGNMENT);
1792 :
1793 : dd("length after aligned: %d", n);
1794 :
1795 0 : node = ngx_slab_alloc_locked(ctx->shpool, n);
1796 :
1797 0 : if (node == NULL) {
1798 0 : ngx_shmtx_unlock(&ctx->shpool->mutex);
1799 :
1800 0 : lua_pushboolean(L, 0);
1801 0 : lua_pushliteral(L, "no memory");
1802 0 : return 2;
1803 : }
1804 :
1805 0 : sd = (ngx_http_lua_shdict_node_t *) &node->color;
1806 :
1807 0 : queue = ngx_http_lua_shdict_get_list_head(sd, key.len);
1808 :
1809 0 : node->key = hash;
1810 0 : sd->key_len = (u_short) key.len;
1811 :
1812 0 : sd->expires = 0;
1813 :
1814 0 : sd->value_len = 0;
1815 :
1816 : dd("setting value type to %d", (int) SHDICT_TLIST);
1817 :
1818 0 : sd->value_type = (uint8_t) SHDICT_TLIST;
1819 :
1820 0 : ngx_memcpy(sd->data, key.data, key.len);
1821 :
1822 0 : ngx_queue_init(queue);
1823 :
1824 0 : ngx_rbtree_insert(&ctx->sh->rbtree, node);
1825 :
1826 0 : ngx_queue_insert_head(&ctx->sh->lru_queue, &sd->queue);
1827 :
1828 0 : push_node:
1829 :
1830 0 : ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ctx->log, 0,
1831 : "lua shared dict list: creating a new list node");
1832 :
1833 0 : n = offsetof(ngx_http_lua_shdict_list_node_t, data)
1834 0 : + value.len;
1835 :
1836 : dd("list node length: %d", n);
1837 :
1838 0 : lnode = ngx_slab_alloc_locked(ctx->shpool, n);
1839 :
1840 0 : if (lnode == NULL) {
1841 :
1842 0 : if (sd->value_len == 0) {
1843 :
1844 0 : ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ctx->log, 0,
1845 : "lua shared dict list: no memory for create"
1846 : " list node and list empty, remove it");
1847 :
1848 0 : ngx_queue_remove(&sd->queue);
1849 :
1850 0 : node = (ngx_rbtree_node_t *)
1851 0 : ((u_char *) sd - offsetof(ngx_rbtree_node_t, color));
1852 :
1853 0 : ngx_rbtree_delete(&ctx->sh->rbtree, node);
1854 :
1855 0 : ngx_slab_free_locked(ctx->shpool, node);
1856 : }
1857 :
1858 0 : ngx_shmtx_unlock(&ctx->shpool->mutex);
1859 :
1860 0 : lua_pushboolean(L, 0);
1861 0 : lua_pushliteral(L, "no memory");
1862 0 : return 2;
1863 : }
1864 :
1865 : dd("setting list length to %d", sd->value_len + 1);
1866 :
1867 0 : sd->value_len = sd->value_len + 1;
1868 :
1869 : dd("setting list node value length to %d", (int) value.len);
1870 :
1871 0 : lnode->value_len = (uint32_t) value.len;
1872 :
1873 : dd("setting list node value type to %d", value_type);
1874 :
1875 0 : lnode->value_type = (uint8_t) value_type;
1876 :
1877 0 : ngx_memcpy(lnode->data, value.data, value.len);
1878 :
1879 0 : if (flags == NGX_HTTP_LUA_SHDICT_LEFT) {
1880 0 : ngx_queue_insert_head(queue, &lnode->queue);
1881 :
1882 : } else {
1883 0 : ngx_queue_insert_tail(queue, &lnode->queue);
1884 : }
1885 :
1886 0 : ngx_shmtx_unlock(&ctx->shpool->mutex);
1887 :
1888 0 : lua_pushnumber(L, sd->value_len);
1889 0 : return 1;
1890 : }
1891 :
1892 :
1893 : static int
1894 0 : ngx_http_lua_shdict_lpop(lua_State *L)
1895 : {
1896 0 : return ngx_http_lua_shdict_pop_helper(L, NGX_HTTP_LUA_SHDICT_LEFT);
1897 : }
1898 :
1899 :
1900 : static int
1901 0 : ngx_http_lua_shdict_rpop(lua_State *L)
1902 : {
1903 0 : return ngx_http_lua_shdict_pop_helper(L, NGX_HTTP_LUA_SHDICT_RIGHT);
1904 : }
1905 :
1906 :
1907 : static int
1908 0 : ngx_http_lua_shdict_pop_helper(lua_State *L, int flags)
1909 : {
1910 : int n;
1911 : ngx_str_t name;
1912 : ngx_str_t key;
1913 : uint32_t hash;
1914 : ngx_int_t rc;
1915 : ngx_http_lua_shdict_ctx_t *ctx;
1916 : ngx_http_lua_shdict_node_t *sd;
1917 : ngx_str_t value;
1918 : int value_type;
1919 : double num;
1920 : ngx_rbtree_node_t *node;
1921 : ngx_shm_zone_t *zone;
1922 : ngx_queue_t *queue;
1923 : ngx_http_lua_shdict_list_node_t *lnode;
1924 :
1925 0 : n = lua_gettop(L);
1926 :
1927 0 : if (n != 2) {
1928 0 : return luaL_error(L, "expecting 2 arguments, "
1929 : "but only seen %d", n);
1930 : }
1931 :
1932 0 : if (lua_type(L, 1) != LUA_TTABLE) {
1933 0 : return luaL_error(L, "bad \"zone\" argument");
1934 : }
1935 :
1936 0 : zone = ngx_http_lua_shdict_get_zone(L, 1);
1937 0 : if (zone == NULL) {
1938 0 : return luaL_error(L, "bad \"zone\" argument");
1939 : }
1940 :
1941 0 : ctx = zone->data;
1942 0 : name = ctx->name;
1943 :
1944 0 : if (lua_isnil(L, 2)) {
1945 0 : lua_pushnil(L);
1946 0 : lua_pushliteral(L, "nil key");
1947 0 : return 2;
1948 : }
1949 :
1950 0 : key.data = (u_char *) luaL_checklstring(L, 2, &key.len);
1951 :
1952 0 : if (key.len == 0) {
1953 0 : lua_pushnil(L);
1954 0 : lua_pushliteral(L, "empty key");
1955 0 : return 2;
1956 : }
1957 :
1958 0 : if (key.len > 65535) {
1959 0 : lua_pushnil(L);
1960 0 : lua_pushliteral(L, "key too long");
1961 0 : return 2;
1962 : }
1963 :
1964 0 : hash = ngx_crc32_short(key.data, key.len);
1965 :
1966 0 : ngx_shmtx_lock(&ctx->shpool->mutex);
1967 :
1968 : #if 1
1969 0 : ngx_http_lua_shdict_expire(ctx, 1);
1970 : #endif
1971 :
1972 0 : rc = ngx_http_lua_shdict_lookup(zone, hash, key.data, key.len, &sd);
1973 :
1974 : dd("shdict lookup returned %d", (int) rc);
1975 :
1976 0 : if (rc == NGX_DECLINED || rc == NGX_DONE) {
1977 0 : ngx_shmtx_unlock(&ctx->shpool->mutex);
1978 0 : lua_pushnil(L);
1979 0 : return 1;
1980 : }
1981 :
1982 : /* rc == NGX_OK */
1983 :
1984 0 : if (sd->value_type != SHDICT_TLIST) {
1985 0 : ngx_shmtx_unlock(&ctx->shpool->mutex);
1986 :
1987 0 : lua_pushnil(L);
1988 0 : lua_pushliteral(L, "value not a list");
1989 0 : return 2;
1990 : }
1991 :
1992 0 : if (sd->value_len <= 0) {
1993 0 : ngx_shmtx_unlock(&ctx->shpool->mutex);
1994 :
1995 0 : return luaL_error(L, "bad lua list length found for key %s "
1996 : "in shared_dict %s: %lu", key.data, name.data,
1997 0 : (unsigned long) sd->value_len);
1998 : }
1999 :
2000 0 : queue = ngx_http_lua_shdict_get_list_head(sd, key.len);
2001 :
2002 0 : if (flags == NGX_HTTP_LUA_SHDICT_LEFT) {
2003 0 : queue = ngx_queue_head(queue);
2004 :
2005 : } else {
2006 0 : queue = ngx_queue_last(queue);
2007 : }
2008 :
2009 0 : lnode = ngx_queue_data(queue, ngx_http_lua_shdict_list_node_t, queue);
2010 :
2011 0 : value_type = lnode->value_type;
2012 :
2013 : dd("data: %p", lnode->data);
2014 : dd("value len: %d", (int) sd->value_len);
2015 :
2016 0 : value.data = lnode->data;
2017 0 : value.len = (size_t) lnode->value_len;
2018 :
2019 0 : switch (value_type) {
2020 :
2021 0 : case SHDICT_TSTRING:
2022 :
2023 0 : lua_pushlstring(L, (char *) value.data, value.len);
2024 0 : break;
2025 :
2026 0 : case SHDICT_TNUMBER:
2027 :
2028 0 : if (value.len != sizeof(double)) {
2029 :
2030 0 : ngx_shmtx_unlock(&ctx->shpool->mutex);
2031 :
2032 0 : return luaL_error(L, "bad lua list node number value size found "
2033 : "for key %s in shared_dict %s: %lu", key.data,
2034 0 : name.data, (unsigned long) value.len);
2035 : }
2036 :
2037 0 : ngx_memcpy(&num, value.data, sizeof(double));
2038 :
2039 0 : lua_pushnumber(L, num);
2040 0 : break;
2041 :
2042 0 : default:
2043 :
2044 0 : ngx_shmtx_unlock(&ctx->shpool->mutex);
2045 :
2046 0 : return luaL_error(L, "bad list node value type found for key %s in "
2047 : "shared_dict %s: %d", key.data, name.data,
2048 : value_type);
2049 : }
2050 :
2051 0 : ngx_queue_remove(queue);
2052 :
2053 0 : ngx_slab_free_locked(ctx->shpool, lnode);
2054 :
2055 0 : if (sd->value_len == 1) {
2056 :
2057 0 : ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ctx->log, 0,
2058 : "lua shared dict list: empty node after pop, "
2059 : "remove it");
2060 :
2061 0 : ngx_queue_remove(&sd->queue);
2062 :
2063 0 : node = (ngx_rbtree_node_t *)
2064 0 : ((u_char *) sd - offsetof(ngx_rbtree_node_t, color));
2065 :
2066 0 : ngx_rbtree_delete(&ctx->sh->rbtree, node);
2067 :
2068 0 : ngx_slab_free_locked(ctx->shpool, node);
2069 :
2070 : } else {
2071 0 : sd->value_len = sd->value_len - 1;
2072 :
2073 0 : ngx_queue_remove(&sd->queue);
2074 0 : ngx_queue_insert_head(&ctx->sh->lru_queue, &sd->queue);
2075 : }
2076 :
2077 0 : ngx_shmtx_unlock(&ctx->shpool->mutex);
2078 :
2079 0 : return 1;
2080 : }
2081 :
2082 :
2083 : static int
2084 0 : ngx_http_lua_shdict_llen(lua_State *L)
2085 : {
2086 : int n;
2087 : ngx_str_t key;
2088 : uint32_t hash;
2089 : ngx_int_t rc;
2090 : ngx_http_lua_shdict_ctx_t *ctx;
2091 : ngx_http_lua_shdict_node_t *sd;
2092 : ngx_shm_zone_t *zone;
2093 :
2094 0 : n = lua_gettop(L);
2095 :
2096 0 : if (n != 2) {
2097 0 : return luaL_error(L, "expecting 2 arguments, "
2098 : "but only seen %d", n);
2099 : }
2100 :
2101 0 : if (lua_type(L, 1) != LUA_TTABLE) {
2102 0 : return luaL_error(L, "bad \"zone\" argument");
2103 : }
2104 :
2105 0 : zone = ngx_http_lua_shdict_get_zone(L, 1);
2106 0 : if (zone == NULL) {
2107 0 : return luaL_error(L, "bad \"zone\" argument");
2108 : }
2109 :
2110 0 : ctx = zone->data;
2111 :
2112 0 : if (lua_isnil(L, 2)) {
2113 0 : lua_pushnil(L);
2114 0 : lua_pushliteral(L, "nil key");
2115 0 : return 2;
2116 : }
2117 :
2118 0 : key.data = (u_char *) luaL_checklstring(L, 2, &key.len);
2119 :
2120 0 : if (key.len == 0) {
2121 0 : lua_pushnil(L);
2122 0 : lua_pushliteral(L, "empty key");
2123 0 : return 2;
2124 : }
2125 :
2126 0 : if (key.len > 65535) {
2127 0 : lua_pushnil(L);
2128 0 : lua_pushliteral(L, "key too long");
2129 0 : return 2;
2130 : }
2131 :
2132 0 : hash = ngx_crc32_short(key.data, key.len);
2133 :
2134 0 : ngx_shmtx_lock(&ctx->shpool->mutex);
2135 :
2136 : #if 1
2137 0 : ngx_http_lua_shdict_expire(ctx, 1);
2138 : #endif
2139 :
2140 0 : rc = ngx_http_lua_shdict_lookup(zone, hash, key.data, key.len, &sd);
2141 :
2142 : dd("shdict lookup returned %d", (int) rc);
2143 :
2144 0 : if (rc == NGX_OK) {
2145 :
2146 0 : if (sd->value_type != SHDICT_TLIST) {
2147 0 : ngx_shmtx_unlock(&ctx->shpool->mutex);
2148 :
2149 0 : lua_pushnil(L);
2150 0 : lua_pushliteral(L, "value not a list");
2151 0 : return 2;
2152 : }
2153 :
2154 0 : ngx_queue_remove(&sd->queue);
2155 0 : ngx_queue_insert_head(&ctx->sh->lru_queue, &sd->queue);
2156 :
2157 0 : ngx_shmtx_unlock(&ctx->shpool->mutex);
2158 :
2159 0 : lua_pushnumber(L, (lua_Number) sd->value_len);
2160 0 : return 1;
2161 : }
2162 :
2163 0 : ngx_shmtx_unlock(&ctx->shpool->mutex);
2164 :
2165 0 : lua_pushnumber(L, 0);
2166 0 : return 1;
2167 : }
2168 :
2169 :
2170 : ngx_shm_zone_t *
2171 0 : ngx_http_lua_find_zone(u_char *name_data, size_t name_len)
2172 : {
2173 : ngx_str_t *name;
2174 : ngx_uint_t i;
2175 : ngx_shm_zone_t *zone;
2176 : ngx_http_lua_shm_zone_ctx_t *ctx;
2177 : volatile ngx_list_part_t *part;
2178 :
2179 0 : part = &ngx_cycle->shared_memory.part;
2180 0 : zone = part->elts;
2181 :
2182 0 : for (i = 0; /* void */ ; i++) {
2183 :
2184 0 : if (i >= part->nelts) {
2185 0 : if (part->next == NULL) {
2186 0 : break;
2187 : }
2188 :
2189 0 : part = part->next;
2190 0 : zone = part->elts;
2191 0 : i = 0;
2192 : }
2193 :
2194 0 : name = &zone[i].shm.name;
2195 :
2196 : dd("name: [%.*s] %d", (int) name->len, name->data, (int) name->len);
2197 : dd("name2: [%.*s] %d", (int) name_len, name_data, (int) name_len);
2198 :
2199 0 : if (name->len == name_len
2200 0 : && ngx_strncmp(name->data, name_data, name_len) == 0)
2201 : {
2202 0 : ctx = (ngx_http_lua_shm_zone_ctx_t *) zone[i].data;
2203 0 : return &ctx->zone;
2204 : }
2205 : }
2206 :
2207 0 : return NULL;
2208 : }
2209 :
2210 :
2211 : #ifndef NGX_LUA_NO_FFI_API
2212 : int
2213 0 : ngx_http_lua_ffi_shdict_store(ngx_shm_zone_t *zone, int op, u_char *key,
2214 : size_t key_len, int value_type, u_char *str_value_buf,
2215 : size_t str_value_len, double num_value, int exptime, int user_flags,
2216 : char **errmsg, int *forcible)
2217 : {
2218 : int i, n;
2219 : u_char c, *p;
2220 : uint32_t hash;
2221 : ngx_int_t rc;
2222 : ngx_time_t *tp;
2223 : ngx_queue_t *queue, *q;
2224 : ngx_rbtree_node_t *node;
2225 : ngx_http_lua_shdict_ctx_t *ctx;
2226 : ngx_http_lua_shdict_node_t *sd;
2227 :
2228 0 : if (zone == NULL) {
2229 0 : return NGX_ERROR;
2230 : }
2231 :
2232 : dd("exptime: %d", exptime);
2233 :
2234 0 : ctx = zone->data;
2235 :
2236 0 : *forcible = 0;
2237 :
2238 0 : hash = ngx_crc32_short(key, key_len);
2239 :
2240 0 : switch (value_type) {
2241 :
2242 0 : case SHDICT_TSTRING:
2243 : /* do nothing */
2244 0 : break;
2245 :
2246 0 : case SHDICT_TNUMBER:
2247 : dd("num value: %lf", num_value);
2248 0 : str_value_buf = (u_char *) &num_value;
2249 0 : str_value_len = sizeof(double);
2250 0 : break;
2251 :
2252 0 : case SHDICT_TBOOLEAN:
2253 0 : c = num_value ? 1 : 0;
2254 0 : str_value_buf = &c;
2255 0 : str_value_len = sizeof(u_char);
2256 0 : break;
2257 :
2258 0 : case LUA_TNIL:
2259 0 : if (op & (NGX_HTTP_LUA_SHDICT_ADD|NGX_HTTP_LUA_SHDICT_REPLACE)) {
2260 0 : *errmsg = "attempt to add or replace nil values";
2261 0 : return NGX_ERROR;
2262 : }
2263 :
2264 0 : str_value_buf = NULL;
2265 0 : str_value_len = 0;
2266 0 : break;
2267 :
2268 0 : default:
2269 0 : *errmsg = "unsupported value type";
2270 0 : return NGX_ERROR;
2271 : }
2272 :
2273 0 : ngx_shmtx_lock(&ctx->shpool->mutex);
2274 :
2275 : #if 1
2276 0 : ngx_http_lua_shdict_expire(ctx, 1);
2277 : #endif
2278 :
2279 0 : rc = ngx_http_lua_shdict_lookup(zone, hash, key, key_len, &sd);
2280 :
2281 : dd("lookup returns %d", (int) rc);
2282 :
2283 0 : if (op & NGX_HTTP_LUA_SHDICT_REPLACE) {
2284 :
2285 0 : if (rc == NGX_DECLINED || rc == NGX_DONE) {
2286 0 : ngx_shmtx_unlock(&ctx->shpool->mutex);
2287 0 : *errmsg = "not found";
2288 0 : return NGX_DECLINED;
2289 : }
2290 :
2291 : /* rc == NGX_OK */
2292 :
2293 0 : goto replace;
2294 : }
2295 :
2296 0 : if (op & NGX_HTTP_LUA_SHDICT_ADD) {
2297 :
2298 0 : if (rc == NGX_OK) {
2299 0 : ngx_shmtx_unlock(&ctx->shpool->mutex);
2300 0 : *errmsg = "exists";
2301 0 : return NGX_DECLINED;
2302 : }
2303 :
2304 0 : if (rc == NGX_DONE) {
2305 : /* exists but expired */
2306 :
2307 : dd("go to replace");
2308 0 : goto replace;
2309 : }
2310 :
2311 : /* rc == NGX_DECLINED */
2312 :
2313 : dd("go to insert");
2314 0 : goto insert;
2315 : }
2316 :
2317 0 : if (rc == NGX_OK || rc == NGX_DONE) {
2318 :
2319 0 : if (value_type == LUA_TNIL) {
2320 0 : goto remove;
2321 : }
2322 :
2323 0 : replace:
2324 :
2325 0 : if (str_value_buf
2326 0 : && str_value_len == (size_t) sd->value_len
2327 0 : && sd->value_type != SHDICT_TLIST)
2328 : {
2329 :
2330 0 : ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ctx->log, 0,
2331 : "lua shared dict set: found old entry and value "
2332 : "size matched, reusing it");
2333 :
2334 0 : ngx_queue_remove(&sd->queue);
2335 0 : ngx_queue_insert_head(&ctx->sh->lru_queue, &sd->queue);
2336 :
2337 0 : sd->key_len = (u_short) key_len;
2338 :
2339 0 : if (exptime > 0) {
2340 0 : tp = ngx_timeofday();
2341 0 : sd->expires = (uint64_t) tp->sec * 1000 + tp->msec
2342 0 : + (uint64_t) exptime;
2343 :
2344 : } else {
2345 0 : sd->expires = 0;
2346 : }
2347 :
2348 0 : sd->user_flags = user_flags;
2349 :
2350 0 : sd->value_len = (uint32_t) str_value_len;
2351 :
2352 : dd("setting value type to %d", value_type);
2353 :
2354 0 : sd->value_type = (uint8_t) value_type;
2355 :
2356 0 : p = ngx_copy(sd->data, key, key_len);
2357 0 : ngx_memcpy(p, str_value_buf, str_value_len);
2358 :
2359 0 : ngx_shmtx_unlock(&ctx->shpool->mutex);
2360 :
2361 0 : return NGX_OK;
2362 : }
2363 :
2364 0 : ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ctx->log, 0,
2365 : "lua shared dict set: found old entry but value size "
2366 : "NOT matched, removing it first");
2367 :
2368 0 : remove:
2369 :
2370 0 : if (sd->value_type == SHDICT_TLIST) {
2371 0 : queue = ngx_http_lua_shdict_get_list_head(sd, key_len);
2372 :
2373 0 : for (q = ngx_queue_head(queue);
2374 : q != ngx_queue_sentinel(queue);
2375 0 : q = ngx_queue_next(q))
2376 : {
2377 0 : p = (u_char *) ngx_queue_data(q,
2378 : ngx_http_lua_shdict_list_node_t,
2379 : queue);
2380 :
2381 0 : ngx_slab_free_locked(ctx->shpool, p);
2382 : }
2383 : }
2384 :
2385 0 : ngx_queue_remove(&sd->queue);
2386 :
2387 0 : node = (ngx_rbtree_node_t *)
2388 0 : ((u_char *) sd - offsetof(ngx_rbtree_node_t, color));
2389 :
2390 0 : ngx_rbtree_delete(&ctx->sh->rbtree, node);
2391 :
2392 0 : ngx_slab_free_locked(ctx->shpool, node);
2393 :
2394 : }
2395 :
2396 0 : insert:
2397 :
2398 : /* rc == NGX_DECLINED or value size unmatch */
2399 :
2400 0 : if (str_value_buf == NULL) {
2401 0 : ngx_shmtx_unlock(&ctx->shpool->mutex);
2402 0 : return NGX_OK;
2403 : }
2404 :
2405 0 : ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ctx->log, 0,
2406 : "lua shared dict set: creating a new entry");
2407 :
2408 0 : n = offsetof(ngx_rbtree_node_t, color)
2409 : + offsetof(ngx_http_lua_shdict_node_t, data)
2410 : + key_len
2411 0 : + str_value_len;
2412 :
2413 0 : node = ngx_slab_alloc_locked(ctx->shpool, n);
2414 :
2415 0 : if (node == NULL) {
2416 :
2417 0 : if (op & NGX_HTTP_LUA_SHDICT_SAFE_STORE) {
2418 0 : ngx_shmtx_unlock(&ctx->shpool->mutex);
2419 :
2420 0 : *errmsg = "no memory";
2421 0 : return NGX_ERROR;
2422 : }
2423 :
2424 0 : ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ctx->log, 0,
2425 : "lua shared dict set: overriding non-expired items "
2426 : "due to memory shortage for entry \"%*s\"", key_len,
2427 : key);
2428 :
2429 0 : for (i = 0; i < 30; i++) {
2430 0 : if (ngx_http_lua_shdict_expire(ctx, 0) == 0) {
2431 0 : break;
2432 : }
2433 :
2434 0 : *forcible = 1;
2435 :
2436 0 : node = ngx_slab_alloc_locked(ctx->shpool, n);
2437 0 : if (node != NULL) {
2438 0 : goto allocated;
2439 : }
2440 : }
2441 :
2442 0 : ngx_shmtx_unlock(&ctx->shpool->mutex);
2443 :
2444 0 : *errmsg = "no memory";
2445 0 : return NGX_ERROR;
2446 : }
2447 :
2448 0 : allocated:
2449 :
2450 0 : sd = (ngx_http_lua_shdict_node_t *) &node->color;
2451 :
2452 0 : node->key = hash;
2453 0 : sd->key_len = (u_short) key_len;
2454 :
2455 0 : if (exptime > 0) {
2456 0 : tp = ngx_timeofday();
2457 0 : sd->expires = (uint64_t) tp->sec * 1000 + tp->msec
2458 0 : + (uint64_t) exptime;
2459 :
2460 : } else {
2461 0 : sd->expires = 0;
2462 : }
2463 :
2464 0 : sd->user_flags = user_flags;
2465 0 : sd->value_len = (uint32_t) str_value_len;
2466 : dd("setting value type to %d", value_type);
2467 0 : sd->value_type = (uint8_t) value_type;
2468 :
2469 0 : p = ngx_copy(sd->data, key, key_len);
2470 0 : ngx_memcpy(p, str_value_buf, str_value_len);
2471 :
2472 0 : ngx_rbtree_insert(&ctx->sh->rbtree, node);
2473 0 : ngx_queue_insert_head(&ctx->sh->lru_queue, &sd->queue);
2474 0 : ngx_shmtx_unlock(&ctx->shpool->mutex);
2475 :
2476 0 : return NGX_OK;
2477 : }
2478 :
2479 :
2480 : int
2481 0 : ngx_http_lua_ffi_shdict_get(ngx_shm_zone_t *zone, u_char *key,
2482 : size_t key_len, int *value_type, u_char **str_value_buf,
2483 : size_t *str_value_len, double *num_value, int *user_flags,
2484 : int get_stale, int *is_stale, char **err)
2485 : {
2486 : ngx_str_t name;
2487 : uint32_t hash;
2488 : ngx_int_t rc;
2489 : ngx_http_lua_shdict_ctx_t *ctx;
2490 : ngx_http_lua_shdict_node_t *sd;
2491 : ngx_str_t value;
2492 :
2493 0 : if (zone == NULL) {
2494 0 : return NGX_ERROR;
2495 : }
2496 :
2497 0 : *err = NULL;
2498 :
2499 0 : ctx = zone->data;
2500 0 : name = ctx->name;
2501 :
2502 0 : hash = ngx_crc32_short(key, key_len);
2503 :
2504 : #if (NGX_DEBUG)
2505 0 : ngx_log_debug3(NGX_LOG_DEBUG_HTTP, ctx->log, 0,
2506 : "fetching key \"%*s\" in shared dict \"%V\"", key_len,
2507 : key, &name);
2508 : #endif /* NGX_DEBUG */
2509 :
2510 0 : ngx_shmtx_lock(&ctx->shpool->mutex);
2511 :
2512 : #if 1
2513 0 : if (!get_stale) {
2514 0 : ngx_http_lua_shdict_expire(ctx, 1);
2515 : }
2516 : #endif
2517 :
2518 0 : rc = ngx_http_lua_shdict_lookup(zone, hash, key, key_len, &sd);
2519 :
2520 : dd("shdict lookup returns %d", (int) rc);
2521 :
2522 0 : if (rc == NGX_DECLINED || (rc == NGX_DONE && !get_stale)) {
2523 0 : ngx_shmtx_unlock(&ctx->shpool->mutex);
2524 0 : *value_type = LUA_TNIL;
2525 0 : return NGX_OK;
2526 : }
2527 :
2528 : /* rc == NGX_OK || (rc == NGX_DONE && get_stale) */
2529 :
2530 0 : *value_type = sd->value_type;
2531 :
2532 : dd("data: %p", sd->data);
2533 : dd("key len: %d", (int) sd->key_len);
2534 :
2535 0 : value.data = sd->data + sd->key_len;
2536 0 : value.len = (size_t) sd->value_len;
2537 :
2538 0 : if (*str_value_len < (size_t) value.len) {
2539 0 : if (*value_type == SHDICT_TBOOLEAN) {
2540 0 : ngx_shmtx_unlock(&ctx->shpool->mutex);
2541 0 : return NGX_ERROR;
2542 : }
2543 :
2544 0 : if (*value_type == SHDICT_TSTRING) {
2545 0 : *str_value_buf = malloc(value.len);
2546 0 : if (*str_value_buf == NULL) {
2547 0 : ngx_shmtx_unlock(&ctx->shpool->mutex);
2548 0 : return NGX_ERROR;
2549 : }
2550 : }
2551 : }
2552 :
2553 0 : switch (*value_type) {
2554 :
2555 0 : case SHDICT_TSTRING:
2556 0 : *str_value_len = value.len;
2557 0 : ngx_memcpy(*str_value_buf, value.data, value.len);
2558 0 : break;
2559 :
2560 0 : case SHDICT_TNUMBER:
2561 :
2562 0 : if (value.len != sizeof(double)) {
2563 0 : ngx_shmtx_unlock(&ctx->shpool->mutex);
2564 0 : ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,
2565 : "bad lua number value size found for key %*s "
2566 : "in shared_dict %V: %z", key_len, key,
2567 : &name, value.len);
2568 0 : return NGX_ERROR;
2569 : }
2570 :
2571 0 : *str_value_len = value.len;
2572 0 : ngx_memcpy(num_value, value.data, sizeof(double));
2573 0 : break;
2574 :
2575 0 : case SHDICT_TBOOLEAN:
2576 :
2577 0 : if (value.len != sizeof(u_char)) {
2578 0 : ngx_shmtx_unlock(&ctx->shpool->mutex);
2579 0 : ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,
2580 : "bad lua boolean value size found for key %*s "
2581 : "in shared_dict %V: %z", key_len, key, &name,
2582 : value.len);
2583 0 : return NGX_ERROR;
2584 : }
2585 :
2586 0 : ngx_memcpy(*str_value_buf, value.data, value.len);
2587 0 : break;
2588 :
2589 0 : case SHDICT_TLIST:
2590 :
2591 0 : ngx_shmtx_unlock(&ctx->shpool->mutex);
2592 :
2593 0 : *err = "value is a list";
2594 0 : return NGX_ERROR;
2595 :
2596 0 : default:
2597 :
2598 0 : ngx_shmtx_unlock(&ctx->shpool->mutex);
2599 0 : ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,
2600 : "bad value type found for key %*s in "
2601 : "shared_dict %V: %d", key_len, key, &name,
2602 : *value_type);
2603 0 : return NGX_ERROR;
2604 : }
2605 :
2606 0 : *user_flags = sd->user_flags;
2607 : dd("user flags: %d", *user_flags);
2608 :
2609 0 : ngx_shmtx_unlock(&ctx->shpool->mutex);
2610 :
2611 0 : if (get_stale) {
2612 :
2613 : /* always return value, flags, stale */
2614 :
2615 0 : *is_stale = (rc == NGX_DONE);
2616 0 : return NGX_OK;
2617 : }
2618 :
2619 0 : return NGX_OK;
2620 : }
2621 :
2622 :
2623 : int
2624 0 : ngx_http_lua_ffi_shdict_incr(ngx_shm_zone_t *zone, u_char *key,
2625 : size_t key_len, double *value, char **err, int has_init, double init,
2626 : int *forcible)
2627 : {
2628 : int i, n;
2629 : uint32_t hash;
2630 : ngx_int_t rc;
2631 : ngx_http_lua_shdict_ctx_t *ctx;
2632 : ngx_http_lua_shdict_node_t *sd;
2633 : double num;
2634 : ngx_rbtree_node_t *node;
2635 : u_char *p;
2636 : ngx_queue_t *queue, *q;
2637 :
2638 0 : if (zone == NULL) {
2639 0 : return NGX_ERROR;
2640 : }
2641 :
2642 0 : ctx = zone->data;
2643 :
2644 0 : *forcible = 0;
2645 :
2646 0 : hash = ngx_crc32_short(key, key_len);
2647 :
2648 : dd("looking up key %.*s in shared dict %.*s", (int) key_len, key,
2649 : (int) ctx->name.len, ctx->name.data);
2650 :
2651 0 : ngx_shmtx_lock(&ctx->shpool->mutex);
2652 : #if 1
2653 0 : ngx_http_lua_shdict_expire(ctx, 1);
2654 : #endif
2655 0 : rc = ngx_http_lua_shdict_lookup(zone, hash, key, key_len, &sd);
2656 :
2657 : dd("shdict lookup returned %d", (int) rc);
2658 :
2659 0 : if (rc == NGX_DECLINED || rc == NGX_DONE) {
2660 0 : if (!has_init) {
2661 0 : ngx_shmtx_unlock(&ctx->shpool->mutex);
2662 0 : *err = "not found";
2663 0 : return NGX_ERROR;
2664 : }
2665 :
2666 : /* add value */
2667 0 : num = *value + init;
2668 :
2669 0 : if (rc == NGX_DONE) {
2670 :
2671 : /* found an expired item */
2672 :
2673 0 : if ((size_t) sd->value_len == sizeof(double)
2674 0 : && sd->value_type != SHDICT_TLIST)
2675 : {
2676 0 : ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ctx->log, 0,
2677 : "lua shared dict incr: found old entry and "
2678 : "value size matched, reusing it");
2679 :
2680 0 : ngx_queue_remove(&sd->queue);
2681 0 : ngx_queue_insert_head(&ctx->sh->lru_queue, &sd->queue);
2682 :
2683 : dd("go to setvalue");
2684 0 : goto setvalue;
2685 : }
2686 :
2687 : dd("go to remove");
2688 0 : goto remove;
2689 : }
2690 :
2691 : dd("go to insert");
2692 0 : goto insert;
2693 : }
2694 :
2695 : /* rc == NGX_OK */
2696 :
2697 0 : if (sd->value_type != SHDICT_TNUMBER || sd->value_len != sizeof(double)) {
2698 0 : ngx_shmtx_unlock(&ctx->shpool->mutex);
2699 0 : *err = "not a number";
2700 0 : return NGX_ERROR;
2701 : }
2702 :
2703 0 : ngx_queue_remove(&sd->queue);
2704 0 : ngx_queue_insert_head(&ctx->sh->lru_queue, &sd->queue);
2705 :
2706 : dd("setting value type to %d", (int) sd->value_type);
2707 :
2708 0 : p = sd->data + key_len;
2709 :
2710 0 : ngx_memcpy(&num, p, sizeof(double));
2711 0 : num += *value;
2712 :
2713 0 : ngx_memcpy(p, (double *) &num, sizeof(double));
2714 :
2715 0 : ngx_shmtx_unlock(&ctx->shpool->mutex);
2716 :
2717 0 : *value = num;
2718 0 : return NGX_OK;
2719 :
2720 0 : remove:
2721 :
2722 0 : ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ctx->log, 0,
2723 : "lua shared dict incr: found old entry but value size "
2724 : "NOT matched, removing it first");
2725 :
2726 0 : if (sd->value_type == SHDICT_TLIST) {
2727 0 : queue = ngx_http_lua_shdict_get_list_head(sd, key_len);
2728 :
2729 0 : for (q = ngx_queue_head(queue);
2730 : q != ngx_queue_sentinel(queue);
2731 0 : q = ngx_queue_next(q))
2732 : {
2733 0 : p = (u_char *) ngx_queue_data(q, ngx_http_lua_shdict_list_node_t,
2734 : queue);
2735 :
2736 0 : ngx_slab_free_locked(ctx->shpool, p);
2737 : }
2738 : }
2739 :
2740 0 : ngx_queue_remove(&sd->queue);
2741 :
2742 0 : node = (ngx_rbtree_node_t *)
2743 0 : ((u_char *) sd - offsetof(ngx_rbtree_node_t, color));
2744 :
2745 0 : ngx_rbtree_delete(&ctx->sh->rbtree, node);
2746 :
2747 0 : ngx_slab_free_locked(ctx->shpool, node);
2748 :
2749 0 : insert:
2750 :
2751 0 : ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ctx->log, 0,
2752 : "lua shared dict incr: creating a new entry");
2753 :
2754 0 : n = offsetof(ngx_rbtree_node_t, color)
2755 : + offsetof(ngx_http_lua_shdict_node_t, data)
2756 : + key_len
2757 0 : + sizeof(double);
2758 :
2759 0 : node = ngx_slab_alloc_locked(ctx->shpool, n);
2760 :
2761 0 : if (node == NULL) {
2762 :
2763 0 : ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ctx->log, 0,
2764 : "lua shared dict incr: overriding non-expired items "
2765 : "due to memory shortage for entry \"%*s\"", key_len,
2766 : key);
2767 :
2768 0 : for (i = 0; i < 30; i++) {
2769 0 : if (ngx_http_lua_shdict_expire(ctx, 0) == 0) {
2770 0 : break;
2771 : }
2772 :
2773 0 : *forcible = 1;
2774 :
2775 0 : node = ngx_slab_alloc_locked(ctx->shpool, n);
2776 0 : if (node != NULL) {
2777 0 : goto allocated;
2778 : }
2779 : }
2780 :
2781 0 : ngx_shmtx_unlock(&ctx->shpool->mutex);
2782 :
2783 0 : *err = "no memory";
2784 0 : return NGX_ERROR;
2785 : }
2786 :
2787 0 : allocated:
2788 :
2789 0 : sd = (ngx_http_lua_shdict_node_t *) &node->color;
2790 :
2791 0 : node->key = hash;
2792 :
2793 0 : sd->key_len = (u_short) key_len;
2794 :
2795 0 : sd->value_len = (uint32_t) sizeof(double);
2796 :
2797 0 : ngx_rbtree_insert(&ctx->sh->rbtree, node);
2798 :
2799 0 : ngx_queue_insert_head(&ctx->sh->lru_queue, &sd->queue);
2800 :
2801 0 : setvalue:
2802 :
2803 0 : sd->user_flags = 0;
2804 :
2805 0 : sd->expires = 0;
2806 :
2807 : dd("setting value type to %d", LUA_TNUMBER);
2808 :
2809 0 : sd->value_type = (uint8_t) LUA_TNUMBER;
2810 :
2811 0 : p = ngx_copy(sd->data, key, key_len);
2812 0 : ngx_memcpy(p, (double *) &num, sizeof(double));
2813 :
2814 0 : ngx_shmtx_unlock(&ctx->shpool->mutex);
2815 :
2816 0 : *value = num;
2817 0 : return NGX_OK;
2818 : }
2819 :
2820 :
2821 : int
2822 0 : ngx_http_lua_ffi_shdict_flush_all(ngx_shm_zone_t *zone)
2823 : {
2824 : ngx_queue_t *q;
2825 : ngx_http_lua_shdict_node_t *sd;
2826 : ngx_http_lua_shdict_ctx_t *ctx;
2827 :
2828 0 : ctx = zone->data;
2829 :
2830 0 : ngx_shmtx_lock(&ctx->shpool->mutex);
2831 :
2832 0 : for (q = ngx_queue_head(&ctx->sh->lru_queue);
2833 0 : q != ngx_queue_sentinel(&ctx->sh->lru_queue);
2834 0 : q = ngx_queue_next(q))
2835 : {
2836 0 : sd = ngx_queue_data(q, ngx_http_lua_shdict_node_t, queue);
2837 0 : sd->expires = 1;
2838 : }
2839 :
2840 0 : ngx_http_lua_shdict_expire(ctx, 0);
2841 :
2842 0 : ngx_shmtx_unlock(&ctx->shpool->mutex);
2843 :
2844 0 : return NGX_OK;
2845 : }
2846 :
2847 :
2848 : static ngx_int_t
2849 0 : ngx_http_lua_shdict_peek(ngx_shm_zone_t *shm_zone, ngx_uint_t hash,
2850 : u_char *kdata, size_t klen, ngx_http_lua_shdict_node_t **sdp)
2851 : {
2852 : ngx_int_t rc;
2853 : ngx_rbtree_node_t *node, *sentinel;
2854 : ngx_http_lua_shdict_ctx_t *ctx;
2855 : ngx_http_lua_shdict_node_t *sd;
2856 :
2857 0 : ctx = shm_zone->data;
2858 :
2859 0 : node = ctx->sh->rbtree.root;
2860 0 : sentinel = ctx->sh->rbtree.sentinel;
2861 :
2862 0 : while (node != sentinel) {
2863 :
2864 0 : if (hash < node->key) {
2865 0 : node = node->left;
2866 0 : continue;
2867 : }
2868 :
2869 0 : if (hash > node->key) {
2870 0 : node = node->right;
2871 0 : continue;
2872 : }
2873 :
2874 : /* hash == node->key */
2875 :
2876 0 : sd = (ngx_http_lua_shdict_node_t *) &node->color;
2877 :
2878 0 : rc = ngx_memn2cmp(kdata, sd->data, klen, (size_t) sd->key_len);
2879 :
2880 0 : if (rc == 0) {
2881 0 : *sdp = sd;
2882 :
2883 0 : return NGX_OK;
2884 : }
2885 :
2886 0 : node = (rc < 0) ? node->left : node->right;
2887 : }
2888 :
2889 0 : *sdp = NULL;
2890 :
2891 0 : return NGX_DECLINED;
2892 : }
2893 :
2894 :
2895 : int
2896 0 : ngx_http_lua_ffi_shdict_get_ttl(ngx_shm_zone_t *zone, u_char *key,
2897 : size_t key_len)
2898 : {
2899 : uint32_t hash;
2900 : uint64_t now;
2901 : uint64_t expires;
2902 : ngx_int_t rc;
2903 : ngx_time_t *tp;
2904 : ngx_http_lua_shdict_ctx_t *ctx;
2905 : ngx_http_lua_shdict_node_t *sd;
2906 :
2907 0 : if (zone == NULL) {
2908 0 : return NGX_ERROR;
2909 : }
2910 :
2911 0 : ctx = zone->data;
2912 0 : hash = ngx_crc32_short(key, key_len);
2913 :
2914 0 : ngx_shmtx_lock(&ctx->shpool->mutex);
2915 :
2916 0 : rc = ngx_http_lua_shdict_peek(zone, hash, key, key_len, &sd);
2917 :
2918 0 : if (rc == NGX_DECLINED) {
2919 0 : ngx_shmtx_unlock(&ctx->shpool->mutex);
2920 :
2921 0 : return NGX_DECLINED;
2922 : }
2923 :
2924 : /* rc == NGX_OK */
2925 :
2926 0 : expires = sd->expires;
2927 :
2928 0 : ngx_shmtx_unlock(&ctx->shpool->mutex);
2929 :
2930 0 : if (expires == 0) {
2931 0 : return 0;
2932 : }
2933 :
2934 0 : tp = ngx_timeofday();
2935 0 : now = (uint64_t) tp->sec * 1000 + tp->msec;
2936 :
2937 0 : return expires - now;
2938 : }
2939 :
2940 :
2941 : int
2942 0 : ngx_http_lua_ffi_shdict_set_expire(ngx_shm_zone_t *zone, u_char *key,
2943 : size_t key_len, int exptime)
2944 : {
2945 : uint32_t hash;
2946 : ngx_int_t rc;
2947 0 : ngx_time_t *tp = NULL;
2948 : ngx_http_lua_shdict_ctx_t *ctx;
2949 : ngx_http_lua_shdict_node_t *sd;
2950 :
2951 0 : if (zone == NULL) {
2952 0 : return NGX_ERROR;
2953 : }
2954 :
2955 0 : if (exptime > 0) {
2956 0 : tp = ngx_timeofday();
2957 : }
2958 :
2959 0 : ctx = zone->data;
2960 0 : hash = ngx_crc32_short(key, key_len);
2961 :
2962 0 : ngx_shmtx_lock(&ctx->shpool->mutex);
2963 :
2964 0 : rc = ngx_http_lua_shdict_peek(zone, hash, key, key_len, &sd);
2965 :
2966 0 : if (rc == NGX_DECLINED) {
2967 0 : ngx_shmtx_unlock(&ctx->shpool->mutex);
2968 :
2969 0 : return NGX_DECLINED;
2970 : }
2971 :
2972 : /* rc == NGX_OK */
2973 :
2974 0 : if (exptime > 0) {
2975 0 : sd->expires = (uint64_t) tp->sec * 1000 + tp->msec
2976 0 : + (uint64_t) exptime;
2977 :
2978 : } else {
2979 0 : sd->expires = 0;
2980 : }
2981 :
2982 0 : ngx_shmtx_unlock(&ctx->shpool->mutex);
2983 :
2984 0 : return NGX_OK;
2985 : }
2986 :
2987 :
2988 : size_t
2989 0 : ngx_http_lua_ffi_shdict_capacity(ngx_shm_zone_t *zone)
2990 : {
2991 0 : return zone->shm.size;
2992 : }
2993 :
2994 :
2995 : # if nginx_version >= 1011007
2996 : size_t
2997 0 : ngx_http_lua_ffi_shdict_free_space(ngx_shm_zone_t *zone)
2998 : {
2999 : size_t bytes;
3000 : ngx_http_lua_shdict_ctx_t *ctx;
3001 :
3002 0 : ctx = zone->data;
3003 :
3004 0 : ngx_shmtx_lock(&ctx->shpool->mutex);
3005 0 : bytes = ctx->shpool->pfree * ngx_pagesize;
3006 0 : ngx_shmtx_unlock(&ctx->shpool->mutex);
3007 :
3008 0 : return bytes;
3009 : }
3010 : # endif /* nginx_version >= 1011007 */
3011 :
3012 :
3013 : #endif /* NGX_LUA_NO_FFI_API */
3014 :
3015 :
3016 : /* vi:set ft=c ts=4 sw=4 et fdm=marker: */
|