varnish-cache/bin/varnishd/cache/cache_expire.c
0
/*-
1
 * Copyright (c) 2006 Verdens Gang AS
2
 * Copyright (c) 2006-2011 Varnish Software AS
3
 * All rights reserved.
4
 *
5
 * Author: Poul-Henning Kamp <phk@phk.freebsd.dk>
6
 *
7
 * SPDX-License-Identifier: BSD-2-Clause
8
 *
9
 * Redistribution and use in source and binary forms, with or without
10
 * modification, are permitted provided that the following conditions
11
 * are met:
12
 * 1. Redistributions of source code must retain the above copyright
13
 *    notice, this list of conditions and the following disclaimer.
14
 * 2. Redistributions in binary form must reproduce the above copyright
15
 *    notice, this list of conditions and the following disclaimer in the
16
 *    documentation and/or other materials provided with the distribution.
17
 *
18
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21
 * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
22
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28
 * SUCH DAMAGE.
29
 *
30
 * LRU and object timer handling.
31
 *
32
 */
33
34
#include "config.h"
35
36
#include <stdlib.h>
37
38
#include "cache_varnishd.h"
39
#include "cache_objhead.h"
40
41
#include "vbh.h"
42
#include "vtim.h"
43
44
struct exp_priv {
45
        unsigned                        magic;
46
#define EXP_PRIV_MAGIC                  0x9db22482
47
        /* shared */
48
        struct lock                     mtx;
49
        VSTAILQ_HEAD(,objcore)          inbox;
50
        pthread_cond_t                  condvar;
51
52
        /* owned by exp thread */
53
        struct worker                   *wrk;
54
        struct vsl_log                  vsl;
55
        struct vbh                      *heap;
56
        pthread_t                       thread;
57
};
58
59
static struct exp_priv *exphdl;
60
static int exp_shutdown = 0;
61
62
/*--------------------------------------------------------------------
63
 * Calculate an object's effective ttl time, taking req.ttl into account
64
 * if it is available.
65
 */
66
67
vtim_real
68 42937
EXP_Ttl(const struct req *req, const struct objcore *oc)
69
{
70
        vtim_dur r;
71
72 42937
        CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC);
73
74 42937
        r = oc->ttl;
75 42937
        if (req != NULL && req->d_ttl > 0. && req->d_ttl < r)
76 139
                r = req->d_ttl;
77 42937
        return (oc->t_origin + r);
78
}
79
80
/*--------------------------------------------------------------------
81
 * Calculate an object's effective ttl+grace time, taking req.grace into
82
 * account if it is available.
83
 */
84
85
vtim_real
86 2925
EXP_Ttl_grace(const struct req *req, const struct objcore *oc)
87
{
88
        vtim_dur g;
89
90 2925
        CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC);
91
92 2925
        g = oc->grace;
93 2925
        if (req != NULL && req->d_grace >= 0. && req->d_grace < g)
94 75
                g = req->d_grace;
95 2925
        return (EXP_Ttl(req, oc) + g);
96
}
97
98
/*--------------------------------------------------------------------
99
 * Post an objcore to the exp_thread's inbox.
100
 */
101
102
static void
103 40524
exp_mail_it(struct objcore *oc, uint8_t cmds)
104
{
105 40524
        CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC);
106 40524
        assert(oc->refcnt > 0);
107 40524
        AZ(cmds & OC_EF_REFD);
108
109 40524
        Lck_AssertHeld(&exphdl->mtx);
110
111 40524
        if (oc->exp_flags & OC_EF_REFD) {
112 40523
                if (!(oc->exp_flags & OC_EF_POSTED)) {
113 40515
                        if (cmds & OC_EF_REMOVE)
114 5940
                                VSTAILQ_INSERT_HEAD(&exphdl->inbox,
115
                                    oc, exp_list);
116
                        else
117 34575
                                VSTAILQ_INSERT_TAIL(&exphdl->inbox,
118
                                    oc, exp_list);
119 40515
                        VSC_C_main->exp_mailed++;
120 40515
                }
121 40523
                oc->exp_flags |= cmds | OC_EF_POSTED;
122 40523
                PTOK(pthread_cond_signal(&exphdl->condvar));
123 40523
        }
