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