xref: /unit/src/nxt_cache.c (revision 2084:7d479274f334)
10Sigor@sysoev.ru 
20Sigor@sysoev.ru /*
30Sigor@sysoev.ru  * Copyright (C) Igor Sysoev
40Sigor@sysoev.ru  * Copyright (C) NGINX, Inc.
50Sigor@sysoev.ru  */
60Sigor@sysoev.ru 
70Sigor@sysoev.ru #include <nxt_main.h>
80Sigor@sysoev.ru 
90Sigor@sysoev.ru 
100Sigor@sysoev.ru /* A cache time resolution is 10ms. */
11*2084Salx.manpages@gmail.com #define nxt_cache_time(thr)                                                   \
120Sigor@sysoev.ru     (uint64_t) (nxt_thread_time(thr) * 100)
130Sigor@sysoev.ru 
140Sigor@sysoev.ru 
150Sigor@sysoev.ru static nxt_int_t nxt_cache_lvlhsh_test(nxt_lvlhsh_query_t *lhq, void *data);
160Sigor@sysoev.ru static nxt_work_handler_t nxt_cache_query_locked(nxt_cache_t *cache,
170Sigor@sysoev.ru     nxt_cache_query_t *q, nxt_lvlhsh_query_t *lhq);
180Sigor@sysoev.ru static nxt_work_handler_t nxt_cache_node_hold(nxt_cache_t *cache,
190Sigor@sysoev.ru     nxt_cache_query_t *q, nxt_lvlhsh_query_t *lhq);
200Sigor@sysoev.ru static nxt_work_handler_t nxt_cache_node_test(nxt_cache_t *cache,
210Sigor@sysoev.ru     nxt_cache_query_t *q);
220Sigor@sysoev.ru 
230Sigor@sysoev.ru static void nxt_cache_wait_handler(nxt_thread_t *thr, void *obj, void *data);
240Sigor@sysoev.ru static void nxt_cache_timeout_handler(nxt_thread_t *thr, void *obj, void *data);
250Sigor@sysoev.ru static void nxt_cache_wake_handler(nxt_thread_t *thr, void *obj, void *data);
260Sigor@sysoev.ru static ssize_t nxt_cache_release_locked(nxt_cache_t *cache,
270Sigor@sysoev.ru     nxt_cache_query_t *q, u_char *buf, size_t size);
280Sigor@sysoev.ru 
290Sigor@sysoev.ru static nxt_cache_node_t *nxt_cache_node_alloc(nxt_cache_t *cache);
300Sigor@sysoev.ru static void nxt_cache_node_free(nxt_cache_t *cache, nxt_cache_node_t *node,
310Sigor@sysoev.ru     nxt_bool_t fast);
320Sigor@sysoev.ru static nxt_cache_query_wait_t *nxt_cache_query_wait_alloc(nxt_cache_t *cache,
330Sigor@sysoev.ru     nxt_bool_t *slow);
340Sigor@sysoev.ru static void nxt_cache_query_wait_free(nxt_cache_t *cache,
350Sigor@sysoev.ru     nxt_cache_query_wait_t *qw);
360Sigor@sysoev.ru 
370Sigor@sysoev.ru 
380Sigor@sysoev.ru /* STUB */
390Sigor@sysoev.ru nxt_int_t nxt_cache_shm_create(nxt_mem_zone_t *pool);
400Sigor@sysoev.ru static void *nxt_cache_shm_alloc(void *data, size_t size, nxt_uint_t nalloc);
410Sigor@sysoev.ru /**/
420Sigor@sysoev.ru 
430Sigor@sysoev.ru 
440Sigor@sysoev.ru nxt_int_t
nxt_cache_shm_create(nxt_mem_zone_t * mz)450Sigor@sysoev.ru nxt_cache_shm_create(nxt_mem_zone_t *mz)
460Sigor@sysoev.ru {
470Sigor@sysoev.ru     nxt_cache_t  *cache;
480Sigor@sysoev.ru 
490Sigor@sysoev.ru     static const nxt_lvlhsh_proto_t  proto  nxt_aligned(64) = {
500Sigor@sysoev.ru         NXT_LVLHSH_LARGE_SLAB,
510Sigor@sysoev.ru         0,
520Sigor@sysoev.ru         nxt_cache_lvlhsh_test,
530Sigor@sysoev.ru         (nxt_lvlhsh_alloc_t) nxt_cache_shm_alloc,
540Sigor@sysoev.ru         (nxt_lvlhsh_free_t) nxt_mem_zone_free,
550Sigor@sysoev.ru     };
560Sigor@sysoev.ru 
570Sigor@sysoev.ru     cache = nxt_mem_zone_zalloc(mz, sizeof(nxt_cache_t));
580Sigor@sysoev.ru 
590Sigor@sysoev.ru     if (cache == NULL) {
600Sigor@sysoev.ru         return NXT_ERROR;
610Sigor@sysoev.ru     }
620Sigor@sysoev.ru 
630Sigor@sysoev.ru     cache->proto = &proto;
640Sigor@sysoev.ru     cache->pool = mz;
650Sigor@sysoev.ru 
660Sigor@sysoev.ru     cache->start_time = nxt_cache_time(nxt_thread());
670Sigor@sysoev.ru 
680Sigor@sysoev.ru     return NXT_OK;
690Sigor@sysoev.ru }
700Sigor@sysoev.ru 
710Sigor@sysoev.ru 
720Sigor@sysoev.ru static void *
nxt_cache_shm_alloc(void * data,size_t size,nxt_uint_t nalloc)730Sigor@sysoev.ru nxt_cache_shm_alloc(void *data, size_t size, nxt_uint_t nalloc)
740Sigor@sysoev.ru {
750Sigor@sysoev.ru     return nxt_mem_zone_align(data, size, size);
760Sigor@sysoev.ru }
770Sigor@sysoev.ru 
780Sigor@sysoev.ru 
790Sigor@sysoev.ru void
nxt_cache_init(nxt_cache_t * cache)800Sigor@sysoev.ru nxt_cache_init(nxt_cache_t *cache)
810Sigor@sysoev.ru {
820Sigor@sysoev.ru     static const nxt_lvlhsh_proto_t  proto  nxt_aligned(64) = {
830Sigor@sysoev.ru         NXT_LVLHSH_LARGE_MEMALIGN,
840Sigor@sysoev.ru         0,
850Sigor@sysoev.ru         nxt_cache_lvlhsh_test,
860Sigor@sysoev.ru         nxt_lvlhsh_alloc,
870Sigor@sysoev.ru         nxt_lvlhsh_free,
880Sigor@sysoev.ru     };
890Sigor@sysoev.ru 
900Sigor@sysoev.ru     cache->proto = &proto;
910Sigor@sysoev.ru 
920Sigor@sysoev.ru     cache->start_time = nxt_cache_time(nxt_thread());
930Sigor@sysoev.ru }
940Sigor@sysoev.ru 
950Sigor@sysoev.ru 
960Sigor@sysoev.ru static nxt_int_t
nxt_cache_lvlhsh_test(nxt_lvlhsh_query_t * lhq,void * data)970Sigor@sysoev.ru nxt_cache_lvlhsh_test(nxt_lvlhsh_query_t *lhq, void *data)
980Sigor@sysoev.ru {
990Sigor@sysoev.ru     nxt_cache_node_t  *node;
1000Sigor@sysoev.ru 
1010Sigor@sysoev.ru     node = data;
1020Sigor@sysoev.ru 
1030Sigor@sysoev.ru     if (nxt_str_eq(&lhq->key, node->key_data, node->key_len)) {
1040Sigor@sysoev.ru         return NXT_OK;
1050Sigor@sysoev.ru     }
1060Sigor@sysoev.ru 
1070Sigor@sysoev.ru     return NXT_DECLINED;
1080Sigor@sysoev.ru }
1090Sigor@sysoev.ru 
1100Sigor@sysoev.ru 
1110Sigor@sysoev.ru nxt_inline void
nxt_cache_lock(nxt_cache_t * cache)1120Sigor@sysoev.ru nxt_cache_lock(nxt_cache_t *cache)
1130Sigor@sysoev.ru {
1140Sigor@sysoev.ru     if (cache->shared) {
1150Sigor@sysoev.ru         nxt_thread_spin_lock(&cache->lock);
1160Sigor@sysoev.ru     }
1170Sigor@sysoev.ru }
1180Sigor@sysoev.ru 
1190Sigor@sysoev.ru 
1200Sigor@sysoev.ru nxt_inline void
nxt_cache_unlock(nxt_cache_t * cache)1210Sigor@sysoev.ru nxt_cache_unlock(nxt_cache_t *cache)
1220Sigor@sysoev.ru {
1230Sigor@sysoev.ru     if (cache->shared) {
1240Sigor@sysoev.ru         nxt_thread_spin_unlock(&cache->lock);
1250Sigor@sysoev.ru     }
1260Sigor@sysoev.ru }
1270Sigor@sysoev.ru 
1280Sigor@sysoev.ru 
1290Sigor@sysoev.ru void
nxt_cache_query(nxt_cache_t * cache,nxt_cache_query_t * q)1300Sigor@sysoev.ru nxt_cache_query(nxt_cache_t *cache, nxt_cache_query_t *q)
1310Sigor@sysoev.ru {
1320Sigor@sysoev.ru     nxt_thread_t        *thr;
1330Sigor@sysoev.ru     nxt_lvlhsh_query_t  lhq;
1340Sigor@sysoev.ru     nxt_work_handler_t  handler;
1350Sigor@sysoev.ru 
1360Sigor@sysoev.ru     thr = nxt_thread();
1370Sigor@sysoev.ru 
1380Sigor@sysoev.ru     if (cache != NULL) {
1390Sigor@sysoev.ru         lhq.key_hash = nxt_murmur_hash2(q->key_data, q->key_len);
1400Sigor@sysoev.ru         lhq.replace = 0;
1410Sigor@sysoev.ru         lhq.key.len = q->key_len;
1420Sigor@sysoev.ru         lhq.key.data = q->key_data;
1430Sigor@sysoev.ru         lhq.proto = cache->proto;
1440Sigor@sysoev.ru         lhq.pool = cache->pool;
1450Sigor@sysoev.ru 
1460Sigor@sysoev.ru         q->now = nxt_cache_time(thr);
1470Sigor@sysoev.ru 
1480Sigor@sysoev.ru         nxt_cache_lock(cache);
1490Sigor@sysoev.ru 
1500Sigor@sysoev.ru         handler = nxt_cache_query_locked(cache, q, &lhq);
1510Sigor@sysoev.ru 
1520Sigor@sysoev.ru         nxt_cache_unlock(cache);
1530Sigor@sysoev.ru 
1540Sigor@sysoev.ru     } else {
1550Sigor@sysoev.ru         handler = q->state->nocache_handler;
1560Sigor@sysoev.ru     }
1570Sigor@sysoev.ru 
1580Sigor@sysoev.ru     handler(thr, q, NULL);
1590Sigor@sysoev.ru }
1600Sigor@sysoev.ru 
1610Sigor@sysoev.ru 
1620Sigor@sysoev.ru static nxt_work_handler_t
nxt_cache_query_locked(nxt_cache_t * cache,nxt_cache_query_t * q,nxt_lvlhsh_query_t * lhq)1630Sigor@sysoev.ru nxt_cache_query_locked(nxt_cache_t *cache, nxt_cache_query_t *q,
1640Sigor@sysoev.ru     nxt_lvlhsh_query_t *lhq)
1650Sigor@sysoev.ru {
1660Sigor@sysoev.ru     nxt_int_t                ret;
1670Sigor@sysoev.ru     nxt_time_t               expiry;
1680Sigor@sysoev.ru     nxt_cache_node_t         *node;
1690Sigor@sysoev.ru     nxt_cache_query_state_t  *state;
1700Sigor@sysoev.ru 
1710Sigor@sysoev.ru     if (q->hold) {
1720Sigor@sysoev.ru         return nxt_cache_node_hold(cache, q, lhq);
1730Sigor@sysoev.ru     }
1740Sigor@sysoev.ru 
1750Sigor@sysoev.ru     ret = nxt_lvlhsh_find(&cache->lvlhsh, lhq);
1760Sigor@sysoev.ru 
1770Sigor@sysoev.ru     state = q->state;
1780Sigor@sysoev.ru 
1790Sigor@sysoev.ru     if (ret != NXT_OK) {
1800Sigor@sysoev.ru         /* NXT_DECLINED */
1810Sigor@sysoev.ru         return state->nocache_handler;
1820Sigor@sysoev.ru     }
1830Sigor@sysoev.ru 
1840Sigor@sysoev.ru     node = lhq->value;
1850Sigor@sysoev.ru     node->count++;
1860Sigor@sysoev.ru     q->node = node;
1870Sigor@sysoev.ru 
1880Sigor@sysoev.ru     expiry = cache->start_time + node->expiry;
1890Sigor@sysoev.ru 
1900Sigor@sysoev.ru     if (q->now < expiry) {
1910Sigor@sysoev.ru         return state->ready_handler;
1920Sigor@sysoev.ru     }
1930Sigor@sysoev.ru 
1940Sigor@sysoev.ru     q->stale = 1;
1950Sigor@sysoev.ru 
1960Sigor@sysoev.ru     return state->stale_handler;
1970Sigor@sysoev.ru }
1980Sigor@sysoev.ru 
1990Sigor@sysoev.ru 
2000Sigor@sysoev.ru static nxt_work_handler_t
nxt_cache_node_hold(nxt_cache_t * cache,nxt_cache_query_t * q,nxt_lvlhsh_query_t * lhq)2010Sigor@sysoev.ru nxt_cache_node_hold(nxt_cache_t *cache, nxt_cache_query_t *q,
2020Sigor@sysoev.ru     nxt_lvlhsh_query_t *lhq)
2030Sigor@sysoev.ru {
2040Sigor@sysoev.ru     nxt_int_t                ret;
2050Sigor@sysoev.ru     nxt_bool_t               slow;
2060Sigor@sysoev.ru     nxt_cache_node_t         *node, *sentinel;
2070Sigor@sysoev.ru     nxt_work_handler_t       handler;
2080Sigor@sysoev.ru     nxt_cache_query_wait_t   *qw;
2090Sigor@sysoev.ru     nxt_cache_query_state_t  *state;
2100Sigor@sysoev.ru 
2110Sigor@sysoev.ru     state = q->state;
2120Sigor@sysoev.ru     sentinel = nxt_cache_node_alloc(cache);
2130Sigor@sysoev.ru 
2140Sigor@sysoev.ru     if (nxt_slow_path(sentinel == NULL)) {
2150Sigor@sysoev.ru         return state->error_handler;
2160Sigor@sysoev.ru     }
2170Sigor@sysoev.ru 
2180Sigor@sysoev.ru     sentinel->key_data = q->key_data;
2190Sigor@sysoev.ru     sentinel->key_len = q->key_len;
2200Sigor@sysoev.ru     lhq->value = sentinel;
2210Sigor@sysoev.ru 
2220Sigor@sysoev.ru     /*
2230Sigor@sysoev.ru      * Try to insert an empty sentinel node to hold updating
2240Sigor@sysoev.ru      * process if there is no existent cache node in cache.
2250Sigor@sysoev.ru      */
2260Sigor@sysoev.ru     ret = nxt_lvlhsh_insert(&cache->lvlhsh, lhq);
2270Sigor@sysoev.ru 
2280Sigor@sysoev.ru     if (ret == NXT_OK) {
2290Sigor@sysoev.ru         /* The sentinel node was successully added. */
2300Sigor@sysoev.ru 
2310Sigor@sysoev.ru         q->node = sentinel;
2320Sigor@sysoev.ru         sentinel->updating = 1;
2330Sigor@sysoev.ru         return state->update_handler;
2340Sigor@sysoev.ru     }
2350Sigor@sysoev.ru 
2360Sigor@sysoev.ru     nxt_cache_node_free(cache, sentinel, 1);
2370Sigor@sysoev.ru 
2380Sigor@sysoev.ru     if (ret == NXT_ERROR) {
2390Sigor@sysoev.ru         return state->error_handler;
2400Sigor@sysoev.ru     }
2410Sigor@sysoev.ru 
2420Sigor@sysoev.ru     /* NXT_DECLINED: a cache node exists. */
2430Sigor@sysoev.ru 
2440Sigor@sysoev.ru     node = lhq->value;
2450Sigor@sysoev.ru     node->count++;
2460Sigor@sysoev.ru     q->node = node;
2470Sigor@sysoev.ru 
2480Sigor@sysoev.ru     handler = nxt_cache_node_test(cache, q);
2490Sigor@sysoev.ru     if (handler != NULL) {
2500Sigor@sysoev.ru         return handler;
2510Sigor@sysoev.ru     }
2520Sigor@sysoev.ru 
2530Sigor@sysoev.ru     /* Add the node to a wait queue. */
2540Sigor@sysoev.ru 
2550Sigor@sysoev.ru     qw = nxt_cache_query_wait_alloc(cache, &slow);
2560Sigor@sysoev.ru     if (nxt_slow_path(qw == NULL)) {
2570Sigor@sysoev.ru         return state->error_handler;
2580Sigor@sysoev.ru     }
2590Sigor@sysoev.ru 
2600Sigor@sysoev.ru     if (slow) {
2610Sigor@sysoev.ru         /* The node state may have been changed during slow allocation. */
2620Sigor@sysoev.ru 
2630Sigor@sysoev.ru         handler = nxt_cache_node_test(cache, q);
2640Sigor@sysoev.ru         if (handler != NULL) {
2650Sigor@sysoev.ru             nxt_cache_query_wait_free(cache, qw);
2660Sigor@sysoev.ru             return handler;
2670Sigor@sysoev.ru         }
2680Sigor@sysoev.ru     }
2690Sigor@sysoev.ru 
2700Sigor@sysoev.ru     qw->query = q;
2710Sigor@sysoev.ru     qw->next = node->waiting;
2720Sigor@sysoev.ru     qw->busy = 0;
2730Sigor@sysoev.ru     qw->deleted = 0;
2740Sigor@sysoev.ru     qw->pid = nxt_pid;
2750Sigor@sysoev.ru     qw->engine = nxt_thread_event_engine();
2760Sigor@sysoev.ru     qw->handler = nxt_cache_wake_handler;
2770Sigor@sysoev.ru     qw->cache = cache;
2780Sigor@sysoev.ru 
2790Sigor@sysoev.ru     node->waiting = qw;
2800Sigor@sysoev.ru 
2810Sigor@sysoev.ru     return nxt_cache_wait_handler;
2820Sigor@sysoev.ru }
2830Sigor@sysoev.ru 
2840Sigor@sysoev.ru 
2850Sigor@sysoev.ru static nxt_work_handler_t
nxt_cache_node_test(nxt_cache_t * cache,nxt_cache_query_t * q)2860Sigor@sysoev.ru nxt_cache_node_test(nxt_cache_t *cache, nxt_cache_query_t *q)
2870Sigor@sysoev.ru {
2880Sigor@sysoev.ru     nxt_time_t               expiry;
2890Sigor@sysoev.ru     nxt_cache_node_t         *node;
2900Sigor@sysoev.ru     nxt_cache_query_state_t  *state;
2910Sigor@sysoev.ru 
2920Sigor@sysoev.ru     q->stale = 0;
2930Sigor@sysoev.ru     state = q->state;
2940Sigor@sysoev.ru     node = q->node;
2950Sigor@sysoev.ru 
2960Sigor@sysoev.ru     expiry = cache->start_time + node->expiry;
2970Sigor@sysoev.ru 
2980Sigor@sysoev.ru     if (q->now < expiry) {
2990Sigor@sysoev.ru         return state->ready_handler;
3000Sigor@sysoev.ru     }
3010Sigor@sysoev.ru 
3020Sigor@sysoev.ru     /*
3030Sigor@sysoev.ru      * A valid stale or empty sentinel cache node.
3040Sigor@sysoev.ru      * The sentinel node can be only in updating state.
3050Sigor@sysoev.ru      */
3060Sigor@sysoev.ru 
3070Sigor@sysoev.ru     if (node->updating) {
3080Sigor@sysoev.ru 
3090Sigor@sysoev.ru         if (node->expiry != 0) {
3100Sigor@sysoev.ru             /* A valid stale cache node. */
3110Sigor@sysoev.ru 
3120Sigor@sysoev.ru             q->stale = 1;
3130Sigor@sysoev.ru 
3140Sigor@sysoev.ru             if (q->use_stale) {
3150Sigor@sysoev.ru                 return state->stale_handler;
3160Sigor@sysoev.ru             }
3170Sigor@sysoev.ru         }
3180Sigor@sysoev.ru 
3190Sigor@sysoev.ru         /* A sentinel node. */
3200Sigor@sysoev.ru         return NULL;
3210Sigor@sysoev.ru     }
3220Sigor@sysoev.ru 
3230Sigor@sysoev.ru     /* A valid stale cache node is not being updated now. */
3240Sigor@sysoev.ru 
3250Sigor@sysoev.ru     q->stale = 1;
3260Sigor@sysoev.ru 
3270Sigor@sysoev.ru     if (q->use_stale) {
3280Sigor@sysoev.ru 
3290Sigor@sysoev.ru         if (q->update_stale) {
3300Sigor@sysoev.ru             node->updating = 1;
3310Sigor@sysoev.ru             return state->update_stale_handler;
3320Sigor@sysoev.ru         }
3330Sigor@sysoev.ru 
3340Sigor@sysoev.ru         return state->stale_handler;
3350Sigor@sysoev.ru     }
3360Sigor@sysoev.ru 
3370Sigor@sysoev.ru     node->updating = 1;
3380Sigor@sysoev.ru     return state->update_handler;
3390Sigor@sysoev.ru }
3400Sigor@sysoev.ru 
3410Sigor@sysoev.ru 
3420Sigor@sysoev.ru static void
nxt_cache_wait_handler(nxt_thread_t * thr,void * obj,void * data)3430Sigor@sysoev.ru nxt_cache_wait_handler(nxt_thread_t *thr, void *obj, void *data)
3440Sigor@sysoev.ru {
3450Sigor@sysoev.ru     nxt_event_timer_t  *ev;
3460Sigor@sysoev.ru     nxt_cache_query_t  *cq;
3470Sigor@sysoev.ru 
3480Sigor@sysoev.ru     cq = obj;
3490Sigor@sysoev.ru 
3500Sigor@sysoev.ru     if (cq->timeout != 0) {
3510Sigor@sysoev.ru 
3520Sigor@sysoev.ru         ev = &cq->timer;
3530Sigor@sysoev.ru 
3540Sigor@sysoev.ru         if (ev->state == NXT_EVENT_TIMER_DISABLED) {
3550Sigor@sysoev.ru             ev->handler = nxt_cache_timeout_handler;
3560Sigor@sysoev.ru             nxt_event_timer_ident(ev, -1);
3570Sigor@sysoev.ru 
3580Sigor@sysoev.ru             nxt_event_timer_add(thr->engine, ev, cq->timeout);
3590Sigor@sysoev.ru         }
3600Sigor@sysoev.ru     }
3610Sigor@sysoev.ru }
3620Sigor@sysoev.ru 
3630Sigor@sysoev.ru 
3640Sigor@sysoev.ru static void
nxt_cache_timeout_handler(nxt_thread_t * thr,void * obj,void * data)3650Sigor@sysoev.ru nxt_cache_timeout_handler(nxt_thread_t *thr, void *obj, void *data)
3660Sigor@sysoev.ru {
3670Sigor@sysoev.ru     nxt_cache_query_t  *cq;
3680Sigor@sysoev.ru     nxt_event_timer_t  *ev;
3690Sigor@sysoev.ru 
3700Sigor@sysoev.ru     ev = obj;
3710Sigor@sysoev.ru 
3720Sigor@sysoev.ru     cq = nxt_event_timer_data(ev, nxt_cache_query_t, timer);
3730Sigor@sysoev.ru 
3740Sigor@sysoev.ru     cq->state->timeout_handler(thr, cq, NULL);
3750Sigor@sysoev.ru }
3760Sigor@sysoev.ru 
3770Sigor@sysoev.ru 
3780Sigor@sysoev.ru static void
nxt_cache_wake_handler(nxt_thread_t * thr,void * obj,void * data)3790Sigor@sysoev.ru nxt_cache_wake_handler(nxt_thread_t *thr, void *obj, void *data)
3800Sigor@sysoev.ru {
3810Sigor@sysoev.ru     nxt_cache_t             *cache;
3820Sigor@sysoev.ru     nxt_work_handler_t      handler;
3830Sigor@sysoev.ru     nxt_cache_query_t       *q;
3840Sigor@sysoev.ru     nxt_cache_query_wait_t  *qw;
3850Sigor@sysoev.ru 
3860Sigor@sysoev.ru     qw = obj;
3870Sigor@sysoev.ru     q = qw->query;
3880Sigor@sysoev.ru     cache = qw->cache;
3890Sigor@sysoev.ru 
3900Sigor@sysoev.ru     nxt_cache_lock(cache);
3910Sigor@sysoev.ru 
3920Sigor@sysoev.ru     handler = nxt_cache_node_test(cache, q);
3930Sigor@sysoev.ru 
3940Sigor@sysoev.ru     if (handler != NULL) {
3950Sigor@sysoev.ru         nxt_cache_query_wait_free(cache, qw);
3960Sigor@sysoev.ru 
3970Sigor@sysoev.ru     } else {
3980Sigor@sysoev.ru         /* Wait again. */
3990Sigor@sysoev.ru         qw->next = q->node->waiting;
4000Sigor@sysoev.ru         q->node->waiting = qw;
4010Sigor@sysoev.ru     }
4020Sigor@sysoev.ru 
4030Sigor@sysoev.ru     nxt_cache_unlock(cache);
4040Sigor@sysoev.ru 
4050Sigor@sysoev.ru     handler(thr, q, NULL);
4060Sigor@sysoev.ru }
4070Sigor@sysoev.ru 
4080Sigor@sysoev.ru 
4090Sigor@sysoev.ru nxt_int_t
nxt_cache_update(nxt_cache_t * cache,nxt_cache_query_t * q)4100Sigor@sysoev.ru nxt_cache_update(nxt_cache_t *cache, nxt_cache_query_t *q)
4110Sigor@sysoev.ru {
4120Sigor@sysoev.ru     nxt_int_t           ret;
4130Sigor@sysoev.ru     nxt_cache_node_t    *node;
4140Sigor@sysoev.ru     nxt_lvlhsh_query_t  lhq;
4150Sigor@sysoev.ru 
4160Sigor@sysoev.ru     node = q->node;
4170Sigor@sysoev.ru 
4180Sigor@sysoev.ru     node->accessed = nxt_cache_time(nxt_thread()) - cache->start_time;
4190Sigor@sysoev.ru 
4200Sigor@sysoev.ru     node->updating = 0;
4210Sigor@sysoev.ru     node->count = 1;
4220Sigor@sysoev.ru 
4230Sigor@sysoev.ru     lhq.key_hash = nxt_murmur_hash2(node->key_data, node->key_len);
4240Sigor@sysoev.ru     lhq.replace = 1;
4250Sigor@sysoev.ru     lhq.key.len = node->key_len;
4260Sigor@sysoev.ru     lhq.key.data = node->key_data;
4270Sigor@sysoev.ru     lhq.value = node;
4280Sigor@sysoev.ru     lhq.proto = cache->proto;
4290Sigor@sysoev.ru     lhq.pool = cache->pool;
4300Sigor@sysoev.ru 
4310Sigor@sysoev.ru     nxt_cache_lock(cache);
4320Sigor@sysoev.ru 
4330Sigor@sysoev.ru     ret = nxt_lvlhsh_insert(&cache->lvlhsh, &lhq);
4340Sigor@sysoev.ru 
4350Sigor@sysoev.ru     if (nxt_fast_path(ret != NXT_OK)) {
4360Sigor@sysoev.ru 
4370Sigor@sysoev.ru         nxt_queue_insert_head(&cache->expiry_queue, &node->link);
4380Sigor@sysoev.ru 
4390Sigor@sysoev.ru         node = lhq.value;
4400Sigor@sysoev.ru 
4410Sigor@sysoev.ru         if (node != NULL) {
4420Sigor@sysoev.ru             /* A replaced node. */
4430Sigor@sysoev.ru 
4440Sigor@sysoev.ru             nxt_queue_remove(&node->link);
4450Sigor@sysoev.ru 
4460Sigor@sysoev.ru             if (node->count != 0) {
4470Sigor@sysoev.ru                 node->deleted = 1;
4480Sigor@sysoev.ru 
4490Sigor@sysoev.ru             } else {
4500Sigor@sysoev.ru                 // delete cache node
4510Sigor@sysoev.ru             }
4520Sigor@sysoev.ru         }
4530Sigor@sysoev.ru     }
4540Sigor@sysoev.ru 
4550Sigor@sysoev.ru     nxt_cache_unlock(cache);
4560Sigor@sysoev.ru 
4570Sigor@sysoev.ru     return ret;
4580Sigor@sysoev.ru }
4590Sigor@sysoev.ru 
4600Sigor@sysoev.ru 
4610Sigor@sysoev.ru void
nxt_cache_release(nxt_cache_t * cache,nxt_cache_query_t * q)4620Sigor@sysoev.ru nxt_cache_release(nxt_cache_t *cache, nxt_cache_query_t *q)
4630Sigor@sysoev.ru {
4640Sigor@sysoev.ru     u_char        *p, *data;
4650Sigor@sysoev.ru     size_t        size;
4660Sigor@sysoev.ru     ssize_t       ret;
4670Sigor@sysoev.ru     nxt_thread_t  *thr;
4680Sigor@sysoev.ru     u_char        buf[1024];
4690Sigor@sysoev.ru 
4700Sigor@sysoev.ru     thr = nxt_thread();
4710Sigor@sysoev.ru     q->now = nxt_cache_time(thr);
4720Sigor@sysoev.ru 
4730Sigor@sysoev.ru     p = buf;
4740Sigor@sysoev.ru     size = sizeof(buf);
4750Sigor@sysoev.ru 
4760Sigor@sysoev.ru     for ( ;; ) {
4770Sigor@sysoev.ru         nxt_cache_lock(cache);
4780Sigor@sysoev.ru 
4790Sigor@sysoev.ru         ret = nxt_cache_release_locked(cache, q, p, size);
4800Sigor@sysoev.ru 
4810Sigor@sysoev.ru         nxt_cache_unlock(cache);
4820Sigor@sysoev.ru 
4830Sigor@sysoev.ru         if (ret == 0) {
4840Sigor@sysoev.ru             return;
4850Sigor@sysoev.ru         }
4860Sigor@sysoev.ru 
4870Sigor@sysoev.ru         size = nxt_abs(ret);
4880Sigor@sysoev.ru 
4890Sigor@sysoev.ru         data = nxt_malloc(size);
4900Sigor@sysoev.ru 
4910Sigor@sysoev.ru         if (data == NULL) {
4920Sigor@sysoev.ru             /* TODO: retry */
4930Sigor@sysoev.ru             return;
4940Sigor@sysoev.ru         }
4950Sigor@sysoev.ru 
4960Sigor@sysoev.ru         if (ret < 0) {
4970Sigor@sysoev.ru             p = data;
4980Sigor@sysoev.ru             continue;
4990Sigor@sysoev.ru         }
5000Sigor@sysoev.ru 
5010Sigor@sysoev.ru         if (p != data) {
5020Sigor@sysoev.ru             nxt_memcpy(data, p, size);
5030Sigor@sysoev.ru         }
5040Sigor@sysoev.ru 
5050Sigor@sysoev.ru         nxt_thread_work_queue_add(thr, &thr->work_queue.main,
5060Sigor@sysoev.ru                                   cache->delete_handler, data, NULL, thr->log);
5070Sigor@sysoev.ru     }
5080Sigor@sysoev.ru }
5090Sigor@sysoev.ru 
5100Sigor@sysoev.ru 
5110Sigor@sysoev.ru static ssize_t
nxt_cache_release_locked(nxt_cache_t * cache,nxt_cache_query_t * q,u_char * buf,size_t size)5120Sigor@sysoev.ru nxt_cache_release_locked(nxt_cache_t *cache, nxt_cache_query_t *q,
5130Sigor@sysoev.ru     u_char *buf, size_t size)
5140Sigor@sysoev.ru {
5150Sigor@sysoev.ru     ssize_t           ret;
5160Sigor@sysoev.ru     nxt_cache_node_t  *node;
5170Sigor@sysoev.ru 
5180Sigor@sysoev.ru     node = q->node;
5190Sigor@sysoev.ru     node->count--;
5200Sigor@sysoev.ru 
5210Sigor@sysoev.ru     if (node->count != 0) {
5220Sigor@sysoev.ru         return 0;
5230Sigor@sysoev.ru     }
5240Sigor@sysoev.ru 
5250Sigor@sysoev.ru     if (!node->deleted) {
5260Sigor@sysoev.ru         /*
5270Sigor@sysoev.ru          * A cache node is locked whilst its count is non zero.
5280Sigor@sysoev.ru          * To minimize number of operations the node's place in expiry
5290Sigor@sysoev.ru          * queue can be updated only if the node is not currently used.
5300Sigor@sysoev.ru          */
5310Sigor@sysoev.ru         node->accessed = q->now - cache->start_time;
5320Sigor@sysoev.ru 
5330Sigor@sysoev.ru         nxt_queue_remove(&node->link);
5340Sigor@sysoev.ru         nxt_queue_insert_head(&cache->expiry_queue, &node->link);
5350Sigor@sysoev.ru 
5360Sigor@sysoev.ru         return 0;
5370Sigor@sysoev.ru     }
5380Sigor@sysoev.ru 
5390Sigor@sysoev.ru     ret = 0;
5400Sigor@sysoev.ru #if 0
5410Sigor@sysoev.ru 
5420Sigor@sysoev.ru     ret = cache->delete_copy(cache, node, buf, size);
5430Sigor@sysoev.ru 
5440Sigor@sysoev.ru     if (ret < 0) {
5450Sigor@sysoev.ru         return ret;
5460Sigor@sysoev.ru     }
5470Sigor@sysoev.ru 
5480Sigor@sysoev.ru #endif
5490Sigor@sysoev.ru 
5500Sigor@sysoev.ru     nxt_cache_node_free(cache, node, 0);
5510Sigor@sysoev.ru 
5520Sigor@sysoev.ru     return ret;
5530Sigor@sysoev.ru }
5540Sigor@sysoev.ru 
5550Sigor@sysoev.ru 
5560Sigor@sysoev.ru static nxt_cache_node_t *
nxt_cache_node_alloc(nxt_cache_t * cache)5570Sigor@sysoev.ru nxt_cache_node_alloc(nxt_cache_t *cache)
5580Sigor@sysoev.ru {
5590Sigor@sysoev.ru     nxt_queue_link_t  *link;
5600Sigor@sysoev.ru     nxt_cache_node_t  *node;
5610Sigor@sysoev.ru 
5620Sigor@sysoev.ru     link = nxt_queue_first(&cache->free_nodes);
5630Sigor@sysoev.ru 
5640Sigor@sysoev.ru     if (nxt_fast_path(link != nxt_queue_tail(&cache->free_nodes))) {
5650Sigor@sysoev.ru         cache->nfree_nodes--;
5660Sigor@sysoev.ru         nxt_queue_remove(link);
5670Sigor@sysoev.ru 
5680Sigor@sysoev.ru         node = nxt_queue_link_data(link, nxt_cache_node_t, link);
5690Sigor@sysoev.ru         nxt_memzero(node, sizeof(nxt_cache_node_t));
5700Sigor@sysoev.ru 
5710Sigor@sysoev.ru         return node;
5720Sigor@sysoev.ru     }
5730Sigor@sysoev.ru 
5740Sigor@sysoev.ru     nxt_cache_unlock(cache);
5750Sigor@sysoev.ru 
5760Sigor@sysoev.ru     node = cache->alloc(cache->data, sizeof(nxt_cache_node_t));
5770Sigor@sysoev.ru 
5780Sigor@sysoev.ru     nxt_cache_lock(cache);
5790Sigor@sysoev.ru 
5800Sigor@sysoev.ru     return node;
5810Sigor@sysoev.ru }
5820Sigor@sysoev.ru 
5830Sigor@sysoev.ru 
5840Sigor@sysoev.ru static void
nxt_cache_node_free(nxt_cache_t * cache,nxt_cache_node_t * node,nxt_bool_t fast)5850Sigor@sysoev.ru nxt_cache_node_free(nxt_cache_t *cache, nxt_cache_node_t *node, nxt_bool_t fast)
5860Sigor@sysoev.ru {
5870Sigor@sysoev.ru     if (fast || cache->nfree_nodes < 32) {
5880Sigor@sysoev.ru         nxt_queue_insert_head(&cache->free_nodes, &node->link);
5890Sigor@sysoev.ru         cache->nfree_nodes++;
5900Sigor@sysoev.ru         return;
5910Sigor@sysoev.ru     }
5920Sigor@sysoev.ru 
5930Sigor@sysoev.ru     nxt_cache_unlock(cache);
5940Sigor@sysoev.ru 
5950Sigor@sysoev.ru     cache->free(cache->data, node);
5960Sigor@sysoev.ru 
5970Sigor@sysoev.ru     nxt_cache_lock(cache);
5980Sigor@sysoev.ru }
5990Sigor@sysoev.ru 
6000Sigor@sysoev.ru 
6010Sigor@sysoev.ru static nxt_cache_query_wait_t *
nxt_cache_query_wait_alloc(nxt_cache_t * cache,nxt_bool_t * slow)6020Sigor@sysoev.ru nxt_cache_query_wait_alloc(nxt_cache_t *cache, nxt_bool_t *slow)
6030Sigor@sysoev.ru {
6040Sigor@sysoev.ru     nxt_cache_query_wait_t  *qw;
6050Sigor@sysoev.ru 
6060Sigor@sysoev.ru     qw = cache->free_query_wait;
6070Sigor@sysoev.ru 
6080Sigor@sysoev.ru     if (nxt_fast_path(qw != NULL)) {
6090Sigor@sysoev.ru         cache->free_query_wait = qw->next;
6100Sigor@sysoev.ru         cache->nfree_query_wait--;
6110Sigor@sysoev.ru 
6120Sigor@sysoev.ru         *slow = 0;
6130Sigor@sysoev.ru         return qw;
6140Sigor@sysoev.ru     }
6150Sigor@sysoev.ru 
6160Sigor@sysoev.ru     nxt_cache_unlock(cache);
6170Sigor@sysoev.ru 
6180Sigor@sysoev.ru     qw = cache->alloc(cache->data, sizeof(nxt_cache_query_wait_t));
6190Sigor@sysoev.ru     *slow = 1;
6200Sigor@sysoev.ru 
6210Sigor@sysoev.ru     nxt_cache_lock(cache);
6220Sigor@sysoev.ru 
6230Sigor@sysoev.ru     return qw;
6240Sigor@sysoev.ru }
6250Sigor@sysoev.ru 
6260Sigor@sysoev.ru 
6270Sigor@sysoev.ru static void
nxt_cache_query_wait_free(nxt_cache_t * cache,nxt_cache_query_wait_t * qw)6280Sigor@sysoev.ru nxt_cache_query_wait_free(nxt_cache_t *cache, nxt_cache_query_wait_t *qw)
6290Sigor@sysoev.ru {
6300Sigor@sysoev.ru     if (cache->nfree_query_wait < 32) {
6310Sigor@sysoev.ru         qw->next = cache->free_query_wait;
6320Sigor@sysoev.ru         cache->free_query_wait = qw;
6330Sigor@sysoev.ru         cache->nfree_query_wait++;
6340Sigor@sysoev.ru         return;
6350Sigor@sysoev.ru     }
6360Sigor@sysoev.ru 
6370Sigor@sysoev.ru     nxt_cache_unlock(cache);
6380Sigor@sysoev.ru 
6390Sigor@sysoev.ru     cache->free(cache->data, qw);
6400Sigor@sysoev.ru 
6410Sigor@sysoev.ru     nxt_cache_lock(cache);
6420Sigor@sysoev.ru }
643