124 40524
}
125
126
/*--------------------------------------------------------------------
127
 * Setup a new ObjCore for control by expire. Should be called with the
128
 * ObjHead locked by HSH_Unbusy(/HSH_Insert) (in private access).
129
 */
130
131
void
132 34550
EXP_RefNewObjcore(struct objcore *oc)
133
{
134 34550
        CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC);
135
136 34550
        Lck_AssertHeld(&oc->objhead->mtx);
137
138 34550
        AZ(oc->exp_flags);
139 34550
        assert(oc->refcnt >= 1);
140 34550
        oc->refcnt++;
141 34550
        oc->exp_flags |= OC_EF_REFD | OC_EF_NEW;
142 34550
}
143
144
145
146
/*--------------------------------------------------------------------
147
 * Call EXP's attention to a an oc
148
 */
149
150
void
151 9073
EXP_Remove(struct objcore *oc)
152
{
153
154 9073
        CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC);
155 9073
        if (oc->exp_flags & OC_EF_REFD) {
156 5949
                Lck_Lock(&exphdl->mtx);
157 5949
                if (oc->exp_flags & OC_EF_NEW) {
158
                        /* EXP_Insert has not been called for this object
159
                         * yet. Mark it for removal, and EXP_Insert will
160
                         * clean up once it is called. */
161 0
                        AZ(oc->exp_flags & OC_EF_POSTED);
162 0
                        oc->exp_flags |= OC_EF_REMOVE;
163 0
                } else
164 5949
                        exp_mail_it(oc, OC_EF_REMOVE);
165 5949
                Lck_Unlock(&exphdl->mtx);
166 5949
        }
167 9073
}
168
169
/*--------------------------------------------------------------------
170
 * Insert new object.
171
 *
172
 * Caller got a oc->refcnt for us.
173
 */
174
175
void
176 52700
EXP_Insert(struct worker *wrk, struct objcore *oc)
177
{
178 52700
        unsigned remove_race = 0;
179
        struct objcore *tmpoc;
180
181 52700
        CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
182 52700
        CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC);
183
184 52700
        AZ(oc->flags & OC_F_BUSY);
185
186 52700
        if (!(oc->exp_flags & OC_EF_REFD))
187 18150
                return;
188
189
        /* One ref held by the caller, and one that wil be owned by
190
         * expiry. */
191 34550
        assert(oc->refcnt >= 2);
192
193 34550
        ObjSendEvent(wrk, oc, OEV_INSERT);
194
195 34550
        Lck_Lock(&exphdl->mtx);
196 34550
        AN(oc->exp_flags & OC_EF_NEW);
197 34550
        oc->exp_flags &= ~OC_EF_NEW;
198 34550
        AZ(oc->exp_flags & (OC_EF_INSERT | OC_EF_MOVE | OC_EF_POSTED));
199 34550
        if (oc->exp_flags & OC_EF_REMOVE) {
200
                /* We raced some other thread executing EXP_Remove */
201 0
                remove_race = 1;
202 0
                oc->exp_flags &= ~(OC_EF_REFD | OC_EF_REMOVE);
203 0
        } else
204 34550
                exp_mail_it(oc, OC_EF_INSERT | OC_EF_MOVE);
205 34550
        Lck_Unlock(&exphdl->mtx);
206
207 34550
        if (remove_race) {
208 0
                ObjSendEvent(wrk, oc, OEV_EXPIRE);
209 0
                tmpoc = oc;
210 0
                assert(oc->refcnt >= 2); /* Silence coverity */
211 0
                (void)HSH_DerefObjCore(wrk, &oc, 0);
212 0
                AZ(oc);
213 0
                assert(tmpoc->refcnt >= 1); /* Silence coverity */
214 0
        }
