xref: /freebsd/contrib/ntp/libntp/lib/isc/pthreads/mutex.c (revision a466cc55373fc3cf86837f09da729535b57e69a1)
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