vinyl-cache/bin/vinyld/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_int.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 the point in time when an object will become stale, taking
64
 * req.max_age into account, if available
65
 */
66
67
vtim_real
68 40789
EXP_Ttl(const struct req *req, const struct objcore *oc)
69
{
70
        vtim_dur r;
71
72 40789
        CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC);
73
74 40789
        r = oc->ttl;
75 40789
        if (req != NULL && req->d_ttl >= 0. && req->d_ttl < r)
76 135
                r = req->d_ttl;
77 40789
        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 2940
EXP_Ttl_grace(const struct req *req, const struct objcore *oc)
87
{
88
        vtim_dur g;
89
90 2940
        CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC);
91
92 2940
        g = oc->grace;
93 2940
        if (req != NULL && req->d_grace >= 0. && req->d_grace < g)
94 42
                g = req->d_grace;
95 2940
        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 36637
exp_mail_it(struct objcore *oc, uint8_t cmds)
104
{
105 36637
        CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC);
106 36637
        assert(oc->refcnt > 0);
107 36637
        AZ(cmds & OC_EF_REFD);
108
109 36637
        Lck_AssertHeld(&exphdl->mtx);
110
111 36637
        if (oc->exp_flags & OC_EF_REFD) {
112 36637
                if (!(oc->exp_flags & OC_EF_POSTED)) {
113 36636
                        if (cmds & OC_EF_REMOVE)
114 5808
                                VSTAILQ_INSERT_HEAD(&exphdl->inbox,
115
                                    oc, exp_list);
116
                        else
117 30828
                                VSTAILQ_INSERT_TAIL(&exphdl->inbox,
118
                                    oc, exp_list);
119 36636
                        VSC_C_main->exp_mailed++;
120 36636
                }
121 36637
                oc->exp_flags |= cmds | OC_EF_POSTED;
122 36637
                PTOK(pthread_cond_signal(&exphdl->condvar));
123 36637
        }
124 36637
}
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 30702
EXP_RefNewObjcore(struct objcore *oc)
133
{
134 30702
        CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC);
135
136 30702
        Lck_AssertHeld(&oc->objhead->mtx);
137
138 30702
        AZ(oc->exp_flags);
139 30702
        assert(oc->refcnt >= 1);
140 30702
        oc->refcnt++;
141 30702
        oc->exp_flags |= OC_EF_REFD | OC_EF_NEW;
142 30702
}
143
144
145
146
/*--------------------------------------------------------------------
147
 * Call EXP's attention to an oc
148
 */
149
150
void
151 9996
EXP_Remove(struct objcore *oc, const struct objcore *new_oc)
152
{
153
154 9996
        CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC);
155 9996
        CHECK_OBJ_ORNULL(new_oc, OBJCORE_MAGIC);
156
157 9996
        if (oc->exp_flags & OC_EF_REFD) {
158 5809
                Lck_Lock(&exphdl->mtx);
159 5809
                if (new_oc != NULL)
160 1588
                        VSC_C_main->n_superseded++;
161 5809
                if (oc->exp_flags & OC_EF_NEW) {
162
                        /* EXP_Insert has not been called for this object
163
                         * yet. Mark it for removal, and EXP_Insert will
164
                         * clean up once it is called. */
165 0
                        AZ(oc->exp_flags & OC_EF_POSTED);
166 0
                        oc->exp_flags |= OC_EF_REMOVE;
167 0
                } else
168 5809
                        exp_mail_it(oc, OC_EF_REMOVE);
169 5809
                Lck_Unlock(&exphdl->mtx);
170 5809
        }
171 9996
}
172
173
/*--------------------------------------------------------------------
174
 * Insert new object.
175
 *
176
 * Caller got a oc->refcnt for us.
177
 */
178
179
void
180 30702
EXP_Insert(struct worker *wrk, struct objcore *oc)
181
{
182 30702
        unsigned remove_race = 0;
183
        struct objcore *tmpoc;
184
185 30702
        CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
186 30702
        CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC);
187
188 30702
        AZ(oc->flags & OC_F_BUSY);
189
190 30702
        if (!(oc->exp_flags & OC_EF_REFD))
191 0
                return;
192
193
        /* One ref held by the caller, and one that will be owned by
194
         * expiry. */
195 30702
        assert(oc->refcnt >= 2);
196
197 30702
        ObjSendEvent(wrk, oc, OEV_INSERT);
198
199 30702
        Lck_Lock(&exphdl->mtx);
200 30702
        AN(oc->exp_flags & OC_EF_NEW);
201 30702
        oc->exp_flags &= ~OC_EF_NEW;
202 30702
        AZ(oc->exp_flags & (OC_EF_INSERT | OC_EF_MOVE | OC_EF_POSTED));