215 52700
}
216
217
/*--------------------------------------------------------------------
218
 * We have changed one or more of the object timers, tell the exp_thread
219
 *
220
 */
221
222
void
223 25
EXP_Rearm(struct objcore *oc, vtim_real now,
224
    vtim_dur ttl, vtim_dur grace, vtim_dur keep)
225
{
226
        vtim_real when;
227
228 25
        CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC);
229 25
        assert(oc->refcnt > 0);
230
231 25
        if (!(oc->exp_flags & OC_EF_REFD))
232 0
                return;
233
234 25
        if (!isnan(ttl))
235 25
                oc->ttl = now + ttl - oc->t_origin;
236 25
        if (!isnan(grace))
237 0
                oc->grace = grace;
238 25
        if (!isnan(keep))
239 0
                oc->keep = keep;
240
241 25
        when = EXP_WHEN(oc);
242
243 50
        VSL(SLT_ExpKill, NO_VXID, "EXP_Rearm p=%p E=%.6f e=%.6f f=0x%x", oc,
244 25
            oc->timer_when, when, oc->flags);
245
246 25
        if (when < oc->t_origin || when < oc->timer_when) {
247 25
                Lck_Lock(&exphdl->mtx);
248 25
                if (oc->exp_flags & OC_EF_NEW) {
249
                        /* EXP_Insert has not been called yet, do nothing
250
                         * as the initial insert will execute the move
251
                         * operation. */
252 0
                } else
253 25
                        exp_mail_it(oc, OC_EF_MOVE);
254 25
                Lck_Unlock(&exphdl->mtx);
255 25
        }
256 25
}
257
258
/*--------------------------------------------------------------------
259
 * Handle stuff in the inbox
260
 */
261
262
static void
263 40515
exp_inbox(struct exp_priv *ep, struct objcore *oc, unsigned flags, double now)
264
{
265
266 40515
        CHECK_OBJ_NOTNULL(ep, EXP_PRIV_MAGIC);
267 40515
        CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC);
268 40515
        assert(oc->refcnt > 0);
269
270 81030
        VSLb(&ep->vsl, SLT_ExpKill, "EXP_Inbox flg=%x p=%p e=%.6f f=0x%x",
271 40515
            flags, oc, oc->timer_when, oc->flags);
272
273 40515
        if (flags & OC_EF_REMOVE) {
274 5940
                if (!(flags & OC_EF_INSERT)) {
275 5940
                        assert(oc->timer_idx != VBH_NOIDX);
276 5940
                        VBH_delete(ep->heap, oc->timer_idx);
277 5940
                }
278 5940
                assert(oc->timer_idx == VBH_NOIDX);
279 5940
                assert(oc->refcnt > 0);
280 5940
                AZ(oc->exp_flags);
281 11880
                VSLb(&ep->vsl, SLT_ExpKill, "EXP_Removed x=%ju t=%.0f h=%jd",
282 5940
                    VXID(ObjGetXID(ep->wrk, oc)), EXP_Ttl(NULL, oc) - now,
283 5940
                    (intmax_t)oc->hits);
284 5940
                ObjSendEvent(ep->wrk, oc, OEV_EXPIRE);
285 5940
                (void)HSH_DerefObjCore(ep->wrk, &oc, 0);
286 5940
                return;
287
        }
288
289 34575
        if (flags & OC_EF_MOVE) {
290 34575
                oc->timer_when = EXP_WHEN(oc);
291 34575
                ObjSendEvent(ep->wrk, oc, OEV_TTLCHG);
292 34575
        }
293
294 69150
        VSLb(&ep->vsl, SLT_ExpKill, "EXP_When p=%p e=%.6f f=0x%x", oc,
295 34575
            oc->timer_when, flags);
