[master] 58b262f Add a callback facility to be notified about events in the expiry module.
Poul-Henning Kamp
phk at FreeBSD.org
Tue Mar 3 11:06:13 CET 2015
commit 58b262f1c8a2f2b5f9b3a977c7f7f799603820a1
Author: Poul-Henning Kamp <phk at FreeBSD.org>
Date: Tue Mar 3 10:05:19 2015 +0000
Add a callback facility to be notified about events in the expiry module.
This is necessary for VMODs which implement secondary hash-keys.
Patches from martin@ with some minor tweaking by me.
diff --git a/bin/varnishd/cache/cache.h b/bin/varnishd/cache/cache.h
index fcb8c69..9b2aec0 100644
--- a/bin/varnishd/cache/cache.h
+++ b/bin/varnishd/cache/cache.h
@@ -778,14 +778,25 @@ void EXP_Clr(struct exp *e);
double EXP_Ttl(const struct req *, const struct exp*);
double EXP_When(const struct exp *exp);
-void EXP_Insert(struct objcore *oc);
-void EXP_Inject(struct objcore *oc, struct lru *lru);
+void EXP_Insert(struct worker *wrk, struct objcore *oc);
+void EXP_Inject(struct worker *wrk, struct objcore *oc, struct lru *lru);
void EXP_Init(void);
void EXP_Rearm(struct objcore *, double now, double ttl, double grace,
double keep);
void EXP_Touch(struct objcore *oc, double now);
int EXP_NukeOne(struct worker *wrk, struct lru *lru);
+enum exp_event_e {
+ EXP_INSERT,
+ EXP_INJECT,
+ EXP_REMOVE,
+};
+typedef void exp_callback_f(struct worker *, struct objcore *,
+ enum exp_event_e, void *priv);
+
+uintptr_t EXP_Register_Callback(exp_callback_f *func, void *priv);
+void EXP_Deregister_Callback(uintptr_t*);
+
/* cache_fetch.c */
enum vbf_fetch_mode_e {
VBF_NORMAL = 0,
diff --git a/bin/varnishd/cache/cache_expire.c b/bin/varnishd/cache/cache_expire.c
index 79a8d2f..0590343 100644
--- a/bin/varnishd/cache/cache_expire.c
+++ b/bin/varnishd/cache/cache_expire.c
@@ -41,6 +41,14 @@
#include "hash/hash_slinger.h"
#include "vtim.h"
+struct exp_callback {
+ unsigned magic;
+#define EXP_CALLBACK_MAGIC 0xab956eb1
+ exp_callback_f *func;
+ void *priv;
+ VTAILQ_ENTRY(exp_callback) list;
+};
+
struct exp_priv {
unsigned magic;
#define EXP_PRIV_MAGIC 0x9db22482
@@ -52,10 +60,33 @@ struct exp_priv {
VTAILQ_HEAD(,objcore) inbox;
struct binheap *heap;
pthread_cond_t condvar;
+
+ VTAILQ_HEAD(,exp_callback) ecb_list;
+ pthread_rwlock_t cb_rwl;
};
static struct exp_priv *exphdl;
+static void
+exp_event(struct worker *wrk, struct objcore *oc, enum exp_event_e e)
+{
+ struct exp_callback *cb;
+
+ /*
+ * Strictly speaking this is not atomic, but neither is VMOD
+ * loading in general, so this is a fair optimization
+ */
+ if (VTAILQ_EMPTY(&exphdl->ecb_list))
+ return;
+
+ AZ(pthread_rwlock_rdlock(&exphdl->cb_rwl));
+ VTAILQ_FOREACH(cb, &exphdl->ecb_list, list) {
+ CHECK_OBJ_NOTNULL(cb, EXP_CALLBACK_MAGIC);
+ cb->func(wrk, oc, e, cb->priv);
+ }
+ AZ(pthread_rwlock_unlock(&exphdl->cb_rwl));
+}
+
/*--------------------------------------------------------------------
* struct exp manipulations
*/
@@ -130,9 +161,10 @@ exp_mail_it(struct objcore *oc)
*/
void
-EXP_Inject(struct objcore *oc, struct lru *lru)
+EXP_Inject(struct worker *wrk, struct objcore *oc, struct lru *lru)
{
+ CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC);
AZ(oc->exp_flags & (OC_EF_OFFLRU | OC_EF_INSERT | OC_EF_MOVE));
@@ -146,6 +178,8 @@ EXP_Inject(struct objcore *oc, struct lru *lru)
oc->timer_when = EXP_When(&oc->exp);
Lck_Unlock(&lru->mtx);
+ exp_event(wrk, oc, EXP_INJECT);
+
exp_mail_it(oc);
}
@@ -157,10 +191,11 @@ EXP_Inject(struct objcore *oc, struct lru *lru)
*/
void
-EXP_Insert(struct objcore *oc)
+EXP_Insert(struct worker *wrk, struct objcore *oc)
{
struct lru *lru;
+ CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC);
HSH_Ref(oc);
@@ -177,6 +212,8 @@ EXP_Insert(struct objcore *oc)
oc->exp_flags |= OC_EF_MOVE;
Lck_Unlock(&lru->mtx);
+ exp_event(wrk, oc, EXP_INSERT);
+
exp_mail_it(oc);
}
@@ -344,6 +381,45 @@ EXP_NukeOne(struct worker *wrk, struct lru *lru)
return (1);
}
+/*--------------------------------------------------------------------*/
+
+uintptr_t
+EXP_Register_Callback(exp_callback_f *func, void *priv)
+{
+ struct exp_callback *ecb;
+
+ AN(func);
+
+ ALLOC_OBJ(ecb, EXP_CALLBACK_MAGIC);
+ AN(ecb);
+ ecb->func = func;
+ ecb->priv = priv;
+ AZ(pthread_rwlock_wrlock(&exphdl->cb_rwl));
+ VTAILQ_INSERT_TAIL(&exphdl->ecb_list, ecb, list);
+ AZ(pthread_rwlock_unlock(&exphdl->cb_rwl));
+ return ((uintptr_t)ecb);
+}
+
+void
+EXP_Deregister_Callback(uintptr_t *handle)
+{
+ struct exp_callback *ecb;
+
+ AN(handle);
+ AN(*handle);
+ AZ(pthread_rwlock_wrlock(&exphdl->cb_rwl));
+ VTAILQ_FOREACH(ecb, &exphdl->ecb_list, list) {
+ CHECK_OBJ_NOTNULL(ecb, EXP_CALLBACK_MAGIC);
+ if ((uintptr_t)ecb == *handle)
+ break;
+ }
+ AN(ecb);
+ VTAILQ_REMOVE(&exphdl->ecb_list, ecb, list);
+ AZ(pthread_rwlock_unlock(&exphdl->cb_rwl));
+ FREE_OBJ(ecb);
+ *handle = 0;
+}
+
/*--------------------------------------------------------------------
* Handle stuff in the inbox
*/
@@ -385,6 +461,7 @@ exp_inbox(struct exp_priv *ep, struct objcore *oc, double now)
binheap_delete(ep->heap, oc->timer_idx);
}
assert(oc->timer_idx == BINHEAP_NOIDX);
+ exp_event(ep->wrk, oc, EXP_REMOVE);
(void)HSH_DerefObjCore(ep->wrk, &oc);
return;
}
@@ -464,6 +541,7 @@ exp_expire(struct exp_priv *ep, double now)
CHECK_OBJ_NOTNULL(oc->objhead, OBJHEAD_MAGIC);
VSLb(&ep->vsl, SLT_ExpKill, "EXP_Expired x=%u t=%.0f",
ObjGetXID(ep->wrk, oc), EXP_Ttl(NULL, &oc->exp) - now);
+ exp_event(ep->wrk, oc, EXP_REMOVE);
(void)HSH_DerefObjCore(ep->wrk, &oc);
return (0);
}
@@ -545,6 +623,8 @@ EXP_Init(void)
Lck_New(&ep->mtx, lck_exp);
AZ(pthread_cond_init(&ep->condvar, NULL));
VTAILQ_INIT(&ep->inbox);
+ AZ(pthread_rwlock_init(&ep->cb_rwl, NULL));
+ VTAILQ_INIT(&ep->ecb_list);
exphdl = ep;
WRK_BgThread(&pt, "cache-timeout", exp_thread, ep);
}
diff --git a/bin/varnishd/cache/cache_hash.c b/bin/varnishd/cache/cache_hash.c
index de35e8a..686d398 100644
--- a/bin/varnishd/cache/cache_hash.c
+++ b/bin/varnishd/cache/cache_hash.c
@@ -693,7 +693,7 @@ HSH_Unbusy(struct worker *wrk, struct objcore *oc)
if (!(oc->flags & OC_F_PRIVATE)) {
BAN_NewObjCore(oc);
- EXP_Insert(oc);
+ EXP_Insert(wrk, oc);
AN(oc->exp_flags & OC_EF_EXP);
AN(oc->ban);
}
diff --git a/bin/varnishd/flint.lnt b/bin/varnishd/flint.lnt
index 408513a..1a34c01 100644
--- a/bin/varnishd/flint.lnt
+++ b/bin/varnishd/flint.lnt
@@ -111,6 +111,7 @@
-emacro(527, NEEDLESS_RETURN) // unreachable code
-sem(EXP_Inject, custodial(1))
+-sem(HSH_Insert, custodial(3))
-sem(WS_Init, custodial(2))
-sem(http_Setup, custodial(2))
-sem(vfp_esi_end, custodial(2))
diff --git a/bin/varnishd/storage/storage_persistent_silo.c b/bin/varnishd/storage/storage_persistent_silo.c
index 8d02ed3..8119aad 100644
--- a/bin/varnishd/storage/storage_persistent_silo.c
+++ b/bin/varnishd/storage/storage_persistent_silo.c
@@ -166,7 +166,7 @@ smp_load_seg(struct worker *wrk, const struct smp_sc *sc,
oc->ban = BAN_RefBan(oc, so->ban, sc->tailban);
HSH_Insert(wrk, so->hash, oc);
oc->exp = so->exp;
- EXP_Inject(oc, sg->lru);
+ EXP_Inject(wrk, oc, sg->lru);
sg->nobj++;
}
Pool_Sumstat(wrk);
diff --git a/bin/varnishtest/tests/m00021.vtc b/bin/varnishtest/tests/m00021.vtc
new file mode 100644
index 0000000..5db3362
--- /dev/null
+++ b/bin/varnishtest/tests/m00021.vtc
@@ -0,0 +1,49 @@
+varnishtest "Test expiry callbacks"
+
+server s1 {
+ rxreq
+ txresp
+} -start
+
+varnish v1 -vcl+backend {} -start
+
+varnish v1 -cliok "param.set debug +vclrel"
+
+logexpect l1 -v v1 -g raw {
+ expect * 0 Debug "exp_cb: registered"
+ expect * 0 Debug "exp_cb: event insert 0x[0-9a-f]+"
+ expect * 0 Debug "exp_cb: event remove 0x[0-9a-f]+"
+ expect * 0 Debug "exp_cb: deregistered"
+} -start
+
+varnish v1 -vcl+backend {
+ import ${vmod_debug};
+
+ sub vcl_init {
+ debug.register_exp_callback();
+ }
+
+ sub vcl_recv {
+ if (req.method == "PURGE") {
+ return (purge);
+ }
+ }
+}
+
+client c1 {
+ txreq
+ rxresp
+ expect resp.status == 200
+
+ txreq -req PURGE
+ rxresp
+} -run
+varnish v1 -expect n_object == 0
+
+varnish v1 -vcl+backend {}
+varnish v1 -cliok "vcl.discard vcl2"
+varnish v1 -cliok "debug.vmod"
+varnish v1 -cliok "vcl.list"
+varnish v1 -expect vmods == 0
+
+logexpect l1 -wait
diff --git a/lib/libvmod_debug/vmod.vcc b/lib/libvmod_debug/vmod.vcc
index b5d108c..8e940f4 100644
--- a/lib/libvmod_debug/vmod.vcc
+++ b/lib/libvmod_debug/vmod.vcc
@@ -93,3 +93,7 @@ Encrypt the HTTP header with quad-ROT13 encryption,
$Function STRING argtest(STRING one, REAL two=2, STRING three="3")
$Function INT vre_limit()
+
+$Function VOID register_exp_callback(PRIV_VCL)
+
+Register the vmod to receive expiry callbacks
diff --git a/lib/libvmod_debug/vmod_debug.c b/lib/libvmod_debug/vmod_debug.c
index b6736fb..9dcfb7d 100644
--- a/lib/libvmod_debug/vmod_debug.c
+++ b/lib/libvmod_debug/vmod_debug.c
@@ -36,6 +36,13 @@
#include "vrt.h"
#include "vcc_if.h"
+struct priv_vcl {
+ unsigned magic;
+#define PRIV_VCL_MAGIC 0x8E62FA9D
+ char *foo;
+ uintptr_t exp_cb;
+};
+
VCL_VOID __match_proto__(td_debug_panic)
vmod_panic(VRT_CTX, const char *str, ...)
{
@@ -65,16 +72,6 @@ vmod_author(VRT_CTX, VCL_ENUM id)
WRONG("Illegal VMOD enum");
}
-int
-init_function(struct vmod_priv *priv, const struct VCL_conf *cfg)
-{
- (void)cfg;
-
- priv->priv = strdup("FOO");
- priv->free = free;
- return (0);
-}
-
VCL_VOID __match_proto__(td_debug_test_priv_call)
vmod_test_priv_call(VRT_CTX, struct vmod_priv *priv)
{
@@ -103,9 +100,13 @@ vmod_test_priv_task(VRT_CTX, struct vmod_priv *priv, VCL_STRING s)
VCL_VOID __match_proto__(td_debug_test_priv_vcl)
vmod_test_priv_vcl(VRT_CTX, struct vmod_priv *priv)
{
+ struct priv_vcl *priv_vcl;
CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
- assert(!strcmp(priv->priv, "FOO"));
+ AN(priv);
+ CAST_OBJ_NOTNULL(priv_vcl, priv->priv, PRIV_VCL_MAGIC);
+ AN(priv_vcl->foo);
+ assert(!strcmp(priv_vcl->foo, "FOO"));
}
VCL_BLOB
@@ -175,3 +176,65 @@ vmod_vre_limit(VRT_CTX)
(void)ctx;
return (cache_param->vre_limits.match);
}
+
+static void __match_proto__(exp_callback_f)
+exp_cb(struct worker *wrk, struct objcore *oc, enum exp_event_e ev, void *priv)
+{
+ const struct priv_vcl *priv_vcl;
+ const char *what;
+
+ CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
+ CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC);
+ CAST_OBJ_NOTNULL(priv_vcl, priv, PRIV_VCL_MAGIC);
+ switch (ev) {
+ case EXP_INSERT: what = "insert"; break;
+ case EXP_INJECT: what = "inject"; break;
+ case EXP_REMOVE: what = "remove"; break;
+ default: WRONG("Wrong exp_event");
+ }
+ VSL(SLT_Debug, 0, "exp_cb: event %s %p", what, oc);
+}
+
+VCL_VOID __match_proto__()
+vmod_register_exp_callback(VRT_CTX, struct vmod_priv *priv)
+{
+ struct priv_vcl *priv_vcl;
+
+ CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
+ CAST_OBJ_NOTNULL(priv_vcl, priv->priv, PRIV_VCL_MAGIC);
+ AZ(priv_vcl->exp_cb);
+ priv_vcl->exp_cb = EXP_Register_Callback(exp_cb, priv_vcl);
+ VSL(SLT_Debug, 0, "exp_cb: registered");
+}
+
+static void __match_proto__(vmod_priv_free_f)
+priv_vcl_free(void *priv)
+{
+ struct priv_vcl *priv_vcl;
+
+ CAST_OBJ_NOTNULL(priv_vcl, priv, PRIV_VCL_MAGIC);
+ AN(priv_vcl->foo);
+ free(priv_vcl->foo);
+ if (priv_vcl->exp_cb != 0) {
+ EXP_Deregister_Callback(&priv_vcl->exp_cb);
+ VSL(SLT_Debug, 0, "exp_cb: deregistered");
+ }
+ FREE_OBJ(priv_vcl);
+ AZ(priv_vcl);
+}
+
+int __match_proto__(vmod_init_f)
+init_function(struct vmod_priv *priv, const struct VCL_conf *cfg)
+{
+ struct priv_vcl *priv_vcl;
+
+ (void)cfg;
+
+ ALLOC_OBJ(priv_vcl, PRIV_VCL_MAGIC);
+ AN(priv_vcl);
+ priv_vcl->foo = strdup("FOO");
+ AN(priv_vcl->foo);
+ priv->priv = priv_vcl;
+ priv->free = priv_vcl_free;
+ return (0);
+}
More information about the varnish-commit
mailing list