diff options
Diffstat (limited to 'lib/isc/pthreads')
-rw-r--r-- | lib/isc/pthreads/Makefile.in | 32 | ||||
-rw-r--r-- | lib/isc/pthreads/condition.c | 72 | ||||
l--------- | lib/isc/pthreads/include/.clang-format | 1 | ||||
-rw-r--r-- | lib/isc/pthreads/include/Makefile.in | 19 | ||||
-rw-r--r-- | lib/isc/pthreads/include/isc/Makefile.in | 36 | ||||
-rw-r--r-- | lib/isc/pthreads/include/isc/condition.h | 64 | ||||
-rw-r--r-- | lib/isc/pthreads/include/isc/mutex.h | 129 | ||||
-rw-r--r-- | lib/isc/pthreads/include/isc/once.h | 33 | ||||
-rw-r--r-- | lib/isc/pthreads/include/isc/thread.h | 74 | ||||
-rw-r--r-- | lib/isc/pthreads/mutex.c | 302 | ||||
-rw-r--r-- | lib/isc/pthreads/thread.c | 129 |
11 files changed, 891 insertions, 0 deletions
diff --git a/lib/isc/pthreads/Makefile.in b/lib/isc/pthreads/Makefile.in new file mode 100644 index 0000000..c79e949 --- /dev/null +++ b/lib/isc/pthreads/Makefile.in @@ -0,0 +1,32 @@ +# 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. + +srcdir = @srcdir@ +VPATH = @srcdir@ +top_srcdir = @top_srcdir@ + +CINCLUDES = -I${srcdir}/include \ + -I${srcdir}/../unix/include \ + -I../include \ + -I${srcdir}/../include \ + -I${srcdir}/.. + +CDEFINES = +CWARNINGS = + +OBJS = condition.@O@ mutex.@O@ thread.@O@ + +SRCS = condition.c mutex.c thread.c + +SUBDIRS = include +TARGETS = ${OBJS} + +@BIND9_MAKE_RULES@ diff --git a/lib/isc/pthreads/condition.c b/lib/isc/pthreads/condition.c new file mode 100644 index 0000000..7657b18 --- /dev/null +++ b/lib/isc/pthreads/condition.c @@ -0,0 +1,72 @@ +/* + * 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 { +#if ISC_MUTEX_PROFILE + presult = pthread_cond_timedwait(c, &m->mutex, &ts); +#else /* if ISC_MUTEX_PROFILE */ + presult = pthread_cond_timedwait(c, m, &ts); +#endif /* if ISC_MUTEX_PROFILE */ + 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); +} diff --git a/lib/isc/pthreads/include/.clang-format b/lib/isc/pthreads/include/.clang-format new file mode 120000 index 0000000..e919bba --- /dev/null +++ b/lib/isc/pthreads/include/.clang-format @@ -0,0 +1 @@ +../../../../.clang-format.headers
\ No newline at end of file diff --git a/lib/isc/pthreads/include/Makefile.in b/lib/isc/pthreads/include/Makefile.in new file mode 100644 index 0000000..60a0a67 --- /dev/null +++ b/lib/isc/pthreads/include/Makefile.in @@ -0,0 +1,19 @@ +# 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. + +srcdir = @srcdir@ +VPATH = @srcdir@ +top_srcdir = @top_srcdir@ + +SUBDIRS = isc +TARGETS = + +@BIND9_MAKE_RULES@ diff --git a/lib/isc/pthreads/include/isc/Makefile.in b/lib/isc/pthreads/include/isc/Makefile.in new file mode 100644 index 0000000..e92364d --- /dev/null +++ b/lib/isc/pthreads/include/isc/Makefile.in @@ -0,0 +1,36 @@ +# 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. + +srcdir = @srcdir@ +VPATH = @srcdir@ +top_srcdir = @top_srcdir@ + +VERSION=@BIND9_VERSION@ + +HEADERS = condition.h mutex.h once.h thread.h + +SUBDIRS = +TARGETS = + +@BIND9_MAKE_RULES@ + +installdirs: + $(SHELL) ${top_srcdir}/mkinstalldirs ${DESTDIR}${includedir}/isc + +install:: installdirs + for i in ${HEADERS}; do \ + ${INSTALL_DATA} $(srcdir)/$$i ${DESTDIR}${includedir}/isc || exit 1; \ + done + +uninstall:: + for i in ${HEADERS}; do \ + rm -f ${DESTDIR}${includedir}/isc/$$i || exit 1; \ + done diff --git a/lib/isc/pthreads/include/isc/condition.h b/lib/isc/pthreads/include/isc/condition.h new file mode 100644 index 0000000..8a79eb4 --- /dev/null +++ b/lib/isc/pthreads/include/isc/condition.h @@ -0,0 +1,64 @@ +/* + * 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. + */ + +#pragma once + +/*! \file */ + +#include <errno.h> + +#include <isc/error.h> +#include <isc/lang.h> +#include <isc/mutex.h> +#include <isc/result.h> +#include <isc/strerr.h> +#include <isc/string.h> +#include <isc/types.h> + +typedef pthread_cond_t isc_condition_t; + +#define isc_condition_init(cond) \ + if (pthread_cond_init(cond, NULL) != 0) { \ + char isc_condition_strbuf[ISC_STRERRORSIZE]; \ + strerror_r(errno, isc_condition_strbuf, \ + sizeof(isc_condition_strbuf)); \ + isc_error_fatal(__FILE__, __LINE__, \ + "pthread_cond_init failed: %s", \ + isc_condition_strbuf); \ + } + +#if ISC_MUTEX_PROFILE +#define isc_condition_wait(cp, mp) \ + ((pthread_cond_wait((cp), &((mp)->mutex)) == 0) ? ISC_R_SUCCESS \ + : ISC_R_UNEXPECTED) +#else /* if ISC_MUTEX_PROFILE */ +#define isc_condition_wait(cp, mp) \ + ((pthread_cond_wait((cp), (mp)) == 0) ? ISC_R_SUCCESS \ + : ISC_R_UNEXPECTED) +#endif /* if ISC_MUTEX_PROFILE */ + +#define isc_condition_signal(cp) \ + ((pthread_cond_signal((cp)) == 0) ? ISC_R_SUCCESS : ISC_R_UNEXPECTED) + +#define isc_condition_broadcast(cp) \ + ((pthread_cond_broadcast((cp)) == 0) ? ISC_R_SUCCESS : ISC_R_UNEXPECTED) + +#define isc_condition_destroy(cp) \ + ((pthread_cond_destroy((cp)) == 0) ? ISC_R_SUCCESS : ISC_R_UNEXPECTED) + +ISC_LANG_BEGINDECLS + +isc_result_t +isc_condition_waituntil(isc_condition_t *, isc_mutex_t *, isc_time_t *); + +ISC_LANG_ENDDECLS diff --git a/lib/isc/pthreads/include/isc/mutex.h b/lib/isc/pthreads/include/isc/mutex.h new file mode 100644 index 0000000..2390cd9 --- /dev/null +++ b/lib/isc/pthreads/include/isc/mutex.h @@ -0,0 +1,129 @@ +/* + * 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. + */ + +#ifndef ISC_MUTEX_H +#define ISC_MUTEX_H 1 + +/*! \file */ + +#include <pthread.h> +#include <stdio.h> + +#include <isc/lang.h> +#include <isc/result.h> /* for ISC_R_ codes */ + +ISC_LANG_BEGINDECLS + +/*! + * Supply mutex attributes that enable deadlock detection + * (helpful when debugging). This is system dependent and + * currently only supported on NetBSD. + */ +#if ISC_MUTEX_DEBUG && defined(__NetBSD__) && defined(PTHREAD_MUTEX_ERRORCHECK) +extern pthread_mutexattr_t isc__mutex_attrs; +#define ISC__MUTEX_ATTRS &isc__mutex_attrs +#else /* if ISC_MUTEX_DEBUG && defined(__NetBSD__) && \ + * defined(PTHREAD_MUTEX_ERRORCHECK) */ +#define ISC__MUTEX_ATTRS NULL +#endif /* if ISC_MUTEX_DEBUG && defined(__NetBSD__) && \ + * defined(PTHREAD_MUTEX_ERRORCHECK) */ + +/* XXX We could do fancier error handling... */ + +/*! + * Define ISC_MUTEX_PROFILE to turn on profiling of mutexes by line. When + * enabled, isc_mutex_stats() can be used to print a table showing the + * number of times each type of mutex was locked and the amount of time + * waiting to obtain the lock. + */ +#ifndef ISC_MUTEX_PROFILE +#define ISC_MUTEX_PROFILE 0 +#endif /* ifndef ISC_MUTEX_PROFILE */ + +#if ISC_MUTEX_PROFILE +typedef struct isc_mutexstats isc_mutexstats_t; + +typedef struct { + pthread_mutex_t mutex; /*%< The actual mutex. */ + isc_mutexstats_t *stats; /*%< Mutex statistics. */ +} isc_mutex_t; +#else /* if ISC_MUTEX_PROFILE */ +typedef pthread_mutex_t isc_mutex_t; +#endif /* if ISC_MUTEX_PROFILE */ + +#if ISC_MUTEX_PROFILE +#define isc_mutex_init(mp) isc_mutex_init_profile((mp), __FILE__, __LINE__) +#else /* if ISC_MUTEX_PROFILE */ +#if ISC_MUTEX_DEBUG && defined(PTHREAD_MUTEX_ERRORCHECK) +#define isc_mutex_init(mp) isc_mutex_init_errcheck((mp)) +#else /* if ISC_MUTEX_DEBUG && defined(PTHREAD_MUTEX_ERRORCHECK) */ +#define isc_mutex_init(mp) isc__mutex_init((mp), __FILE__, __LINE__) +void +isc__mutex_init(isc_mutex_t *mp, const char *file, unsigned int line); +#endif /* if ISC_MUTEX_DEBUG && defined(PTHREAD_MUTEX_ERRORCHECK) */ +#endif /* if ISC_MUTEX_PROFILE */ + +#if ISC_MUTEX_PROFILE +#define isc_mutex_lock(mp) isc_mutex_lock_profile((mp), __FILE__, __LINE__) +#else /* if ISC_MUTEX_PROFILE */ +#define isc_mutex_lock(mp) \ + ((pthread_mutex_lock((mp)) == 0) ? ISC_R_SUCCESS : ISC_R_UNEXPECTED) +#endif /* if ISC_MUTEX_PROFILE */ + +#if ISC_MUTEX_PROFILE +#define isc_mutex_unlock(mp) isc_mutex_unlock_profile((mp), __FILE__, __LINE__) +#else /* if ISC_MUTEX_PROFILE */ +#define isc_mutex_unlock(mp) \ + ((pthread_mutex_unlock((mp)) == 0) ? ISC_R_SUCCESS : ISC_R_UNEXPECTED) +#endif /* if ISC_MUTEX_PROFILE */ + +#if ISC_MUTEX_PROFILE +#define isc_mutex_trylock(mp) \ + ((pthread_mutex_trylock((&(mp)->mutex)) == 0) ? ISC_R_SUCCESS \ + : ISC_R_LOCKBUSY) +#else /* if ISC_MUTEX_PROFILE */ +#define isc_mutex_trylock(mp) \ + ((pthread_mutex_trylock((mp)) == 0) ? ISC_R_SUCCESS : ISC_R_LOCKBUSY) +#endif /* if ISC_MUTEX_PROFILE */ + +#if ISC_MUTEX_PROFILE +#define isc_mutex_destroy(mp) \ + RUNTIME_CHECK(pthread_mutex_destroy((&(mp)->mutex)) == 0) +#else /* if ISC_MUTEX_PROFILE */ +#define isc_mutex_destroy(mp) RUNTIME_CHECK(pthread_mutex_destroy((mp)) == 0) +#endif /* if ISC_MUTEX_PROFILE */ + +#if ISC_MUTEX_PROFILE +#define isc_mutex_stats(fp) isc_mutex_statsprofile(fp); +#else /* if ISC_MUTEX_PROFILE */ +#define isc_mutex_stats(fp) +#endif /* if ISC_MUTEX_PROFILE */ + +#if ISC_MUTEX_PROFILE + +void +isc_mutex_init_profile(isc_mutex_t *mp, const char *_file, int _line); +isc_result_t +isc_mutex_lock_profile(isc_mutex_t *mp, const char *_file, int _line); +isc_result_t +isc_mutex_unlock_profile(isc_mutex_t *mp, const char *_file, int _line); + +void +isc_mutex_statsprofile(FILE *fp); +#endif /* ISC_MUTEX_PROFILE */ + +void +isc_mutex_init_errcheck(isc_mutex_t *mp); + +ISC_LANG_ENDDECLS +#endif /* ISC_MUTEX_H */ diff --git a/lib/isc/pthreads/include/isc/once.h b/lib/isc/pthreads/include/isc/once.h new file mode 100644 index 0000000..481dba5 --- /dev/null +++ b/lib/isc/pthreads/include/isc/once.h @@ -0,0 +1,33 @@ +/* + * 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. + */ + +#ifndef ISC_ONCE_H +#define ISC_ONCE_H 1 + +/*! \file */ + +#include <pthread.h> + +#include <isc/platform.h> +#include <isc/result.h> + +typedef pthread_once_t isc_once_t; + +#define ISC_ONCE_INIT PTHREAD_ONCE_INIT + +/* XXX We could do fancier error handling... */ + +#define isc_once_do(op, f) \ + ((pthread_once((op), (f)) == 0) ? ISC_R_SUCCESS : ISC_R_UNEXPECTED) + +#endif /* ISC_ONCE_H */ diff --git a/lib/isc/pthreads/include/isc/thread.h b/lib/isc/pthreads/include/isc/thread.h new file mode 100644 index 0000000..6f83191 --- /dev/null +++ b/lib/isc/pthreads/include/isc/thread.h @@ -0,0 +1,74 @@ +/* + * 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. + */ + +#ifndef ISC_THREAD_H +#define ISC_THREAD_H 1 + +/*! \file */ + +#include <pthread.h> + +#if defined(HAVE_PTHREAD_NP_H) +#include <pthread_np.h> +#endif /* if defined(HAVE_PTHREAD_NP_H) */ + +#include <isc/lang.h> +#include <isc/result.h> + +#if defined(HAVE_THREAD_LOCAL) +#include <threads.h> +extern thread_local size_t isc_tid_v; +#elif defined(HAVE___THREAD) +extern __thread size_t isc_tid_v; +#endif /* if defined(HAVE_THREAD_LOCAL) */ + +ISC_LANG_BEGINDECLS + +typedef pthread_t isc_thread_t; +typedef void *isc_threadresult_t; +typedef void *isc_threadarg_t; +typedef isc_threadresult_t (*isc_threadfunc_t)(isc_threadarg_t); + +void +isc_thread_create(isc_threadfunc_t, isc_threadarg_t, isc_thread_t *); + +void +isc_thread_join(isc_thread_t thread, isc_threadresult_t *result); + +void +isc_thread_yield(void); + +void +isc_thread_setname(isc_thread_t thread, const char *name); + +#define isc_thread_self (uintptr_t) pthread_self + +/*** + *** Thread-Local Storage + ***/ + +#if defined(HAVE_TLS) +#if defined(HAVE_THREAD_LOCAL) +#define ISC_THREAD_LOCAL static thread_local +#elif defined(HAVE___THREAD) +#define ISC_THREAD_LOCAL static __thread +#else /* if defined(HAVE_THREAD_LOCAL) */ +#error "Unknown method for defining a TLS variable!" +#endif /* if defined(HAVE_THREAD_LOCAL) */ +#else /* if defined(HAVE_TLS) */ +#error "Thread-local storage support is required!" +#endif /* if defined(HAVE_TLS) */ + +ISC_LANG_ENDDECLS + +#endif /* ISC_THREAD_H */ diff --git a/lib/isc/pthreads/mutex.c b/lib/isc/pthreads/mutex.c new file mode 100644 index 0000000..aa3dc53 --- /dev/null +++ b/lib/isc/pthreads/mutex.c @@ -0,0 +1,302 @@ +/* + * 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 <stdbool.h> +#include <stdio.h> +#include <sys/time.h> +#include <time.h> + +#include <isc/mutex.h> +#include <isc/once.h> +#include <isc/print.h> +#include <isc/strerr.h> +#include <isc/string.h> +#include <isc/util.h> + +#if ISC_MUTEX_PROFILE + +/*@{*/ +/*% Operations on timevals; adapted from FreeBSD's sys/time.h */ +#define timevalclear(tvp) ((tvp)->tv_sec = (tvp)->tv_usec = 0) +#define timevaladd(vvp, uvp) \ + do { \ + (vvp)->tv_sec += (uvp)->tv_sec; \ + (vvp)->tv_usec += (uvp)->tv_usec; \ + if ((vvp)->tv_usec >= 1000000) { \ + (vvp)->tv_sec++; \ + (vvp)->tv_usec -= 1000000; \ + } \ + } while (0) +#define timevalsub(vvp, uvp) \ + do { \ + (vvp)->tv_sec -= (uvp)->tv_sec; \ + (vvp)->tv_usec -= (uvp)->tv_usec; \ + if ((vvp)->tv_usec < 0) { \ + (vvp)->tv_sec--; \ + (vvp)->tv_usec += 1000000; \ + } \ + } while (0) + +/*@}*/ + +#define ISC_MUTEX_MAX_LOCKERS 32 + +typedef struct { + const char *file; + int line; + unsigned count; + struct timeval locked_total; + struct timeval wait_total; +} isc_mutexlocker_t; + +struct isc_mutexstats { + const char *file; /*%< File mutex was created in. */ + int line; /*%< Line mutex was created on. */ + unsigned count; + struct timeval lock_t; + struct timeval locked_total; + struct timeval wait_total; + isc_mutexlocker_t *cur_locker; + isc_mutexlocker_t lockers[ISC_MUTEX_MAX_LOCKERS]; +}; + +#ifndef ISC_MUTEX_PROFTABLESIZE +#define ISC_MUTEX_PROFTABLESIZE (1024 * 1024) +#endif /* ifndef ISC_MUTEX_PROFTABLESIZE */ +static isc_mutexstats_t stats[ISC_MUTEX_PROFTABLESIZE]; +static int stats_next = 0; +static bool stats_init = false; +static pthread_mutex_t statslock = PTHREAD_MUTEX_INITIALIZER; + +void +isc_mutex_init_profile(isc_mutex_t *mp, const char *file, int line) { + int i, err; + + err = pthread_mutex_init(&mp->mutex, NULL); + if (err != 0) { + strerror_r(err, strbuf, sizeof(strbuf)); + isc_error_fatal(file, line, "pthread_mutex_init failed: %s", + strbuf); + } + + RUNTIME_CHECK(pthread_mutex_lock(&statslock) == 0); + + if (!stats_init) { + stats_init = true; + } + + /* + * If all statistics entries have been used, give up and trigger an + * assertion failure. There would be no other way to deal with this + * because we'd like to keep record of all locks for the purpose of + * debugging and the number of necessary locks is unpredictable. + * If this failure is triggered while debugging, named should be + * rebuilt with an increased ISC_MUTEX_PROFTABLESIZE. + */ + RUNTIME_CHECK(stats_next < ISC_MUTEX_PROFTABLESIZE); + mp->stats = &stats[stats_next++]; + + RUNTIME_CHECK(pthread_mutex_unlock(&statslock) == 0); + + mp->stats->file = file; + mp->stats->line = line; + mp->stats->count = 0; + timevalclear(&mp->stats->locked_total); + timevalclear(&mp->stats->wait_total); + for (i = 0; i < ISC_MUTEX_MAX_LOCKERS; i++) { + mp->stats->lockers[i].file = NULL; + mp->stats->lockers[i].line = 0; + mp->stats->lockers[i].count = 0; + timevalclear(&mp->stats->lockers[i].locked_total); + timevalclear(&mp->stats->lockers[i].wait_total); + } +} + +isc_result_t +isc_mutex_lock_profile(isc_mutex_t *mp, const char *file, int line) { + struct timeval prelock_t; + struct timeval postlock_t; + isc_mutexlocker_t *locker = NULL; + int i; + + gettimeofday(&prelock_t, NULL); + + if (pthread_mutex_lock(&mp->mutex) != 0) { + return (ISC_R_UNEXPECTED); + } + + gettimeofday(&postlock_t, NULL); + mp->stats->lock_t = postlock_t; + + timevalsub(&postlock_t, &prelock_t); + + mp->stats->count++; + timevaladd(&mp->stats->wait_total, &postlock_t); + + for (i = 0; i < ISC_MUTEX_MAX_LOCKERS; i++) { + if (mp->stats->lockers[i].file == NULL) { + locker = &mp->stats->lockers[i]; + locker->file = file; + locker->line = line; + break; + } else if (mp->stats->lockers[i].file == file && + mp->stats->lockers[i].line == line) + { + locker = &mp->stats->lockers[i]; + break; + } + } + + if (locker != NULL) { + locker->count++; + timevaladd(&locker->wait_total, &postlock_t); + } + + mp->stats->cur_locker = locker; + + return (ISC_R_SUCCESS); +} + +isc_result_t +isc_mutex_unlock_profile(isc_mutex_t *mp, const char *file, int line) { + struct timeval unlock_t; + + UNUSED(file); + UNUSED(line); + + if (mp->stats->cur_locker != NULL) { + gettimeofday(&unlock_t, NULL); + timevalsub(&unlock_t, &mp->stats->lock_t); + timevaladd(&mp->stats->locked_total, &unlock_t); + timevaladd(&mp->stats->cur_locker->locked_total, &unlock_t); + mp->stats->cur_locker = NULL; + } + + return ((pthread_mutex_unlock((&mp->mutex)) == 0) ? ISC_R_SUCCESS + : ISC_R_UNEXPECTED); +} + +void +isc_mutex_statsprofile(FILE *fp) { + isc_mutexlocker_t *locker; + int i, j; + + fprintf(fp, "Mutex stats (in us)\n"); + for (i = 0; i < stats_next; i++) { + fprintf(fp, "%-12s %4d: %10u %lu.%06lu %lu.%06lu %5d\n", + stats[i].file, stats[i].line, stats[i].count, + stats[i].locked_total.tv_sec, + stats[i].locked_total.tv_usec, + stats[i].wait_total.tv_sec, stats[i].wait_total.tv_usec, + i); + for (j = 0; j < ISC_MUTEX_MAX_LOCKERS; j++) { + locker = &stats[i].lockers[j]; + if (locker->file == NULL) { + continue; + } + fprintf(fp, + " %-11s %4d: %10u %lu.%06lu %lu.%06lu %5d\n", + locker->file, locker->line, locker->count, + locker->locked_total.tv_sec, + locker->locked_total.tv_usec, + locker->wait_total.tv_sec, + locker->wait_total.tv_usec, i); + } + } +} + +#endif /* ISC_MUTEX_PROFILE */ + +#if ISC_MUTEX_DEBUG && defined(PTHREAD_MUTEX_ERRORCHECK) + +static bool errcheck_initialized = false; +static pthread_mutexattr_t errcheck; +static isc_once_t once_errcheck = ISC_ONCE_INIT; + +static void +initialize_errcheck(void) { + RUNTIME_CHECK(pthread_mutexattr_init(&errcheck) == 0); + RUNTIME_CHECK(pthread_mutexattr_settype(&errcheck, + PTHREAD_MUTEX_ERRORCHECK) == 0); + errcheck_initialized = true; +} + +void +isc_mutex_init_errcheck(isc_mutex_t *mp) { + isc_result_t result; + int err; + + result = isc_once_do(&once_errcheck, initialize_errcheck); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + + err = pthread_mutex_init(mp, &errcheck); + if (err != 0) { + strerror_r(err, strbuf, sizeof(strbuf)); + isc_error_fatal(file, line, "pthread_mutex_init failed: %s", + strbuf); + } +} +#endif /* if ISC_MUTEX_DEBUG && defined(PTHREAD_MUTEX_ERRORCHECK) */ + +#if ISC_MUTEX_DEBUG && defined(__NetBSD__) && defined(PTHREAD_MUTEX_ERRORCHECK) +pthread_mutexattr_t isc__mutex_attrs = { + PTHREAD_MUTEX_ERRORCHECK, /* m_type */ + 0 /* m_flags, which appears to be unused. */ +}; +#endif /* if ISC_MUTEX_DEBUG && defined(__NetBSD__) && \ + * defined(PTHREAD_MUTEX_ERRORCHECK) */ + +#if !(ISC_MUTEX_DEBUG && defined(PTHREAD_MUTEX_ERRORCHECK)) && \ + !ISC_MUTEX_PROFILE + +#ifdef HAVE_PTHREAD_MUTEX_ADAPTIVE_NP +static bool attr_initialized = false; +static pthread_mutexattr_t attr; +static isc_once_t once_attr = ISC_ONCE_INIT; +#endif /* HAVE_PTHREAD_MUTEX_ADAPTIVE_NP */ + +#ifdef HAVE_PTHREAD_MUTEX_ADAPTIVE_NP +static void +initialize_attr(void) { + RUNTIME_CHECK(pthread_mutexattr_init(&attr) == 0); + RUNTIME_CHECK(pthread_mutexattr_settype( + &attr, PTHREAD_MUTEX_ADAPTIVE_NP) == 0); + attr_initialized = true; +} +#endif /* HAVE_PTHREAD_MUTEX_ADAPTIVE_NP */ + +void +isc__mutex_init(isc_mutex_t *mp, const char *file, unsigned int line) { + int err; + +#ifdef HAVE_PTHREAD_MUTEX_ADAPTIVE_NP + isc_result_t result = ISC_R_SUCCESS; + result = isc_once_do(&once_attr, initialize_attr); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + + err = pthread_mutex_init(mp, &attr); +#else /* HAVE_PTHREAD_MUTEX_ADAPTIVE_NP */ + err = pthread_mutex_init(mp, ISC__MUTEX_ATTRS); +#endif /* HAVE_PTHREAD_MUTEX_ADAPTIVE_NP */ + if (err != 0) { + char strbuf[ISC_STRERRORSIZE]; + strerror_r(err, strbuf, sizeof(strbuf)); + isc_error_fatal(file, line, "pthread_mutex_init failed: %s", + strbuf); + } +} +#endif /* if !(ISC_MUTEX_DEBUG && defined(PTHREAD_MUTEX_ERRORCHECK)) && \ + * !ISC_MUTEX_PROFILE */ diff --git a/lib/isc/pthreads/thread.c b/lib/isc/pthreads/thread.c new file mode 100644 index 0000000..4c7380c --- /dev/null +++ b/lib/isc/pthreads/thread.c @@ -0,0 +1,129 @@ +/* + * 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 */ + +#if defined(HAVE_SCHED_H) +#include <sched.h> +#endif /* if defined(HAVE_SCHED_H) */ + +#if defined(HAVE_CPUSET_H) +#include <sys/cpuset.h> +#include <sys/param.h> +#endif /* if defined(HAVE_CPUSET_H) */ + +#if defined(HAVE_SYS_PROCSET_H) +#include <sys/processor.h> +#include <sys/procset.h> +#include <sys/types.h> +#endif /* if defined(HAVE_SYS_PROCSET_H) */ + +#include <isc/strerr.h> +#include <isc/thread.h> +#include <isc/util.h> + +#include "trampoline_p.h" + +#ifndef THREAD_MINSTACKSIZE +#define THREAD_MINSTACKSIZE (1024U * 1024) +#endif /* ifndef THREAD_MINSTACKSIZE */ + +#define _FATAL(r, f) \ + { \ + char strbuf[ISC_STRERRORSIZE]; \ + strerror_r(r, strbuf, sizeof(strbuf)); \ + isc_error_fatal(__FILE__, __LINE__, f " failed: %s", strbuf); \ + } + +void +isc_thread_create(isc_threadfunc_t func, isc_threadarg_t arg, + isc_thread_t *thread) { + pthread_attr_t attr; + isc__trampoline_t *trampoline_arg; + + trampoline_arg = isc__trampoline_get(func, arg); + +#if defined(HAVE_PTHREAD_ATTR_GETSTACKSIZE) && \ + defined(HAVE_PTHREAD_ATTR_SETSTACKSIZE) + size_t stacksize; +#endif /* if defined(HAVE_PTHREAD_ATTR_GETSTACKSIZE) && \ + * defined(HAVE_PTHREAD_ATTR_SETSTACKSIZE) */ + int ret; + + pthread_attr_init(&attr); + +#if defined(HAVE_PTHREAD_ATTR_GETSTACKSIZE) && \ + defined(HAVE_PTHREAD_ATTR_SETSTACKSIZE) + ret = pthread_attr_getstacksize(&attr, &stacksize); + if (ret != 0) { + _FATAL(ret, "pthread_attr_getstacksize()"); + } + + if (stacksize < THREAD_MINSTACKSIZE) { + ret = pthread_attr_setstacksize(&attr, THREAD_MINSTACKSIZE); + if (ret != 0) { + _FATAL(ret, "pthread_attr_setstacksize()"); + } + } +#endif /* if defined(HAVE_PTHREAD_ATTR_GETSTACKSIZE) && \ + * defined(HAVE_PTHREAD_ATTR_SETSTACKSIZE) */ + + ret = pthread_create(thread, &attr, isc__trampoline_run, + trampoline_arg); + if (ret != 0) { + _FATAL(ret, "pthread_create()"); + } + + pthread_attr_destroy(&attr); + + return; +} + +void +isc_thread_join(isc_thread_t thread, isc_threadresult_t *result) { + int ret = pthread_join(thread, result); + if (ret != 0) { + _FATAL(ret, "pthread_join()"); + } +} + +void +isc_thread_setname(isc_thread_t thread, const char *name) { +#if defined(HAVE_PTHREAD_SETNAME_NP) && !defined(__APPLE__) + /* + * macOS has pthread_setname_np but only works on the + * current thread so it's not used here + */ +#if defined(__NetBSD__) + (void)pthread_setname_np(thread, name, NULL); +#else /* if defined(__NetBSD__) */ + (void)pthread_setname_np(thread, name); +#endif /* if defined(__NetBSD__) */ +#elif defined(HAVE_PTHREAD_SET_NAME_NP) + (void)pthread_set_name_np(thread, name); +#else /* if defined(HAVE_PTHREAD_SETNAME_NP) && !defined(__APPLE__) */ + UNUSED(thread); + UNUSED(name); +#endif /* if defined(HAVE_PTHREAD_SETNAME_NP) && !defined(__APPLE__) */ +} + +void +isc_thread_yield(void) { +#if defined(HAVE_SCHED_YIELD) + sched_yield(); +#elif defined(HAVE_PTHREAD_YIELD) + pthread_yield(); +#elif defined(HAVE_PTHREAD_YIELD_NP) + pthread_yield_np(); +#endif /* if defined(HAVE_SCHED_YIELD) */ +} |