296
297
        /*
298
         * XXX: There are some pathological cases here, were we
299
         * XXX: insert or move an expired object, only to find out
300
         * XXX: the next moment and rip them out again.
301
         */
302
303 34575
        if (flags & OC_EF_INSERT) {
304 34550
                assert(oc->timer_idx == VBH_NOIDX);
305 34550
                VBH_insert(exphdl->heap, oc);
306 34550
                assert(oc->timer_idx != VBH_NOIDX);
307 34575
        } else if (flags & OC_EF_MOVE) {
308 25
                assert(oc->timer_idx != VBH_NOIDX);
309 25
                VBH_reorder(exphdl->heap, oc->timer_idx);
310 25
                assert(oc->timer_idx != VBH_NOIDX);
311 25
        } else {
312 0
                WRONG("Objcore state wrong in inbox");
313
        }
314 40515
}
315
316
/*--------------------------------------------------------------------
317
 * Expire stuff from the binheap
318
 */
319
320
static vtim_real
321 124427
exp_expire(struct exp_priv *ep, vtim_real now)
322
{
323
        struct objcore *oc;
324
325 124427
        CHECK_OBJ_NOTNULL(ep, EXP_PRIV_MAGIC);
326
327 124427
        oc = VBH_root(ep->heap);
328 124427
        if (oc == NULL)
329 51926
                return (now + 355. / 113.);
330 145002
        VSLb(&ep->vsl, SLT_ExpKill, "EXP_Inspect p=%p e=%.6f f=0x%x", oc,
331 72501
            oc->timer_when - now, oc->flags);
332
333 72501
        CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC);
334
335
        /* Ready ? */
336 72501
        if (oc->timer_when > now)
337 69502
                return (oc->timer_when);
338
339 2999
        VSC_C_main->n_expired++;
340
341 2999
        Lck_Lock(&ep->mtx);
342 2999
        if (oc->exp_flags & OC_EF_POSTED) {
343 0
                oc->exp_flags |= OC_EF_REMOVE;
344 0
                oc = NULL;
345 0
        } else {
346 2999
                oc->exp_flags &= ~OC_EF_REFD;
347
        }
348 2999
        Lck_Unlock(&ep->mtx);
349 2999
        if (oc != NULL) {
350 2999
                if (!(oc->flags & OC_F_DYING))
351 2999
                        HSH_Kill(oc);
352
353
                /* Remove from binheap */
354 2999
                assert(oc->timer_idx != VBH_NOIDX);
355 2999
                VBH_delete(ep->heap, oc->timer_idx);
356 2999
                assert(oc->timer_idx == VBH_NOIDX);
357
358 2999
                CHECK_OBJ_NOTNULL(oc->objhead, OBJHEAD_MAGIC);
359 5998
                VSLb(&ep->vsl, SLT_ExpKill, "EXP_Expired x=%ju t=%.0f h=%jd",
360 2999
                    VXID(ObjGetXID(ep->wrk, oc)), EXP_Ttl(NULL, oc) - now,
361 2999
                    (intmax_t)oc->hits);
362 2999
                ObjSendEvent(ep->wrk, oc, OEV_EXPIRE);
363 2999
                (void)HSH_DerefObjCore(ep->wrk, &oc, 0);
364 2999
        }
365 2999
        return (0);
366 124427
}
367
368
/*--------------------------------------------------------------------
369
 * This thread monitors the root of the binary heap and whenever an
370
 * object expires, accounting also for graceability, it is killed.
371
 */
