1/* $NetBSD: mutex.c,v 1.6 2020/05/25 20:47:22 christos Exp $ */ 2 3/* 4 * Copyright (C) 2004, 2005, 2007, 2008, 2011, 2012 Internet Systems Consortium, Inc. ("ISC") 5 * Copyright (C) 2000-2002 Internet Software Consortium. 6 * 7 * Permission to use, copy, modify, and/or distribute this software for any 8 * purpose with or without fee is hereby granted, provided that the above 9 * copyright notice and this permission notice appear in all copies. 10 * 11 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH 12 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 13 * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, 14 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 15 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 16 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 17 * PERFORMANCE OF THIS SOFTWARE. 18 */ 19 20/* Id: mutex.c,v 1.18 2011/01/04 23:47:14 tbox Exp */ 21 22/*! \file */ 23 24#include <config.h> 25 26#include <stdio.h> 27#include <time.h> 28#include <sys/time.h> 29#include <errno.h> 30 31#include <isc/mutex.h> 32#include <isc/util.h> 33#include <isc/strerror.h> 34 35#if HAVE_PTHREADS < 5 /* HP-UX 10.20 has 4, needs this */ 36# define pthread_mutex_init(m, a) \ 37 pthread_mutex_init(m, (a) \ 38 ? *(const pthread_mutexattr_t *)(a) \ 39 : pthread_mutexattr_default) 40# define PTHREAD_MUTEX_RECURSIVE MUTEX_RECURSIVE_NP 41# define pthread_mutexattr_settype pthread_mutexattr_setkind_np 42#endif 43 44#if ISC_MUTEX_PROFILE 45 46/*@{*/ 47/*% Operations on timevals; adapted from FreeBSD's sys/time.h */ 48#define timevalclear(tvp) ((tvp)->tv_sec = (tvp)->tv_usec = 0) 49#define timevaladd(vvp, uvp) \ 50 do { \ 51 (vvp)->tv_sec += (uvp)->tv_sec; \ 52 (vvp)->tv_usec += (uvp)->tv_usec; \ 53 if ((vvp)->tv_usec >= 1000000) { \ 54 (vvp)->tv_sec++; \ 55 (vvp)->tv_usec -= 1000000; \ 56 } \ 57 } while (0) 58#define timevalsub(vvp, uvp) \ 59 do { \ 60 (vvp)->tv_sec -= (uvp)->tv_sec; \ 61 (vvp)->tv_usec -= (uvp)->tv_usec; \ 62 if ((vvp)->tv_usec < 0) { \ 63 (vvp)->tv_sec--; \ 64 (vvp)->tv_usec += 1000000; \ 65 } \ 66 } while (0) 67 68/*@}*/ 69 70#define ISC_MUTEX_MAX_LOCKERS 32 71 72typedef struct { 73 const char * file; 74 int line; 75 unsigned count; 76 struct timeval locked_total; 77 struct timeval wait_total; 78} isc_mutexlocker_t; 79 80struct isc_mutexstats { 81 const char * file; /*%< File mutex was created in. */ 82 int line; /*%< Line mutex was created on. */ 83 unsigned count; 84 struct timeval lock_t; 85 struct timeval locked_total; 86 struct timeval wait_total; 87 isc_mutexlocker_t * cur_locker; 88 isc_mutexlocker_t lockers[ISC_MUTEX_MAX_LOCKERS]; 89}; 90 91#ifndef ISC_MUTEX_PROFTABLESIZE 92#define ISC_MUTEX_PROFTABLESIZE (1024 * 1024) 93#endif 94static isc_mutexstats_t stats[ISC_MUTEX_PROFTABLESIZE]; 95static int stats_next = 0; 96static isc_boolean_t stats_init = ISC_FALSE; 97static pthread_mutex_t statslock = PTHREAD_MUTEX_INITIALIZER; 98 99 100isc_result_t 101isc_mutex_init_profile(isc_mutex_t *mp, const char *file, int line) { 102 int i, err; 103 104 err = pthread_mutex_init(&mp->mutex, NULL); 105 if (err == ENOMEM) 106 return (ISC_R_NOMEMORY); 107 if (err != 0) 108 return (ISC_R_UNEXPECTED); 109 110 RUNTIME_CHECK(pthread_mutex_lock(&statslock) == 0); 111 112 if (stats_init == ISC_FALSE) 113 stats_init = ISC_TRUE; 114 115 /* 116 * If all statistics entries have been used, give up and trigger an 117 * assertion failure. There would be no other way to deal with this 118 * because we'd like to keep record of all locks for the purpose of 119 * debugging and the number of necessary locks is unpredictable. 120 * If this failure is triggered while debugging, named should be 121 * rebuilt with an increased ISC_MUTEX_PROFTABLESIZE. 122 */ 123 RUNTIME_CHECK(stats_next < ISC_MUTEX_PROFTABLESIZE); 124 mp->stats = &stats[stats_next++]; 125 126 RUNTIME_CHECK(pthread_mutex_unlock(&statslock) == 0); 127 128 mp->stats->file = file; 129 mp->stats->line = line; 130 mp->stats->count = 0; 131 timevalclear(&mp->stats->locked_total); 132 timevalclear(&mp->stats->wait_total); 133 for (i = 0; i < ISC_MUTEX_MAX_LOCKERS; i++) { 134 mp->stats->lockers[i].file = NULL; 135 mp->stats->lockers[i].line = 0; 136 mp->stats->lockers[i].count = 0; 137 timevalclear(&mp->stats->lockers[i].locked_total); 138 timevalclear(&mp->stats->lockers[i].wait_total); 139 } 140 141 return (ISC_R_SUCCESS); 142} 143 144isc_result_t 145isc_mutex_lock_profile(isc_mutex_t *mp, const char *file, int line) { 146 struct timeval prelock_t; 147 struct timeval postlock_t; 148 isc_mutexlocker_t *locker = NULL; 149 int i; 150 151 gettimeofday(&prelock_t, NULL); 152 153 if (pthread_mutex_lock(&mp->mutex) != 0) 154 return (ISC_R_UNEXPECTED); 155 156 gettimeofday(&postlock_t, NULL); 157 mp->stats->lock_t = postlock_t; 158 159 timevalsub(&postlock_t, &prelock_t); 160 161 mp->stats->count++; 162 timevaladd(&mp->stats->wait_total, &postlock_t); 163 164 for (i = 0; i < ISC_MUTEX_MAX_LOCKERS; i++) { 165 if (mp->stats->lockers[i].file == NULL) { 166 locker = &mp->stats->lockers[i]; 167 locker->file = file; 168 locker->line = line; 169 break; 170 } else if (mp->stats->lockers[i].file == file && 171 mp->stats->lockers[i].line == line) { 172 locker = &mp->stats->lockers[i]; 173 break; 174 } 175 } 176 177 if (locker != NULL) { 178 locker->count++; 179 timevaladd(&locker->wait_total, &postlock_t); 180 } 181 182 mp->stats->cur_locker = locker; 183 184 return (ISC_R_SUCCESS); 185} 186 187isc_result_t 188isc_mutex_unlock_profile(isc_mutex_t *mp, const char *file, int line) { 189 struct timeval unlock_t; 190 191 UNUSED(file); 192 UNUSED(line); 193 194 if (mp->stats->cur_locker != NULL) { 195 gettimeofday(&unlock_t, NULL); 196 timevalsub(&unlock_t, &mp->stats->lock_t); 197 timevaladd(&mp->stats->locked_total, &unlock_t); 198 timevaladd(&mp->stats->cur_locker->locked_total, &unlock_t); 199 mp->stats->cur_locker = NULL; 200 } 201 202 return ((pthread_mutex_unlock((&mp->mutex)) == 0) ? \ 203 ISC_R_SUCCESS : ISC_R_UNEXPECTED); 204} 205 206 207void 208isc_mutex_statsprofile(FILE *fp) { 209 isc_mutexlocker_t *locker; 210 int i, j; 211 212 fprintf(fp, "Mutex stats (in us)\n"); 213 for (i = 0; i < stats_next; i++) { 214 fprintf(fp, "%-12s %4d: %10u %lu.%06lu %lu.%06lu %5d\n", 215 stats[i].file, stats[i].line, stats[i].count, 216 stats[i].locked_total.tv_sec, 217 stats[i].locked_total.tv_usec, 218 stats[i].wait_total.tv_sec, 219 stats[i].wait_total.tv_usec, 220 i); 221 for (j = 0; j < ISC_MUTEX_MAX_LOCKERS; j++) { 222 locker = &stats[i].lockers[j]; 223 if (locker->file == NULL) 224 continue; 225 fprintf(fp, " %-11s %4d: %10u %lu.%06lu %lu.%06lu %5d\n", 226 locker->file, locker->line, locker->count, 227 locker->locked_total.tv_sec, 228 locker->locked_total.tv_usec, 229 locker->wait_total.tv_sec, 230 locker->wait_total.tv_usec, 231 i); 232 } 233 } 234} 235 236#endif /* ISC_MUTEX_PROFILE */ 237 238#if ISC_MUTEX_DEBUG && defined(PTHREAD_MUTEX_ERRORCHECK) 239isc_result_t 240isc_mutex_init_errcheck(isc_mutex_t *mp) 241{ 242 pthread_mutexattr_t attr; 243 int err; 244 245 if (pthread_mutexattr_init(&attr) != 0) 246 return (ISC_R_UNEXPECTED); 247 248 if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK) != 0) { 249 pthread_mutexattr_destroy(&attr); 250 return (ISC_R_UNEXPECTED); 251 } 252 253 err = pthread_mutex_init(mp, &attr) != 0) 254 pthread_mutexattr_destroy(&attr); 255 if (err == ENOMEM) 256 return (ISC_R_NOMEMORY); 257 return ((err == 0) ? ISC_R_SUCCESS : ISC_R_UNEXPECTED); 258} 259#endif 260 261#if ISC_MUTEX_DEBUG && defined(__NetBSD__) && defined(PTHREAD_MUTEX_ERRORCHECK) 262pthread_mutexattr_t isc__mutex_attrs = { 263 PTHREAD_MUTEX_ERRORCHECK, /* m_type */ 264 0 /* m_flags, which appears to be unused. */ 265}; 266#endif 267 268#if !(ISC_MUTEX_DEBUG && defined(PTHREAD_MUTEX_ERRORCHECK)) && !ISC_MUTEX_PROFILE 269isc_result_t 270isc__mutex_init(isc_mutex_t *mp, const char *file, unsigned int line) { 271 char strbuf[ISC_STRERRORSIZE]; 272 isc_result_t result = ISC_R_SUCCESS; 273 int err; 274 275 err = pthread_mutex_init(mp, ISC__MUTEX_ATTRS); 276 if (err == ENOMEM) 277 return (ISC_R_NOMEMORY); 278 if (err != 0) { 279 isc__strerror(errno, strbuf, sizeof(strbuf)); 280 UNEXPECTED_ERROR(file, line, "isc_mutex_init() failed: %s", 281 strbuf); 282 result = ISC_R_UNEXPECTED; 283 } 284 return (result); 285} 286#endif 287