203 30702
        if (oc->exp_flags & OC_EF_REMOVE) {
204
                /* We raced some other thread executing EXP_Remove */
205 0
                remove_race = 1;
206 0
                oc->exp_flags &= ~(OC_EF_REFD | OC_EF_REMOVE);
207 0
        } else
208 30702
                exp_mail_it(oc, OC_EF_INSERT | OC_EF_MOVE);
209 30702
        Lck_Unlock(&exphdl->mtx);
210
211 30702
        if (remove_race) {
212 0
                ObjSendEvent(wrk, oc, OEV_EXPIRE);
213 0
                tmpoc = oc;
214 0
                assert(oc->refcnt >= 2); /* Silence coverity */
215 0
                (void)HSH_DerefObjCore(wrk, &oc);
216 0
                AZ(oc);
217 0
                assert(tmpoc->refcnt >= 1); /* Silence coverity */
218 0
        }
219 30702
}
220
221
/*--------------------------------------------------------------------
222
 * Reduce object timers
223
 */
224
225
void
226 210
EXP_Reduce(struct objcore *oc, vtim_real now,
227
    vtim_dur ttl, vtim_dur grace, vtim_dur keep)
228
{
229
230 210
        CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC);
231 210
        assert(oc->refcnt > 0);
232
233 210
        if (!isnan(ttl) && now + ttl - oc->t_origin >= oc->ttl)
234 126
                ttl = NAN;
235 210
        if (!isnan(grace) && grace >= oc->grace)
236 42
                grace = NAN;
237 210
        if (!isnan(keep) && keep >= oc->keep)
238 21
                keep = NAN;
239
240 210
        EXP_Rearm(oc, now, ttl, grace, keep);
241 210
}
242
243
/*--------------------------------------------------------------------
244
 * We have changed one or more of the object timers, tell the exp_thread
245
 *
246
 */
247
248
static inline void
249 210
apply_timers(struct objcore *oc, vtim_real now,
250
    vtim_dur ttl, vtim_dur grace, vtim_dur keep)
251
{
252 210
        if (!isnan(ttl))
253 84
                oc->ttl = now + ttl - oc->t_origin;
254 210
        if (!isnan(grace))
255 42
                oc->grace = grace;
256 210
        if (!isnan(keep))
257 42
                oc->keep = keep;
258 210
}
259
260
void
261 210
EXP_Rearm(struct objcore *oc, vtim_real now,
262
    vtim_dur ttl, vtim_dur grace, vtim_dur keep)
263
{
264
        vtim_real when;
265
266 210
        CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC);
267 210
        assert(oc->refcnt > 0);
268
269 210
        if (oc->flags & OC_F_BUSY) {
270 0
                struct objhead *oh = oc->objhead;
271
                uint8_t flags;
272
273 0
                Lck_Lock(&oh->mtx);
274 0
                flags = oc->flags;
275 0
                if (flags & OC_F_BUSY)
276 0
                        apply_timers(oc, now, ttl, grace, keep);
277 0
                Lck_Unlock(&oh->mtx);
278 0
                if (flags & OC_F_BUSY)
279 0
                        return;
280 0
        }
281
282 210
        if (!(oc->exp_flags & OC_EF_REFD))
283 0
                return;
284
285 210
        apply_timers(oc, now, ttl, grace, keep);
286
287 210
        when = EXP_WHEN(oc);
288
289 420
        VSL(SLT_ExpKill, NO_VXID, "EXP_Rearm p=%p E=%.6f e=%.6f f=0x%x", oc,
290 210
            oc->timer_when, when, oc->flags);
291
292 210
        if (when < oc->t_origin || when < oc->timer_when) {
293 126
                Lck_Lock(&exphdl->mtx);
294 126
                if (oc->exp_flags & OC_EF_NEW) {
295
                        /* EXP_Insert has not been called yet, do nothing
296
                         * as the initial insert will execute the move
297
                         * operation. */
298 0
                } else
299 126
                        exp_mail_it(oc, OC_EF_MOVE);
300 126
                Lck_Unlock(&exphdl->mtx);
301 126
        }
302 210
}
303
304
/*--------------------------------------------------------------------
305
 * Handle stuff in the inbox
306
 */
307
308
static void
309 36636
exp_inbox(struct exp_priv *ep, struct objcore *oc, unsigned flags, vtim_real now)
310
{
311
312 36636
        CHECK_OBJ_NOTNULL(ep, EXP_PRIV_MAGIC);
313 36636
        CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC);
314 36636
        assert(oc->refcnt > 0);
315
316 73272
        VSLb(&ep->vsl, SLT_ExpKill, "EXP_Inbox flg=%x p=%p e=%.6f f=0x%x",
317 36636
            flags, oc, oc->timer_when, oc->flags);
