1 /* $NetBSD: mutex.c,v 1.1.1.1 2009/12/13 16:54:32 kardel Exp $ */ 2 3 /* 4 * Copyright (C) 2004, 2005, 2007, 2008 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.16 2008/04/04 23:47:01 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 ISC_MUTEX_PROFILE 36 37 /*@{*/ 38 /*% Operations on timevals; adapted from FreeBSD's sys/time.h */ 39 #define timevalclear(tvp) ((tvp)->tv_sec = (tvp)->tv_usec = 0) 40 #define timevaladd(vvp, uvp) \ 41 do { \ 42 (vvp)->tv_sec += (uvp)->tv_sec; \ 43 (vvp)->tv_usec += (uvp)->tv_usec; \ 44 if ((vvp)->tv_usec >= 1000000) { \ 45 (vvp)->tv_sec++; \ 46 (vvp)->tv_usec -= 1000000; \ 47 } \ 48 } while (0) 49 #define timevalsub(vvp, uvp) \ 50 do { \ 51 (vvp)->tv_sec -= (uvp)->tv_sec; \ 52 (vvp)->tv_usec -= (uvp)->tv_usec; \ 53 if ((vvp)->tv_usec < 0) { \ 54 (vvp)->tv_sec--; \ 55 (vvp)->tv_usec += 1000000; \ 56 } \ 57 } while (0) 58 59 /*@}*/ 60 61 #define ISC_MUTEX_MAX_LOCKERS 32 62 63 typedef struct { 64 const char * file; 65 int line; 66 unsigned count; 67 struct timeval locked_total; 68 struct timeval wait_total; 69 } isc_mutexlocker_t; 70 71 struct isc_mutexstats { 72 const char * file; /*%< File mutex was created in. */ 73 int line; /*%< Line mutex was created on. */ 74 unsigned count; 75 struct timeval lock_t; 76 struct timeval locked_total; 77 struct timeval wait_total; 78 isc_mutexlocker_t * cur_locker; 79 isc_mutexlocker_t lockers[ISC_MUTEX_MAX_LOCKERS]; 80 }; 81 82 #ifndef ISC_MUTEX_PROFTABLESIZE 83 #define ISC_MUTEX_PROFTABLESIZE (16 * 1024) 84 #endif 85 static isc_mutexstats_t stats[ISC_MUTEX_PROFTABLESIZE]; 86 static int stats_next = 0; 87 static isc_boolean_t stats_init = ISC_FALSE; 88 static pthread_mutex_t statslock = PTHREAD_MUTEX_INITIALIZER; 89 90 91 isc_result_t 92 isc_mutex_init_profile(isc_mutex_t *mp, const char *file, int line) { 93 int i, err; 94 95 err = pthread_mutex_init(&mp->mutex, NULL); 96 if (err == ENOMEM) 97 return (ISC_R_NOMEMORY); 98 if (err != 0) 99 return (ISC_R_UNEXPECTED); 100 101 RUNTIME_CHECK(pthread_mutex_lock(&statslock) == 0); 102 103 if (stats_init == ISC_FALSE) 104 stats_init = ISC_TRUE; 105 106 /* 107 * If all statistics entries have been used, give up and trigger an 108 * assertion failure. There would be no other way to deal with this 109 * because we'd like to keep record of all locks for the purpose of 110 * debugging and the number of necessary locks is unpredictable. 111 * If this failure is triggered while debugging, named should be 112 * rebuilt with an increased ISC_MUTEX_PROFTABLESIZE. 113 */ 114 RUNTIME_CHECK(stats_next < ISC_MUTEX_PROFTABLESIZE); 115 mp->stats = &stats[stats_next++]; 116 117 RUNTIME_CHECK(pthread_mutex_unlock(&statslock) == 0); 118 119 mp->stats->file = file; 120 mp->stats->line = line; 121 mp->stats->count = 0; 122 timevalclear(&mp->stats->locked_total); 123 timevalclear(&mp->stats->wait_total); 124 for (i = 0; i < ISC_MUTEX_MAX_LOCKERS; i++) { 125 mp->stats->lockers[i].file = NULL; 126 mp->stats->lockers[i].line = 0; 127 mp->stats->lockers[i].count = 0; 128 timevalclear(&mp->stats->lockers[i].locked_total); 129 timevalclear(&mp->stats->lockers[i].wait_total); 130 } 131 132 return (ISC_R_SUCCESS); 133 } 134 135 isc_result_t 136 isc_mutex_lock_profile(isc_mutex_t *mp, const char *file, int line) { 137 struct timeval prelock_t; 138 struct timeval postlock_t; 139 isc_mutexlocker_t *locker = NULL; 140 int i; 141 142 gettimeofday(&prelock_t, NULL); 143 144 if (pthread_mutex_lock(&mp->mutex) != 0) 145 return (ISC_R_UNEXPECTED); 146 147 gettimeofday(&postlock_t, NULL); 148 mp->stats->lock_t = postlock_t; 149 150 timevalsub(&postlock_t, &prelock_t); 151 152 mp->stats->count++; 153 timevaladd(&mp->stats->wait_total, &postlock_t); 154 155 for (i = 0; i < ISC_MUTEX_MAX_LOCKERS; i++) { 156 if (mp->stats->lockers[i].file == NULL) { 157 locker = &mp->stats->lockers[i]; 158 locker->file = file; 159 locker->line = line; 160 break; 161 } else if (mp->stats->lockers[i].file == file && 162 mp->stats->lockers[i].line == line) { 163 locker = &mp->stats->lockers[i]; 164 break; 165 } 166 } 167 168 if (locker != NULL) { 169 locker->count++; 170 timevaladd(&locker->wait_total, &postlock_t); 171 } 172 173 mp->stats->cur_locker = locker; 174 175 return (ISC_R_SUCCESS); 176 } 177 178 isc_result_t 179 isc_mutex_unlock_profile(isc_mutex_t *mp, const char *file, int line) { 180 struct timeval unlock_t; 181 182 UNUSED(file); 183 UNUSED(line); 184 185 if (mp->stats->cur_locker != NULL) { 186 gettimeofday(&unlock_t, NULL); 187 timevalsub(&unlock_t, &mp->stats->lock_t); 188 timevaladd(&mp->stats->locked_total, &unlock_t); 189 timevaladd(&mp->stats->cur_locker->locked_total, &unlock_t); 190 mp->stats->cur_locker = NULL; 191 } 192 193 return ((pthread_mutex_unlock((&mp->mutex)) == 0) ? \ 194 ISC_R_SUCCESS : ISC_R_UNEXPECTED); 195 } 196 197 198 void 199 isc_mutex_statsprofile(FILE *fp) { 200 isc_mutexlocker_t *locker; 201 int i, j; 202 203 fprintf(fp, "Mutex stats (in us)\n"); 204 for (i = 0; i < stats_next; i++) { 205 fprintf(fp, "%-12s %4d: %10u %lu.%06lu %lu.%06lu\n", 206 stats[i].file, stats[i].line, stats[i].count, 207 stats[i].locked_total.tv_sec, 208 stats[i].locked_total.tv_usec, 209 stats[i].wait_total.tv_sec, 210 stats[i].wait_total.tv_usec 211 ); 212 for (j = 0; j < ISC_MUTEX_MAX_LOCKERS; j++) { 213 locker = &stats[i].lockers[j]; 214 if (locker->file == NULL) 215 continue; 216 fprintf(fp, " %-11s %4d: %10u %lu.%06lu %lu.%06lu\n", 217 locker->file, locker->line, locker->count, 218 locker->locked_total.tv_sec, 219 locker->locked_total.tv_usec, 220 locker->wait_total.tv_sec, 221 locker->wait_total.tv_usec 222 ); 223 } 224 } 225 } 226 227 #endif /* ISC_MUTEX_PROFILE */ 228 229 #if ISC_MUTEX_DEBUG && defined(PTHREAD_MUTEX_ERRORCHECK) 230 isc_result_t 231 isc_mutex_init_errcheck(isc_mutex_t *mp) 232 { 233 pthread_mutexattr_t attr; 234 int err; 235 236 if (pthread_mutexattr_init(&attr) != 0) 237 return (ISC_R_UNEXPECTED); 238 239 if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK) != 0) 240 return (ISC_R_UNEXPECTED); 241 242 err = pthread_mutex_init(mp, &attr) != 0) 243 if (err == ENOMEM) 244 return (ISC_R_NOMEMORY); 245 return ((err == 0) ? ISC_R_SUCCESS : ISC_R_UNEXPECTED); 246 } 247 #endif 248 249 #if ISC_MUTEX_DEBUG && defined(__NetBSD__) && defined(PTHREAD_MUTEX_ERRORCHECK) 250 pthread_mutexattr_t isc__mutex_attrs = { 251 PTHREAD_MUTEX_ERRORCHECK, /* m_type */ 252 0 /* m_flags, which appears to be unused. */ 253 }; 254 #endif 255 256 #if !(ISC_MUTEX_DEBUG && defined(PTHREAD_MUTEX_ERRORCHECK)) && !ISC_MUTEX_PROFILE 257 isc_result_t 258 isc__mutex_init(isc_mutex_t *mp, const char *file, unsigned int line) { 259 char strbuf[ISC_STRERRORSIZE]; 260 isc_result_t result = ISC_R_SUCCESS; 261 int err; 262 263 err = pthread_mutex_init(mp, ISC__MUTEX_ATTRS); 264 if (err == ENOMEM) 265 return (ISC_R_NOMEMORY); 266 if (err != 0) { 267 isc__strerror(errno, strbuf, sizeof(strbuf)); 268 UNEXPECTED_ERROR(file, line, "isc_mutex_init() failed: %s", 269 strbuf); 270 result = ISC_R_UNEXPECTED; 271 } 272 return (result); 273 } 274 #endif 275