xref: /unit/src/nxt_cache.c (revision 0)
1*0Sigor@sysoev.ru 
2*0Sigor@sysoev.ru /*
3*0Sigor@sysoev.ru  * Copyright (C) Igor Sysoev
4*0Sigor@sysoev.ru  * Copyright (C) NGINX, Inc.
5*0Sigor@sysoev.ru  */
6*0Sigor@sysoev.ru 
7*0Sigor@sysoev.ru #include <nxt_main.h>
8*0Sigor@sysoev.ru 
9*0Sigor@sysoev.ru 
10*0Sigor@sysoev.ru /* A cache time resolution is 10ms. */
11*0Sigor@sysoev.ru #define                                                                       \
12*0Sigor@sysoev.ru nxt_cache_time(thr)                                                           \
13*0Sigor@sysoev.ru     (uint64_t) (nxt_thread_time(thr) * 100)
14*0Sigor@sysoev.ru 
15*0Sigor@sysoev.ru 
16*0Sigor@sysoev.ru static nxt_int_t nxt_cache_lvlhsh_test(nxt_lvlhsh_query_t *lhq, void *data);
17*0Sigor@sysoev.ru static nxt_work_handler_t nxt_cache_query_locked(nxt_cache_t *cache,
18*0Sigor@sysoev.ru     nxt_cache_query_t *q, nxt_lvlhsh_query_t *lhq);
19*0Sigor@sysoev.ru static nxt_work_handler_t nxt_cache_node_hold(nxt_cache_t *cache,
20*0Sigor@sysoev.ru     nxt_cache_query_t *q, nxt_lvlhsh_query_t *lhq);
21*0Sigor@sysoev.ru static nxt_work_handler_t nxt_cache_node_test(nxt_cache_t *cache,
22*0Sigor@sysoev.ru     nxt_cache_query_t *q);
23*0Sigor@sysoev.ru 
24*0Sigor@sysoev.ru static void nxt_cache_wait_handler(nxt_thread_t *thr, void *obj, void *data);
25*0Sigor@sysoev.ru static void nxt_cache_timeout_handler(nxt_thread_t *thr, void *obj, void *data);
26*0Sigor@sysoev.ru static void nxt_cache_wake_handler(nxt_thread_t *thr, void *obj, void *data);
27*0Sigor@sysoev.ru static ssize_t nxt_cache_release_locked(nxt_cache_t *cache,
28*0Sigor@sysoev.ru     nxt_cache_query_t *q, u_char *buf, size_t size);
29*0Sigor@sysoev.ru 
30*0Sigor@sysoev.ru static nxt_cache_node_t *nxt_cache_node_alloc(nxt_cache_t *cache);
31*0Sigor@sysoev.ru static void nxt_cache_node_free(nxt_cache_t *cache, nxt_cache_node_t *node,
32*0Sigor@sysoev.ru     nxt_bool_t fast);
33*0Sigor@sysoev.ru static nxt_cache_query_wait_t *nxt_cache_query_wait_alloc(nxt_cache_t *cache,
34*0Sigor@sysoev.ru     nxt_bool_t *slow);
35*0Sigor@sysoev.ru static void nxt_cache_query_wait_free(nxt_cache_t *cache,
36*0Sigor@sysoev.ru     nxt_cache_query_wait_t *qw);
37*0Sigor@sysoev.ru 
38*0Sigor@sysoev.ru 
39*0Sigor@sysoev.ru /* STUB */
40*0Sigor@sysoev.ru nxt_int_t nxt_cache_shm_create(nxt_mem_zone_t *pool);
41*0Sigor@sysoev.ru static void *nxt_cache_shm_alloc(void *data, size_t size, nxt_uint_t nalloc);
42*0Sigor@sysoev.ru /**/
43*0Sigor@sysoev.ru 
44*0Sigor@sysoev.ru 
45*0Sigor@sysoev.ru nxt_int_t
46*0Sigor@sysoev.ru nxt_cache_shm_create(nxt_mem_zone_t *mz)
47*0Sigor@sysoev.ru {
48*0Sigor@sysoev.ru     nxt_cache_t  *cache;
49*0Sigor@sysoev.ru 
50*0Sigor@sysoev.ru     static const nxt_lvlhsh_proto_t  proto  nxt_aligned(64) = {
51*0Sigor@sysoev.ru         NXT_LVLHSH_LARGE_SLAB,
52*0Sigor@sysoev.ru         0,
53*0Sigor@sysoev.ru         nxt_cache_lvlhsh_test,
54*0Sigor@sysoev.ru         (nxt_lvlhsh_alloc_t) nxt_cache_shm_alloc,
55*0Sigor@sysoev.ru         (nxt_lvlhsh_free_t) nxt_mem_zone_free,
56*0Sigor@sysoev.ru     };
57*0Sigor@sysoev.ru 
58*0Sigor@sysoev.ru     cache = nxt_mem_zone_zalloc(mz, sizeof(nxt_cache_t));
59*0Sigor@sysoev.ru 
60*0Sigor@sysoev.ru     if (cache == NULL) {
61*0Sigor@sysoev.ru         return NXT_ERROR;
62*0Sigor@sysoev.ru     }
63*0Sigor@sysoev.ru 
64*0Sigor@sysoev.ru     cache->proto = &proto;
65*0Sigor@sysoev.ru     cache->pool = mz;
66*0Sigor@sysoev.ru 
67*0Sigor@sysoev.ru     cache->start_time = nxt_cache_time(nxt_thread());
68*0Sigor@sysoev.ru 
69*0Sigor@sysoev.ru     return NXT_OK;
70*0Sigor@sysoev.ru }
71*0Sigor@sysoev.ru 
72*0Sigor@sysoev.ru 
73*0Sigor@sysoev.ru static void *
74*0Sigor@sysoev.ru nxt_cache_shm_alloc(void *data, size_t size, nxt_uint_t nalloc)
75*0Sigor@sysoev.ru {
76*0Sigor@sysoev.ru     return nxt_mem_zone_align(data, size, size);
77*0Sigor@sysoev.ru }
78*0Sigor@sysoev.ru 
79*0Sigor@sysoev.ru 
80*0Sigor@sysoev.ru void
81*0Sigor@sysoev.ru nxt_cache_init(nxt_cache_t *cache)
82*0Sigor@sysoev.ru {
83*0Sigor@sysoev.ru     static const nxt_lvlhsh_proto_t  proto  nxt_aligned(64) = {
84*0Sigor@sysoev.ru         NXT_LVLHSH_LARGE_MEMALIGN,
85*0Sigor@sysoev.ru         0,
86*0Sigor@sysoev.ru         nxt_cache_lvlhsh_test,
87*0Sigor@sysoev.ru         nxt_lvlhsh_alloc,
88*0Sigor@sysoev.ru         nxt_lvlhsh_free,
89*0Sigor@sysoev.ru     };
90*0Sigor@sysoev.ru 
91*0Sigor@sysoev.ru     cache->proto = &proto;
92*0Sigor@sysoev.ru 
93*0Sigor@sysoev.ru     cache->start_time = nxt_cache_time(nxt_thread());
94*0Sigor@sysoev.ru }
95*0Sigor@sysoev.ru 
96*0Sigor@sysoev.ru 
97*0Sigor@sysoev.ru static nxt_int_t
98*0Sigor@sysoev.ru nxt_cache_lvlhsh_test(nxt_lvlhsh_query_t *lhq, void *data)
99*0Sigor@sysoev.ru {
100*0Sigor@sysoev.ru     nxt_cache_node_t  *node;
101*0Sigor@sysoev.ru 
102*0Sigor@sysoev.ru     node = data;
103*0Sigor@sysoev.ru 
104*0Sigor@sysoev.ru     if (nxt_str_eq(&lhq->key, node->key_data, node->key_len)) {
105*0Sigor@sysoev.ru         return NXT_OK;
106*0Sigor@sysoev.ru     }
107*0Sigor@sysoev.ru 
108*0Sigor@sysoev.ru     return NXT_DECLINED;
109*0Sigor@sysoev.ru }
110*0Sigor@sysoev.ru 
111*0Sigor@sysoev.ru 
112*0Sigor@sysoev.ru nxt_inline void
113*0Sigor@sysoev.ru nxt_cache_lock(nxt_cache_t *cache)
114*0Sigor@sysoev.ru {
115*0Sigor@sysoev.ru     if (cache->shared) {
116*0Sigor@sysoev.ru         nxt_thread_spin_lock(&cache->lock);
117*0Sigor@sysoev.ru     }
118*0Sigor@sysoev.ru }
119*0Sigor@sysoev.ru 
120*0Sigor@sysoev.ru 
121*0Sigor@sysoev.ru nxt_inline void
122*0Sigor@sysoev.ru nxt_cache_unlock(nxt_cache_t *cache)
123*0Sigor@sysoev.ru {
124*0Sigor@sysoev.ru     if (cache->shared) {
125*0Sigor@sysoev.ru         nxt_thread_spin_unlock(&cache->lock);
126*0Sigor@sysoev.ru     }
127*0Sigor@sysoev.ru }
128*0Sigor@sysoev.ru 
129*0Sigor@sysoev.ru 
130*0Sigor@sysoev.ru void
131*0Sigor@sysoev.ru nxt_cache_query(nxt_cache_t *cache, nxt_cache_query_t *q)
132*0Sigor@sysoev.ru {
133*0Sigor@sysoev.ru     nxt_thread_t        *thr;
134*0Sigor@sysoev.ru     nxt_lvlhsh_query_t  lhq;
135*0Sigor@sysoev.ru     nxt_work_handler_t  handler;
136*0Sigor@sysoev.ru 
137*0Sigor@sysoev.ru     thr = nxt_thread();
138*0Sigor@sysoev.ru 
139*0Sigor@sysoev.ru     if (cache != NULL) {
140*0Sigor@sysoev.ru         lhq.key_hash = nxt_murmur_hash2(q->key_data, q->key_len);
141*0Sigor@sysoev.ru         lhq.replace = 0;
142*0Sigor@sysoev.ru         lhq.key.len = q->key_len;
143*0Sigor@sysoev.ru         lhq.key.data = q->key_data;
144*0Sigor@sysoev.ru         lhq.proto = cache->proto;
145*0Sigor@sysoev.ru         lhq.pool = cache->pool;
146*0Sigor@sysoev.ru 
147*0Sigor@sysoev.ru         q->now = nxt_cache_time(thr);
148*0Sigor@sysoev.ru 
149*0Sigor@sysoev.ru         nxt_cache_lock(cache);
150*0Sigor@sysoev.ru 
151*0Sigor@sysoev.ru         handler = nxt_cache_query_locked(cache, q, &lhq);
152*0Sigor@sysoev.ru 
153*0Sigor@sysoev.ru         nxt_cache_unlock(cache);
154*0Sigor@sysoev.ru 
155*0Sigor@sysoev.ru     } else {
156*0Sigor@sysoev.ru         handler = q->state->nocache_handler;
157*0Sigor@sysoev.ru     }
158*0Sigor@sysoev.ru 
159*0Sigor@sysoev.ru     handler(thr, q, NULL);
160*0Sigor@sysoev.ru }
161*0Sigor@sysoev.ru 
162*0Sigor@sysoev.ru 
163*0Sigor@sysoev.ru static nxt_work_handler_t
164*0Sigor@sysoev.ru nxt_cache_query_locked(nxt_cache_t *cache, nxt_cache_query_t *q,
165*0Sigor@sysoev.ru     nxt_lvlhsh_query_t *lhq)
166*0Sigor@sysoev.ru {
167*0Sigor@sysoev.ru     nxt_int_t                ret;
168*0Sigor@sysoev.ru     nxt_time_t               expiry;
169*0Sigor@sysoev.ru     nxt_cache_node_t         *node;
170*0Sigor@sysoev.ru     nxt_cache_query_state_t  *state;
171*0Sigor@sysoev.ru 
172*0Sigor@sysoev.ru     if (q->hold) {
173*0Sigor@sysoev.ru         return nxt_cache_node_hold(cache, q, lhq);
174*0Sigor@sysoev.ru     }
175*0Sigor@sysoev.ru 
176*0Sigor@sysoev.ru     ret = nxt_lvlhsh_find(&cache->lvlhsh, lhq);
177*0Sigor@sysoev.ru 
178*0Sigor@sysoev.ru     state = q->state;
179*0Sigor@sysoev.ru 
180*0Sigor@sysoev.ru     if (ret != NXT_OK) {
181*0Sigor@sysoev.ru         /* NXT_DECLINED */
182*0Sigor@sysoev.ru         return state->nocache_handler;
183*0Sigor@sysoev.ru     }
184*0Sigor@sysoev.ru 
185*0Sigor@sysoev.ru     node = lhq->value;
186*0Sigor@sysoev.ru     node->count++;
187*0Sigor@sysoev.ru     q->node = node;
188*0Sigor@sysoev.ru 
189*0Sigor@sysoev.ru     expiry = cache->start_time + node->expiry;
190*0Sigor@sysoev.ru 
191*0Sigor@sysoev.ru     if (q->now < expiry) {
192*0Sigor@sysoev.ru         return state->ready_handler;
193*0Sigor@sysoev.ru     }
194*0Sigor@sysoev.ru 
195*0Sigor@sysoev.ru     q->stale = 1;
196*0Sigor@sysoev.ru 
197*0Sigor@sysoev.ru     return state->stale_handler;
198*0Sigor@sysoev.ru }
199*0Sigor@sysoev.ru 
200*0Sigor@sysoev.ru 
201*0Sigor@sysoev.ru static nxt_work_handler_t
202*0Sigor@sysoev.ru nxt_cache_node_hold(nxt_cache_t *cache, nxt_cache_query_t *q,
203*0Sigor@sysoev.ru     nxt_lvlhsh_query_t *lhq)
204*0Sigor@sysoev.ru {
205*0Sigor@sysoev.ru     nxt_int_t                ret;
206*0Sigor@sysoev.ru     nxt_bool_t               slow;
207*0Sigor@sysoev.ru     nxt_cache_node_t         *node, *sentinel;
208*0Sigor@sysoev.ru     nxt_work_handler_t       handler;
209*0Sigor@sysoev.ru     nxt_cache_query_wait_t   *qw;
210*0Sigor@sysoev.ru     nxt_cache_query_state_t  *state;
211*0Sigor@sysoev.ru 
212*0Sigor@sysoev.ru     state = q->state;
213*0Sigor@sysoev.ru     sentinel = nxt_cache_node_alloc(cache);
214*0Sigor@sysoev.ru 
215*0Sigor@sysoev.ru     if (nxt_slow_path(sentinel == NULL)) {
216*0Sigor@sysoev.ru         return state->error_handler;
217*0Sigor@sysoev.ru     }
218*0Sigor@sysoev.ru 
219*0Sigor@sysoev.ru     sentinel->key_data = q->key_data;
220*0Sigor@sysoev.ru     sentinel->key_len = q->key_len;
221*0Sigor@sysoev.ru     lhq->value = sentinel;
222*0Sigor@sysoev.ru 
223*0Sigor@sysoev.ru     /*
224*0Sigor@sysoev.ru      * Try to insert an empty sentinel node to hold updating
225*0Sigor@sysoev.ru      * process if there is no existent cache node in cache.
226*0Sigor@sysoev.ru      */
227*0Sigor@sysoev.ru     ret = nxt_lvlhsh_insert(&cache->lvlhsh, lhq);
228*0Sigor@sysoev.ru 
229*0Sigor@sysoev.ru     if (ret == NXT_OK) {
230*0Sigor@sysoev.ru         /* The sentinel node was successully added. */
231*0Sigor@sysoev.ru 
232*0Sigor@sysoev.ru         q->node = sentinel;
233*0Sigor@sysoev.ru         sentinel->updating = 1;
234*0Sigor@sysoev.ru         return state->update_handler;
235*0Sigor@sysoev.ru     }
236*0Sigor@sysoev.ru 
237*0Sigor@sysoev.ru     nxt_cache_node_free(cache, sentinel, 1);
238*0Sigor@sysoev.ru 
239*0Sigor@sysoev.ru     if (ret == NXT_ERROR) {
240*0Sigor@sysoev.ru         return state->error_handler;
241*0Sigor@sysoev.ru     }
242*0Sigor@sysoev.ru 
243*0Sigor@sysoev.ru     /* NXT_DECLINED: a cache node exists. */
244*0Sigor@sysoev.ru 
245*0Sigor@sysoev.ru     node = lhq->value;
246*0Sigor@sysoev.ru     node->count++;
247*0Sigor@sysoev.ru     q->node = node;
248*0Sigor@sysoev.ru 
249*0Sigor@sysoev.ru     handler = nxt_cache_node_test(cache, q);
250*0Sigor@sysoev.ru     if (handler != NULL) {
251*0Sigor@sysoev.ru         return handler;
252*0Sigor@sysoev.ru     }
253*0Sigor@sysoev.ru 
254*0Sigor@sysoev.ru     /* Add the node to a wait queue. */
255*0Sigor@sysoev.ru 
256*0Sigor@sysoev.ru     qw = nxt_cache_query_wait_alloc(cache, &slow);
257*0Sigor@sysoev.ru     if (nxt_slow_path(qw == NULL)) {
258*0Sigor@sysoev.ru         return state->error_handler;
259*0Sigor@sysoev.ru     }
260*0Sigor@sysoev.ru 
261*0Sigor@sysoev.ru     if (slow) {
262*0Sigor@sysoev.ru         /* The node state may have been changed during slow allocation. */
263*0Sigor@sysoev.ru 
264*0Sigor@sysoev.ru         handler = nxt_cache_node_test(cache, q);
265*0Sigor@sysoev.ru         if (handler != NULL) {
266*0Sigor@sysoev.ru             nxt_cache_query_wait_free(cache, qw);
267*0Sigor@sysoev.ru             return handler;
268*0Sigor@sysoev.ru         }
269*0Sigor@sysoev.ru     }
270*0Sigor@sysoev.ru 
271*0Sigor@sysoev.ru     qw->query = q;
272*0Sigor@sysoev.ru     qw->next = node->waiting;
273*0Sigor@sysoev.ru     qw->busy = 0;
274*0Sigor@sysoev.ru     qw->deleted = 0;
275*0Sigor@sysoev.ru     qw->pid = nxt_pid;
276*0Sigor@sysoev.ru     qw->engine = nxt_thread_event_engine();
277*0Sigor@sysoev.ru     qw->handler = nxt_cache_wake_handler;
278*0Sigor@sysoev.ru     qw->cache = cache;
279*0Sigor@sysoev.ru 
280*0Sigor@sysoev.ru     node->waiting = qw;
281*0Sigor@sysoev.ru 
282*0Sigor@sysoev.ru     return nxt_cache_wait_handler;
283*0Sigor@sysoev.ru }
284*0Sigor@sysoev.ru 
285*0Sigor@sysoev.ru 
286*0Sigor@sysoev.ru static nxt_work_handler_t
287*0Sigor@sysoev.ru nxt_cache_node_test(nxt_cache_t *cache, nxt_cache_query_t *q)
288*0Sigor@sysoev.ru {
289*0Sigor@sysoev.ru     nxt_time_t               expiry;
290*0Sigor@sysoev.ru     nxt_cache_node_t         *node;
291*0Sigor@sysoev.ru     nxt_cache_query_state_t  *state;
292*0Sigor@sysoev.ru 
293*0Sigor@sysoev.ru     q->stale = 0;
294*0Sigor@sysoev.ru     state = q->state;
295*0Sigor@sysoev.ru     node = q->node;
296*0Sigor@sysoev.ru 
297*0Sigor@sysoev.ru     expiry = cache->start_time + node->expiry;
298*0Sigor@sysoev.ru 
299*0Sigor@sysoev.ru     if (q->now < expiry) {
300*0Sigor@sysoev.ru         return state->ready_handler;
301*0Sigor@sysoev.ru     }
302*0Sigor@sysoev.ru 
303*0Sigor@sysoev.ru     /*
304*0Sigor@sysoev.ru      * A valid stale or empty sentinel cache node.
305*0Sigor@sysoev.ru      * The sentinel node can be only in updating state.
306*0Sigor@sysoev.ru      */
307*0Sigor@sysoev.ru 
308*0Sigor@sysoev.ru     if (node->updating) {
309*0Sigor@sysoev.ru 
310*0Sigor@sysoev.ru         if (node->expiry != 0) {
311*0Sigor@sysoev.ru             /* A valid stale cache node. */
312*0Sigor@sysoev.ru 
313*0Sigor@sysoev.ru             q->stale = 1;
314*0Sigor@sysoev.ru 
315*0Sigor@sysoev.ru             if (q->use_stale) {
316*0Sigor@sysoev.ru                 return state->stale_handler;
317*0Sigor@sysoev.ru             }
318*0Sigor@sysoev.ru         }
319*0Sigor@sysoev.ru 
320*0Sigor@sysoev.ru         /* A sentinel node. */
321*0Sigor@sysoev.ru         return NULL;
322*0Sigor@sysoev.ru     }
323*0Sigor@sysoev.ru 
324*0Sigor@sysoev.ru     /* A valid stale cache node is not being updated now. */
325*0Sigor@sysoev.ru 
326*0Sigor@sysoev.ru     q->stale = 1;
327*0Sigor@sysoev.ru 
328*0Sigor@sysoev.ru     if (q->use_stale) {
329*0Sigor@sysoev.ru 
330*0Sigor@sysoev.ru         if (q->update_stale) {
331*0Sigor@sysoev.ru             node->updating = 1;
332*0Sigor@sysoev.ru             return state->update_stale_handler;
333*0Sigor@sysoev.ru         }
334*0Sigor@sysoev.ru 
335*0Sigor@sysoev.ru         return state->stale_handler;
336*0Sigor@sysoev.ru     }
337*0Sigor@sysoev.ru 
338*0Sigor@sysoev.ru     node->updating = 1;
339*0Sigor@sysoev.ru     return state->update_handler;
340*0Sigor@sysoev.ru }
341*0Sigor@sysoev.ru 
342*0Sigor@sysoev.ru 
343*0Sigor@sysoev.ru static void
344*0Sigor@sysoev.ru nxt_cache_wait_handler(nxt_thread_t *thr, void *obj, void *data)
345*0Sigor@sysoev.ru {
346*0Sigor@sysoev.ru     nxt_event_timer_t  *ev;
347*0Sigor@sysoev.ru     nxt_cache_query_t  *cq;
348*0Sigor@sysoev.ru 
349*0Sigor@sysoev.ru     cq = obj;
350*0Sigor@sysoev.ru 
351*0Sigor@sysoev.ru     if (cq->timeout != 0) {
352*0Sigor@sysoev.ru 
353*0Sigor@sysoev.ru         ev = &cq->timer;
354*0Sigor@sysoev.ru 
355*0Sigor@sysoev.ru         if (ev->state == NXT_EVENT_TIMER_DISABLED) {
356*0Sigor@sysoev.ru             ev->handler = nxt_cache_timeout_handler;
357*0Sigor@sysoev.ru             nxt_event_timer_ident(ev, -1);
358*0Sigor@sysoev.ru 
359*0Sigor@sysoev.ru             nxt_event_timer_add(thr->engine, ev, cq->timeout);
360*0Sigor@sysoev.ru         }
361*0Sigor@sysoev.ru     }
362*0Sigor@sysoev.ru }
363*0Sigor@sysoev.ru 
364*0Sigor@sysoev.ru 
365*0Sigor@sysoev.ru static void
366*0Sigor@sysoev.ru nxt_cache_timeout_handler(nxt_thread_t *thr, void *obj, void *data)
367*0Sigor@sysoev.ru {
368*0Sigor@sysoev.ru     nxt_cache_query_t  *cq;
369*0Sigor@sysoev.ru     nxt_event_timer_t  *ev;
370*0Sigor@sysoev.ru 
371*0Sigor@sysoev.ru     ev = obj;
372*0Sigor@sysoev.ru 
373*0Sigor@sysoev.ru     cq = nxt_event_timer_data(ev, nxt_cache_query_t, timer);
374*0Sigor@sysoev.ru 
375*0Sigor@sysoev.ru     cq->state->timeout_handler(thr, cq, NULL);
376*0Sigor@sysoev.ru }
377*0Sigor@sysoev.ru 
378*0Sigor@sysoev.ru 
379*0Sigor@sysoev.ru static void
380*0Sigor@sysoev.ru nxt_cache_wake_handler(nxt_thread_t *thr, void *obj, void *data)
381*0Sigor@sysoev.ru {
382*0Sigor@sysoev.ru     nxt_cache_t             *cache;
383*0Sigor@sysoev.ru     nxt_work_handler_t      handler;
384*0Sigor@sysoev.ru     nxt_cache_query_t       *q;
385*0Sigor@sysoev.ru     nxt_cache_query_wait_t  *qw;
386*0Sigor@sysoev.ru 
387*0Sigor@sysoev.ru     qw = obj;
388*0Sigor@sysoev.ru     q = qw->query;
389*0Sigor@sysoev.ru     cache = qw->cache;
390*0Sigor@sysoev.ru 
391*0Sigor@sysoev.ru     nxt_cache_lock(cache);
392*0Sigor@sysoev.ru 
393*0Sigor@sysoev.ru     handler = nxt_cache_node_test(cache, q);
394*0Sigor@sysoev.ru 
395*0Sigor@sysoev.ru     if (handler != NULL) {
396*0Sigor@sysoev.ru         nxt_cache_query_wait_free(cache, qw);
397*0Sigor@sysoev.ru 
398*0Sigor@sysoev.ru     } else {
399*0Sigor@sysoev.ru         /* Wait again. */
400*0Sigor@sysoev.ru         qw->next = q->node->waiting;
401*0Sigor@sysoev.ru         q->node->waiting = qw;
402*0Sigor@sysoev.ru     }
403*0Sigor@sysoev.ru 
404*0Sigor@sysoev.ru     nxt_cache_unlock(cache);
405*0Sigor@sysoev.ru 
406*0Sigor@sysoev.ru     handler(thr, q, NULL);
407*0Sigor@sysoev.ru }
408*0Sigor@sysoev.ru 
409*0Sigor@sysoev.ru 
410*0Sigor@sysoev.ru nxt_int_t
411*0Sigor@sysoev.ru nxt_cache_update(nxt_cache_t *cache, nxt_cache_query_t *q)
412*0Sigor@sysoev.ru {
413*0Sigor@sysoev.ru     nxt_int_t           ret;
414*0Sigor@sysoev.ru     nxt_cache_node_t    *node;
415*0Sigor@sysoev.ru     nxt_lvlhsh_query_t  lhq;
416*0Sigor@sysoev.ru 
417*0Sigor@sysoev.ru     node = q->node;
418*0Sigor@sysoev.ru 
419*0Sigor@sysoev.ru     node->accessed = nxt_cache_time(nxt_thread()) - cache->start_time;
420*0Sigor@sysoev.ru 
421*0Sigor@sysoev.ru     node->updating = 0;
422*0Sigor@sysoev.ru     node->count = 1;
423*0Sigor@sysoev.ru 
424*0Sigor@sysoev.ru     lhq.key_hash = nxt_murmur_hash2(node->key_data, node->key_len);
425*0Sigor@sysoev.ru     lhq.replace = 1;
426*0Sigor@sysoev.ru     lhq.key.len = node->key_len;
427*0Sigor@sysoev.ru     lhq.key.data = node->key_data;
428*0Sigor@sysoev.ru     lhq.value = node;
429*0Sigor@sysoev.ru     lhq.proto = cache->proto;
430*0Sigor@sysoev.ru     lhq.pool = cache->pool;
431*0Sigor@sysoev.ru 
432*0Sigor@sysoev.ru     nxt_cache_lock(cache);
433*0Sigor@sysoev.ru 
434*0Sigor@sysoev.ru     ret = nxt_lvlhsh_insert(&cache->lvlhsh, &lhq);
435*0Sigor@sysoev.ru 
436*0Sigor@sysoev.ru     if (nxt_fast_path(ret != NXT_OK)) {
437*0Sigor@sysoev.ru 
438*0Sigor@sysoev.ru         nxt_queue_insert_head(&cache->expiry_queue, &node->link);
439*0Sigor@sysoev.ru 
440*0Sigor@sysoev.ru         node = lhq.value;
441*0Sigor@sysoev.ru 
442*0Sigor@sysoev.ru         if (node != NULL) {
443*0Sigor@sysoev.ru             /* A replaced node. */
444*0Sigor@sysoev.ru 
445*0Sigor@sysoev.ru             nxt_queue_remove(&node->link);
446*0Sigor@sysoev.ru 
447*0Sigor@sysoev.ru             if (node->count != 0) {
448*0Sigor@sysoev.ru                 node->deleted = 1;
449*0Sigor@sysoev.ru 
450*0Sigor@sysoev.ru             } else {
451*0Sigor@sysoev.ru                 // delete cache node
452*0Sigor@sysoev.ru             }
453*0Sigor@sysoev.ru         }
454*0Sigor@sysoev.ru     }
455*0Sigor@sysoev.ru 
456*0Sigor@sysoev.ru     nxt_cache_unlock(cache);
457*0Sigor@sysoev.ru 
458*0Sigor@sysoev.ru     return ret;
459*0Sigor@sysoev.ru }
460*0Sigor@sysoev.ru 
461*0Sigor@sysoev.ru 
462*0Sigor@sysoev.ru void
463*0Sigor@sysoev.ru nxt_cache_release(nxt_cache_t *cache, nxt_cache_query_t *q)
464*0Sigor@sysoev.ru {
465*0Sigor@sysoev.ru     u_char        *p, *data;
466*0Sigor@sysoev.ru     size_t        size;
467*0Sigor@sysoev.ru     ssize_t       ret;
468*0Sigor@sysoev.ru     nxt_thread_t  *thr;
469*0Sigor@sysoev.ru     u_char        buf[1024];
470*0Sigor@sysoev.ru 
471*0Sigor@sysoev.ru     thr = nxt_thread();
472*0Sigor@sysoev.ru     q->now = nxt_cache_time(thr);
473*0Sigor@sysoev.ru 
474*0Sigor@sysoev.ru     p = buf;
475*0Sigor@sysoev.ru     size = sizeof(buf);
476*0Sigor@sysoev.ru 
477*0Sigor@sysoev.ru     for ( ;; ) {
478*0Sigor@sysoev.ru         nxt_cache_lock(cache);
479*0Sigor@sysoev.ru 
480*0Sigor@sysoev.ru         ret = nxt_cache_release_locked(cache, q, p, size);
481*0Sigor@sysoev.ru 
482*0Sigor@sysoev.ru         nxt_cache_unlock(cache);
483*0Sigor@sysoev.ru 
484*0Sigor@sysoev.ru         if (ret == 0) {
485*0Sigor@sysoev.ru             return;
486*0Sigor@sysoev.ru         }
487*0Sigor@sysoev.ru 
488*0Sigor@sysoev.ru         size = nxt_abs(ret);
489*0Sigor@sysoev.ru 
490*0Sigor@sysoev.ru         data = nxt_malloc(size);
491*0Sigor@sysoev.ru 
492*0Sigor@sysoev.ru         if (data == NULL) {
493*0Sigor@sysoev.ru             /* TODO: retry */
494*0Sigor@sysoev.ru             return;
495*0Sigor@sysoev.ru         }
496*0Sigor@sysoev.ru 
497*0Sigor@sysoev.ru         if (ret < 0) {
498*0Sigor@sysoev.ru             p = data;
499*0Sigor@sysoev.ru             continue;
500*0Sigor@sysoev.ru         }
501*0Sigor@sysoev.ru 
502*0Sigor@sysoev.ru         if (p != data) {
503*0Sigor@sysoev.ru             nxt_memcpy(data, p, size);
504*0Sigor@sysoev.ru         }
505*0Sigor@sysoev.ru 
506*0Sigor@sysoev.ru         nxt_thread_work_queue_add(thr, &thr->work_queue.main,
507*0Sigor@sysoev.ru                                   cache->delete_handler, data, NULL, thr->log);
508*0Sigor@sysoev.ru     }
509*0Sigor@sysoev.ru }
510*0Sigor@sysoev.ru 
511*0Sigor@sysoev.ru 
512*0Sigor@sysoev.ru static ssize_t
513*0Sigor@sysoev.ru nxt_cache_release_locked(nxt_cache_t *cache, nxt_cache_query_t *q,
514*0Sigor@sysoev.ru     u_char *buf, size_t size)
515*0Sigor@sysoev.ru {
516*0Sigor@sysoev.ru     ssize_t           ret;
517*0Sigor@sysoev.ru     nxt_cache_node_t  *node;
518*0Sigor@sysoev.ru 
519*0Sigor@sysoev.ru     node = q->node;
520*0Sigor@sysoev.ru     node->count--;
521*0Sigor@sysoev.ru 
522*0Sigor@sysoev.ru     if (node->count != 0) {
523*0Sigor@sysoev.ru         return 0;
524*0Sigor@sysoev.ru     }
525*0Sigor@sysoev.ru 
526*0Sigor@sysoev.ru     if (!node->deleted) {
527*0Sigor@sysoev.ru         /*
528*0Sigor@sysoev.ru          * A cache node is locked whilst its count is non zero.
529*0Sigor@sysoev.ru          * To minimize number of operations the node's place in expiry
530*0Sigor@sysoev.ru          * queue can be updated only if the node is not currently used.
531*0Sigor@sysoev.ru          */
532*0Sigor@sysoev.ru         node->accessed = q->now - cache->start_time;
533*0Sigor@sysoev.ru 
534*0Sigor@sysoev.ru         nxt_queue_remove(&node->link);
535*0Sigor@sysoev.ru         nxt_queue_insert_head(&cache->expiry_queue, &node->link);
536*0Sigor@sysoev.ru 
537*0Sigor@sysoev.ru         return 0;
538*0Sigor@sysoev.ru     }
539*0Sigor@sysoev.ru 
540*0Sigor@sysoev.ru     ret = 0;
541*0Sigor@sysoev.ru #if 0
542*0Sigor@sysoev.ru 
543*0Sigor@sysoev.ru     ret = cache->delete_copy(cache, node, buf, size);
544*0Sigor@sysoev.ru 
545*0Sigor@sysoev.ru     if (ret < 0) {
546*0Sigor@sysoev.ru         return ret;
547*0Sigor@sysoev.ru     }
548*0Sigor@sysoev.ru 
549*0Sigor@sysoev.ru #endif
550*0Sigor@sysoev.ru 
551*0Sigor@sysoev.ru     nxt_cache_node_free(cache, node, 0);
552*0Sigor@sysoev.ru 
553*0Sigor@sysoev.ru     return ret;
554*0Sigor@sysoev.ru }
555*0Sigor@sysoev.ru 
556*0Sigor@sysoev.ru 
557*0Sigor@sysoev.ru static nxt_cache_node_t *
558*0Sigor@sysoev.ru nxt_cache_node_alloc(nxt_cache_t *cache)
559*0Sigor@sysoev.ru {
560*0Sigor@sysoev.ru     nxt_queue_link_t  *link;
561*0Sigor@sysoev.ru     nxt_cache_node_t  *node;
562*0Sigor@sysoev.ru 
563*0Sigor@sysoev.ru     link = nxt_queue_first(&cache->free_nodes);
564*0Sigor@sysoev.ru 
565*0Sigor@sysoev.ru     if (nxt_fast_path(link != nxt_queue_tail(&cache->free_nodes))) {
566*0Sigor@sysoev.ru         cache->nfree_nodes--;
567*0Sigor@sysoev.ru         nxt_queue_remove(link);
568*0Sigor@sysoev.ru 
569*0Sigor@sysoev.ru         node = nxt_queue_link_data(link, nxt_cache_node_t, link);
570*0Sigor@sysoev.ru         nxt_memzero(node, sizeof(nxt_cache_node_t));
571*0Sigor@sysoev.ru 
572*0Sigor@sysoev.ru         return node;
573*0Sigor@sysoev.ru     }
574*0Sigor@sysoev.ru 
575*0Sigor@sysoev.ru     nxt_cache_unlock(cache);
576*0Sigor@sysoev.ru 
577*0Sigor@sysoev.ru     node = cache->alloc(cache->data, sizeof(nxt_cache_node_t));
578*0Sigor@sysoev.ru 
579*0Sigor@sysoev.ru     nxt_cache_lock(cache);
580*0Sigor@sysoev.ru 
581*0Sigor@sysoev.ru     return node;
582*0Sigor@sysoev.ru }
583*0Sigor@sysoev.ru 
584*0Sigor@sysoev.ru 
585*0Sigor@sysoev.ru static void
586*0Sigor@sysoev.ru nxt_cache_node_free(nxt_cache_t *cache, nxt_cache_node_t *node, nxt_bool_t fast)
587*0Sigor@sysoev.ru {
588*0Sigor@sysoev.ru     if (fast || cache->nfree_nodes < 32) {
589*0Sigor@sysoev.ru         nxt_queue_insert_head(&cache->free_nodes, &node->link);
590*0Sigor@sysoev.ru         cache->nfree_nodes++;
591*0Sigor@sysoev.ru         return;
592*0Sigor@sysoev.ru     }
593*0Sigor@sysoev.ru 
594*0Sigor@sysoev.ru     nxt_cache_unlock(cache);
595*0Sigor@sysoev.ru 
596*0Sigor@sysoev.ru     cache->free(cache->data, node);
597*0Sigor@sysoev.ru 
598*0Sigor@sysoev.ru     nxt_cache_lock(cache);
599*0Sigor@sysoev.ru }
600*0Sigor@sysoev.ru 
601*0Sigor@sysoev.ru 
602*0Sigor@sysoev.ru static nxt_cache_query_wait_t *
603*0Sigor@sysoev.ru nxt_cache_query_wait_alloc(nxt_cache_t *cache, nxt_bool_t *slow)
604*0Sigor@sysoev.ru {
605*0Sigor@sysoev.ru     nxt_cache_query_wait_t  *qw;
606*0Sigor@sysoev.ru 
607*0Sigor@sysoev.ru     qw = cache->free_query_wait;
608*0Sigor@sysoev.ru 
609*0Sigor@sysoev.ru     if (nxt_fast_path(qw != NULL)) {
610*0Sigor@sysoev.ru         cache->free_query_wait = qw->next;
611*0Sigor@sysoev.ru         cache->nfree_query_wait--;
612*0Sigor@sysoev.ru 
613*0Sigor@sysoev.ru         *slow = 0;
614*0Sigor@sysoev.ru         return qw;
615*0Sigor@sysoev.ru     }
616*0Sigor@sysoev.ru 
617*0Sigor@sysoev.ru     nxt_cache_unlock(cache);
618*0Sigor@sysoev.ru 
619*0Sigor@sysoev.ru     qw = cache->alloc(cache->data, sizeof(nxt_cache_query_wait_t));
620*0Sigor@sysoev.ru     *slow = 1;
621*0Sigor@sysoev.ru 
622*0Sigor@sysoev.ru     nxt_cache_lock(cache);
623*0Sigor@sysoev.ru 
624*0Sigor@sysoev.ru     return qw;
625*0Sigor@sysoev.ru }
626*0Sigor@sysoev.ru 
627*0Sigor@sysoev.ru 
628*0Sigor@sysoev.ru static void
629*0Sigor@sysoev.ru nxt_cache_query_wait_free(nxt_cache_t *cache, nxt_cache_query_wait_t *qw)
630*0Sigor@sysoev.ru {
631*0Sigor@sysoev.ru     if (cache->nfree_query_wait < 32) {
632*0Sigor@sysoev.ru         qw->next = cache->free_query_wait;
633*0Sigor@sysoev.ru         cache->free_query_wait = qw;
634*0Sigor@sysoev.ru         cache->nfree_query_wait++;
635*0Sigor@sysoev.ru         return;
636*0Sigor@sysoev.ru     }
637*0Sigor@sysoev.ru 
638*0Sigor@sysoev.ru     nxt_cache_unlock(cache);
639*0Sigor@sysoev.ru 
640*0Sigor@sysoev.ru     cache->free(cache->data, qw);
641*0Sigor@sysoev.ru 
642*0Sigor@sysoev.ru     nxt_cache_lock(cache);
643*0Sigor@sysoev.ru }
644