318
319 36636
        if (flags & OC_EF_REMOVE) {
320 5808
                if (!(flags & OC_EF_INSERT)) {
321 5808
                        assert(oc->timer_idx != VBH_NOIDX);
322 5808
                        VBH_delete(ep->heap, oc->timer_idx);
323 5808
                }
324 5808
                assert(oc->timer_idx == VBH_NOIDX);
325 5808
                assert(oc->refcnt > 0);
326 5808
                AZ(oc->exp_flags);
327 11616
                VSLb(&ep->vsl, SLT_ExpKill, "EXP_Removed x=%ju t=%.0f h=%jd",
328 5808
                    VXID(ObjGetXID(ep->wrk, oc)), EXP_Ttl(NULL, oc) - now,
329 5808
                    (intmax_t)oc->hits);
330 5808
                ObjSendEvent(ep->wrk, oc, OEV_EXPIRE);
331 5808
                (void)HSH_DerefObjCore(ep->wrk, &oc);
332 5808
                return;
333
        }
334
335 30828
        if (flags & OC_EF_MOVE) {
336 30828
                oc->timer_when = EXP_WHEN(oc);
337 30828
                ObjSendEvent(ep->wrk, oc, OEV_TTLCHG);
338 30828
        }
339
340 61656
        VSLb(&ep->vsl, SLT_ExpKill, "EXP_When p=%p e=%.6f f=0x%x", oc,
341 30828
            oc->timer_when, flags);
342
343
        /*
344
         * XXX: There are some pathological cases here, were we
345
         * XXX: insert or move an expired object, only to find out
346
         * XXX: the next moment and rip them out again.
347
         */
348
349 30828
        if (flags & OC_EF_INSERT) {
350 30702
                assert(oc->timer_idx == VBH_NOIDX);
351 30702
                VBH_insert(exphdl->heap, oc);
352 30702
                assert(oc->timer_idx != VBH_NOIDX);
353 30828
        } else if (flags & OC_EF_MOVE) {
354 126
                assert(oc->timer_idx != VBH_NOIDX);
355 126
                VBH_reorder(exphdl->heap, oc->timer_idx);
356 126
                assert(oc->timer_idx != VBH_NOIDX);
357 126
        } else {
358 0
                WRONG("Objcore state wrong in inbox");
359
        }
360 36636
}
361
362
/*--------------------------------------------------------------------
363
 * Expire stuff from the binheap
364
 */
365
366
static vtim_real
367 114597
exp_expire(struct exp_priv *ep, vtim_real now)
368
{
369
        struct objcore *oc;
370
371 114597
        CHECK_OBJ_NOTNULL(ep, EXP_PRIV_MAGIC);
372
373 114597
        oc = VBH_root(ep->heap);
374 114597
        if (oc == NULL)
375 49216
                return (now + 355. / 113.);
376 130762
        VSLb(&ep->vsl, SLT_ExpKill, "EXP_Inspect p=%p e=%.6f f=0x%x", oc,
377 65381
            oc->timer_when - now, oc->flags);
378
379 65381
        CHECK_OBJ(oc, OBJCORE_MAGIC);
380
381
        /* Ready ? */
382 65381
        if (oc->timer_when > now)
383 62462
                return (oc->timer_when);
384
385 2919
        VSC_C_main->n_expired++;
386
387 2919
        Lck_Lock(&ep->mtx);
388 2919
        if (oc->exp_flags & OC_EF_POSTED) {
389 0
                oc->exp_flags |= OC_EF_REMOVE;
390 0
                oc = NULL;
391 0
        } else {
392 2919
                oc->exp_flags &= ~OC_EF_REFD;
393
        }
394 2919
        Lck_Unlock(&ep->mtx);
395 2919
        if (oc != NULL) {
396 2919
                if (!(oc->flags & OC_F_DYING))
397 2919
                        HSH_Kill(oc);
398
399
                /* Remove from binheap */
400 2919
                assert(oc->timer_idx != VBH_NOIDX);
401 2919
                VBH_delete(ep->heap, oc->timer_idx);
402 2919
                assert(oc->timer_idx == VBH_NOIDX);
403
404 2919
                CHECK_OBJ_NOTNULL(oc->objhead, OBJHEAD_MAGIC);
405 5838
                VSLb(&ep->vsl, SLT_ExpKill, "EXP_Expired x=%ju t=%.0f h=%jd",
406 2919
                    VXID(ObjGetXID(ep->wrk, oc)), EXP_Ttl(NULL, oc) - now,
407 2919
                    (intmax_t)oc->hits);
408 2919
                ObjSendEvent(ep->wrk, oc, OEV_EXPIRE);
409 2919
                (void)HSH_DerefObjCore(ep->wrk, &oc);
410 2919
        }
411 2919
        return (0);
412 114597
}
413
414
/*--------------------------------------------------------------------
415
 * This thread monitors the root of the binary heap and whenever an
416
 * object expires, accounting also for graceability, it is killed.
417
 */