372
373
static int v_matchproto_(vbh_cmp_t)
374 31151
object_cmp(void *priv, const void *a, const void *b)
375
{
376
        const struct objcore *aa, *bb;
377
378 31151
        (void)priv;
379 31151
        CAST_OBJ_NOTNULL(aa, a, OBJCORE_MAGIC);
380 31151
        CAST_OBJ_NOTNULL(bb, b, OBJCORE_MAGIC);
381 31151
        return (aa->timer_when < bb->timer_when);
382
}
383
384
static void v_matchproto_(vbh_update_t)
385 65947
object_update(void *priv, void *p, unsigned u)
386
{
387
        struct objcore *oc;
388
389 65947
        (void)priv;
390 65947
        CAST_OBJ_NOTNULL(oc, p, OBJCORE_MAGIC);
391 65947
        oc->timer_idx = u;
392 65947
}
393
394
static void * v_matchproto_(bgthread_t)
395 22194
exp_thread(struct worker *wrk, void *priv)
396
{
397
        struct objcore *oc;
398 22194
        vtim_real t = 0, tnext = 0;
399
        struct exp_priv *ep;
400 22194
        unsigned flags = 0;
401
402 22194
        CAST_OBJ_NOTNULL(ep, priv, EXP_PRIV_MAGIC);
403 22194
        ep->wrk = wrk;
404 22194
        VSL_Setup(&ep->vsl, NULL, 0);
405 22194
        ep->heap = VBH_new(NULL, object_cmp, object_update);
406 22194
        AN(ep->heap);
407 187136
        while (exp_shutdown == 0) {
408
409 164942
                Lck_Lock(&ep->mtx);
410 164942
                oc = VSTAILQ_FIRST(&ep->inbox);
411 164942
                CHECK_OBJ_ORNULL(oc, OBJCORE_MAGIC);
412 164942
                if (oc != NULL) {
413 40515
                        assert(oc->refcnt >= 1);
414 40515
                        VSTAILQ_REMOVE(&ep->inbox, oc, objcore, exp_list);
415 40515
                        VSC_C_main->exp_received++;
416 40515
                        tnext = 0;
417 40515
                        flags = oc->exp_flags;
418 40515
                        if (flags & OC_EF_REMOVE)
419 5940
                                oc->exp_flags = 0;
420
                        else
421 34575
                                oc->exp_flags &= OC_EF_REFD;
422 164942
                } else if (tnext > t) {
423 62104
                        VSL_Flush(&ep->vsl, 0);
424 62104
                        Pool_Sumstat(wrk);
425 62104
                        (void)Lck_CondWaitUntil(&ep->condvar, &ep->mtx, tnext);
426 62104
                }
427 164942
                Lck_Unlock(&ep->mtx);
428
429 164942
                t = VTIM_real();
430
431 164942
                if (oc != NULL)
432 40515
                        exp_inbox(ep, oc, flags, t);
433
                else
434 124427
                        tnext = exp_expire(ep, t);
435
        }
436 22194
        return (NULL);
437
}
438
439
/*--------------------------------------------------------------------*/
440
441
void
442 22194
EXP_Init(void)
443
{
444
        struct exp_priv *ep;
445
        pthread_t pt;
446
447 22194
        ALLOC_OBJ(ep, EXP_PRIV_MAGIC);
448 22194
        AN(ep);
449
450 22194
        Lck_New(&ep->mtx, lck_exp);
451 22194
        PTOK(pthread_cond_init(&ep->condvar, NULL));
452 22194
        VSTAILQ_INIT(&ep->inbox);
453 22194
        WRK_BgThread(&pt, "cache-exp", exp_thread, ep);
454 22194
        ep->thread = pt;
455 22194
        exphdl = ep;
456 22194
}
457
458
void
459 21900
EXP_Shutdown(void)
460
{
461 21900
        struct exp_priv *ep = exphdl;
462
        void *status;
463
464 21900
        Lck_Lock(&ep->mtx);
465 21900
        exp_shutdown = 1;
466 21900
        PTOK(pthread_cond_signal(&ep->condvar));
467 21900
        Lck_Unlock(&ep->mtx);
468
469 21900
        AN(ep->thread);
470 21900
        PTOK(pthread_join(ep->thread, &status));
471 21900
        AZ(status);
472 21900
        memset(&ep->thread, 0, sizeof ep->thread);
473
474
        /* XXX could cleanup more - not worth it for now */
475 21900
}