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 *) &num;
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