mutex.c

Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2004, 2005, 2007, 2008, 2011, 2012, 2014, 2015  Internet Systems Consortium, Inc. ("ISC")
00003  * Copyright (C) 2000-2002  Internet Software Consortium.
00004  *
00005  * Permission to use, copy, modify, and/or distribute this software for any
00006  * purpose with or without fee is hereby granted, provided that the above
00007  * copyright notice and this permission notice appear in all copies.
00008  *
00009  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
00010  * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
00011  * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
00012  * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
00013  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
00014  * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
00015  * PERFORMANCE OF THIS SOFTWARE.
00016  */
00017 
00018 /* $Id: mutex.c,v 1.18 2011/01/04 23:47:14 tbox Exp $ */
00019 
00020 /*! \file */
00021 
00022 #include <config.h>
00023 
00024 #include <stdio.h>
00025 #include <time.h>
00026 #include <sys/time.h>
00027 #include <errno.h>
00028 
00029 #include <isc/mutex.h>
00030 #include <isc/util.h>
00031 #include <isc/strerror.h>
00032 #include <isc/once.h>
00033 
00034 #if ISC_MUTEX_PROFILE
00035 
00036 /*@{*/
00037 /*% Operations on timevals; adapted from FreeBSD's sys/time.h */
00038 #define timevalclear(tvp)      ((tvp)->tv_sec = (tvp)->tv_usec = 0)
00039 #define timevaladd(vvp, uvp)                                            \
00040         do {                                                            \
00041                 (vvp)->tv_sec += (uvp)->tv_sec;                         \
00042                 (vvp)->tv_usec += (uvp)->tv_usec;                       \
00043                 if ((vvp)->tv_usec >= 1000000) {                        \
00044                         (vvp)->tv_sec++;                                \
00045                         (vvp)->tv_usec -= 1000000;                      \
00046                 }                                                       \
00047         } while (0)
00048 #define timevalsub(vvp, uvp)                                            \
00049         do {                                                            \
00050                 (vvp)->tv_sec -= (uvp)->tv_sec;                         \
00051                 (vvp)->tv_usec -= (uvp)->tv_usec;                       \
00052                 if ((vvp)->tv_usec < 0) {                               \
00053                         (vvp)->tv_sec--;                                \
00054                         (vvp)->tv_usec += 1000000;                      \
00055                 }                                                       \
00056         } while (0)
00057 
00058 /*@}*/
00059 
00060 #define ISC_MUTEX_MAX_LOCKERS 32
00061 
00062 typedef struct {
00063         const char *            file;
00064         int                     line;
00065         unsigned                count;
00066         struct timeval          locked_total;
00067         struct timeval          wait_total;
00068 } isc_mutexlocker_t;
00069 
00070 struct isc_mutexstats {
00071         const char *            file;   /*%< File mutex was created in. */
00072         int                     line;   /*%< Line mutex was created on. */
00073         unsigned                count;
00074         struct timeval          lock_t;
00075         struct timeval          locked_total;
00076         struct timeval          wait_total;
00077         isc_mutexlocker_t *     cur_locker;
00078         isc_mutexlocker_t       lockers[ISC_MUTEX_MAX_LOCKERS];
00079 };
00080 
00081 #ifndef ISC_MUTEX_PROFTABLESIZE
00082 #define ISC_MUTEX_PROFTABLESIZE (1024 * 1024)
00083 #endif
00084 static isc_mutexstats_t stats[ISC_MUTEX_PROFTABLESIZE];
00085 static int stats_next = 0;
00086 static isc_boolean_t stats_init = ISC_FALSE;
00087 static pthread_mutex_t statslock = PTHREAD_MUTEX_INITIALIZER;
00088 
00089 
00090 isc_result_t
00091 isc_mutex_init_profile(isc_mutex_t *mp, const char *file, int line) {
00092         int i, err;
00093 
00094         err = pthread_mutex_init(&mp->mutex, NULL);
00095         if (err == ENOMEM)
00096                 return (ISC_R_NOMEMORY);
00097         if (err != 0)
00098                 return (ISC_R_UNEXPECTED);
00099 
00100         RUNTIME_CHECK(pthread_mutex_lock(&statslock) == 0);
00101 
00102         if (stats_init == ISC_FALSE)
00103                 stats_init = ISC_TRUE;
00104 
00105         /*
00106          * If all statistics entries have been used, give up and trigger an
00107          * assertion failure.  There would be no other way to deal with this
00108          * because we'd like to keep record of all locks for the purpose of
00109          * debugging and the number of necessary locks is unpredictable.
00110          * If this failure is triggered while debugging, named should be
00111          * rebuilt with an increased ISC_MUTEX_PROFTABLESIZE.
00112          */
00113         RUNTIME_CHECK(stats_next < ISC_MUTEX_PROFTABLESIZE);
00114         mp->stats = &stats[stats_next++];
00115 
00116         RUNTIME_CHECK(pthread_mutex_unlock(&statslock) == 0);
00117 
00118         mp->stats->file = file;
00119         mp->stats->line = line;
00120         mp->stats->count = 0;
00121         timevalclear(&mp->stats->locked_total);
00122         timevalclear(&mp->stats->wait_total);
00123         for (i = 0; i < ISC_MUTEX_MAX_LOCKERS; i++) {
00124                 mp->stats->lockers[i].file = NULL;
00125                 mp->stats->lockers[i].line = 0;
00126                 mp->stats->lockers[i].count = 0;
00127                 timevalclear(&mp->stats->lockers[i].locked_total);
00128                 timevalclear(&mp->stats->lockers[i].wait_total);
00129         }
00130 
00131         return (ISC_R_SUCCESS);
00132 }
00133 
00134 isc_result_t
00135 isc_mutex_lock_profile(isc_mutex_t *mp, const char *file, int line) {
00136         struct timeval prelock_t;
00137         struct timeval postlock_t;
00138         isc_mutexlocker_t *locker = NULL;
00139         int i;
00140 
00141         gettimeofday(&prelock_t, NULL);
00142 
00143         if (pthread_mutex_lock(&mp->mutex) != 0)
00144                 return (ISC_R_UNEXPECTED);
00145 
00146         gettimeofday(&postlock_t, NULL);
00147         mp->stats->lock_t = postlock_t;
00148 
00149         timevalsub(&postlock_t, &prelock_t);
00150 
00151         mp->stats->count++;
00152         timevaladd(&mp->stats->wait_total, &postlock_t);
00153 
00154         for (i = 0; i < ISC_MUTEX_MAX_LOCKERS; i++) {
00155                 if (mp->stats->lockers[i].file == NULL) {
00156                         locker = &mp->stats->lockers[i];
00157                         locker->file = file;
00158                         locker->line = line;
00159                         break;
00160                 } else if (mp->stats->lockers[i].file == file &&
00161                            mp->stats->lockers[i].line == line) {
00162                         locker = &mp->stats->lockers[i];
00163                         break;
00164                 }
00165         }
00166 
00167         if (locker != NULL) {
00168                 locker->count++;
00169                 timevaladd(&locker->wait_total, &postlock_t);
00170         }
00171 
00172         mp->stats->cur_locker = locker;
00173 
00174         return (ISC_R_SUCCESS);
00175 }
00176 
00177 isc_result_t
00178 isc_mutex_unlock_profile(isc_mutex_t *mp, const char *file, int line) {
00179         struct timeval unlock_t;
00180 
00181         UNUSED(file);
00182         UNUSED(line);
00183 
00184         if (mp->stats->cur_locker != NULL) {
00185                 gettimeofday(&unlock_t, NULL);
00186                 timevalsub(&unlock_t, &mp->stats->lock_t);
00187                 timevaladd(&mp->stats->locked_total, &unlock_t);
00188                 timevaladd(&mp->stats->cur_locker->locked_total, &unlock_t);
00189                 mp->stats->cur_locker = NULL;
00190         }
00191 
00192         return ((pthread_mutex_unlock((&mp->mutex)) == 0) ? \
00193                 ISC_R_SUCCESS : ISC_R_UNEXPECTED);
00194 }
00195 
00196 
00197 void
00198 isc_mutex_statsprofile(FILE *fp) {
00199         isc_mutexlocker_t *locker;
00200         int i, j;
00201 
00202         fprintf(fp, "Mutex stats (in us)\n");
00203         for (i = 0; i < stats_next; i++) {
00204                 fprintf(fp, "%-12s %4d: %10u  %lu.%06lu %lu.%06lu %5d\n",
00205                         stats[i].file, stats[i].line, stats[i].count,
00206                         stats[i].locked_total.tv_sec,
00207                         stats[i].locked_total.tv_usec,
00208                         stats[i].wait_total.tv_sec,
00209                         stats[i].wait_total.tv_usec,
00210                         i);
00211                 for (j = 0; j < ISC_MUTEX_MAX_LOCKERS; j++) {
00212                         locker = &stats[i].lockers[j];
00213                         if (locker->file == NULL)
00214                                 continue;
00215                         fprintf(fp, " %-11s %4d: %10u  %lu.%06lu %lu.%06lu %5d\n",
00216                                 locker->file, locker->line, locker->count,
00217                                 locker->locked_total.tv_sec,
00218                                 locker->locked_total.tv_usec,
00219                                 locker->wait_total.tv_sec,
00220                                 locker->wait_total.tv_usec,
00221                                 i);
00222                 }
00223         }
00224 }
00225 
00226 #endif /* ISC_MUTEX_PROFILE */
00227 
00228 #if ISC_MUTEX_DEBUG && defined(PTHREAD_MUTEX_ERRORCHECK)
00229 
00230 static isc_boolean_t errcheck_initialized = ISC_FALSE;
00231 static pthread_mutexattr_t errcheck;
00232 static isc_once_t once_errcheck = ISC_ONCE_INIT;
00233 
00234 static void
00235 initialize_errcheck(void) {
00236         RUNTIME_CHECK(pthread_mutexattr_init(&errcheck) == 0);
00237         RUNTIME_CHECK(pthread_mutexattr_settype
00238                       (&errcheck, PTHREAD_MUTEX_ERRORCHECK) == 0);
00239         errcheck_initialized = ISC_TRUE;
00240 }
00241 
00242 isc_result_t
00243 isc_mutex_init_errcheck(isc_mutex_t *mp) {
00244         isc_result_t result;
00245         int err;
00246 
00247         result = isc_once_do(&once_errcheck, initialize_errcheck);
00248         RUNTIME_CHECK(result == ISC_R_SUCCESS);
00249 
00250         err = pthread_mutex_init(mp, &errcheck);
00251         if (err == ENOMEM)
00252                 return (ISC_R_NOMEMORY);
00253         return ((err == 0) ? ISC_R_SUCCESS : ISC_R_UNEXPECTED);
00254 }
00255 #endif
00256 
00257 #if ISC_MUTEX_DEBUG && defined(__NetBSD__) && defined(PTHREAD_MUTEX_ERRORCHECK)
00258 pthread_mutexattr_t isc__mutex_attrs = {
00259         PTHREAD_MUTEX_ERRORCHECK,       /* m_type */
00260         0                               /* m_flags, which appears to be unused. */
00261 };
00262 #endif
00263 
00264 #if !(ISC_MUTEX_DEBUG && defined(PTHREAD_MUTEX_ERRORCHECK)) && !ISC_MUTEX_PROFILE
00265 
00266 #ifdef HAVE_PTHREAD_MUTEX_ADAPTIVE_NP
00267 static isc_boolean_t attr_initialized = ISC_FALSE;
00268 static pthread_mutexattr_t attr;
00269 static isc_once_t once_attr = ISC_ONCE_INIT;
00270 #endif /* HAVE_PTHREAD_MUTEX_ADAPTIVE_NP */
00271 
00272 #ifdef HAVE_PTHREAD_MUTEX_ADAPTIVE_NP
00273 static void
00274 initialize_attr(void) {
00275         RUNTIME_CHECK(pthread_mutexattr_init(&attr) == 0);
00276         RUNTIME_CHECK(pthread_mutexattr_settype
00277                       (&attr, PTHREAD_MUTEX_ADAPTIVE_NP) == 0);
00278         attr_initialized = ISC_TRUE;
00279 }
00280 #endif /* HAVE_PTHREAD_MUTEX_ADAPTIVE_NP */
00281 
00282 isc_result_t
00283 isc__mutex_init(isc_mutex_t *mp, const char *file, unsigned int line) {
00284         char strbuf[ISC_STRERRORSIZE];
00285         isc_result_t result = ISC_R_SUCCESS;
00286         int err;
00287 
00288 #ifdef HAVE_PTHREAD_MUTEX_ADAPTIVE_NP
00289         result = isc_once_do(&once_attr, initialize_attr);
00290         RUNTIME_CHECK(result == ISC_R_SUCCESS);
00291 
00292         err = pthread_mutex_init(mp, &attr);
00293 #else /* HAVE_PTHREAD_MUTEX_ADAPTIVE_NP */
00294         err = pthread_mutex_init(mp, ISC__MUTEX_ATTRS);
00295 #endif /* HAVE_PTHREAD_MUTEX_ADAPTIVE_NP */
00296 
00297         if (err == ENOMEM)
00298                 return (ISC_R_NOMEMORY);
00299         if (err != 0) {
00300                 isc__strerror(err, strbuf, sizeof(strbuf));
00301                 UNEXPECTED_ERROR(file, line, "isc_mutex_init() failed: %s",
00302                                  strbuf);
00303                 result = ISC_R_UNEXPECTED;
00304         }
00305         return (result);
00306 }
00307 #endif

Generated on Tue Apr 28 17:41:05 2015 by Doxygen 1.5.4 for BIND9 Internals 9.11.0pre-alpha