bind9/lib/isc/condition.c
Ondřej Surý ebcfb16576 Enable tracking of pthreads mutexes
Some POSIX threads implementations (e.g. FreeBSD's libthr) allocate
memory on the heap when pthread_mutex_init() is called.  Every call to
that function must be accompanied by a corresponding call to
pthread_mutex_destroy() or else the memory allocated for the mutex 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 mutexes that allocate memory using malloc() and release it
using free().  This enables using jemalloc for detecting missing
pthread_mutex_destroy() calls on any platform on which it works
reliably.

Introduce a new ISC_TRACK_PTHREADS_OBJECTS preprocessor macro, which
causes isc_mutex_t structures to be allocated on the heap by
isc_mutex_init() and freed by isc_mutex_destroy().  Reuse existing mutex
macros (after renaming them appropriately) for other operations.
2022-07-13 13:19:32 +02:00

76 lines
1.9 KiB
C

/*
* Copyright (C) Internet Systems Consortium, Inc. ("ISC")
*
* SPDX-License-Identifier: MPL-2.0
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, you can obtain one at https://mozilla.org/MPL/2.0/.
*
* See the COPYRIGHT file distributed with this work for additional
* information regarding copyright ownership.
*/
/*! \file */
#include <errno.h>
#include <isc/condition.h>
#include <isc/strerr.h>
#include <isc/string.h>
#include <isc/time.h>
#include <isc/util.h>
isc_result_t
isc_condition_waituntil(isc_condition_t *c, isc_mutex_t *m, isc_time_t *t) {
int presult;
isc_result_t result;
struct timespec ts;
char strbuf[ISC_STRERRORSIZE];
REQUIRE(c != NULL && m != NULL && t != NULL);
/*
* POSIX defines a timespec's tv_sec as time_t.
*/
result = isc_time_secondsastimet(t, &ts.tv_sec);
/*
* If we have a range error ts.tv_sec is most probably a signed
* 32 bit value. Set ts.tv_sec to INT_MAX. This is a kludge.
*/
if (result == ISC_R_RANGE) {
ts.tv_sec = INT_MAX;
} else if (result != ISC_R_SUCCESS) {
return (result);
}
/*!
* POSIX defines a timespec's tv_nsec as long. isc_time_nanoseconds
* ensures its return value is < 1 billion, which will fit in a long.
*/
ts.tv_nsec = (long)isc_time_nanoseconds(t);
do {
pthread_mutex_t *mutex;
#ifdef ISC_TRACK_PTHREADS_OBJECTS
mutex = *m;
#else /* ISC_TRACK_PTHREADS_OBJECTS */
mutex = m;
#endif /* ISC_TRACK_PTHREADS_OBJECTS */
presult = pthread_cond_timedwait(c, mutex, &ts);
if (presult == 0) {
return (ISC_R_SUCCESS);
}
if (presult == ETIMEDOUT) {
return (ISC_R_TIMEDOUT);
}
} while (presult == EINTR);
strerror_r(presult, strbuf, sizeof(strbuf));
UNEXPECTED_ERROR(__FILE__, __LINE__,
"pthread_cond_timedwait() returned %s", strbuf);
return (ISC_R_UNEXPECTED);
}