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_expire(ngx_http_lua_shdict_ctx_t *ctx,
19 ngx_uint_t n);
20 static ngx_int_t ngx_http_lua_shdict_lookup(ngx_shm_zone_t *shm_zone,
21 ngx_uint_t hash, u_char *kdata, size_t klen,
22 ngx_http_lua_shdict_node_t **sdp);
23 static int ngx_http_lua_shdict_flush_expired(lua_State *L);
24 static int ngx_http_lua_shdict_get_keys(lua_State *L);
25 static int ngx_http_lua_shdict_lpush(lua_State *L);
26 static int ngx_http_lua_shdict_rpush(lua_State *L);
27 static int ngx_http_lua_shdict_push_helper(lua_State *L, int flags);
28 static int ngx_http_lua_shdict_lpop(lua_State *L);
29 static int ngx_http_lua_shdict_rpop(lua_State *L);
30 static int ngx_http_lua_shdict_pop_helper(lua_State *L, int flags);
31 static int ngx_http_lua_shdict_llen(lua_State *L);
32
33
34 static ngx_inline ngx_shm_zone_t *ngx_http_lua_shdict_get_zone(lua_State *L,
35 int index);
36
37
38 #define NGX_HTTP_LUA_SHDICT_ADD 0x0001
39 #define NGX_HTTP_LUA_SHDICT_REPLACE 0x0002
40 #define NGX_HTTP_LUA_SHDICT_SAFE_STORE 0x0004
41
42
43 #define NGX_HTTP_LUA_SHDICT_LEFT 0x0001
44 #define NGX_HTTP_LUA_SHDICT_RIGHT 0x0002
45
46
47 enum {
48 SHDICT_USERDATA_INDEX = 1,
49 };
50
51
52 enum {
53 SHDICT_TNIL = 0, /* same as LUA_TNIL */
54 SHDICT_TBOOLEAN = 1, /* same as LUA_TBOOLEAN */
55 SHDICT_TNUMBER = 3, /* same as LUA_TNUMBER */
56 SHDICT_TSTRING = 4, /* same as LUA_TSTRING */
57 SHDICT_TLIST = 5,
58 };
59
60
61 static ngx_inline ngx_queue_t *
ngx_http_lua_shdict_get_list_head(ngx_http_lua_shdict_node_t * sd,size_t len)62 ngx_http_lua_shdict_get_list_head(ngx_http_lua_shdict_node_t *sd, size_t len)
63 {
64 return (ngx_queue_t *) ngx_align_ptr(((u_char *) &sd->data + len),
65 NGX_ALIGNMENT);
66 }
67
68
69 ngx_int_t
ngx_http_lua_shdict_init_zone(ngx_shm_zone_t * shm_zone,void * data)70 ngx_http_lua_shdict_init_zone(ngx_shm_zone_t *shm_zone, void *data)
71 {
72 ngx_http_lua_shdict_ctx_t *octx = data;
73
74 size_t len;
75 ngx_http_lua_shdict_ctx_t *ctx;
76
77 dd("init zone");
78
79 ctx = shm_zone->data;
80
81 if (octx) {
82 ctx->sh = octx->sh;
83 ctx->shpool = octx->shpool;
84
85 return NGX_OK;
86 }
87
88 ctx->shpool = (ngx_slab_pool_t *) shm_zone->shm.addr;
89
90 if (shm_zone->shm.exists) {
91 ctx->sh = ctx->shpool->data;
92
93 return NGX_OK;
94 }
95
96 ctx->sh = ngx_slab_alloc(ctx->shpool, sizeof(ngx_http_lua_shdict_shctx_t));
97 if (ctx->sh == NULL) {
98 return NGX_ERROR;
99 }
100
101 ctx->shpool->data = ctx->sh;
102
103 ngx_rbtree_init(&ctx->sh->rbtree, &ctx->sh->sentinel,
104 ngx_http_lua_shdict_rbtree_insert_value);
105
106 ngx_queue_init(&ctx->sh->lru_queue);
107
108 len = sizeof(" in lua_shared_dict zone \"\"") + shm_zone->shm.name.len;
109
110 ctx->shpool->log_ctx = ngx_slab_alloc(ctx->shpool, len);
111 if (ctx->shpool->log_ctx == NULL) {
112 return NGX_ERROR;
113 }
114
115 ngx_sprintf(ctx->shpool->log_ctx, " in lua_shared_dict zone \"%V\"%Z",
116 &shm_zone->shm.name);
117
118 ctx->shpool->log_nomem = 0;
119
120 return NGX_OK;
121 }
122
123
124 void
ngx_http_lua_shdict_rbtree_insert_value(ngx_rbtree_node_t * temp,ngx_rbtree_node_t * node,ngx_rbtree_node_t * sentinel)125 ngx_http_lua_shdict_rbtree_insert_value(ngx_rbtree_node_t *temp,
126 ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel)
127 {
128 ngx_rbtree_node_t **p;
129 ngx_http_lua_shdict_node_t *sdn, *sdnt;
130
131 for ( ;; ) {
132
133 if (node->key < temp->key) {
134
135 p = &temp->left;
136
137 } else if (node->key > temp->key) {
138
139 p = &temp->right;
140
141 } else { /* node->key == temp->key */
142
143 sdn = (ngx_http_lua_shdict_node_t *) &node->color;
144 sdnt = (ngx_http_lua_shdict_node_t *) &temp->color;
145
146 p = ngx_memn2cmp(sdn->data, sdnt->data, sdn->key_len,
147 sdnt->key_len) < 0 ? &temp->left : &temp->right;
148 }
149
150 if (*p == sentinel) {
151 break;
152 }
153
154 temp = *p;
155 }
156
157 *p = node;
158 node->parent = temp;
159 node->left = sentinel;
160 node->right = sentinel;
161 ngx_rbt_red(node);
162 }
163
164
165 static ngx_int_t
ngx_http_lua_shdict_lookup(ngx_shm_zone_t * shm_zone,ngx_uint_t hash,u_char * kdata,size_t klen,ngx_http_lua_shdict_node_t ** sdp)166 ngx_http_lua_shdict_lookup(ngx_shm_zone_t *shm_zone, ngx_uint_t hash,
167 u_char *kdata, size_t klen, ngx_http_lua_shdict_node_t **sdp)
168 {
169 ngx_int_t rc;
170 ngx_time_t *tp;
171 uint64_t now;
172 int64_t ms;
173 ngx_rbtree_node_t *node, *sentinel;
174 ngx_http_lua_shdict_ctx_t *ctx;
175 ngx_http_lua_shdict_node_t *sd;
176
177 ctx = shm_zone->data;
178
179 node = ctx->sh->rbtree.root;
180 sentinel = ctx->sh->rbtree.sentinel;
181
182 while (node != sentinel) {
183
184 if (hash < node->key) {
185 node = node->left;
186 continue;
187 }
188
189 if (hash > node->key) {
190 node = node->right;
191 continue;
192 }
193
194 /* hash == node->key */
195
196 sd = (ngx_http_lua_shdict_node_t *) &node->color;
197
198 rc = ngx_memn2cmp(kdata, sd->data, klen, (size_t) sd->key_len);
199
200 if (rc == 0) {
201 ngx_queue_remove(&sd->queue);
202 ngx_queue_insert_head(&ctx->sh->lru_queue, &sd->queue);
203
204 *sdp = sd;
205
206 dd("node expires: %lld", (long long) sd->expires);
207
208 if (sd->expires != 0) {
209 tp = ngx_timeofday();
210
211 now = (uint64_t) tp->sec * 1000 + tp->msec;
212 ms = sd->expires - now;
213
214 dd("time to live: %lld", (long long) ms);
215
216 if (ms < 0) {
217 dd("node already expired");
218 return NGX_DONE;
219 }
220 }
221
222 return NGX_OK;
223 }
224
225 node = (rc < 0) ? node->left : node->right;
226 }
227
228 *sdp = NULL;
229
230 return NGX_DECLINED;
231 }
232
233
234 static int
ngx_http_lua_shdict_expire(ngx_http_lua_shdict_ctx_t * ctx,ngx_uint_t n)235 ngx_http_lua_shdict_expire(ngx_http_lua_shdict_ctx_t *ctx, ngx_uint_t n)
236 {
237 ngx_time_t *tp;
238 uint64_t now;
239 ngx_queue_t *q, *list_queue, *lq;
240 int64_t ms;
241 ngx_rbtree_node_t *node;
242 ngx_http_lua_shdict_node_t *sd;
243 int freed = 0;
244 ngx_http_lua_shdict_list_node_t *lnode;
245
246 tp = ngx_timeofday();
247
248 now = (uint64_t) tp->sec * 1000 + tp->msec;
249
250 /*
251 * n == 1 deletes one or two expired entries
252 * n == 0 deletes oldest entry by force
253 * and one or two zero rate entries
254 */
255
256 while (n < 3) {
257
258 if (ngx_queue_empty(&ctx->sh->lru_queue)) {
259 return freed;
260 }
261
262 q = ngx_queue_last(&ctx->sh->lru_queue);
263
264 sd = ngx_queue_data(q, ngx_http_lua_shdict_node_t, queue);
265
266 if (n++ != 0) {
267
268 if (sd->expires == 0) {
269 return freed;
270 }
271
272 ms = sd->expires - now;
273 if (ms > 0) {
274 return freed;
275 }
276 }
277
278 if (sd->value_type == SHDICT_TLIST) {
279 list_queue = ngx_http_lua_shdict_get_list_head(sd, sd->key_len);
280
281 for (lq = ngx_queue_head(list_queue);
282 lq != ngx_queue_sentinel(list_queue);
283 lq = ngx_queue_next(lq))
284 {
285 lnode = ngx_queue_data(lq, ngx_http_lua_shdict_list_node_t,
286 queue);
287
288 ngx_slab_free_locked(ctx->shpool, lnode);
289 }
290 }
291
292 ngx_queue_remove(q);
293
294 node = (ngx_rbtree_node_t *)
295 ((u_char *) sd - offsetof(ngx_rbtree_node_t, color));
296
297 ngx_rbtree_delete(&ctx->sh->rbtree, node);
298
299 ngx_slab_free_locked(ctx->shpool, node);
300
301 freed++;
302 }
303
304 return freed;
305 }
306
307
308 void
ngx_http_lua_inject_shdict_api(ngx_http_lua_main_conf_t * lmcf,lua_State * L)309 ngx_http_lua_inject_shdict_api(ngx_http_lua_main_conf_t *lmcf, lua_State *L)
310 {
311 ngx_http_lua_shdict_ctx_t *ctx;
312 ngx_uint_t i;
313 ngx_shm_zone_t **zone;
314 ngx_shm_zone_t **zone_udata;
315
316 if (lmcf->shdict_zones != NULL) {
317 lua_createtable(L, 0, lmcf->shdict_zones->nelts /* nrec */);
318 /* ngx.shared */
319
320 lua_createtable(L, 0 /* narr */, 22 /* nrec */); /* shared mt */
321
322 lua_pushcfunction(L, ngx_http_lua_shdict_lpush);
323 lua_setfield(L, -2, "lpush");
324
325 lua_pushcfunction(L, ngx_http_lua_shdict_rpush);
326 lua_setfield(L, -2, "rpush");
327
328 lua_pushcfunction(L, ngx_http_lua_shdict_lpop);
329 lua_setfield(L, -2, "lpop");
330
331 lua_pushcfunction(L, ngx_http_lua_shdict_rpop);
332 lua_setfield(L, -2, "rpop");
333
334 lua_pushcfunction(L, ngx_http_lua_shdict_llen);
335 lua_setfield(L, -2, "llen");
336
337 lua_pushcfunction(L, ngx_http_lua_shdict_flush_expired);
338 lua_setfield(L, -2, "flush_expired");
339
340 lua_pushcfunction(L, ngx_http_lua_shdict_get_keys);
341 lua_setfield(L, -2, "get_keys");
342
343 lua_pushvalue(L, -1); /* shared mt mt */
344 lua_setfield(L, -2, "__index"); /* shared mt */
345
346 zone = lmcf->shdict_zones->elts;
347
348 for (i = 0; i < lmcf->shdict_zones->nelts; i++) {
349 ctx = zone[i]->data;
350
351 lua_pushlstring(L, (char *) ctx->name.data, ctx->name.len);
352 /* shared mt key */
353
354 lua_createtable(L, 1 /* narr */, 0 /* nrec */);
355 /* table of zone[i] */
356 zone_udata = lua_newuserdata(L, sizeof(ngx_shm_zone_t *));
357 /* shared mt key ud */
358 *zone_udata = zone[i];
359 lua_rawseti(L, -2, SHDICT_USERDATA_INDEX); /* {zone[i]} */
360 lua_pushvalue(L, -3); /* shared mt key ud mt */
361 lua_setmetatable(L, -2); /* shared mt key ud */
362 lua_rawset(L, -4); /* shared mt */
363 }
364
365 lua_pop(L, 1); /* shared */
366
367 } else {
368 lua_newtable(L); /* ngx.shared */
369 }
370
371 lua_setfield(L, -2, "shared");
372 }
373
374
375 static ngx_inline ngx_shm_zone_t *
ngx_http_lua_shdict_get_zone(lua_State * L,int index)376 ngx_http_lua_shdict_get_zone(lua_State *L, int index)
377 {
378 ngx_shm_zone_t *zone;
379 ngx_shm_zone_t **zone_udata;
380
381 lua_rawgeti(L, index, SHDICT_USERDATA_INDEX);
382 zone_udata = lua_touserdata(L, -1);
383 lua_pop(L, 1);
384
385 if (zone_udata == NULL) {
386 return NULL;
387 }
388
389 zone = *zone_udata;
390 return zone;
391 }
392
393
394 static int
ngx_http_lua_shdict_flush_expired(lua_State * L)395 ngx_http_lua_shdict_flush_expired(lua_State *L)
396 {
397 ngx_queue_t *q, *prev, *list_queue, *lq;
398 ngx_http_lua_shdict_node_t *sd;
399 ngx_http_lua_shdict_ctx_t *ctx;
400 ngx_shm_zone_t *zone;
401 ngx_time_t *tp;
402 int freed = 0;
403 int attempts = 0;
404 ngx_rbtree_node_t *node;
405 uint64_t now;
406 int n;
407 ngx_http_lua_shdict_list_node_t *lnode;
408
409 n = lua_gettop(L);
410
411 if (n != 1 && n != 2) {
412 return luaL_error(L, "expecting 1 or 2 argument(s), but saw %d", n);
413 }
414
415 luaL_checktype(L, 1, LUA_TTABLE);
416
417 zone = ngx_http_lua_shdict_get_zone(L, 1);
418 if (zone == NULL) {
419 return luaL_error(L, "bad user data for the ngx_shm_zone_t pointer");
420 }
421
422 if (n == 2) {
423 attempts = luaL_checkint(L, 2);
424 }
425
426 ctx = zone->data;
427
428 ngx_shmtx_lock(&ctx->shpool->mutex);
429
430 if (ngx_queue_empty(&ctx->sh->lru_queue)) {
431 ngx_shmtx_unlock(&ctx->shpool->mutex);
432 lua_pushnumber(L, 0);
433 return 1;
434 }
435
436 tp = ngx_timeofday();
437
438 now = (uint64_t) tp->sec * 1000 + tp->msec;
439
440 q = ngx_queue_last(&ctx->sh->lru_queue);
441
442 while (q != ngx_queue_sentinel(&ctx->sh->lru_queue)) {
443 prev = ngx_queue_prev(q);
444
445 sd = ngx_queue_data(q, ngx_http_lua_shdict_node_t, queue);
446
447 if (sd->expires != 0 && sd->expires <= now) {
448
449 if (sd->value_type == SHDICT_TLIST) {
450 list_queue = ngx_http_lua_shdict_get_list_head(sd, sd->key_len);
451
452 for (lq = ngx_queue_head(list_queue);
453 lq != ngx_queue_sentinel(list_queue);
454 lq = ngx_queue_next(lq))
455 {
456 lnode = ngx_queue_data(lq, ngx_http_lua_shdict_list_node_t,
457 queue);
458
459 ngx_slab_free_locked(ctx->shpool, lnode);
460 }
461 }
462
463 ngx_queue_remove(q);
464
465 node = (ngx_rbtree_node_t *)
466 ((u_char *) sd - offsetof(ngx_rbtree_node_t, color));
467
468 ngx_rbtree_delete(&ctx->sh->rbtree, node);
469 ngx_slab_free_locked(ctx->shpool, node);
470 freed++;
471
472 if (attempts && freed == attempts) {
473 break;
474 }
475 }
476
477 q = prev;
478 }
479
480 ngx_shmtx_unlock(&ctx->shpool->mutex);
481
482 lua_pushnumber(L, freed);
483 return 1;
484 }
485
486
487 /*
488 * This trades CPU for memory. This is potentially slow. O(2n)
489 */
490
491 static int
ngx_http_lua_shdict_get_keys(lua_State * L)492 ngx_http_lua_shdict_get_keys(lua_State *L)
493 {
494 ngx_queue_t *q, *prev;
495 ngx_http_lua_shdict_node_t *sd;
496 ngx_http_lua_shdict_ctx_t *ctx;
497 ngx_shm_zone_t *zone;
498 ngx_time_t *tp;
499 int total = 0;
500 int attempts = 1024;
501 uint64_t now;
502 int n;
503
504 n = lua_gettop(L);
505
506 if (n != 1 && n != 2) {
507 return luaL_error(L, "expecting 1 or 2 argument(s), "
508 "but saw %d", n);
509 }
510
511 luaL_checktype(L, 1, LUA_TTABLE);
512
513 zone = ngx_http_lua_shdict_get_zone(L, 1);
514 if (zone == NULL) {
515 return luaL_error(L, "bad user data for the ngx_shm_zone_t pointer");
516 }
517
518 if (n == 2) {
519 attempts = luaL_checkint(L, 2);
520 }
521
522 ctx = zone->data;
523
524 ngx_shmtx_lock(&ctx->shpool->mutex);
525
526 if (ngx_queue_empty(&ctx->sh->lru_queue)) {
527 ngx_shmtx_unlock(&ctx->shpool->mutex);
528 lua_createtable(L, 0, 0);
529 return 1;
530 }
531
532 tp = ngx_timeofday();
533
534 now = (uint64_t) tp->sec * 1000 + tp->msec;
535
536 /* first run through: get total number of elements we need to allocate */
537
538 q = ngx_queue_last(&ctx->sh->lru_queue);
539
540 while (q != ngx_queue_sentinel(&ctx->sh->lru_queue)) {
541 prev = ngx_queue_prev(q);
542
543 sd = ngx_queue_data(q, ngx_http_lua_shdict_node_t, queue);
544
545 if (sd->expires == 0 || sd->expires > now) {
546 total++;
547 if (attempts && total == attempts) {
548 break;
549 }
550 }
551
552 q = prev;
553 }
554
555 lua_createtable(L, total, 0);
556
557 /* second run through: add keys to table */
558
559 total = 0;
560 q = ngx_queue_last(&ctx->sh->lru_queue);
561
562 while (q != ngx_queue_sentinel(&ctx->sh->lru_queue)) {
563 prev = ngx_queue_prev(q);
564
565 sd = ngx_queue_data(q, ngx_http_lua_shdict_node_t, queue);
566
567 if (sd->expires == 0 || sd->expires > now) {
568 lua_pushlstring(L, (char *) sd->data, sd->key_len);
569 lua_rawseti(L, -2, ++total);
570 if (attempts && total == attempts) {
571 break;
572 }
573 }
574
575 q = prev;
576 }
577
578 ngx_shmtx_unlock(&ctx->shpool->mutex);
579
580 /* table is at top of stack */
581 return 1;
582 }
583
584
585 ngx_int_t
ngx_http_lua_shared_dict_get(ngx_shm_zone_t * zone,u_char * key_data,size_t key_len,ngx_http_lua_value_t * value)586 ngx_http_lua_shared_dict_get(ngx_shm_zone_t *zone, u_char *key_data,
587 size_t key_len, ngx_http_lua_value_t *value)
588 {
589 u_char *data;
590 size_t len;
591 uint32_t hash;
592 ngx_int_t rc;
593 ngx_http_lua_shdict_ctx_t *ctx;
594 ngx_http_lua_shdict_node_t *sd;
595
596 if (zone == NULL) {
597 return NGX_ERROR;
598 }
599
600 hash = ngx_crc32_short(key_data, key_len);
601
602 ctx = zone->data;
603
604 ngx_shmtx_lock(&ctx->shpool->mutex);
605
606 rc = ngx_http_lua_shdict_lookup(zone, hash, key_data, key_len, &sd);
607
608 dd("shdict lookup returned %d", (int) rc);
609
610 if (rc == NGX_DECLINED || rc == NGX_DONE) {
611 ngx_shmtx_unlock(&ctx->shpool->mutex);
612
613 return rc;
614 }
615
616 /* rc == NGX_OK */
617
618 value->type = sd->value_type;
619
620 dd("type: %d", (int) value->type);
621
622 data = sd->data + sd->key_len;
623 len = (size_t) sd->value_len;
624
625 switch (value->type) {
626
627 case SHDICT_TSTRING:
628
629 if (value->value.s.data == NULL || value->value.s.len == 0) {
630 ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, "no string buffer "
631 "initialized");
632 ngx_shmtx_unlock(&ctx->shpool->mutex);
633 return NGX_ERROR;
634 }
635
636 if (len > value->value.s.len) {
637 len = value->value.s.len;
638
639 } else {
640 value->value.s.len = len;
641 }
642
643 ngx_memcpy(value->value.s.data, data, len);
644 break;
645
646 case SHDICT_TNUMBER:
647
648 if (len != sizeof(double)) {
649 ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, "bad lua number "
650 "value size found for key %*s: %lu", key_len,
651 key_data, (unsigned long) len);
652
653 ngx_shmtx_unlock(&ctx->shpool->mutex);
654 return NGX_ERROR;
655 }
656
657 ngx_memcpy(&value->value.b, data, len);
658 break;
659
660 case SHDICT_TBOOLEAN:
661
662 if (len != sizeof(u_char)) {
663 ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, "bad lua boolean "
664 "value size found for key %*s: %lu", key_len,
665 key_data, (unsigned long) len);
666
667 ngx_shmtx_unlock(&ctx->shpool->mutex);
668 return NGX_ERROR;
669 }
670
671 value->value.b = *data;
672 break;
673
674 default:
675 ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, "bad lua value type "
676 "found for key %*s: %d", key_len, key_data,
677 (int) value->type);
678
679 ngx_shmtx_unlock(&ctx->shpool->mutex);
680 return NGX_ERROR;
681 }
682
683 ngx_shmtx_unlock(&ctx->shpool->mutex);
684 return NGX_OK;
685 }
686
687
688 static int
ngx_http_lua_shdict_lpush(lua_State * L)689 ngx_http_lua_shdict_lpush(lua_State *L)
690 {
691 return ngx_http_lua_shdict_push_helper(L, NGX_HTTP_LUA_SHDICT_LEFT);
692 }
693
694
695 static int
ngx_http_lua_shdict_rpush(lua_State * L)696 ngx_http_lua_shdict_rpush(lua_State *L)
697 {
698 return ngx_http_lua_shdict_push_helper(L, NGX_HTTP_LUA_SHDICT_RIGHT);
699 }
700
701
702 static int
ngx_http_lua_shdict_push_helper(lua_State * L,int flags)703 ngx_http_lua_shdict_push_helper(lua_State *L, int flags)
704 {
705 int n;
706 ngx_str_t key;
707 uint32_t hash;
708 ngx_int_t rc;
709 ngx_http_lua_shdict_ctx_t *ctx;
710 ngx_http_lua_shdict_node_t *sd;
711 ngx_str_t value;
712 int value_type;
713 double num;
714 ngx_rbtree_node_t *node;
715 ngx_shm_zone_t *zone;
716 ngx_queue_t *queue, *q;
717 ngx_http_lua_shdict_list_node_t *lnode;
718
719 n = lua_gettop(L);
720
721 if (n != 3) {
722 return luaL_error(L, "expecting 3 arguments, "
723 "but only seen %d", n);
724 }
725
726 if (lua_type(L, 1) != LUA_TTABLE) {
727 return luaL_error(L, "bad \"zone\" argument");
728 }
729
730 zone = ngx_http_lua_shdict_get_zone(L, 1);
731 if (zone == NULL) {
732 return luaL_error(L, "bad \"zone\" argument");
733 }
734
735 ctx = zone->data;
736
737 if (lua_isnil(L, 2)) {
738 lua_pushnil(L);
739 lua_pushliteral(L, "nil key");
740 return 2;
741 }
742
743 key.data = (u_char *) luaL_checklstring(L, 2, &key.len);
744
745 if (key.len == 0) {
746 lua_pushnil(L);
747 lua_pushliteral(L, "empty key");
748 return 2;
749 }
750
751 if (key.len > 65535) {
752 lua_pushnil(L);
753 lua_pushliteral(L, "key too long");
754 return 2;
755 }
756
757 hash = ngx_crc32_short(key.data, key.len);
758
759 value_type = lua_type(L, 3);
760
761 switch (value_type) {
762
763 case SHDICT_TSTRING:
764 value.data = (u_char *) lua_tolstring(L, 3, &value.len);
765 break;
766
767 case SHDICT_TNUMBER:
768 value.len = sizeof(double);
769 num = lua_tonumber(L, 3);
770 value.data = (u_char *) #
771 break;
772
773 default:
774 lua_pushnil(L);
775 lua_pushliteral(L, "bad value type");
776 return 2;
777 }
778
779 ngx_shmtx_lock(&ctx->shpool->mutex);
780
781 #if 1
782 ngx_http_lua_shdict_expire(ctx, 1);
783 #endif
784
785 rc = ngx_http_lua_shdict_lookup(zone, hash, key.data, key.len, &sd);
786
787 dd("shdict lookup returned %d", (int) rc);
788
789 /* exists but expired */
790
791 if (rc == NGX_DONE) {
792
793 if (sd->value_type != SHDICT_TLIST) {
794 /* TODO: reuse when length matched */
795
796 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ctx->log, 0,
797 "lua shared dict push: found old entry and value "
798 "type not matched, remove it first");
799
800 ngx_queue_remove(&sd->queue);
801
802 node = (ngx_rbtree_node_t *)
803 ((u_char *) sd - offsetof(ngx_rbtree_node_t, color));
804
805 ngx_rbtree_delete(&ctx->sh->rbtree, node);
806
807 ngx_slab_free_locked(ctx->shpool, node);
808
809 dd("go to init_list");
810 goto init_list;
811 }
812
813 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ctx->log, 0,
814 "lua shared dict push: found old entry and value "
815 "type matched, reusing it");
816
817 sd->expires = 0;
818
819 /* free list nodes */
820
821 queue = ngx_http_lua_shdict_get_list_head(sd, key.len);
822
823 for (q = ngx_queue_head(queue);
824 q != ngx_queue_sentinel(queue);
825 q = ngx_queue_next(q))
826 {
827 /* TODO: reuse matched size list node */
828 lnode = ngx_queue_data(q, ngx_http_lua_shdict_list_node_t, queue);
829 ngx_slab_free_locked(ctx->shpool, lnode);
830 }
831
832 ngx_queue_init(queue);
833
834 ngx_queue_remove(&sd->queue);
835 ngx_queue_insert_head(&ctx->sh->lru_queue, &sd->queue);
836
837 dd("go to push_node");
838 goto push_node;
839 }
840
841 /* exists and not expired */
842
843 if (rc == NGX_OK) {
844
845 if (sd->value_type != SHDICT_TLIST) {
846 ngx_shmtx_unlock(&ctx->shpool->mutex);
847
848 lua_pushnil(L);
849 lua_pushliteral(L, "value not a list");
850 return 2;
851 }
852
853 queue = ngx_http_lua_shdict_get_list_head(sd, key.len);
854
855 ngx_queue_remove(&sd->queue);
856 ngx_queue_insert_head(&ctx->sh->lru_queue, &sd->queue);
857
858 dd("go to push_node");
859 goto push_node;
860 }
861
862 /* rc == NGX_DECLINED, not found */
863
864 init_list:
865
866 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ctx->log, 0,
867 "lua shared dict list: creating a new entry");
868
869 /* NOTICE: we assume the begin point aligned in slab, be careful */
870 n = offsetof(ngx_rbtree_node_t, color)
871 + offsetof(ngx_http_lua_shdict_node_t, data)
872 + key.len
873 + sizeof(ngx_queue_t);
874
875 dd("length before aligned: %d", n);
876
877 n = (int) (uintptr_t) ngx_align_ptr(n, NGX_ALIGNMENT);
878
879 dd("length after aligned: %d", n);
880
881 node = ngx_slab_alloc_locked(ctx->shpool, n);
882
883 if (node == NULL) {
884 ngx_shmtx_unlock(&ctx->shpool->mutex);
885
886 lua_pushboolean(L, 0);
887 lua_pushliteral(L, "no memory");
888 return 2;
889 }
890
891 sd = (ngx_http_lua_shdict_node_t *) &node->color;
892
893 queue = ngx_http_lua_shdict_get_list_head(sd, key.len);
894
895 node->key = hash;
896 sd->key_len = (u_short) key.len;
897
898 sd->expires = 0;
899
900 sd->value_len = 0;
901
902 dd("setting value type to %d", (int) SHDICT_TLIST);
903
904 sd->value_type = (uint8_t) SHDICT_TLIST;
905
906 ngx_memcpy(sd->data, key.data, key.len);
907
908 ngx_queue_init(queue);
909
910 ngx_rbtree_insert(&ctx->sh->rbtree, node);
911
912 ngx_queue_insert_head(&ctx->sh->lru_queue, &sd->queue);
913
914 push_node:
915
916 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ctx->log, 0,
917 "lua shared dict list: creating a new list node");
918
919 n = offsetof(ngx_http_lua_shdict_list_node_t, data)
920 + value.len;
921
922 dd("list node length: %d", n);
923
924 lnode = ngx_slab_alloc_locked(ctx->shpool, n);
925
926 if (lnode == NULL) {
927
928 if (sd->value_len == 0) {
929
930 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ctx->log, 0,
931 "lua shared dict list: no memory for create"
932 " list node and list empty, remove it");
933
934 ngx_queue_remove(&sd->queue);
935
936 node = (ngx_rbtree_node_t *)
937 ((u_char *) sd - offsetof(ngx_rbtree_node_t, color));
938
939 ngx_rbtree_delete(&ctx->sh->rbtree, node);
940
941 ngx_slab_free_locked(ctx->shpool, node);
942 }
943
944 ngx_shmtx_unlock(&ctx->shpool->mutex);
945
946 lua_pushboolean(L, 0);
947 lua_pushliteral(L, "no memory");
948 return 2;
949 }
950
951 dd("setting list length to %d", sd->value_len + 1);
952
953 sd->value_len = sd->value_len + 1;
954
955 dd("setting list node value length to %d", (int) value.len);
956
957 lnode->value_len = (uint32_t) value.len;
958
959 dd("setting list node value type to %d", value_type);
960
961 lnode->value_type = (uint8_t) value_type;
962
963 ngx_memcpy(lnode->data, value.data, value.len);
964
965 if (flags == NGX_HTTP_LUA_SHDICT_LEFT) {
966 ngx_queue_insert_head(queue, &lnode->queue);
967
968 } else {
969 ngx_queue_insert_tail(queue, &lnode->queue);
970 }
971
972 ngx_shmtx_unlock(&ctx->shpool->mutex);
973
974 lua_pushnumber(L, sd->value_len);
975 return 1;
976 }
977
978
979 static int
ngx_http_lua_shdict_lpop(lua_State * L)980 ngx_http_lua_shdict_lpop(lua_State *L)
981 {
982 return ngx_http_lua_shdict_pop_helper(L, NGX_HTTP_LUA_SHDICT_LEFT);
983 }
984
985
986 static int
ngx_http_lua_shdict_rpop(lua_State * L)987 ngx_http_lua_shdict_rpop(lua_State *L)
988 {
989 return ngx_http_lua_shdict_pop_helper(L, NGX_HTTP_LUA_SHDICT_RIGHT);
990 }
991
992
993 static int
ngx_http_lua_shdict_pop_helper(lua_State * L,int flags)994 ngx_http_lua_shdict_pop_helper(lua_State *L, int flags)
995 {
996 int n;
997 ngx_str_t name;
998 ngx_str_t key;
999 uint32_t hash;
1000 ngx_int_t rc;
1001 ngx_http_lua_shdict_ctx_t *ctx;
1002 ngx_http_lua_shdict_node_t *sd;
1003 ngx_str_t value;
1004 int value_type;
1005 double num;
1006 ngx_rbtree_node_t *node;
1007 ngx_shm_zone_t *zone;
1008 ngx_queue_t *queue;
1009 ngx_http_lua_shdict_list_node_t *lnode;
1010
1011 n = lua_gettop(L);
1012
1013 if (n != 2) {
1014 return luaL_error(L, "expecting 2 arguments, "
1015 "but only seen %d", n);
1016 }
1017
1018 if (lua_type(L, 1) != LUA_TTABLE) {
1019 return luaL_error(L, "bad \"zone\" argument");
1020 }
1021
1022 zone = ngx_http_lua_shdict_get_zone(L, 1);
1023 if (zone == NULL) {
1024 return luaL_error(L, "bad \"zone\" argument");
1025 }
1026
1027 ctx = zone->data;
1028 name = ctx->name;
1029
1030 if (lua_isnil(L, 2)) {
1031 lua_pushnil(L);
1032 lua_pushliteral(L, "nil key");
1033 return 2;
1034 }
1035
1036 key.data = (u_char *) luaL_checklstring(L, 2, &key.len);
1037
1038 if (key.len == 0) {
1039 lua_pushnil(L);
1040 lua_pushliteral(L, "empty key");
1041 return 2;
1042 }
1043
1044 if (key.len > 65535) {
1045 lua_pushnil(L);
1046 lua_pushliteral(L, "key too long");
1047 return 2;
1048 }
1049
1050 hash = ngx_crc32_short(key.data, key.len);
1051
1052 ngx_shmtx_lock(&ctx->shpool->mutex);
1053
1054 #if 1
1055 ngx_http_lua_shdict_expire(ctx, 1);
1056 #endif
1057
1058 rc = ngx_http_lua_shdict_lookup(zone, hash, key.data, key.len, &sd);
1059
1060 dd("shdict lookup returned %d", (int) rc);
1061
1062 if (rc == NGX_DECLINED || rc == NGX_DONE) {
1063 ngx_shmtx_unlock(&ctx->shpool->mutex);
1064 lua_pushnil(L);
1065 return 1;
1066 }
1067
1068 /* rc == NGX_OK */
1069
1070 if (sd->value_type != SHDICT_TLIST) {
1071 ngx_shmtx_unlock(&ctx->shpool->mutex);
1072
1073 lua_pushnil(L);
1074 lua_pushliteral(L, "value not a list");
1075 return 2;
1076 }
1077
1078 if (sd->value_len <= 0) {
1079 ngx_shmtx_unlock(&ctx->shpool->mutex);
1080
1081 return luaL_error(L, "bad lua list length found for key %s "
1082 "in shared_dict %s: %lu", key.data, name.data,
1083 (unsigned long) sd->value_len);
1084 }
1085
1086 queue = ngx_http_lua_shdict_get_list_head(sd, key.len);
1087
1088 if (flags == NGX_HTTP_LUA_SHDICT_LEFT) {
1089 queue = ngx_queue_head(queue);
1090
1091 } else {
1092 queue = ngx_queue_last(queue);
1093 }
1094
1095 lnode = ngx_queue_data(queue, ngx_http_lua_shdict_list_node_t, queue);
1096
1097 value_type = lnode->value_type;
1098
1099 dd("data: %p", lnode->data);
1100 dd("value len: %d", (int) sd->value_len);
1101
1102 value.data = lnode->data;
1103 value.len = (size_t) lnode->value_len;
1104
1105 switch (value_type) {
1106
1107 case SHDICT_TSTRING:
1108
1109 lua_pushlstring(L, (char *) value.data, value.len);
1110 break;
1111
1112 case SHDICT_TNUMBER:
1113
1114 if (value.len != sizeof(double)) {
1115
1116 ngx_shmtx_unlock(&ctx->shpool->mutex);
1117
1118 return luaL_error(L, "bad lua list node number value size found "
1119 "for key %s in shared_dict %s: %lu", key.data,
1120 name.data, (unsigned long) value.len);
1121 }
1122
1123 ngx_memcpy(&num, value.data, sizeof(double));
1124
1125 lua_pushnumber(L, num);
1126 break;
1127
1128 default:
1129
1130 ngx_shmtx_unlock(&ctx->shpool->mutex);
1131
1132 return luaL_error(L, "bad list node value type found for key %s in "
1133 "shared_dict %s: %d", key.data, name.data,
1134 value_type);
1135 }
1136
1137 ngx_queue_remove(queue);
1138
1139 ngx_slab_free_locked(ctx->shpool, lnode);
1140
1141 if (sd->value_len == 1) {
1142
1143 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ctx->log, 0,
1144 "lua shared dict list: empty node after pop, "
1145 "remove it");
1146
1147 ngx_queue_remove(&sd->queue);
1148
1149 node = (ngx_rbtree_node_t *)
1150 ((u_char *) sd - offsetof(ngx_rbtree_node_t, color));
1151
1152 ngx_rbtree_delete(&ctx->sh->rbtree, node);
1153
1154 ngx_slab_free_locked(ctx->shpool, node);
1155
1156 } else {
1157 sd->value_len = sd->value_len - 1;
1158
1159 ngx_queue_remove(&sd->queue);
1160 ngx_queue_insert_head(&ctx->sh->lru_queue, &sd->queue);
1161 }
1162
1163 ngx_shmtx_unlock(&ctx->shpool->mutex);
1164
1165 return 1;
1166 }
1167
1168
1169 static int
ngx_http_lua_shdict_llen(lua_State * L)1170 ngx_http_lua_shdict_llen(lua_State *L)
1171 {
1172 int n;
1173 ngx_str_t key;
1174 uint32_t hash;
1175 ngx_int_t rc;
1176 ngx_http_lua_shdict_ctx_t *ctx;
1177 ngx_http_lua_shdict_node_t *sd;
1178 ngx_shm_zone_t *zone;
1179
1180 n = lua_gettop(L);
1181
1182 if (n != 2) {
1183 return luaL_error(L, "expecting 2 arguments, "
1184 "but only seen %d", n);
1185 }
1186
1187 if (lua_type(L, 1) != LUA_TTABLE) {
1188 return luaL_error(L, "bad \"zone\" argument");
1189 }
1190
1191 zone = ngx_http_lua_shdict_get_zone(L, 1);
1192 if (zone == NULL) {
1193 return luaL_error(L, "bad \"zone\" argument");
1194 }
1195
1196 ctx = zone->data;
1197
1198 if (lua_isnil(L, 2)) {
1199 lua_pushnil(L);
1200 lua_pushliteral(L, "nil key");
1201 return 2;
1202 }
1203
1204 key.data = (u_char *) luaL_checklstring(L, 2, &key.len);
1205
1206 if (key.len == 0) {
1207 lua_pushnil(L);
1208 lua_pushliteral(L, "empty key");
1209 return 2;
1210 }
1211
1212 if (key.len > 65535) {
1213 lua_pushnil(L);
1214 lua_pushliteral(L, "key too long");
1215 return 2;
1216 }
1217
1218 hash = ngx_crc32_short(key.data, key.len);
1219
1220 ngx_shmtx_lock(&ctx->shpool->mutex);
1221
1222 #if 1
1223 ngx_http_lua_shdict_expire(ctx, 1);
1224 #endif
1225
1226 rc = ngx_http_lua_shdict_lookup(zone, hash, key.data, key.len, &sd);
1227
1228 dd("shdict lookup returned %d", (int) rc);
1229
1230 if (rc == NGX_OK) {
1231
1232 if (sd->value_type != SHDICT_TLIST) {
1233 ngx_shmtx_unlock(&ctx->shpool->mutex);
1234
1235 lua_pushnil(L);
1236 lua_pushliteral(L, "value not a list");
1237 return 2;
1238 }
1239
1240 ngx_queue_remove(&sd->queue);
1241 ngx_queue_insert_head(&ctx->sh->lru_queue, &sd->queue);
1242
1243 ngx_shmtx_unlock(&ctx->shpool->mutex);
1244
1245 lua_pushnumber(L, (lua_Number) sd->value_len);
1246 return 1;
1247 }
1248
1249 ngx_shmtx_unlock(&ctx->shpool->mutex);
1250
1251 lua_pushnumber(L, 0);
1252 return 1;
1253 }
1254
1255
1256 ngx_shm_zone_t *
ngx_http_lua_find_zone(u_char * name_data,size_t name_len)1257 ngx_http_lua_find_zone(u_char *name_data, size_t name_len)
1258 {
1259 ngx_str_t *name;
1260 ngx_uint_t i;
1261 ngx_shm_zone_t *zone;
1262 ngx_http_lua_shm_zone_ctx_t *ctx;
1263 volatile ngx_list_part_t *part;
1264
1265 part = &ngx_cycle->shared_memory.part;
1266 zone = part->elts;
1267
1268 for (i = 0; /* void */ ; i++) {
1269
1270 if (i >= part->nelts) {
1271 if (part->next == NULL) {
1272 break;
1273 }
1274
1275 part = part->next;
1276 zone = part->elts;
1277 i = 0;
1278 }
1279
1280 name = &zone[i].shm.name;
1281
1282 dd("name: [%.*s] %d", (int) name->len, name->data, (int) name->len);
1283 dd("name2: [%.*s] %d", (int) name_len, name_data, (int) name_len);
1284
1285 if (name->len == name_len
1286 && ngx_strncmp(name->data, name_data, name_len) == 0)
1287 {
1288 ctx = (ngx_http_lua_shm_zone_ctx_t *) zone[i].data;
1289 return &ctx->zone;
1290 }
1291 }
1292
1293 return NULL;
1294 }
1295
1296
1297 ngx_shm_zone_t *
ngx_http_lua_ffi_shdict_udata_to_zone(void * zone_udata)1298 ngx_http_lua_ffi_shdict_udata_to_zone(void *zone_udata)
1299 {
1300 if (zone_udata == NULL) {
1301 return NULL;
1302 }
1303
1304 return *(ngx_shm_zone_t **) zone_udata;
1305 }
1306
1307
1308 int
ngx_http_lua_ffi_shdict_store(ngx_shm_zone_t * zone,int op,u_char * key,size_t key_len,int value_type,u_char * str_value_buf,size_t str_value_len,double num_value,long exptime,int user_flags,char ** errmsg,int * forcible)1309 ngx_http_lua_ffi_shdict_store(ngx_shm_zone_t *zone, int op, u_char *key,
1310 size_t key_len, int value_type, u_char *str_value_buf,
1311 size_t str_value_len, double num_value, long exptime, int user_flags,
1312 char **errmsg, int *forcible)
1313 {
1314 int i, n;
1315 u_char c, *p;
1316 uint32_t hash;
1317 ngx_int_t rc;
1318 ngx_time_t *tp;
1319 ngx_queue_t *queue, *q;
1320 ngx_rbtree_node_t *node;
1321 ngx_http_lua_shdict_ctx_t *ctx;
1322 ngx_http_lua_shdict_node_t *sd;
1323
1324 dd("exptime: %ld", exptime);
1325
1326 ctx = zone->data;
1327
1328 *forcible = 0;
1329
1330 hash = ngx_crc32_short(key, key_len);
1331
1332 switch (value_type) {
1333
1334 case SHDICT_TSTRING:
1335 /* do nothing */
1336 break;
1337
1338 case SHDICT_TNUMBER:
1339 dd("num value: %lf", num_value);
1340 str_value_buf = (u_char *) &num_value;
1341 str_value_len = sizeof(double);
1342 break;
1343
1344 case SHDICT_TBOOLEAN:
1345 c = num_value ? 1 : 0;
1346 str_value_buf = &c;
1347 str_value_len = sizeof(u_char);
1348 break;
1349
1350 case LUA_TNIL:
1351 if (op & (NGX_HTTP_LUA_SHDICT_ADD|NGX_HTTP_LUA_SHDICT_REPLACE)) {
1352 *errmsg = "attempt to add or replace nil values";
1353 return NGX_ERROR;
1354 }
1355
1356 str_value_buf = NULL;
1357 str_value_len = 0;
1358 break;
1359
1360 default:
1361 *errmsg = "unsupported value type";
1362 return NGX_ERROR;
1363 }
1364
1365 ngx_shmtx_lock(&ctx->shpool->mutex);
1366
1367 #if 1
1368 ngx_http_lua_shdict_expire(ctx, 1);
1369 #endif
1370
1371 rc = ngx_http_lua_shdict_lookup(zone, hash, key, key_len, &sd);
1372
1373 dd("lookup returns %d", (int) rc);
1374
1375 if (op & NGX_HTTP_LUA_SHDICT_REPLACE) {
1376
1377 if (rc == NGX_DECLINED || rc == NGX_DONE) {
1378 ngx_shmtx_unlock(&ctx->shpool->mutex);
1379 *errmsg = "not found";
1380 return NGX_DECLINED;
1381 }
1382
1383 /* rc == NGX_OK */
1384
1385 goto replace;
1386 }
1387
1388 if (op & NGX_HTTP_LUA_SHDICT_ADD) {
1389
1390 if (rc == NGX_OK) {
1391 ngx_shmtx_unlock(&ctx->shpool->mutex);
1392 *errmsg = "exists";
1393 return NGX_DECLINED;
1394 }
1395
1396 if (rc == NGX_DONE) {
1397 /* exists but expired */
1398
1399 dd("go to replace");
1400 goto replace;
1401 }
1402
1403 /* rc == NGX_DECLINED */
1404
1405 dd("go to insert");
1406 goto insert;
1407 }
1408
1409 if (rc == NGX_OK || rc == NGX_DONE) {
1410
1411 if (value_type == LUA_TNIL) {
1412 goto remove;
1413 }
1414
1415 replace:
1416
1417 if (str_value_buf
1418 && str_value_len == (size_t) sd->value_len
1419 && sd->value_type != SHDICT_TLIST)
1420 {
1421
1422 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ctx->log, 0,
1423 "lua shared dict set: found old entry and value "
1424 "size matched, reusing it");
1425
1426 ngx_queue_remove(&sd->queue);
1427 ngx_queue_insert_head(&ctx->sh->lru_queue, &sd->queue);
1428
1429 sd->key_len = (u_short) key_len;
1430
1431 if (exptime > 0) {
1432 tp = ngx_timeofday();
1433 sd->expires = (uint64_t) tp->sec * 1000 + tp->msec
1434 + (uint64_t) exptime;
1435
1436 } else {
1437 sd->expires = 0;
1438 }
1439
1440 sd->user_flags = user_flags;
1441
1442 sd->value_len = (uint32_t) str_value_len;
1443
1444 dd("setting value type to %d", value_type);
1445
1446 sd->value_type = (uint8_t) value_type;
1447
1448 p = ngx_copy(sd->data, key, key_len);
1449 ngx_memcpy(p, str_value_buf, str_value_len);
1450
1451 ngx_shmtx_unlock(&ctx->shpool->mutex);
1452
1453 return NGX_OK;
1454 }
1455
1456 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ctx->log, 0,
1457 "lua shared dict set: found old entry but value size "
1458 "NOT matched, removing it first");
1459
1460 remove:
1461
1462 if (sd->value_type == SHDICT_TLIST) {
1463 queue = ngx_http_lua_shdict_get_list_head(sd, key_len);
1464
1465 for (q = ngx_queue_head(queue);
1466 q != ngx_queue_sentinel(queue);
1467 q = ngx_queue_next(q))
1468 {
1469 p = (u_char *) ngx_queue_data(q,
1470 ngx_http_lua_shdict_list_node_t,
1471 queue);
1472
1473 ngx_slab_free_locked(ctx->shpool, p);
1474 }
1475 }
1476
1477 ngx_queue_remove(&sd->queue);
1478
1479 node = (ngx_rbtree_node_t *)
1480 ((u_char *) sd - offsetof(ngx_rbtree_node_t, color));
1481
1482 ngx_rbtree_delete(&ctx->sh->rbtree, node);
1483
1484 ngx_slab_free_locked(ctx->shpool, node);
1485
1486 }
1487
1488 insert:
1489
1490 /* rc == NGX_DECLINED or value size unmatch */
1491
1492 if (str_value_buf == NULL) {
1493 ngx_shmtx_unlock(&ctx->shpool->mutex);
1494 return NGX_OK;
1495 }
1496
1497 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ctx->log, 0,
1498 "lua shared dict set: creating a new entry");
1499
1500 n = offsetof(ngx_rbtree_node_t, color)
1501 + offsetof(ngx_http_lua_shdict_node_t, data)
1502 + key_len
1503 + str_value_len;
1504
1505 node = ngx_slab_alloc_locked(ctx->shpool, n);
1506
1507 if (node == NULL) {
1508
1509 if (op & NGX_HTTP_LUA_SHDICT_SAFE_STORE) {
1510 ngx_shmtx_unlock(&ctx->shpool->mutex);
1511
1512 *errmsg = "no memory";
1513 return NGX_ERROR;
1514 }
1515
1516 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ctx->log, 0,
1517 "lua shared dict set: overriding non-expired items "
1518 "due to memory shortage for entry \"%*s\"", key_len,
1519 key);
1520
1521 for (i = 0; i < 30; i++) {
1522 if (ngx_http_lua_shdict_expire(ctx, 0) == 0) {
1523 break;
1524 }
1525
1526 *forcible = 1;
1527
1528 node = ngx_slab_alloc_locked(ctx->shpool, n);
1529 if (node != NULL) {
1530 goto allocated;
1531 }
1532 }
1533
1534 ngx_shmtx_unlock(&ctx->shpool->mutex);
1535
1536 *errmsg = "no memory";
1537 return NGX_ERROR;
1538 }
1539
1540 allocated:
1541
1542 sd = (ngx_http_lua_shdict_node_t *) &node->color;
1543
1544 node->key = hash;
1545 sd->key_len = (u_short) key_len;
1546
1547 if (exptime > 0) {
1548 tp = ngx_timeofday();
1549 sd->expires = (uint64_t) tp->sec * 1000 + tp->msec
1550 + (uint64_t) exptime;
1551
1552 } else {
1553 sd->expires = 0;
1554 }
1555
1556 sd->user_flags = user_flags;
1557 sd->value_len = (uint32_t) str_value_len;
1558 dd("setting value type to %d", value_type);
1559 sd->value_type = (uint8_t) value_type;
1560
1561 p = ngx_copy(sd->data, key, key_len);
1562 ngx_memcpy(p, str_value_buf, str_value_len);
1563
1564 ngx_rbtree_insert(&ctx->sh->rbtree, node);
1565 ngx_queue_insert_head(&ctx->sh->lru_queue, &sd->queue);
1566 ngx_shmtx_unlock(&ctx->shpool->mutex);
1567
1568 return NGX_OK;
1569 }
1570
1571
1572 int
ngx_http_lua_ffi_shdict_get(ngx_shm_zone_t * zone,u_char * key,size_t key_len,int * value_type,u_char ** str_value_buf,size_t * str_value_len,double * num_value,int * user_flags,int get_stale,int * is_stale,char ** err)1573 ngx_http_lua_ffi_shdict_get(ngx_shm_zone_t *zone, u_char *key,
1574 size_t key_len, int *value_type, u_char **str_value_buf,
1575 size_t *str_value_len, double *num_value, int *user_flags,
1576 int get_stale, int *is_stale, char **err)
1577 {
1578 ngx_str_t name;
1579 uint32_t hash;
1580 ngx_int_t rc;
1581 ngx_http_lua_shdict_ctx_t *ctx;
1582 ngx_http_lua_shdict_node_t *sd;
1583 ngx_str_t value;
1584
1585 *err = NULL;
1586
1587 ctx = zone->data;
1588 name = ctx->name;
1589
1590 hash = ngx_crc32_short(key, key_len);
1591
1592 #if (NGX_DEBUG)
1593 ngx_log_debug3(NGX_LOG_DEBUG_HTTP, ctx->log, 0,
1594 "fetching key \"%*s\" in shared dict \"%V\"", key_len,
1595 key, &name);
1596 #endif /* NGX_DEBUG */
1597
1598 ngx_shmtx_lock(&ctx->shpool->mutex);
1599
1600 #if 1
1601 if (!get_stale) {
1602 ngx_http_lua_shdict_expire(ctx, 1);
1603 }
1604 #endif
1605
1606 rc = ngx_http_lua_shdict_lookup(zone, hash, key, key_len, &sd);
1607
1608 dd("shdict lookup returns %d", (int) rc);
1609
1610 if (rc == NGX_DECLINED || (rc == NGX_DONE && !get_stale)) {
1611 ngx_shmtx_unlock(&ctx->shpool->mutex);
1612 *value_type = LUA_TNIL;
1613 return NGX_OK;
1614 }
1615
1616 /* rc == NGX_OK || (rc == NGX_DONE && get_stale) */
1617
1618 *value_type = sd->value_type;
1619
1620 dd("data: %p", sd->data);
1621 dd("key len: %d", (int) sd->key_len);
1622
1623 value.data = sd->data + sd->key_len;
1624 value.len = (size_t) sd->value_len;
1625
1626 if (*str_value_len < (size_t) value.len) {
1627 if (*value_type == SHDICT_TBOOLEAN) {
1628 ngx_shmtx_unlock(&ctx->shpool->mutex);
1629 return NGX_ERROR;
1630 }
1631
1632 if (*value_type == SHDICT_TSTRING) {
1633 *str_value_buf = malloc(value.len);
1634 if (*str_value_buf == NULL) {
1635 ngx_shmtx_unlock(&ctx->shpool->mutex);
1636 return NGX_ERROR;
1637 }
1638 }
1639 }
1640
1641 switch (*value_type) {
1642
1643 case SHDICT_TSTRING:
1644 *str_value_len = value.len;
1645 ngx_memcpy(*str_value_buf, value.data, value.len);
1646 break;
1647
1648 case SHDICT_TNUMBER:
1649
1650 if (value.len != sizeof(double)) {
1651 ngx_shmtx_unlock(&ctx->shpool->mutex);
1652 ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,
1653 "bad lua number value size found for key %*s "
1654 "in shared_dict %V: %z", key_len, key,
1655 &name, value.len);
1656 return NGX_ERROR;
1657 }
1658
1659 *str_value_len = value.len;
1660 ngx_memcpy(num_value, value.data, sizeof(double));
1661 break;
1662
1663 case SHDICT_TBOOLEAN:
1664
1665 if (value.len != sizeof(u_char)) {
1666 ngx_shmtx_unlock(&ctx->shpool->mutex);
1667 ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,
1668 "bad lua boolean value size found for key %*s "
1669 "in shared_dict %V: %z", key_len, key, &name,
1670 value.len);
1671 return NGX_ERROR;
1672 }
1673
1674 ngx_memcpy(*str_value_buf, value.data, value.len);
1675 break;
1676
1677 case SHDICT_TLIST:
1678
1679 ngx_shmtx_unlock(&ctx->shpool->mutex);
1680
1681 *err = "value is a list";
1682 return NGX_ERROR;
1683
1684 default:
1685
1686 ngx_shmtx_unlock(&ctx->shpool->mutex);
1687 ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,
1688 "bad value type found for key %*s in "
1689 "shared_dict %V: %d", key_len, key, &name,
1690 *value_type);
1691 return NGX_ERROR;
1692 }
1693
1694 *user_flags = sd->user_flags;
1695 dd("user flags: %d", *user_flags);
1696
1697 ngx_shmtx_unlock(&ctx->shpool->mutex);
1698
1699 if (get_stale) {
1700
1701 /* always return value, flags, stale */
1702
1703 *is_stale = (rc == NGX_DONE);
1704 return NGX_OK;
1705 }
1706
1707 return NGX_OK;
1708 }
1709
1710
1711 int
ngx_http_lua_ffi_shdict_incr(ngx_shm_zone_t * zone,u_char * key,size_t key_len,double * value,char ** err,int has_init,double init,long init_ttl,int * forcible)1712 ngx_http_lua_ffi_shdict_incr(ngx_shm_zone_t *zone, u_char *key,
1713 size_t key_len, double *value, char **err, int has_init, double init,
1714 long init_ttl, int *forcible)
1715 {
1716 int i, n;
1717 uint32_t hash;
1718 ngx_int_t rc;
1719 ngx_time_t *tp = NULL;
1720 ngx_http_lua_shdict_ctx_t *ctx;
1721 ngx_http_lua_shdict_node_t *sd;
1722 double num;
1723 ngx_rbtree_node_t *node;
1724 u_char *p;
1725 ngx_queue_t *queue, *q;
1726
1727 if (init_ttl > 0) {
1728 tp = ngx_timeofday();
1729 }
1730
1731 ctx = zone->data;
1732
1733 *forcible = 0;
1734
1735 hash = ngx_crc32_short(key, key_len);
1736
1737 dd("looking up key %.*s in shared dict %.*s", (int) key_len, key,
1738 (int) ctx->name.len, ctx->name.data);
1739
1740 ngx_shmtx_lock(&ctx->shpool->mutex);
1741 #if 1
1742 ngx_http_lua_shdict_expire(ctx, 1);
1743 #endif
1744 rc = ngx_http_lua_shdict_lookup(zone, hash, key, key_len, &sd);
1745
1746 dd("shdict lookup returned %d", (int) rc);
1747
1748 if (rc == NGX_DECLINED || rc == NGX_DONE) {
1749 if (!has_init) {
1750 ngx_shmtx_unlock(&ctx->shpool->mutex);
1751 *err = "not found";
1752 return NGX_ERROR;
1753 }
1754
1755 /* add value */
1756 num = *value + init;
1757
1758 if (rc == NGX_DONE) {
1759
1760 /* found an expired item */
1761
1762 if ((size_t) sd->value_len == sizeof(double)
1763 && sd->value_type != SHDICT_TLIST)
1764 {
1765 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ctx->log, 0,
1766 "lua shared dict incr: found old entry and "
1767 "value size matched, reusing it");
1768
1769 ngx_queue_remove(&sd->queue);
1770 ngx_queue_insert_head(&ctx->sh->lru_queue, &sd->queue);
1771
1772 dd("go to setvalue");
1773 goto setvalue;
1774 }
1775
1776 dd("go to remove");
1777 goto remove;
1778 }
1779
1780 dd("go to insert");
1781 goto insert;
1782 }
1783
1784 /* rc == NGX_OK */
1785
1786 if (sd->value_type != SHDICT_TNUMBER || sd->value_len != sizeof(double)) {
1787 ngx_shmtx_unlock(&ctx->shpool->mutex);
1788 *err = "not a number";
1789 return NGX_ERROR;
1790 }
1791
1792 ngx_queue_remove(&sd->queue);
1793 ngx_queue_insert_head(&ctx->sh->lru_queue, &sd->queue);
1794
1795 dd("setting value type to %d", (int) sd->value_type);
1796
1797 p = sd->data + key_len;
1798
1799 ngx_memcpy(&num, p, sizeof(double));
1800 num += *value;
1801
1802 ngx_memcpy(p, (double *) &num, sizeof(double));
1803
1804 ngx_shmtx_unlock(&ctx->shpool->mutex);
1805
1806 *value = num;
1807 return NGX_OK;
1808
1809 remove:
1810
1811 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ctx->log, 0,
1812 "lua shared dict incr: found old entry but value size "
1813 "NOT matched, removing it first");
1814
1815 if (sd->value_type == SHDICT_TLIST) {
1816 queue = ngx_http_lua_shdict_get_list_head(sd, key_len);
1817
1818 for (q = ngx_queue_head(queue);
1819 q != ngx_queue_sentinel(queue);
1820 q = ngx_queue_next(q))
1821 {
1822 p = (u_char *) ngx_queue_data(q, ngx_http_lua_shdict_list_node_t,
1823 queue);
1824
1825 ngx_slab_free_locked(ctx->shpool, p);
1826 }
1827 }
1828
1829 ngx_queue_remove(&sd->queue);
1830
1831 node = (ngx_rbtree_node_t *)
1832 ((u_char *) sd - offsetof(ngx_rbtree_node_t, color));
1833
1834 ngx_rbtree_delete(&ctx->sh->rbtree, node);
1835
1836 ngx_slab_free_locked(ctx->shpool, node);
1837
1838 insert:
1839
1840 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ctx->log, 0,
1841 "lua shared dict incr: creating a new entry");
1842
1843 n = offsetof(ngx_rbtree_node_t, color)
1844 + offsetof(ngx_http_lua_shdict_node_t, data)
1845 + key_len
1846 + sizeof(double);
1847
1848 node = ngx_slab_alloc_locked(ctx->shpool, n);
1849
1850 if (node == NULL) {
1851
1852 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ctx->log, 0,
1853 "lua shared dict incr: overriding non-expired items "
1854 "due to memory shortage for entry \"%*s\"", key_len,
1855 key);
1856
1857 for (i = 0; i < 30; i++) {
1858 if (ngx_http_lua_shdict_expire(ctx, 0) == 0) {
1859 break;
1860 }
1861
1862 *forcible = 1;
1863
1864 node = ngx_slab_alloc_locked(ctx->shpool, n);
1865 if (node != NULL) {
1866 goto allocated;
1867 }
1868 }
1869
1870 ngx_shmtx_unlock(&ctx->shpool->mutex);
1871
1872 *err = "no memory";
1873 return NGX_ERROR;
1874 }
1875
1876 allocated:
1877
1878 sd = (ngx_http_lua_shdict_node_t *) &node->color;
1879
1880 node->key = hash;
1881
1882 sd->key_len = (u_short) key_len;
1883
1884 sd->value_len = (uint32_t) sizeof(double);
1885
1886 ngx_rbtree_insert(&ctx->sh->rbtree, node);
1887
1888 ngx_queue_insert_head(&ctx->sh->lru_queue, &sd->queue);
1889
1890 setvalue:
1891
1892 sd->user_flags = 0;
1893
1894 if (init_ttl > 0) {
1895 sd->expires = (uint64_t) tp->sec * 1000 + tp->msec
1896 + (uint64_t) init_ttl;
1897
1898 } else {
1899 sd->expires = 0;
1900 }
1901
1902 dd("setting value type to %d", LUA_TNUMBER);
1903
1904 sd->value_type = (uint8_t) LUA_TNUMBER;
1905
1906 p = ngx_copy(sd->data, key, key_len);
1907 ngx_memcpy(p, (double *) &num, sizeof(double));
1908
1909 ngx_shmtx_unlock(&ctx->shpool->mutex);
1910
1911 *value = num;
1912 return NGX_OK;
1913 }
1914
1915
1916 int
ngx_http_lua_ffi_shdict_flush_all(ngx_shm_zone_t * zone)1917 ngx_http_lua_ffi_shdict_flush_all(ngx_shm_zone_t *zone)
1918 {
1919 ngx_queue_t *q;
1920 ngx_http_lua_shdict_node_t *sd;
1921 ngx_http_lua_shdict_ctx_t *ctx;
1922
1923 ctx = zone->data;
1924
1925 ngx_shmtx_lock(&ctx->shpool->mutex);
1926
1927 for (q = ngx_queue_head(&ctx->sh->lru_queue);
1928 q != ngx_queue_sentinel(&ctx->sh->lru_queue);
1929 q = ngx_queue_next(q))
1930 {
1931 sd = ngx_queue_data(q, ngx_http_lua_shdict_node_t, queue);
1932 sd->expires = 1;
1933 }
1934
1935 ngx_http_lua_shdict_expire(ctx, 0);
1936
1937 ngx_shmtx_unlock(&ctx->shpool->mutex);
1938
1939 return NGX_OK;
1940 }
1941
1942
1943 static ngx_int_t
ngx_http_lua_shdict_peek(ngx_shm_zone_t * shm_zone,ngx_uint_t hash,u_char * kdata,size_t klen,ngx_http_lua_shdict_node_t ** sdp)1944 ngx_http_lua_shdict_peek(ngx_shm_zone_t *shm_zone, ngx_uint_t hash,
1945 u_char *kdata, size_t klen, ngx_http_lua_shdict_node_t **sdp)
1946 {
1947 ngx_int_t rc;
1948 ngx_rbtree_node_t *node, *sentinel;
1949 ngx_http_lua_shdict_ctx_t *ctx;
1950 ngx_http_lua_shdict_node_t *sd;
1951
1952 ctx = shm_zone->data;
1953
1954 node = ctx->sh->rbtree.root;
1955 sentinel = ctx->sh->rbtree.sentinel;
1956
1957 while (node != sentinel) {
1958
1959 if (hash < node->key) {
1960 node = node->left;
1961 continue;
1962 }
1963
1964 if (hash > node->key) {
1965 node = node->right;
1966 continue;
1967 }
1968
1969 /* hash == node->key */
1970
1971 sd = (ngx_http_lua_shdict_node_t *) &node->color;
1972
1973 rc = ngx_memn2cmp(kdata, sd->data, klen, (size_t) sd->key_len);
1974
1975 if (rc == 0) {
1976 *sdp = sd;
1977
1978 return NGX_OK;
1979 }
1980
1981 node = (rc < 0) ? node->left : node->right;
1982 }
1983
1984 *sdp = NULL;
1985
1986 return NGX_DECLINED;
1987 }
1988
1989
1990 long
ngx_http_lua_ffi_shdict_get_ttl(ngx_shm_zone_t * zone,u_char * key,size_t key_len)1991 ngx_http_lua_ffi_shdict_get_ttl(ngx_shm_zone_t *zone, u_char *key,
1992 size_t key_len)
1993 {
1994 uint32_t hash;
1995 uint64_t now;
1996 uint64_t expires;
1997 ngx_int_t rc;
1998 ngx_time_t *tp;
1999 ngx_http_lua_shdict_ctx_t *ctx;
2000 ngx_http_lua_shdict_node_t *sd;
2001
2002 ctx = zone->data;
2003 hash = ngx_crc32_short(key, key_len);
2004
2005 ngx_shmtx_lock(&ctx->shpool->mutex);
2006
2007 rc = ngx_http_lua_shdict_peek(zone, hash, key, key_len, &sd);
2008
2009 if (rc == NGX_DECLINED) {
2010 ngx_shmtx_unlock(&ctx->shpool->mutex);
2011
2012 return NGX_DECLINED;
2013 }
2014
2015 /* rc == NGX_OK */
2016
2017 expires = sd->expires;
2018
2019 ngx_shmtx_unlock(&ctx->shpool->mutex);
2020
2021 if (expires == 0) {
2022 return 0;
2023 }
2024
2025 tp = ngx_timeofday();
2026 now = (uint64_t) tp->sec * 1000 + tp->msec;
2027
2028 return expires - now;
2029 }
2030
2031
2032 int
ngx_http_lua_ffi_shdict_set_expire(ngx_shm_zone_t * zone,u_char * key,size_t key_len,long exptime)2033 ngx_http_lua_ffi_shdict_set_expire(ngx_shm_zone_t *zone, u_char *key,
2034 size_t key_len, long exptime)
2035 {
2036 uint32_t hash;
2037 ngx_int_t rc;
2038 ngx_time_t *tp = NULL;
2039 ngx_http_lua_shdict_ctx_t *ctx;
2040 ngx_http_lua_shdict_node_t *sd;
2041
2042 if (exptime > 0) {
2043 tp = ngx_timeofday();
2044 }
2045
2046 ctx = zone->data;
2047 hash = ngx_crc32_short(key, key_len);
2048
2049 ngx_shmtx_lock(&ctx->shpool->mutex);
2050
2051 rc = ngx_http_lua_shdict_peek(zone, hash, key, key_len, &sd);
2052
2053 if (rc == NGX_DECLINED) {
2054 ngx_shmtx_unlock(&ctx->shpool->mutex);
2055
2056 return NGX_DECLINED;
2057 }
2058
2059 /* rc == NGX_OK */
2060
2061 if (exptime > 0) {
2062 sd->expires = (uint64_t) tp->sec * 1000 + tp->msec
2063 + (uint64_t) exptime;
2064
2065 } else {
2066 sd->expires = 0;
2067 }
2068
2069 ngx_shmtx_unlock(&ctx->shpool->mutex);
2070
2071 return NGX_OK;
2072 }
2073
2074
2075 size_t
ngx_http_lua_ffi_shdict_capacity(ngx_shm_zone_t * zone)2076 ngx_http_lua_ffi_shdict_capacity(ngx_shm_zone_t *zone)
2077 {
2078 return zone->shm.size;
2079 }
2080
2081
2082 #if (nginx_version >= 1011007)
2083 size_t
ngx_http_lua_ffi_shdict_free_space(ngx_shm_zone_t * zone)2084 ngx_http_lua_ffi_shdict_free_space(ngx_shm_zone_t *zone)
2085 {
2086 size_t bytes;
2087 ngx_http_lua_shdict_ctx_t *ctx;
2088
2089 ctx = zone->data;
2090
2091 ngx_shmtx_lock(&ctx->shpool->mutex);
2092 bytes = ctx->shpool->pfree * ngx_pagesize;
2093 ngx_shmtx_unlock(&ctx->shpool->mutex);
2094
2095 return bytes;
2096 }
2097 #endif
2098
2099
2100 /* vi:set ft=c ts=4 sw=4 et fdm=marker: */
2101