1258945Sroberto/* 2280849Scy * Copyright (C) 2004, 2005, 2007, 2008, 2011, 2012 Internet Systems Consortium, Inc. ("ISC") 3258945Sroberto * Copyright (C) 2000-2002 Internet Software Consortium. 4258945Sroberto * 5258945Sroberto * Permission to use, copy, modify, and/or distribute this software for any 6258945Sroberto * purpose with or without fee is hereby granted, provided that the above 7258945Sroberto * copyright notice and this permission notice appear in all copies. 8258945Sroberto * 9258945Sroberto * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH 10258945Sroberto * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 11258945Sroberto * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, 12258945Sroberto * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 13258945Sroberto * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 14258945Sroberto * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 15258945Sroberto * PERFORMANCE OF THIS SOFTWARE. 16258945Sroberto */ 17258945Sroberto 18280849Scy/* $Id: mutex.c,v 1.18 2011/01/04 23:47:14 tbox Exp $ */ 19258945Sroberto 20258945Sroberto/*! \file */ 21258945Sroberto 22258945Sroberto#include <config.h> 23258945Sroberto 24258945Sroberto#include <stdio.h> 25258945Sroberto#include <time.h> 26258945Sroberto#include <sys/time.h> 27258945Sroberto#include <errno.h> 28258945Sroberto 29258945Sroberto#include <isc/mutex.h> 30258945Sroberto#include <isc/util.h> 31258945Sroberto#include <isc/strerror.h> 32258945Sroberto 33280849Scy#if HAVE_PTHREADS < 5 /* HP-UX 10.20 has 4, needs this */ 34280849Scy# define pthread_mutex_init(m, a) \ 35280849Scy pthread_mutex_init(m, (a) \ 36280849Scy ? *(const pthread_mutexattr_t *)(a) \ 37280849Scy : pthread_mutexattr_default) 38280849Scy# define PTHREAD_MUTEX_RECURSIVE MUTEX_RECURSIVE_NP 39280849Scy# define pthread_mutexattr_settype pthread_mutexattr_setkind_np 40280849Scy#endif 41280849Scy 42258945Sroberto#if ISC_MUTEX_PROFILE 43258945Sroberto 44258945Sroberto/*@{*/ 45258945Sroberto/*% Operations on timevals; adapted from FreeBSD's sys/time.h */ 46258945Sroberto#define timevalclear(tvp) ((tvp)->tv_sec = (tvp)->tv_usec = 0) 47258945Sroberto#define timevaladd(vvp, uvp) \ 48258945Sroberto do { \ 49258945Sroberto (vvp)->tv_sec += (uvp)->tv_sec; \ 50258945Sroberto (vvp)->tv_usec += (uvp)->tv_usec; \ 51258945Sroberto if ((vvp)->tv_usec >= 1000000) { \ 52258945Sroberto (vvp)->tv_sec++; \ 53258945Sroberto (vvp)->tv_usec -= 1000000; \ 54258945Sroberto } \ 55258945Sroberto } while (0) 56258945Sroberto#define timevalsub(vvp, uvp) \ 57258945Sroberto do { \ 58258945Sroberto (vvp)->tv_sec -= (uvp)->tv_sec; \ 59258945Sroberto (vvp)->tv_usec -= (uvp)->tv_usec; \ 60258945Sroberto if ((vvp)->tv_usec < 0) { \ 61258945Sroberto (vvp)->tv_sec--; \ 62258945Sroberto (vvp)->tv_usec += 1000000; \ 63258945Sroberto } \ 64258945Sroberto } while (0) 65258945Sroberto 66258945Sroberto/*@}*/ 67258945Sroberto 68258945Sroberto#define ISC_MUTEX_MAX_LOCKERS 32 69258945Sroberto 70258945Srobertotypedef struct { 71258945Sroberto const char * file; 72258945Sroberto int line; 73258945Sroberto unsigned count; 74258945Sroberto struct timeval locked_total; 75258945Sroberto struct timeval wait_total; 76258945Sroberto} isc_mutexlocker_t; 77258945Sroberto 78258945Srobertostruct isc_mutexstats { 79258945Sroberto const char * file; /*%< File mutex was created in. */ 80258945Sroberto int line; /*%< Line mutex was created on. */ 81258945Sroberto unsigned count; 82258945Sroberto struct timeval lock_t; 83258945Sroberto struct timeval locked_total; 84258945Sroberto struct timeval wait_total; 85258945Sroberto isc_mutexlocker_t * cur_locker; 86258945Sroberto isc_mutexlocker_t lockers[ISC_MUTEX_MAX_LOCKERS]; 87258945Sroberto}; 88258945Sroberto 89258945Sroberto#ifndef ISC_MUTEX_PROFTABLESIZE 90280849Scy#define ISC_MUTEX_PROFTABLESIZE (1024 * 1024) 91258945Sroberto#endif 92258945Srobertostatic isc_mutexstats_t stats[ISC_MUTEX_PROFTABLESIZE]; 93258945Srobertostatic int stats_next = 0; 94258945Srobertostatic isc_boolean_t stats_init = ISC_FALSE; 95258945Srobertostatic pthread_mutex_t statslock = PTHREAD_MUTEX_INITIALIZER; 96258945Sroberto 97258945Sroberto 98258945Srobertoisc_result_t 99258945Srobertoisc_mutex_init_profile(isc_mutex_t *mp, const char *file, int line) { 100258945Sroberto int i, err; 101258945Sroberto 102258945Sroberto err = pthread_mutex_init(&mp->mutex, NULL); 103258945Sroberto if (err == ENOMEM) 104258945Sroberto return (ISC_R_NOMEMORY); 105258945Sroberto if (err != 0) 106258945Sroberto return (ISC_R_UNEXPECTED); 107258945Sroberto 108258945Sroberto RUNTIME_CHECK(pthread_mutex_lock(&statslock) == 0); 109258945Sroberto 110258945Sroberto if (stats_init == ISC_FALSE) 111258945Sroberto stats_init = ISC_TRUE; 112258945Sroberto 113258945Sroberto /* 114258945Sroberto * If all statistics entries have been used, give up and trigger an 115258945Sroberto * assertion failure. There would be no other way to deal with this 116258945Sroberto * because we'd like to keep record of all locks for the purpose of 117258945Sroberto * debugging and the number of necessary locks is unpredictable. 118258945Sroberto * If this failure is triggered while debugging, named should be 119258945Sroberto * rebuilt with an increased ISC_MUTEX_PROFTABLESIZE. 120258945Sroberto */ 121258945Sroberto RUNTIME_CHECK(stats_next < ISC_MUTEX_PROFTABLESIZE); 122258945Sroberto mp->stats = &stats[stats_next++]; 123258945Sroberto 124258945Sroberto RUNTIME_CHECK(pthread_mutex_unlock(&statslock) == 0); 125258945Sroberto 126258945Sroberto mp->stats->file = file; 127258945Sroberto mp->stats->line = line; 128258945Sroberto mp->stats->count = 0; 129258945Sroberto timevalclear(&mp->stats->locked_total); 130258945Sroberto timevalclear(&mp->stats->wait_total); 131258945Sroberto for (i = 0; i < ISC_MUTEX_MAX_LOCKERS; i++) { 132258945Sroberto mp->stats->lockers[i].file = NULL; 133258945Sroberto mp->stats->lockers[i].line = 0; 134258945Sroberto mp->stats->lockers[i].count = 0; 135258945Sroberto timevalclear(&mp->stats->lockers[i].locked_total); 136258945Sroberto timevalclear(&mp->stats->lockers[i].wait_total); 137258945Sroberto } 138258945Sroberto 139258945Sroberto return (ISC_R_SUCCESS); 140258945Sroberto} 141258945Sroberto 142258945Srobertoisc_result_t 143258945Srobertoisc_mutex_lock_profile(isc_mutex_t *mp, const char *file, int line) { 144258945Sroberto struct timeval prelock_t; 145258945Sroberto struct timeval postlock_t; 146258945Sroberto isc_mutexlocker_t *locker = NULL; 147258945Sroberto int i; 148258945Sroberto 149258945Sroberto gettimeofday(&prelock_t, NULL); 150258945Sroberto 151258945Sroberto if (pthread_mutex_lock(&mp->mutex) != 0) 152258945Sroberto return (ISC_R_UNEXPECTED); 153258945Sroberto 154258945Sroberto gettimeofday(&postlock_t, NULL); 155258945Sroberto mp->stats->lock_t = postlock_t; 156258945Sroberto 157258945Sroberto timevalsub(&postlock_t, &prelock_t); 158258945Sroberto 159258945Sroberto mp->stats->count++; 160258945Sroberto timevaladd(&mp->stats->wait_total, &postlock_t); 161258945Sroberto 162258945Sroberto for (i = 0; i < ISC_MUTEX_MAX_LOCKERS; i++) { 163258945Sroberto if (mp->stats->lockers[i].file == NULL) { 164258945Sroberto locker = &mp->stats->lockers[i]; 165258945Sroberto locker->file = file; 166258945Sroberto locker->line = line; 167258945Sroberto break; 168258945Sroberto } else if (mp->stats->lockers[i].file == file && 169258945Sroberto mp->stats->lockers[i].line == line) { 170258945Sroberto locker = &mp->stats->lockers[i]; 171258945Sroberto break; 172258945Sroberto } 173258945Sroberto } 174258945Sroberto 175258945Sroberto if (locker != NULL) { 176258945Sroberto locker->count++; 177258945Sroberto timevaladd(&locker->wait_total, &postlock_t); 178258945Sroberto } 179258945Sroberto 180258945Sroberto mp->stats->cur_locker = locker; 181258945Sroberto 182258945Sroberto return (ISC_R_SUCCESS); 183258945Sroberto} 184258945Sroberto 185258945Srobertoisc_result_t 186258945Srobertoisc_mutex_unlock_profile(isc_mutex_t *mp, const char *file, int line) { 187258945Sroberto struct timeval unlock_t; 188258945Sroberto 189258945Sroberto UNUSED(file); 190258945Sroberto UNUSED(line); 191258945Sroberto 192258945Sroberto if (mp->stats->cur_locker != NULL) { 193258945Sroberto gettimeofday(&unlock_t, NULL); 194258945Sroberto timevalsub(&unlock_t, &mp->stats->lock_t); 195258945Sroberto timevaladd(&mp->stats->locked_total, &unlock_t); 196258945Sroberto timevaladd(&mp->stats->cur_locker->locked_total, &unlock_t); 197258945Sroberto mp->stats->cur_locker = NULL; 198258945Sroberto } 199258945Sroberto 200258945Sroberto return ((pthread_mutex_unlock((&mp->mutex)) == 0) ? \ 201258945Sroberto ISC_R_SUCCESS : ISC_R_UNEXPECTED); 202258945Sroberto} 203258945Sroberto 204258945Sroberto 205258945Srobertovoid 206258945Srobertoisc_mutex_statsprofile(FILE *fp) { 207258945Sroberto isc_mutexlocker_t *locker; 208258945Sroberto int i, j; 209258945Sroberto 210258945Sroberto fprintf(fp, "Mutex stats (in us)\n"); 211258945Sroberto for (i = 0; i < stats_next; i++) { 212280849Scy fprintf(fp, "%-12s %4d: %10u %lu.%06lu %lu.%06lu %5d\n", 213258945Sroberto stats[i].file, stats[i].line, stats[i].count, 214258945Sroberto stats[i].locked_total.tv_sec, 215258945Sroberto stats[i].locked_total.tv_usec, 216258945Sroberto stats[i].wait_total.tv_sec, 217280849Scy stats[i].wait_total.tv_usec, 218280849Scy i); 219258945Sroberto for (j = 0; j < ISC_MUTEX_MAX_LOCKERS; j++) { 220258945Sroberto locker = &stats[i].lockers[j]; 221258945Sroberto if (locker->file == NULL) 222258945Sroberto continue; 223280849Scy fprintf(fp, " %-11s %4d: %10u %lu.%06lu %lu.%06lu %5d\n", 224258945Sroberto locker->file, locker->line, locker->count, 225258945Sroberto locker->locked_total.tv_sec, 226258945Sroberto locker->locked_total.tv_usec, 227258945Sroberto locker->wait_total.tv_sec, 228280849Scy locker->wait_total.tv_usec, 229280849Scy i); 230258945Sroberto } 231258945Sroberto } 232258945Sroberto} 233258945Sroberto 234258945Sroberto#endif /* ISC_MUTEX_PROFILE */ 235258945Sroberto 236258945Sroberto#if ISC_MUTEX_DEBUG && defined(PTHREAD_MUTEX_ERRORCHECK) 237258945Srobertoisc_result_t 238258945Srobertoisc_mutex_init_errcheck(isc_mutex_t *mp) 239258945Sroberto{ 240258945Sroberto pthread_mutexattr_t attr; 241258945Sroberto int err; 242258945Sroberto 243258945Sroberto if (pthread_mutexattr_init(&attr) != 0) 244258945Sroberto return (ISC_R_UNEXPECTED); 245258945Sroberto 246280849Scy if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK) != 0) { 247280849Scy pthread_mutexattr_destroy(&attr); 248258945Sroberto return (ISC_R_UNEXPECTED); 249280849Scy } 250258945Sroberto 251258945Sroberto err = pthread_mutex_init(mp, &attr) != 0) 252280849Scy pthread_mutexattr_destroy(&attr); 253258945Sroberto if (err == ENOMEM) 254258945Sroberto return (ISC_R_NOMEMORY); 255258945Sroberto return ((err == 0) ? ISC_R_SUCCESS : ISC_R_UNEXPECTED); 256258945Sroberto} 257258945Sroberto#endif 258258945Sroberto 259258945Sroberto#if ISC_MUTEX_DEBUG && defined(__NetBSD__) && defined(PTHREAD_MUTEX_ERRORCHECK) 260258945Srobertopthread_mutexattr_t isc__mutex_attrs = { 261258945Sroberto PTHREAD_MUTEX_ERRORCHECK, /* m_type */ 262258945Sroberto 0 /* m_flags, which appears to be unused. */ 263258945Sroberto}; 264258945Sroberto#endif 265258945Sroberto 266258945Sroberto#if !(ISC_MUTEX_DEBUG && defined(PTHREAD_MUTEX_ERRORCHECK)) && !ISC_MUTEX_PROFILE 267258945Srobertoisc_result_t 268258945Srobertoisc__mutex_init(isc_mutex_t *mp, const char *file, unsigned int line) { 269258945Sroberto char strbuf[ISC_STRERRORSIZE]; 270258945Sroberto isc_result_t result = ISC_R_SUCCESS; 271258945Sroberto int err; 272258945Sroberto 273258945Sroberto err = pthread_mutex_init(mp, ISC__MUTEX_ATTRS); 274258945Sroberto if (err == ENOMEM) 275258945Sroberto return (ISC_R_NOMEMORY); 276258945Sroberto if (err != 0) { 277258945Sroberto isc__strerror(errno, strbuf, sizeof(strbuf)); 278258945Sroberto UNEXPECTED_ERROR(file, line, "isc_mutex_init() failed: %s", 279258945Sroberto strbuf); 280258945Sroberto result = ISC_R_UNEXPECTED; 281258945Sroberto } 282258945Sroberto return (result); 283258945Sroberto} 284258945Sroberto#endif 285