418
419
static int v_matchproto_(vbh_cmp_t)
420 26936
object_cmp(void *priv, const void *a, const void *b)
421
{
422
        const struct objcore *aa, *bb;
423
424 26936
        (void)priv;
425 26936
        CAST_OBJ_NOTNULL(aa, a, OBJCORE_MAGIC);
426 26936
        CAST_OBJ_NOTNULL(bb, b, OBJCORE_MAGIC);
427 26936
        return (aa->timer_when < bb->timer_when);
428
}
429
430
static void v_matchproto_(vbh_update_t)
431 59541
object_update(void *priv, void *p, unsigned u)
432
{
433
        struct objcore *oc;
434
435 59541
        (void)priv;
436 59541
        CAST_OBJ_NOTNULL(oc, p, OBJCORE_MAGIC);
437 59541
        oc->timer_idx = u;
438 59541
}
439
440
static void * v_matchproto_(bgthread_t)
441 20884
exp_thread(struct worker *wrk, void *priv)
442
{
443
        struct objcore *oc;
444 20884
        vtim_real t = 0, tnext = 0;
445
        struct exp_priv *ep;
446 20884
        unsigned flags = 0;
447
448 20884
        CAST_OBJ_NOTNULL(ep, priv, EXP_PRIV_MAGIC);
449 20884
        ep->wrk = wrk;
450 20884
        VSL_Alloc(&ep->vsl);
451 20884
        AZ(wrk->vsl);
452 20884
        wrk->vsl = &ep->vsl;
453 20884
        ep->heap = VBH_new(NULL, object_cmp, object_update);
454 20884
        AN(ep->heap);
455 172117
        while (exp_shutdown == 0) {
456
457 151233
                Lck_Lock(&ep->mtx);
458 151233
                oc = VSTAILQ_FIRST(&ep->inbox);
459 151233
                CHECK_OBJ_ORNULL(oc, OBJCORE_MAGIC);
460 151233
                if (oc != NULL) {
461 36636
                        assert(oc->refcnt >= 1);
462 36636
                        assert(oc->exp_flags & OC_EF_POSTED);
463 36636
                        VSTAILQ_REMOVE(&ep->inbox, oc, objcore, exp_list);
464 36636
                        VSC_C_main->exp_received++;
465 36636
                        tnext = 0;
466 36636
                        flags = oc->exp_flags;
467 36636
                        if (flags & OC_EF_REMOVE)
468 5808
                                oc->exp_flags = 0;
469
                        else
470 30828
                                oc->exp_flags &= OC_EF_REFD;
471 151233
                } else if (tnext > t) {
472 57198
                        VSL_Flush(&ep->vsl, 0);
473 57198
                        Pool_Sumstat(wrk);
474 57198
                        (void)Lck_CondWaitUntil(&ep->condvar, &ep->mtx, tnext);
475 57198
                }
476 151233
                Lck_Unlock(&ep->mtx);
477
478 151233
                t = VTIM_real();
479
480 151233
                if (oc != NULL)
481 36636
                        exp_inbox(ep, oc, flags, t);
482
                else
483 114597
                        tnext = exp_expire(ep, t);
484
        }
485 20884
        wrk->vsl = NULL;
486 20884
        VSL_Free(&ep->vsl);
487 20884
        return (NULL);
488
}
489
490
/*--------------------------------------------------------------------*/
491
492
void
493 20884
EXP_Init(void)
494
{
495
        struct exp_priv *ep;
496
        pthread_t pt;
497
498 20884
        ALLOC_OBJ(ep, EXP_PRIV_MAGIC);
499 20884
        AN(ep);
500
501 20884
        Lck_New(&ep->mtx, lck_exp);
502 20884
        PTOK(pthread_cond_init(&ep->condvar, NULL));
503 20884
        VSTAILQ_INIT(&ep->inbox);
504 20884
        WRK_BgThread(&pt, "cache-exp", exp_thread, ep);
505 20884
        ep->thread = pt;
506 20884
        exphdl = ep;
507 20884
}
508
509
void
510 20622
EXP_Shutdown(void)
511
{
512 20622
        struct exp_priv *ep = exphdl;
513
        void *status;
514
515 20622
        Lck_Lock(&ep->mtx);
516 20622
        exp_shutdown = 1;
517 20622
        PTOK(pthread_cond_signal(&ep->condvar));
518 20622
        Lck_Unlock(&ep->mtx);
519
520 20622
        AN(ep->thread);
521 20622
        PTOK(pthread_join(ep->thread, &status));
522 20622
        AZ(status);
523 20622
        memset(&ep->thread, 0, sizeof ep->thread);
524
525
        /* XXX could cleanup more - not worth it for now */
526 20622
}