From d391310034370bc1f33e8b344f2942c9ff973e1e Mon Sep 17 00:00:00 2001 From: Stephane Cance Date: Fri, 28 Jun 2024 14:45:58 +0200 Subject: [PATCH 1/3] libvsc: introduce VSC_cond to track condition variables --- lib/libvsc/Makefile.am | 1 + lib/libvsc/VSC_cond.vsc | 47 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 lib/libvsc/VSC_cond.vsc diff --git a/lib/libvsc/Makefile.am b/lib/libvsc/Makefile.am index 992b4d94cc0..73bf68920e6 100644 --- a/lib/libvsc/Makefile.am +++ b/lib/libvsc/Makefile.am @@ -7,6 +7,7 @@ AM_CPPFLAGS = \ -I$(top_builddir)/include VSC_SRC = \ + VSC_cond.vsc \ VSC_lck.vsc \ VSC_main.vsc \ VSC_mempool.vsc \ diff --git a/lib/libvsc/VSC_cond.vsc b/lib/libvsc/VSC_cond.vsc new file mode 100644 index 00000000000..03723229441 --- /dev/null +++ b/lib/libvsc/VSC_cond.vsc @@ -0,0 +1,47 @@ +.. + This is *NOT* a RST file but the syntax has been chosen so + that it may become an RST file at some later date. + +.. varnish_vsc_begin:: cond + :oneliner: Condition Counters + :order: 70 + + Counters which track the activity in the different classes + of condition variables. + +.. varnish_vsc:: init + :type: counter + :level: debug + :oneliner: Initialized condition variables. + +.. varnish_vsc:: fini + :type: counter + :level: debug + :oneliner: Uninitialized condition variables. + +.. varnish_vsc:: signal + :type: counter + :level: debug + :oneliner: Number of times a signal is posted on condition variables. + +.. varnish_vsc:: broadcast + :type: counter + :level: debug + :oneliner: Number of times a broadcast is posted on condition variables. + +.. varnish_vsc:: waits + :type: counter + :level: debug + :oneliner: Number of times a wait occurred on condition variables. + +.. varnish_vsc:: waiting + :type: gauge + :level: debug + :oneliner: Number of currently waiting thread on condition variables. + +.. varnish_vsc:: wait_duration_ns + :type: counter + :level: debug + :oneliner: Amount of nanoseconds spent waiting of condition variables. + +.. varnish_vsc_end:: cond From 7e89175ffa8d87bdd018548a014dc2c0e038f4b1 Mon Sep 17 00:00:00 2001 From: Stephane Cance Date: Wed, 14 Feb 2024 13:01:02 +0100 Subject: [PATCH 2/3] vsync: add synchronization devices to libvarnish This abstracts pthread_mutex_t and pthread_cond_t entirely and lives in libvarnish making it usable outside of varnishd. Instrumentation is abstracted and is externally supplied. This also exposes the ability to use the monotonic clock to wait on conditions, macOS <= 14.4 appear to not have the POSIX API allowing this, a transparent portability trade-off is proposed for this case. --- configure.ac | 1 + include/Makefile.am | 1 + include/vdef.h | 2 + include/vsync.h | 519 +++++++++++++++++++++++++++++++++++++ lib/libvarnish/Makefile.am | 1 + lib/libvarnish/vsync.c | 63 +++++ 6 files changed, 587 insertions(+) create mode 100644 include/vsync.h create mode 100644 lib/libvarnish/vsync.c diff --git a/configure.ac b/configure.ac index 25959ebafcb..4ab5fcadd4c 100644 --- a/configure.ac +++ b/configure.ac @@ -229,6 +229,7 @@ save_LIBS="${LIBS}" LIBS="${PTHREAD_LIBS}" AC_CHECK_FUNCS([pthread_mutex_isowned_np]) AC_CHECK_FUNCS([pthread_getattr_np]) +AC_CHECK_FUNCS([pthread_condattr_setclock]) LIBS="${save_LIBS}" AC_CHECK_DECL([__SUNPRO_C], [SUNCC="yes"], [SUNCC="no"]) diff --git a/include/Makefile.am b/include/Makefile.am index 347724d70a8..43ebb976eaf 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -79,6 +79,7 @@ nobase_pkginclude_HEADERS += \ vsa.h \ vsb.h \ vsha256.h \ + vsync.h \ vtcp.h \ vte.h \ vtim.h \ diff --git a/include/vdef.h b/include/vdef.h index b287e595f25..f3e792b8f3c 100644 --- a/include/vdef.h +++ b/include/vdef.h @@ -44,6 +44,8 @@ #endif #define VDEF_H_INCLUDED +#include + /* Safe printf into a fixed-size buffer */ #define bprintf(buf, fmt, ...) \ do { \ diff --git a/include/vsync.h b/include/vsync.h new file mode 100644 index 00000000000..18fcf33b662 --- /dev/null +++ b/include/vsync.h @@ -0,0 +1,519 @@ +/*- + * Copyright (c) 2006 Verdens Gang AS + * Copyright (c) 2006-2019 Varnish Software AS + * All rights reserved. + * + * Author: Stephane Cance + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Varnish Synchronisation devices. + */ +#ifdef VSYNC_H_INCLUDED +# error "vsync.h included multiple times" +#endif /* VSYNC_H_INCLUDED */ +#define VSYNC_H_INCLUDED + +#include + +/********************************************************************** + * Assertions + */ + +#define VSYNC_assert(e) \ + do { \ + if (!(e)) \ + VAS_Fail((func), (file), (line), #e, VAS_ASSERT); \ + } while (0) + +#define VSYNC__PTOK(e, var) \ + do { \ + int var = (e); \ + if (!var) \ + break; \ + errno = var; \ + VAS_Fail((func), (file), (line), #e" failed", VAS_WRONG); \ + } while (0) + +#define VSYNC_PTOK(e) VSYNC__PTOK((e), VUNIQ_NAME(_pterr)) + +/********************************************************************** + * Event instrumentation. + */ + +struct VSC_lck; +struct VSC_cond; + +enum vsync_mtx_event { + VSYNC_MTX_INIT, + VSYNC_MTX_FINI, + VSYNC_MTX_LOCK, + VSYNC_MTX_UNLOCK, +}; + +enum vsync_cond_event { + VSYNC_COND_INIT, + VSYNC_COND_FINI, + VSYNC_COND_SIGNAL, + VSYNC_COND_BROADCAST, + VSYNC_COND_WAIT_START, + VSYNC_COND_WAIT_END, +}; + +typedef void vsync_mtx_event_f(const char *func, const char *file, + int line, enum vsync_mtx_event evt, struct VSC_lck *vsc); +typedef void vsync_cond_event_f(const char *func, const char *file, + int line, enum vsync_cond_event evt, struct VSC_cond *vsc_cond, + struct VSC_lck *vsc_lck, vtim_mono *start_atp); + +extern vsync_mtx_event_f *VSYNC_mtx_event_func; +extern vsync_cond_event_f *VSYNC_cond_event_func; + +#define VSYNC_mtx_event(evt, vsc) \ + VSYNC__mtx_event((func), (file), (line), VSYNC_MTX_##evt, (vsc)) +#define VSYNC_cond_event(evt, vsccond, vscmtx, at) \ + VSYNC__cond_event((func), (file), (line), VSYNC_COND_##evt, (vsccond), \ + (vscmtx), (at)) + +static inline void +VSYNC__mtx_event(const char *func, const char *file, int line, + enum vsync_mtx_event evt, struct VSC_lck *vsc) +{ + if ((vsc != NULL) && (VSYNC_mtx_event_func != NULL)) + VSYNC_mtx_event_func(func, file, line, evt, vsc); +} + +static inline void +VSYNC__cond_event(const char *func, const char *file, int line, + enum vsync_cond_event evt, struct VSC_cond *vsccond, struct VSC_lck *vscmtx, + vtim_mono *start_atp) +{ + if ((vsccond != NULL) && (VSYNC_cond_event_func != NULL)) { + VSYNC_cond_event_func(func, file, line, evt, vsccond, vscmtx, + start_atp); + } +} + +/********************************************************************** + * Mutex + */ +struct vsync_mtx { + pthread_mutex_t mtx; + struct VSC_lck *vsc; + unsigned held; + pthread_t owner; +}; + +#define VSYNC_MTX_INITIALIZER { .mtx = PTHREAD_MUTEX_INITIALIZER } + +#define VSYNC_mtx_init(m, vsc) \ + VSYNC_mtx__init((m), (vsc), __func__, __FILE__, __LINE__) +#define VSYNC_mtx_fini(m) \ + VSYNC_mtx__fini((m), __func__, __FILE__, __LINE__) + +#define VSYNC_mtx_lock(m) \ + VSYNC_mtx__lock((m), __func__, __FILE__, __LINE__) +#define VSYNC_mtx_unlock(m) \ + VSYNC_mtx__unlock((m), __func__, __FILE__, __LINE__) +#define VSYNC_mtx_trylock(m) \ + VSYNC_mtx__trylock((m), __func__, __FILE__, __LINE__) +#define VSYNC_mtx_timedlock(m, realtim) \ + VSYNC_mtx__timedlock((m), (realtim), __func__, __FILE__, __LINE__) + +#define VSYNC_mtx_assert_held(m) \ + VSYNC_mtx__assert_held((m), __func__, __FILE__, __LINE__) + +static inline void +VSYNC_mtx__init(struct vsync_mtx *m, struct VSC_lck *vsc, const char *func, + const char *file, int line) +{ + VSYNC_assert(m != NULL); + VSYNC_PTOK(pthread_mutex_init(&m->mtx, /* attr: */NULL)); + m->held = 0; + m->vsc = vsc; + + VSYNC_mtx_event(INIT, m->vsc); +} + +static inline void +VSYNC_mtx__fini(struct vsync_mtx *m, const char *func, const char *file, + int line) +{ + VSYNC_assert(m != NULL); + VSYNC_assert(!m->held); + VSYNC_PTOK(pthread_mutex_destroy(&m->mtx)); + + VSYNC_mtx_event(FINI, m->vsc); + + m->vsc = NULL; +} + +static inline void +VSYNC_mtx__lock(struct vsync_mtx *m, const char *func, const char *file, + int line) +{ + VSYNC_assert(m != NULL); + VSYNC_PTOK(pthread_mutex_lock(&m->mtx)); + + VSYNC_assert(!m->held); + m->held = 1; + m->owner = pthread_self(); + + VSYNC_mtx_event(LOCK, m->vsc); +} + +static inline void +VSYNC_mtx__assert_held(struct vsync_mtx *m, const char *func, + const char *file, int line) +{ + VSYNC_assert(m != NULL); + VSYNC_assert(m->held); + VSYNC_assert(pthread_equal(m->owner, pthread_self())); +} + +static inline void +VSYNC_mtx__unlock(struct vsync_mtx *m, const char *func, const char *file, + int line) +{ + VSYNC_mtx_event(UNLOCK, m->vsc); + + VSYNC_mtx__assert_held(m, func, file, line); + m->held = 0; + VSYNC_PTOK(pthread_mutex_unlock(&m->mtx)); +} + +static inline unsigned +VSYNC_mtx__trylock(struct vsync_mtx *m, const char *func, const char *file, + int line) +{ + int err; + + VSYNC_assert(m != NULL); + err = pthread_mutex_trylock(&m->mtx); + if (err == EBUSY) + return (0); + VSYNC_PTOK(err); + + VSYNC_assert(!m->held); + m->held = 1; + m->owner = pthread_self(); + + VSYNC_mtx_event(LOCK, m->vsc); + + return (1); +} + +static inline unsigned +VSYNC_mtx__timedlock(struct vsync_mtx *m, vtim_real realtim, const char *func, + const char *file, int line) +{ + struct timespec ts; + int err; + + ts.tv_sec = (time_t)realtim; + ts.tv_nsec = (realtim - (vtim_real)ts.tv_sec) * 1e9; + VSYNC_assert(ts.tv_sec >= 0); + VSYNC_assert(ts.tv_nsec >= 0); + + VSYNC_assert(m != NULL); + err = pthread_mutex_timedlock(&m->mtx, &ts); + + if (err == ETIMEDOUT) + return (0); + VSYNC_PTOK(err); + + VSYNC_assert(!m->held); + m->held = 1; + m->owner = pthread_self(); + + VSYNC_mtx_event(LOCK, m->vsc); + + return (1); +} + +/********************************************************************** + * Condition + */ + +struct vsync_cond { + pthread_cond_t cond; + clockid_t clock_id; + struct VSC_cond *vsc; +}; + +#define VSYNC_COND_INITIALIZER \ + { \ + .cond = PTHREAD_COND_INITIALIZER, \ + .clock_id = CLOCK_REALTIME, \ + } + +#define VSYNC_cond_init(c, clock_id, vsc) \ + VSYNC_cond__init((c), (clock_id), (vsc), __func__, __FILE__, __LINE__) + +#define VSYNC_cond_fini(c) \ + VSYNC_cond__fini((c), __func__, __FILE__, __LINE__) + +#define VSYNC_cond_broadcast(c) \ + VSYNC_cond__broadcast((c), __func__, __FILE__, __LINE__) + +#define VSYNC_cond_signal(c) \ + VSYNC_cond__signal((c), __func__, __FILE__, __LINE__) + +#define VSYNC_cond_wait(c, m) \ + VSYNC_cond__wait((c), (m), __func__, __FILE__, __LINE__) + +/* wait until deadline */ +#define VSYNC_cond_timedwait_real(c, m, tim) \ + VSYNC_cond__timedwait_real((c), (m), (tim), __func__, __FILE__, __LINE__) +#define VSYNC_cond_timedwait_mono(c, m, tim) \ + VSYNC_cond__timedwait_mono((c), (m), (tim), __func__, __FILE__, __LINE__) + +/* wait until deadline or forever if deadline is INF */ +#define VSYNC_cond_wait_until_real(c, m, tim) \ + VSYNC_cond__wait_until_real((c), (m), (tim), __func__, __FILE__, __LINE__) +#define VSYNC_cond_wait_until_mono(c, m, tim) \ + VSYNC_cond__wait_until_mono((c), (m), (tim), __func__, __FILE__, __LINE__) + + +void VSYNC_cond_clock_init(pthread_cond_t *cond, clockid_t *idp, + const char *func, const char *file, int line); + +static inline void +VSYNC_cond__init(struct vsync_cond *c, clockid_t clock_id, struct VSC_cond *vsc, + const char *func, const char *file, int line) +{ + VSYNC_assert(c != NULL); + + c->clock_id = clock_id; + VSYNC_cond_clock_init(&c->cond, &c->clock_id, func, file, line); + + c->vsc = vsc; + + VSYNC_cond_event(INIT, c->vsc, NULL, NULL); +} + +static inline void +VSYNC_cond__fini(struct vsync_cond *c, const char *func, const char *file, + int line) +{ + VSYNC_assert(c != NULL); + + VSYNC_PTOK(pthread_cond_destroy(&c->cond)); + + VSYNC_cond_event(FINI, c->vsc, NULL, NULL); + + c->vsc = NULL; +} + +static inline void +VSYNC_cond__broadcast(struct vsync_cond *c, const char *func, const char *file, + int line) +{ + VSYNC_assert(c != NULL); + + VSYNC_PTOK(pthread_cond_broadcast(&c->cond)); + + VSYNC_cond_event(BROADCAST, c->vsc, NULL, NULL); +} + +static inline void +VSYNC_cond__signal(struct vsync_cond *c, const char *func, const char *file, + int line) +{ + VSYNC_assert(c != NULL); + + VSYNC_PTOK(pthread_cond_signal(&c->cond)); + + VSYNC_cond_event(SIGNAL, c->vsc, NULL, NULL); +} + +static inline void +VSYNC_cond__wait(struct vsync_cond *c, struct vsync_mtx *m, const char *func, + const char *file, int line) +{ + vtim_mono tm;; + int err; + + VSYNC_assert(c != NULL); + + VSYNC_mtx__assert_held(m, func, file, line); + + VSYNC_cond_event(WAIT_START, c->vsc, m->vsc, &tm); + + m->held = 0; + + /* + * Although POSIX states that EINTR shall never be returned here, this + * has apparently been seen on some buggy implementations. This blindly + * makes the assumption that both the state of the mutex and of the + * cond remain valid and consistent when this happens, similarly to + * the Solaris specific non-POSIX style constructs. + */ + err = pthread_cond_wait(&c->cond, &m->mtx); + VSYNC_assert(err == 0 || err == EINTR); + + VSYNC_assert(!m->held); + m->held = 1; + m->owner = pthread_self(); + + VSYNC_cond_event(WAIT_END, c->vsc, m->vsc, &tm); +} + +static inline unsigned +VSYNC_cond__timedwait(struct vsync_cond *c, struct vsync_mtx *m, + const struct timespec *abstime, const char *func, const char *file, + int line) +{ + vtim_mono tm; + int err; + + VSYNC_assert(c != NULL); + + VSYNC_mtx__assert_held(m, func, file, line); + + VSYNC_cond_event(WAIT_START, c->vsc, m->vsc, &tm); + + m->held = 0; + + err = pthread_cond_timedwait(&c->cond, &m->mtx, abstime); + + VSYNC_assert(!m->held); + m->held = 1; + m->owner = pthread_self(); + + VSYNC_cond_event(WAIT_END, c->vsc, m->vsc, &tm); + + if (err == ETIMEDOUT) + return (0); + + /* See VSYNC_cond__wait about EINTR support */ + VSYNC_assert(err == 0 || err == EINTR); + + return (1); +} + +static inline unsigned +VSYNC_cond__timedwait_real(struct vsync_cond *c, struct vsync_mtx *m, + vtim_real realtim, const char *func, const char *file, int line) +{ + struct timespec ts; + + VSYNC_assert(c != NULL); + + VSYNC_assert(c->clock_id == CLOCK_REALTIME); + + ts.tv_sec = (time_t)realtim; + ts.tv_nsec = (realtim - (vtim_real)ts.tv_sec) * 1e9; + VSYNC_assert(ts.tv_sec >= 0); + VSYNC_assert(ts.tv_nsec >= 0); + + return (VSYNC_cond__timedwait(c, m, &ts, func, file, line)); +} + +static inline unsigned +VSYNC_cond__wait_until_real(struct vsync_cond *c, struct vsync_mtx *m, + vtim_real deadline, const char *func, const char *file, int line) +{ + VSYNC_assert(c != NULL); + + if (isinf(deadline)) { + VSYNC_cond__wait(c, m, func, file, line); + return (1); + } + + return (VSYNC_cond__timedwait_real(c, m, deadline, func, file, line)); +} + +static inline vtim_real +VSYNC_mono_to_real(vtim_mono monotim) +{ + vtim_real ref_real; + vtim_mono ref_mono; + + /* + * macOS apparently does not support pthread_condattr_setclock(3p) + * this is a severely degraded implementation which does not protect + * against time jumps and breaks the no drift principle built into + * the pthread_cond_timedwait(3p) API, it is assumed that this is + * acceptable to callers as long as drift is positive. + */ + ref_mono = VTIM_mono(); + ref_real = VTIM_real(); + + return monotim - ref_mono + ref_real; +} + +static inline unsigned +VSYNC_cond__timedwait_mono(struct vsync_cond *c, struct vsync_mtx *m, + vtim_mono deadline, const char *func, const char *file, int line) +{ + struct timespec ts; + + VSYNC_assert(c != NULL); + VSYNC_assert(!isinf(deadline)); + + /* + * macOS apparently does not support pthread_condattr_setclock(3p) + * meaning that a cond required to be monotonic may end up being + * instrumented with the realtime clock. + */ + if (c->clock_id == CLOCK_REALTIME) { + return (VSYNC_cond__timedwait_real(c, m, + VSYNC_mono_to_real(deadline), func, file, line)); + } + + VSYNC_assert(c->clock_id == CLOCK_MONOTONIC); + + ts.tv_sec = (time_t)deadline; + ts.tv_nsec = (deadline - (vtim_mono)ts.tv_sec) * 1e9; + VSYNC_assert(ts.tv_sec >= 0); + VSYNC_assert(ts.tv_nsec >= 0); + + return (VSYNC_cond__timedwait(c, m, &ts, func, file, line)); +} + +static inline unsigned +VSYNC_cond__wait_until_mono(struct vsync_cond *c, struct vsync_mtx *m, + vtim_mono deadline, const char *func, const char *file, int line) +{ + VSYNC_assert(c != NULL); + + if (isinf(deadline)) { + VSYNC_cond__wait(c, m, func, file, line); + return (1); + } + + return (VSYNC_cond__timedwait_mono(c, m, deadline, func, file, line)); + +} + +/********************************************************************** + * Cleanups + */ + +#ifndef VSYNC_KEEP_ASSERTS +# undef VSYNC_assert +# undef VSYNC__PTOK +# undef VSYNC_PTOK +#endif /* VSYNC_KEEP_ASSERTS */ + +#undef VSYNC_event diff --git a/lib/libvarnish/Makefile.am b/lib/libvarnish/Makefile.am index 22109111070..38e2b774fe6 100644 --- a/lib/libvarnish/Makefile.am +++ b/lib/libvarnish/Makefile.am @@ -40,6 +40,7 @@ libvarnish_la_SOURCES = \ vsha256.c \ vss.c \ vsub.c \ + vsync.c \ vtcp.c \ vte.c \ vtim.c \ diff --git a/lib/libvarnish/vsync.c b/lib/libvarnish/vsync.c new file mode 100644 index 00000000000..6509b9599e7 --- /dev/null +++ b/lib/libvarnish/vsync.c @@ -0,0 +1,63 @@ +/*- + * Copyright (c) 2006 Verdens Gang AS + * Copyright (c) 2006-2019 Varnish Software AS + * All rights reserved. + * + * Author: Stephane Cance + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Varnish Synchronisation devices. + */ +#include "config.h" + +#include +#include +#include + +#include "vdef.h" +#include "vas.h" +#include "vtim.h" +#define VSYNC_KEEP_ASSERTS +#include "vsync.h" + +vsync_mtx_event_f *VSYNC_mtx_event_func = NULL; +vsync_cond_event_f *VSYNC_cond_event_func = NULL; + +void +VSYNC_cond_clock_init(pthread_cond_t *cond, clockid_t *idp, const char *func, + const char *file, int line) +{ + pthread_condattr_t attr; + + VSYNC_assert(cond != NULL); + VSYNC_assert(idp != NULL); + + VSYNC_PTOK(pthread_condattr_init(&attr)); +#ifdef HAVE_PTHREAD_CONDATTR_SETCLOCK + VSYNC_PTOK(pthread_condattr_setclock(&attr, *idp)); +#else + *idp = CLOCK_REALTIME; +#endif /* HAVE_PTHREAD_CONDATTR_SETCLOCK */ + VSYNC_PTOK(pthread_cond_init(cond, &attr)); + VSYNC_PTOK(pthread_condattr_destroy(&attr)); +} From a9f6a4bd8c2fe29b068730fa310c477bc9995faa Mon Sep 17 00:00:00 2001 From: Stephane Cance Date: Wed, 26 Jun 2024 14:18:18 +0200 Subject: [PATCH 3/3] varnishd: introduce vsync event tracking to varnishd --- bin/varnishd/Makefile.am | 1 + bin/varnishd/cache/cache_main.c | 1 + bin/varnishd/cache/cache_varnishd.h | 3 + bin/varnishd/cache/cache_vsync.c | 122 ++++++++++++++++++++++++++++ 4 files changed, 127 insertions(+) create mode 100644 bin/varnishd/cache/cache_vsync.c diff --git a/bin/varnishd/Makefile.am b/bin/varnishd/Makefile.am index aabfce05036..73a8b97b8c3 100644 --- a/bin/varnishd/Makefile.am +++ b/bin/varnishd/Makefile.am @@ -53,6 +53,7 @@ varnishd_SOURCES = \ cache/cache_vrt_var.c \ cache/cache_vrt_vcl.c \ cache/cache_vrt_vmod.c \ + cache/cache_vsync.c \ cache/cache_wrk.c \ cache/cache_ws_common.c \ common/common_vsc.c \ diff --git a/bin/varnishd/cache/cache_main.c b/bin/varnishd/cache/cache_main.c index d29d3d3aec5..c3ace3484ec 100644 --- a/bin/varnishd/cache/cache_main.c +++ b/bin/varnishd/cache/cache_main.c @@ -402,6 +402,7 @@ child_main(int sigmagic, size_t altstksz) VSM_Init(); /* First, LCK needs it. */ LCK_Init(); /* Second, locking */ + SYNC_Init(); Lck_New(&vxid_lock, lck_vxid); diff --git a/bin/varnishd/cache/cache_varnishd.h b/bin/varnishd/cache/cache_varnishd.h index 9bd5e249041..8e73cbe09b3 100644 --- a/bin/varnishd/cache/cache_varnishd.h +++ b/bin/varnishd/cache/cache_varnishd.h @@ -604,3 +604,6 @@ void SMP_Ready(void); if (DO_DEBUG(debug_bit)) \ WRK_Log(SLT_Debug, __VA_ARGS__); \ } while (0) + +/* cache_vsync.c */ +void SYNC_Init(void); diff --git a/bin/varnishd/cache/cache_vsync.c b/bin/varnishd/cache/cache_vsync.c new file mode 100644 index 00000000000..2c259168e16 --- /dev/null +++ b/bin/varnishd/cache/cache_vsync.c @@ -0,0 +1,122 @@ +/*- + * Copyright (c) 2024 Varnish Software AS + * All rights reserved. + * + * Author: Stephane Cance + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "config.h" + +#include +#include + +#include "cache_varnishd.h" + +#include "vtim.h" +#include "vsync.h" + +#include "VSC_lck.h" +#include "VSC_cond.h" + +static void +mtx_sync_evt(const char *func, const char *file, + int line, enum vsync_mtx_event evt, struct VSC_lck *vsc) +{ + AN(vsc); + + (void)func; + (void)line; + (void)file; + + + switch (evt) { + case VSYNC_MTX_INIT: + __sync_fetch_and_add(&vsc->creat, 1); + break; + case VSYNC_MTX_FINI: + __sync_fetch_and_add(&vsc->destroy, 1); + break; + case VSYNC_MTX_LOCK: + __sync_fetch_and_add(&vsc->locks, 1); + break; + case VSYNC_MTX_UNLOCK: + break; + } +} + +static void +cond_sync_evt(const char *func, const char *file, int line, + enum vsync_cond_event evt, struct VSC_cond *vsc_cond, + struct VSC_lck *vsc_mtx, double *start_atp) +{ + vtim_mono now; + + AN(vsc_cond); + + (void)func; + (void)line; + (void)file; + (void)vsc_mtx; + + switch (evt) { + case VSYNC_COND_INIT: + __sync_fetch_and_add(&vsc_cond->init, 1); + AZ(start_atp); + break; + case VSYNC_COND_FINI: + __sync_fetch_and_add(&vsc_cond->fini, 1); + AZ(start_atp); + break; + case VSYNC_COND_SIGNAL: + __sync_fetch_and_add(&vsc_cond->signal, 1); + AZ(start_atp); + break; + case VSYNC_COND_BROADCAST: + __sync_fetch_and_add(&vsc_cond->broadcast, 1); + AZ(start_atp); + break; + case VSYNC_COND_WAIT_START: + __sync_fetch_and_add(&vsc_cond->waiting, 1); + AN(start_atp); + *start_atp = VTIM_mono(); + break; + case VSYNC_COND_WAIT_END: + AN(start_atp); + now = VTIM_mono(); + assert(now > *start_atp); + + __sync_fetch_and_sub(&vsc_cond->waiting, 1); + __sync_fetch_and_add(&vsc_cond->waits, 1); + __sync_fetch_and_add(&vsc_cond->wait_duration_ns, + (uint64_t)((now - *start_atp) * 1e9)); + break; + } +} + +void +SYNC_Init(void) +{ + VSYNC_mtx_event_func = mtx_sync_evt; + VSYNC_cond_event_func = cond_sync_evt; +}