1/*-
2 * See the file LICENSE for redistribution information.
3 *
4 * Copyright (c) 1999,2008 Oracle.  All rights reserved.
5 *
6 * $Id: mut_pthread.c,v 12.30 2008/01/08 20:58:43 bostic Exp $
7 */
8
9#include "db_config.h"
10
11#include "db_int.h"
12
13/*
14 * This is where we load in architecture/compiler specific mutex code.
15 */
16#define	LOAD_ACTUAL_MUTEX_CODE
17#include "dbinc/mutex_int.h"
18
19#ifdef HAVE_MUTEX_SOLARIS_LWP
20#define	pthread_cond_destroy(x)		0
21#define	pthread_cond_signal		_lwp_cond_signal
22#define	pthread_cond_wait		_lwp_cond_wait
23#define	pthread_mutex_destroy(x)	0
24#define	pthread_mutex_lock		_lwp_mutex_lock
25#define	pthread_mutex_trylock		_lwp_mutex_trylock
26#define	pthread_mutex_unlock		_lwp_mutex_unlock
27#endif
28#ifdef HAVE_MUTEX_UI_THREADS
29#define	pthread_cond_destroy(x)		cond_destroy
30#define	pthread_cond_signal		cond_signal
31#define	pthread_cond_wait		cond_wait
32#define	pthread_mutex_destroy		mutex_destroy
33#define	pthread_mutex_lock		mutex_lock
34#define	pthread_mutex_trylock		mutex_trylock
35#define	pthread_mutex_unlock		mutex_unlock
36#endif
37
38#define	PTHREAD_UNLOCK_ATTEMPTS	5
39
40/*
41 * IBM's MVS pthread mutex implementation returns -1 and sets errno rather than
42 * returning errno itself.  As -1 is not a valid errno value, assume functions
43 * returning -1 have set errno.  If they haven't, return a random error value.
44 */
45#define	RET_SET(f, ret) do {						\
46	if (((ret) = (f)) == -1 && ((ret) = errno) == 0)		\
47		(ret) = EAGAIN;						\
48} while (0)
49
50/*
51 * __db_pthread_mutex_init --
52 *	Initialize a pthread mutex.
53 *
54 * PUBLIC: int __db_pthread_mutex_init __P((ENV *, db_mutex_t, u_int32_t));
55 */
56int
57__db_pthread_mutex_init(env, mutex, flags)
58	ENV *env;
59	db_mutex_t mutex;
60	u_int32_t flags;
61{
62	DB_MUTEX *mutexp;
63	DB_MUTEXMGR *mtxmgr;
64	DB_MUTEXREGION *mtxregion;
65	int ret;
66
67	mtxmgr = env->mutex_handle;
68	mtxregion = mtxmgr->reginfo.primary;
69	mutexp = MUTEXP_SET(mutex);
70	ret = 0;
71
72#ifdef HAVE_MUTEX_PTHREADS
73	{
74	pthread_condattr_t condattr, *condattrp = NULL;
75	pthread_mutexattr_t mutexattr, *mutexattrp = NULL;
76
77	if (!LF_ISSET(DB_MUTEX_PROCESS_ONLY)) {
78		RET_SET((pthread_mutexattr_init(&mutexattr)), ret);
79#ifndef HAVE_MUTEX_THREAD_ONLY
80		if (ret == 0)
81			RET_SET((pthread_mutexattr_setpshared(
82			    &mutexattr, PTHREAD_PROCESS_SHARED)), ret);
83#endif
84		mutexattrp = &mutexattr;
85	}
86
87	if (ret == 0)
88		RET_SET((pthread_mutex_init(&mutexp->mutex, mutexattrp)), ret);
89	if (mutexattrp != NULL)
90		(void)pthread_mutexattr_destroy(mutexattrp);
91	if (ret == 0 && LF_ISSET(DB_MUTEX_SELF_BLOCK)) {
92		if (!LF_ISSET(DB_MUTEX_PROCESS_ONLY)) {
93			RET_SET((pthread_condattr_init(&condattr)), ret);
94			if (ret == 0) {
95				condattrp = &condattr;
96#ifndef HAVE_MUTEX_THREAD_ONLY
97				RET_SET((pthread_condattr_setpshared(
98				    &condattr, PTHREAD_PROCESS_SHARED)), ret);
99#endif
100			}
101		}
102
103		if (ret == 0)
104			RET_SET(
105			    (pthread_cond_init(&mutexp->cond, condattrp)), ret);
106
107		F_SET(mutexp, DB_MUTEX_SELF_BLOCK);
108		if (condattrp != NULL)
109			(void)pthread_condattr_destroy(condattrp);
110	}
111
112	}
113#endif
114#ifdef HAVE_MUTEX_SOLARIS_LWP
115	/*
116	 * XXX
117	 * Gcc complains about missing braces in the static initializations of
118	 * lwp_cond_t and lwp_mutex_t structures because the structures contain
119	 * sub-structures/unions and the Solaris include file that defines the
120	 * initialization values doesn't have surrounding braces.  There's not
121	 * much we can do.
122	 */
123	if (LF_ISSET(DB_MUTEX_PROCESS_ONLY)) {
124		static lwp_mutex_t mi = DEFAULTMUTEX;
125
126		mutexp->mutex = mi;
127	} else {
128		static lwp_mutex_t mi = SHAREDMUTEX;
129
130		mutexp->mutex = mi;
131	}
132	if (LF_ISSET(DB_MUTEX_SELF_BLOCK)) {
133		if (LF_ISSET(DB_MUTEX_PROCESS_ONLY)) {
134			static lwp_cond_t ci = DEFAULTCV;
135
136			mutexp->cond = ci;
137		} else {
138			static lwp_cond_t ci = SHAREDCV;
139
140			mutexp->cond = ci;
141		}
142		F_SET(mutexp, DB_MUTEX_SELF_BLOCK);
143	}
144#endif
145#ifdef HAVE_MUTEX_UI_THREADS
146	{
147	int type;
148
149	type = LF_ISSET(DB_MUTEX_PROCESS_ONLY) ? USYNC_THREAD : USYNC_PROCESS;
150
151	ret = mutex_init(&mutexp->mutex, type, NULL);
152	if (ret == 0 && LF_ISSET(DB_MUTEX_SELF_BLOCK)) {
153		ret = cond_init(&mutexp->cond, type, NULL);
154
155		F_SET(mutexp, DB_MUTEX_SELF_BLOCK);
156	}}
157#endif
158
159	if (ret != 0) {
160		__db_err(env, ret, "unable to initialize mutex");
161	}
162	return (ret);
163}
164
165/*
166 * __db_pthread_mutex_lock
167 *	Lock on a mutex, blocking if necessary.
168 *
169 * PUBLIC: int __db_pthread_mutex_lock __P((ENV *, db_mutex_t));
170 */
171int
172__db_pthread_mutex_lock(env, mutex)
173	ENV *env;
174	db_mutex_t mutex;
175{
176	DB_ENV *dbenv;
177	DB_MUTEX *mutexp;
178	DB_MUTEXMGR *mtxmgr;
179	DB_MUTEXREGION *mtxregion;
180	int i, ret;
181
182	dbenv = env->dbenv;
183
184	if (!MUTEX_ON(env) || F_ISSET(dbenv, DB_ENV_NOLOCKING))
185		return (0);
186
187	mtxmgr = env->mutex_handle;
188	mtxregion = mtxmgr->reginfo.primary;
189	mutexp = MUTEXP_SET(mutex);
190
191	CHECK_MTX_THREAD(env, mutexp);
192
193#if defined(HAVE_STATISTICS) && !defined(HAVE_MUTEX_HYBRID)
194	/*
195	 * We want to know which mutexes are contentious, but don't want to
196	 * do an interlocked test here -- that's slower when the underlying
197	 * system has adaptive mutexes and can perform optimizations like
198	 * spinning only if the thread holding the mutex is actually running
199	 * on a CPU.  Make a guess, using a normal load instruction.
200	 */
201	if (F_ISSET(mutexp, DB_MUTEX_LOCKED))
202		++mutexp->mutex_set_wait;
203	else
204		++mutexp->mutex_set_nowait;
205#endif
206
207	RET_SET((pthread_mutex_lock(&mutexp->mutex)), ret);
208	if (ret != 0)
209		goto err;
210
211	if (F_ISSET(mutexp, DB_MUTEX_SELF_BLOCK)) {
212		/*
213		 * If we are using hybrid mutexes then the pthread mutexes
214		 * are only used to wait after spinning on the TAS mutex.
215		 * Set the wait flag before checking to see if the mutex
216		 * is still locked.  The holder will clear the bit before
217		 * checking the wait flag.
218		 */
219#ifdef HAVE_MUTEX_HYBRID
220		mutexp->wait++;
221		MUTEX_MEMBAR(mutexp->wait);
222#endif
223		while (F_ISSET(mutexp, DB_MUTEX_LOCKED)) {
224			RET_SET((pthread_cond_wait(
225			    &mutexp->cond, &mutexp->mutex)), ret);
226			/*
227			 * !!!
228			 * Solaris bug workaround:
229			 * pthread_cond_wait() sometimes returns ETIME -- out
230			 * of sheer paranoia, check both ETIME and ETIMEDOUT.
231			 * We believe this happens when the application uses
232			 * SIGALRM for some purpose, e.g., the C library sleep
233			 * call, and Solaris delivers the signal to the wrong
234			 * LWP.
235			 */
236			if (ret != 0 && ret != EINTR &&
237#ifdef ETIME
238			    ret != ETIME &&
239#endif
240			    ret != ETIMEDOUT) {
241				(void)pthread_mutex_unlock(&mutexp->mutex);
242				goto err;
243			}
244		}
245
246#ifdef HAVE_MUTEX_HYBRID
247		mutexp->wait--;
248#else
249		F_SET(mutexp, DB_MUTEX_LOCKED);
250		dbenv->thread_id(dbenv, &mutexp->pid, &mutexp->tid);
251#endif
252
253		/*
254		 * According to HP-UX engineers contacted by Netscape,
255		 * pthread_mutex_unlock() will occasionally return EFAULT
256		 * for no good reason on mutexes in shared memory regions,
257		 * and the correct caller behavior is to try again.  Do
258		 * so, up to PTHREAD_UNLOCK_ATTEMPTS consecutive times.
259		 * Note that we don't bother to restrict this to HP-UX;
260		 * it should be harmless elsewhere. [#2471]
261		 */
262		i = PTHREAD_UNLOCK_ATTEMPTS;
263		do {
264			RET_SET((pthread_mutex_unlock(&mutexp->mutex)), ret);
265		} while (ret == EFAULT && --i > 0);
266		if (ret != 0)
267			goto err;
268	} else {
269#ifdef DIAGNOSTIC
270		if (F_ISSET(mutexp, DB_MUTEX_LOCKED)) {
271			char buf[DB_THREADID_STRLEN];
272			(void)dbenv->thread_id_string(dbenv,
273			    mutexp->pid, mutexp->tid, buf);
274			__db_errx(env,
275		    "pthread lock failed: lock currently in use: pid/tid: %s",
276			    buf);
277			ret = EINVAL;
278			goto err;
279		}
280#endif
281		F_SET(mutexp, DB_MUTEX_LOCKED);
282		dbenv->thread_id(dbenv, &mutexp->pid, &mutexp->tid);
283	}
284
285#ifdef DIAGNOSTIC
286	/*
287	 * We want to switch threads as often as possible.  Yield every time
288	 * we get a mutex to ensure contention.
289	 */
290	if (F_ISSET(dbenv, DB_ENV_YIELDCPU))
291		__os_yield(env, 0, 0);
292#endif
293	return (0);
294
295err:	__db_err(env, ret, "pthread lock failed");
296	return (__env_panic(env, ret));
297}
298
299/*
300 * __db_pthread_mutex_unlock --
301 *	Release a mutex.
302 *
303 * PUBLIC: int __db_pthread_mutex_unlock __P((ENV *, db_mutex_t));
304 */
305int
306__db_pthread_mutex_unlock(env, mutex)
307	ENV *env;
308	db_mutex_t mutex;
309{
310	DB_ENV *dbenv;
311	DB_MUTEX *mutexp;
312	DB_MUTEXMGR *mtxmgr;
313	DB_MUTEXREGION *mtxregion;
314	int i, ret;
315
316	dbenv = env->dbenv;
317
318	if (!MUTEX_ON(env) || F_ISSET(dbenv, DB_ENV_NOLOCKING))
319		return (0);
320
321	mtxmgr = env->mutex_handle;
322	mtxregion = mtxmgr->reginfo.primary;
323	mutexp = MUTEXP_SET(mutex);
324
325#if !defined(HAVE_MUTEX_HYBRID) && defined(DIAGNOSTIC)
326	if (!F_ISSET(mutexp, DB_MUTEX_LOCKED)) {
327		__db_errx(
328		    env, "pthread unlock failed: lock already unlocked");
329		return (__env_panic(env, EACCES));
330	}
331#endif
332	if (F_ISSET(mutexp, DB_MUTEX_SELF_BLOCK)) {
333		RET_SET((pthread_mutex_lock(&mutexp->mutex)), ret);
334		if (ret != 0)
335			goto err;
336
337		F_CLR(mutexp, DB_MUTEX_LOCKED);
338
339		RET_SET((pthread_cond_signal(&mutexp->cond)), ret);
340		if (ret != 0)
341			goto err;
342	} else
343		F_CLR(mutexp, DB_MUTEX_LOCKED);
344
345	/* See comment above;  workaround for [#2471]. */
346	i = PTHREAD_UNLOCK_ATTEMPTS;
347	do {
348		RET_SET((pthread_mutex_unlock(&mutexp->mutex)), ret);
349	} while (ret == EFAULT && --i > 0);
350
351err:	if (ret != 0) {
352		__db_err(env, ret, "pthread unlock failed");
353		return (__env_panic(env, ret));
354	}
355	return (ret);
356}
357
358/*
359 * __db_pthread_mutex_destroy --
360 *	Destroy a mutex.
361 *
362 * PUBLIC: int __db_pthread_mutex_destroy __P((ENV *, db_mutex_t));
363 */
364int
365__db_pthread_mutex_destroy(env, mutex)
366	ENV *env;
367	db_mutex_t mutex;
368{
369	DB_MUTEX *mutexp;
370	DB_MUTEXMGR *mtxmgr;
371	DB_MUTEXREGION *mtxregion;
372	int ret, t_ret;
373
374	if (!MUTEX_ON(env))
375		return (0);
376
377	mtxmgr = env->mutex_handle;
378	mtxregion = mtxmgr->reginfo.primary;
379	mutexp = MUTEXP_SET(mutex);
380
381	ret = 0;
382	if (F_ISSET(mutexp, DB_MUTEX_SELF_BLOCK)) {
383		RET_SET((pthread_cond_destroy(&mutexp->cond)), ret);
384		if (ret != 0)
385			__db_err(env, ret, "unable to destroy cond");
386	}
387	RET_SET((pthread_mutex_destroy(&mutexp->mutex)), t_ret);
388	if (t_ret != 0) {
389		__db_err(env, t_ret, "unable to destroy mutex");
390		if (ret == 0)
391			ret = t_ret;
392	}
393	return (ret);
394}
395