]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
Enable tracking of pthreads rwlocks
authorOndřej Surý <ondrej@isc.org>
Wed, 13 Jul 2022 11:19:32 +0000 (13:19 +0200)
committerMichał Kępień <michal@isc.org>
Wed, 13 Jul 2022 11:19:32 +0000 (13:19 +0200)
Some POSIX threads implementations (e.g. FreeBSD's libthr) allocate
memory on the heap when pthread_rwlock_init() is called.  Every call to
that function must be accompanied by a corresponding call to
pthread_rwlock_destroy() or else the memory allocated for the rwlock
will leak.

jemalloc can be used for detecting memory allocations which are not
released by a process when it exits.  Unfortunately, since jemalloc is
also the system allocator on FreeBSD and a special (profiling-enabled)
build of jemalloc is required for memory leak detection, this method
cannot be used for detecting leaked memory allocated by libthr on a
stock FreeBSD installation.

However, libthr's behavior can be emulated on any platform by
implementing alternative versions of libisc functions for creating and
destroying rwlocks that allocate memory using malloc() and release it
using free().  This enables using jemalloc for detecting missing
pthread_rwlock_destroy() calls on any platform on which it works
reliably.

When the newly introduced ISC_TRACK_PTHREADS_OBJECTS preprocessor macro
is set (and --enable-pthread-rwlock is used), allocate isc_rwlock_t
structures on the heap in isc_rwlock_init() and free them in
isc_rwlock_destroy().  Reuse existing functions defined in
lib/isc/rwlock.c for other operations, but rename them first, so that
they contain triple underscores (to indicate that these functions are
implementation-specific, unlike their mutex and condition variable
counterparts, which always use the pthreads implementation).  Define the
isc__rwlock_init() macro so that it is a logical counterpart of
isc__mutex_init() and isc__condition_init(); adjust isc___rwlock_init()
accordingly.  Remove a redundant function prototype for
isc__rwlock_lock() and rename that (static) function to rwlock_lock() in
order to avoid having to use quadruple underscores.

lib/isc/include/isc/rwlock.h
lib/isc/include/isc/types.h
lib/isc/rwlock.c

index ba0ba0d43d9bfe75c5f8c274c3a701aeaff70f1f..b9682e051a64deb4ef500dfaddedad169f1b27ec 100644 (file)
@@ -14,6 +14,7 @@
 #pragma once
 
 #include <inttypes.h>
+#include <stdlib.h>
 
 /*! \file isc/rwlock.h */
 
@@ -21,6 +22,7 @@
 #include <isc/condition.h>
 #include <isc/lang.h>
 #include <isc/types.h>
+#include <isc/util.h>
 
 ISC_LANG_BEGINDECLS
 
@@ -38,6 +40,42 @@ struct isc_rwlock {
        atomic_bool      downgrade;
 };
 
+#if ISC_TRACK_PTHREADS_OBJECTS
+
+typedef struct isc_rwlock *isc_rwlock_t;
+typedef struct isc_rwlock  isc__rwlock_t;
+
+#define isc_rwlock_init(rwl, rq, wq)            \
+       {                                       \
+               *rwl = malloc(sizeof(**rwl));   \
+               isc__rwlock_init(*rwl, rq, wq); \
+       }
+#define isc_rwlock_lock(rwl, type)    isc___rwlock_lock(*rwl, type)
+#define isc_rwlock_trylock(rwl, type) isc___rwlock_trylock(*rwl, type)
+#define isc_rwlock_unlock(rwl, type)  isc___rwlock_unlock(*rwl, type)
+#define isc_rwlock_tryupgrade(rwl)    isc___rwlock_tryupgrade(*rwl)
+#define isc_rwlock_downgrade(rwl)     isc___rwlock_downgrade(*rwl)
+#define isc_rwlock_destroy(rwl)             \
+       {                                   \
+               isc___rwlock_destroy(*rwl); \
+               free(*rwl);                 \
+       }
+
+#else /* ISC_TRACK_PTHREADS_OBJECTS */
+
+typedef struct isc_rwlock isc_rwlock_t;
+typedef struct isc_rwlock isc__rwlock_t;
+
+#define isc_rwlock_init(rwl, rq, wq)  isc__rwlock_init(rwl, rq, wq)
+#define isc_rwlock_lock(rwl, type)    isc___rwlock_lock(rwl, type)
+#define isc_rwlock_trylock(rwl, type) isc___rwlock_trylock(rwl, type)
+#define isc_rwlock_unlock(rwl, type)  isc___rwlock_unlock(rwl, type)
+#define isc_rwlock_tryupgrade(rwl)    isc___rwlock_tryupgrade(rwl)
+#define isc_rwlock_downgrade(rwl)     isc___rwlock_downgrade(rwl)
+#define isc_rwlock_destroy(rwl)              isc___rwlock_destroy(rwl)
+
+#endif /* ISC_TRACK_PTHREADS_OBJECTS */
+
 #else /* USE_PTHREAD_RWLOCK */
 
 struct isc_rwlock {
@@ -76,28 +114,45 @@ struct isc_rwlock {
        unsigned int write_quota;
 };
 
+typedef struct isc_rwlock isc_rwlock_t;
+typedef struct isc_rwlock isc__rwlock_t;
+
+#define isc_rwlock_init(rwl, rq, wq)  isc__rwlock_init(rwl, rq, wq)
+#define isc_rwlock_lock(rwl, type)    isc___rwlock_lock(rwl, type)
+#define isc_rwlock_trylock(rwl, type) isc___rwlock_trylock(rwl, type)
+#define isc_rwlock_unlock(rwl, type)  isc___rwlock_unlock(rwl, type)
+#define isc_rwlock_tryupgrade(rwl)    isc___rwlock_tryupgrade(rwl)
+#define isc_rwlock_downgrade(rwl)     isc___rwlock_downgrade(rwl)
+#define isc_rwlock_destroy(rwl)              isc___rwlock_destroy(rwl)
+
 #endif /* USE_PTHREAD_RWLOCK */
 
-void
-isc_rwlock_init(isc_rwlock_t *rwl, unsigned int read_quota,
-               unsigned int write_quota);
+#define isc__rwlock_init(rwl, rq, wq)                      \
+       {                                                  \
+               int _ret = isc___rwlock_init(rwl, rq, wq); \
+               ERRNO_CHECK(isc___rwlock_init, _ret);      \
+       }
+
+int
+isc___rwlock_init(isc__rwlock_t *rwl, unsigned int read_quota,
+                 unsigned int write_quota);
 
 void
-isc_rwlock_lock(isc_rwlock_t *rwl, isc_rwlocktype_t type);
+isc___rwlock_lock(isc__rwlock_t *rwl, isc_rwlocktype_t type);
 
 isc_result_t
-isc_rwlock_trylock(isc_rwlock_t *rwl, isc_rwlocktype_t type);
+isc___rwlock_trylock(isc__rwlock_t *rwl, isc_rwlocktype_t type);
 
 void
-isc_rwlock_unlock(isc_rwlock_t *rwl, isc_rwlocktype_t type);
+isc___rwlock_unlock(isc__rwlock_t *rwl, isc_rwlocktype_t type);
 
 isc_result_t
-isc_rwlock_tryupgrade(isc_rwlock_t *rwl);
+isc___rwlock_tryupgrade(isc__rwlock_t *rwl);
 
 void
-isc_rwlock_downgrade(isc_rwlock_t *rwl);
+isc___rwlock_downgrade(isc__rwlock_t *rwl);
 
 void
-isc_rwlock_destroy(isc_rwlock_t *rwl);
+isc___rwlock_destroy(isc__rwlock_t *rwl);
 
 ISC_LANG_ENDDECLS
index 78d5ce4a7377cbf1f76bf2156e4fe229eb648160..d7d9bf0b7cf79724c4feb9d2e77a116649cd2275 100644 (file)
@@ -72,7 +72,6 @@ typedef struct isc_quota       isc_quota_t;    /*%< Quota */
 typedef struct isc_ratelimiter  isc_ratelimiter_t;   /*%< Rate Limiter */
 typedef struct isc_region       isc_region_t;        /*%< Region */
 typedef uint64_t                isc_resourcevalue_t; /*%< Resource Value */
-typedef struct isc_rwlock       isc_rwlock_t;        /*%< Read Write Lock */
 typedef struct isc_sockaddr     isc_sockaddr_t;      /*%< Socket Address */
 typedef ISC_LIST(isc_sockaddr_t) isc_sockaddrlist_t;  /*%< Socket Address List
                                                       * */
index 01b7e91f25aded29429af5c18c305d42d0f50912..511c88c4f579b065d4221d085ecacaf25866f0d2 100644 (file)
 #include <errno.h>
 #include <pthread.h>
 
-void
-isc_rwlock_init(isc_rwlock_t *rwl, unsigned int read_quota,
-               unsigned int write_quota) {
+int
+isc___rwlock_init(isc__rwlock_t *rwl, unsigned int read_quota,
+                 unsigned int write_quota) {
        int ret;
        UNUSED(read_quota);
        UNUSED(write_quota);
 
        ret = pthread_rwlock_init(&rwl->rwlock, NULL);
-       ERRNO_CHECK(pthread_rwlock_init, ret);
 
        atomic_init(&rwl->downgrade, false);
+
+       return (ret);
 }
 
 void
-isc_rwlock_lock(isc_rwlock_t *rwl, isc_rwlocktype_t type) {
+isc___rwlock_lock(isc__rwlock_t *rwl, isc_rwlocktype_t type) {
        switch (type) {
        case isc_rwlocktype_read:
                RUNTIME_CHECK(pthread_rwlock_rdlock(&rwl->rwlock) == 0);
@@ -71,7 +72,7 @@ isc_rwlock_lock(isc_rwlock_t *rwl, isc_rwlocktype_t type) {
 }
 
 isc_result_t
-isc_rwlock_trylock(isc_rwlock_t *rwl, isc_rwlocktype_t type) {
+isc___rwlock_trylock(isc__rwlock_t *rwl, isc_rwlocktype_t type) {
        int ret = 0;
        switch (type) {
        case isc_rwlocktype_read:
@@ -101,19 +102,19 @@ isc_rwlock_trylock(isc_rwlock_t *rwl, isc_rwlocktype_t type) {
 }
 
 void
-isc_rwlock_unlock(isc_rwlock_t *rwl, isc_rwlocktype_t type) {
+isc___rwlock_unlock(isc__rwlock_t *rwl, isc_rwlocktype_t type) {
        UNUSED(type);
        RUNTIME_CHECK(pthread_rwlock_unlock(&rwl->rwlock) == 0);
 }
 
 isc_result_t
-isc_rwlock_tryupgrade(isc_rwlock_t *rwl) {
+isc___rwlock_tryupgrade(isc__rwlock_t *rwl) {
        UNUSED(rwl);
        return (ISC_R_LOCKBUSY);
 }
 
 void
-isc_rwlock_downgrade(isc_rwlock_t *rwl) {
+isc___rwlock_downgrade(isc__rwlock_t *rwl) {
        atomic_store_release(&rwl->downgrade, true);
        RUNTIME_CHECK(pthread_rwlock_unlock(&rwl->rwlock) == 0);
        RUNTIME_CHECK(pthread_rwlock_rdlock(&rwl->rwlock) == 0);
@@ -121,7 +122,7 @@ isc_rwlock_downgrade(isc_rwlock_t *rwl) {
 }
 
 void
-isc_rwlock_destroy(isc_rwlock_t *rwl) {
+isc___rwlock_destroy(isc__rwlock_t *rwl) {
        pthread_rwlock_destroy(&rwl->rwlock);
 }
 
@@ -165,16 +166,13 @@ isc_rwlock_destroy(isc_rwlock_t *rwl) {
 #define isc_rwlock_pause()
 #endif /* if defined(_MSC_VER) */
 
-static void
-isc__rwlock_lock(isc_rwlock_t *rwl, isc_rwlocktype_t type);
-
 #ifdef ISC_RWLOCK_TRACE
 #include <stdio.h> /* Required for fprintf/stderr. */
 
 #include <isc/thread.h> /* Required for isc_thread_self(). */
 
 static void
-print_lock(const char *operation, isc_rwlock_t *rwl, isc_rwlocktype_t type) {
+print_lock(const char *operation, isc__rwlock_t *rwl, isc_rwlocktype_t type) {
        fprintf(stderr,
                "rwlock %p thread %" PRIuPTR " %s(%s): "
                "write_requests=%u, write_completions=%u, "
@@ -189,9 +187,9 @@ print_lock(const char *operation, isc_rwlock_t *rwl, isc_rwlocktype_t type) {
 }
 #endif                 /* ISC_RWLOCK_TRACE */
 
-void
-isc_rwlock_init(isc_rwlock_t *rwl, unsigned int read_quota,
-               unsigned int write_quota) {
+int
+isc___rwlock_init(isc__rwlock_t *rwl, unsigned int read_quota,
+                 unsigned int write_quota) {
        REQUIRE(rwl != NULL);
 
        /*
@@ -221,10 +219,12 @@ isc_rwlock_init(isc_rwlock_t *rwl, unsigned int read_quota,
        isc_condition_init(&rwl->writeable);
 
        rwl->magic = RWLOCK_MAGIC;
+
+       return (0);
 }
 
 void
-isc_rwlock_destroy(isc_rwlock_t *rwl) {
+isc___rwlock_destroy(isc__rwlock_t *rwl) {
        REQUIRE(VALID_RWLOCK(rwl));
 
        REQUIRE(atomic_load_acquire(&rwl->write_requests) ==
@@ -304,7 +304,7 @@ isc_rwlock_destroy(isc_rwlock_t *rwl) {
 #define READER_INCR   0x2
 
 static void
-isc__rwlock_lock(isc_rwlock_t *rwl, isc_rwlocktype_t type) {
+rwlock_lock(isc__rwlock_t *rwl, isc_rwlocktype_t type) {
        int32_t cntflag;
 
        REQUIRE(VALID_RWLOCK(rwl));
@@ -421,14 +421,14 @@ isc__rwlock_lock(isc_rwlock_t *rwl, isc_rwlocktype_t type) {
 }
 
 void
-isc_rwlock_lock(isc_rwlock_t *rwl, isc_rwlocktype_t type) {
+isc___rwlock_lock(isc__rwlock_t *rwl, isc_rwlocktype_t type) {
        int32_t cnt = 0;
        int32_t spins = atomic_load_acquire(&rwl->spins) * 2 + 10;
        int32_t max_cnt = ISC_MAX(spins, RWLOCK_MAX_ADAPTIVE_COUNT);
 
        do {
                if (cnt++ >= max_cnt) {
-                       isc__rwlock_lock(rwl, type);
+                       rwlock_lock(rwl, type);
                        break;
                }
                isc_rwlock_pause();
@@ -438,7 +438,7 @@ isc_rwlock_lock(isc_rwlock_t *rwl, isc_rwlocktype_t type) {
 }
 
 isc_result_t
-isc_rwlock_trylock(isc_rwlock_t *rwl, isc_rwlocktype_t type) {
+isc___rwlock_trylock(isc__rwlock_t *rwl, isc_rwlocktype_t type) {
        int32_t cntflag;
 
        REQUIRE(VALID_RWLOCK(rwl));
@@ -505,7 +505,7 @@ isc_rwlock_trylock(isc_rwlock_t *rwl, isc_rwlocktype_t type) {
 }
 
 isc_result_t
-isc_rwlock_tryupgrade(isc_rwlock_t *rwl) {
+isc___rwlock_tryupgrade(isc__rwlock_t *rwl) {
        REQUIRE(VALID_RWLOCK(rwl));
 
        int_fast32_t reader_incr = READER_INCR;
@@ -534,7 +534,7 @@ isc_rwlock_tryupgrade(isc_rwlock_t *rwl) {
 }
 
 void
-isc_rwlock_downgrade(isc_rwlock_t *rwl) {
+isc___rwlock_downgrade(isc__rwlock_t *rwl) {
        int32_t prev_readers;
 
        REQUIRE(VALID_RWLOCK(rwl));
@@ -558,7 +558,7 @@ isc_rwlock_downgrade(isc_rwlock_t *rwl) {
 }
 
 void
-isc_rwlock_unlock(isc_rwlock_t *rwl, isc_rwlocktype_t type) {
+isc___rwlock_unlock(isc__rwlock_t *rwl, isc_rwlocktype_t type) {
        int32_t prev_cnt;
 
        REQUIRE(VALID_RWLOCK(rwl));