1/*
2 * See the file LICENSE for redistribution information.
3 *
4 * Copyright (c) 1996-2009 Oracle.  All rights reserved.
5 *
6 * $Id$
7 */
8
9#ifndef _DB_MUTEX_H_
10#define	_DB_MUTEX_H_
11
12#ifdef HAVE_MUTEX_SUPPORT
13/* The inlined trylock calls need access to the details of mutexes. */
14#define	LOAD_ACTUAL_MUTEX_CODE
15#include "dbinc/mutex_int.h"
16
17#ifndef HAVE_SHARED_LATCHES
18 #error "Shared latches are required in DB 4.8 and above"
19#endif
20#endif
21
22#if defined(__cplusplus)
23extern "C" {
24#endif
25
26/*
27 * By default, spin 50 times per processor if fail to acquire a test-and-set
28 * mutex, we have anecdotal evidence it's a reasonable value.
29 */
30#define	MUTEX_SPINS_PER_PROCESSOR	50
31
32/*
33 * Mutexes are represented by unsigned, 32-bit integral values.  As the
34 * OOB value is 0, mutexes can be initialized by zero-ing out the memory
35 * in which they reside.
36 */
37#define	MUTEX_INVALID	0
38
39/*
40 * We track mutex allocations by ID.
41 */
42#define	MTX_APPLICATION		 1
43#define	MTX_ATOMIC_EMULATION	 2
44#define	MTX_DB_HANDLE		 3
45#define	MTX_ENV_DBLIST		 4
46#define	MTX_ENV_HANDLE		 5
47#define	MTX_ENV_REGION		 6
48#define	MTX_LOCK_REGION		 7
49#define	MTX_LOGICAL_LOCK	 8
50#define	MTX_LOG_FILENAME	 9
51#define	MTX_LOG_FLUSH		10
52#define	MTX_LOG_HANDLE		11
53#define	MTX_LOG_REGION		12
54#define	MTX_MPOOLFILE_HANDLE	13
55#define	MTX_MPOOL_BH		14
56#define	MTX_MPOOL_FH		15
57#define	MTX_MPOOL_FILE_BUCKET	16
58#define	MTX_MPOOL_HANDLE	17
59#define	MTX_MPOOL_HASH_BUCKET	18
60#define	MTX_MPOOL_REGION	19
61#define	MTX_MUTEX_REGION	20
62#define	MTX_MUTEX_TEST		21
63#define	MTX_REP_CHKPT		22
64#define	MTX_REP_DATABASE	23
65#define	MTX_REP_EVENT		24
66#define	MTX_REP_REGION		25
67#define	MTX_REPMGR		26
68#define	MTX_SEQUENCE		27
69#define	MTX_TWISTER		28
70#define	MTX_TXN_ACTIVE		29
71#define	MTX_TXN_CHKPT		30
72#define	MTX_TXN_COMMIT		31
73#define	MTX_TXN_MVCC		32
74#define	MTX_TXN_REGION		33
75
76#define	MTX_MAX_ENTRY		33
77
78/* Redirect mutex calls to the correct functions. */
79#if !defined(HAVE_MUTEX_HYBRID) && (					\
80    defined(HAVE_MUTEX_PTHREADS) ||					\
81    defined(HAVE_MUTEX_SOLARIS_LWP) ||					\
82    defined(HAVE_MUTEX_UI_THREADS))
83#define	__mutex_init(a, b, c)		__db_pthread_mutex_init(a, b, c)
84#define	__mutex_lock(a, b)		__db_pthread_mutex_lock(a, b)
85#define	__mutex_unlock(a, b)		__db_pthread_mutex_unlock(a, b)
86#define	__mutex_destroy(a, b)		__db_pthread_mutex_destroy(a, b)
87#define	__mutex_trylock(a, b)		__db_pthread_mutex_trylock(a, b)
88/*
89 * These trylock versions do not support DB_ENV_FAILCHK. Callers which loop
90 * checking mutexes which are held by dead processes or threads might spin.
91 * These have ANSI-style definitions because this file can be included by
92 * C++ files, and extern "C" affects linkage only, not argument typing.
93 */
94static inline int __db_pthread_mutex_trylock(ENV *env, db_mutex_t mutex)
95{
96	int ret;
97	DB_MUTEX *mutexp;
98	if (!MUTEX_ON(env) || F_ISSET(env->dbenv, DB_ENV_NOLOCKING))
99		return (0);
100	mutexp = MUTEXP_SET(env->mutex_handle, mutex);
101#ifdef HAVE_SHARED_LATCHES
102	if (F_ISSET(mutexp, DB_MUTEX_SHARED))
103		ret = pthread_rwlock_trywrlock(&mutexp->u.rwlock);
104	    else
105#endif
106	if ((ret = pthread_mutex_trylock(&mutexp->u.m.mutex)) == 0)
107		F_SET(mutexp, DB_MUTEX_LOCKED);
108	if (ret == EBUSY)
109		ret = DB_LOCK_NOTGRANTED;
110#ifdef HAVE_STATISTICS
111	if (ret == 0)
112		++mutexp->mutex_set_nowait;
113#endif
114	return (ret);
115}
116#ifdef HAVE_SHARED_LATCHES
117#define	__mutex_rdlock(a, b)		__db_pthread_mutex_readlock(a, b)
118#define	__mutex_tryrdlock(a, b)		__db_pthread_mutex_tryreadlock(a, b)
119static inline int __db_pthread_mutex_tryreadlock(ENV *env, db_mutex_t mutex)
120{
121	int ret;
122	DB_MUTEX *mutexp;
123	if (!MUTEX_ON(env) || F_ISSET(env->dbenv, DB_ENV_NOLOCKING))
124		return (0);
125	mutexp = MUTEXP_SET(env->mutex_handle, mutex);
126	if (F_ISSET(mutexp, DB_MUTEX_SHARED))
127		ret = pthread_rwlock_tryrdlock(&mutexp->u.rwlock);
128	else
129		return (EINVAL);
130	if (ret == EBUSY)
131		ret = DB_LOCK_NOTGRANTED;
132#ifdef HAVE_STATISTICS
133	if (ret == 0)
134		++mutexp->mutex_set_rd_nowait;
135#endif
136	return (ret);
137}
138#endif
139#elif defined(HAVE_MUTEX_WIN32) || defined(HAVE_MUTEX_WIN32_GCC)
140#define	__mutex_init(a, b, c)		__db_win32_mutex_init(a, b, c)
141#define	__mutex_lock(a, b)		__db_win32_mutex_lock(a, b)
142#define	__mutex_trylock(a, b)		__db_win32_mutex_trylock(a, b)
143#define	__mutex_unlock(a, b)		__db_win32_mutex_unlock(a, b)
144#define	__mutex_destroy(a, b)		__db_win32_mutex_destroy(a, b)
145#ifdef HAVE_SHARED_LATCHES
146#define	__mutex_rdlock(a, b)		__db_win32_mutex_readlock(a, b)
147#define	__mutex_tryrdlock(a, b)		__db_win32_mutex_tryreadlock(a, b)
148#endif
149#elif defined(HAVE_MUTEX_FCNTL)
150#define	__mutex_init(a, b, c)		__db_fcntl_mutex_init(a, b, c)
151#define	__mutex_lock(a, b)		__db_fcntl_mutex_lock(a, b)
152#define	__mutex_trylock(a, b)		__db_fcntl_mutex_trylock(a, b)
153#define	__mutex_unlock(a, b)		__db_fcntl_mutex_unlock(a, b)
154#define	__mutex_destroy(a, b)		__db_fcntl_mutex_destroy(a, b)
155#else
156#define	__mutex_init(a, b, c)		__db_tas_mutex_init(a, b, c)
157#define	__mutex_lock(a, b)		__db_tas_mutex_lock(a, b)
158#define	__mutex_trylock(a, b)		__db_tas_mutex_trylock(a, b)
159#define	__mutex_unlock(a, b)		__db_tas_mutex_unlock(a, b)
160#define	__mutex_destroy(a, b)		__db_tas_mutex_destroy(a, b)
161#if defined(HAVE_SHARED_LATCHES)
162#define	__mutex_rdlock(a, b)		__db_tas_mutex_readlock(a, b)
163#define	__mutex_tryrdlock(a,b)		__db_tas_mutex_tryreadlock(a, b)
164#endif
165#endif
166
167/*
168 * When there is no method to get a shared latch, fall back to
169 * implementing __mutex_rdlock() as getting an exclusive one.
170 * This occurs either when !HAVE_SHARED_LATCHES or HAVE_MUTEX_FCNTL.
171 */
172#ifndef __mutex_rdlock
173#define	__mutex_rdlock(a, b)		__mutex_lock(a, b)
174#endif
175#ifndef __mutex_tryrdlock
176#define	__mutex_tryrdlock(a, b)		__mutex_trylock(a, b)
177#endif
178
179/*
180 * Lock/unlock a mutex.  If the mutex was never required, the thread of
181 * control can proceed without it.
182 *
183 * We never fail to acquire or release a mutex without panicing.  Simplify
184 * the macros to always return a panic value rather than saving the actual
185 * return value of the mutex routine.
186 */
187#ifdef HAVE_MUTEX_SUPPORT
188#define	MUTEX_LOCK(env, mutex) do {					\
189	if ((mutex) != MUTEX_INVALID &&					\
190	    __mutex_lock(env, mutex) != 0)				\
191		return (DB_RUNRECOVERY);				\
192} while (0)
193
194/*
195 * Always check the return value of MUTEX_TRYLOCK()!  Expect 0 on success,
196 * or DB_LOCK_NOTGRANTED, or possibly DB_RUNRECOVERY for failchk.
197 */
198#define	MUTEX_TRYLOCK(env, mutex)					\
199	(((mutex) == MUTEX_INVALID) ? 0 : __mutex_trylock(env, mutex))
200
201/*
202 * Acquire a DB_MUTEX_SHARED "mutex" in shared mode.
203 */
204#define	MUTEX_READLOCK(env, mutex) do {					\
205	if ((mutex) != MUTEX_INVALID &&					\
206	    __mutex_rdlock(env, mutex) != 0)				\
207		return (DB_RUNRECOVERY);				\
208} while (0)
209#define	MUTEX_TRY_READLOCK(env, mutex)					\
210	((mutex) != MUTEX_INVALID ? __mutex_tryrdlock(env, mutex) : 0)
211
212#define	MUTEX_UNLOCK(env, mutex) do {					\
213	if ((mutex) != MUTEX_INVALID &&					\
214	    __mutex_unlock(env, mutex) != 0)				\
215		return (DB_RUNRECOVERY);				\
216} while (0)
217#else
218/*
219 * There are calls to lock/unlock mutexes outside of #ifdef's -- replace
220 * the call with something the compiler can discard, but which will make
221 * if-then-else blocks work correctly.
222 */
223#define	MUTEX_LOCK(env, mutex)		(mutex) = (mutex)
224#define	MUTEX_TRYLOCK(env, mutex)	(mutex) = (mutex)
225#define	MUTEX_READLOCK(env, mutex)	(mutex) = (mutex)
226#define	MUTEX_TRY_READLOCK(env, mutex)	(mutex) = (mutex)
227#define	MUTEX_UNLOCK(env, mutex)	(mutex) = (mutex)
228#define	MUTEX_REQUIRED(env, mutex)	(mutex) = (mutex)
229#define	MUTEX_REQUIRED_READ(env, mutex)	(mutex) = (mutex)
230#endif
231
232/*
233 * Berkeley DB ports may require single-threading at places in the code.
234 */
235#ifdef HAVE_MUTEX_VXWORKS
236#include "taskLib.h"
237/*
238 * Use the taskLock() mutex to eliminate a race where two tasks are
239 * trying to initialize the global lock at the same time.
240 */
241#define	DB_BEGIN_SINGLE_THREAD do {					\
242	if (DB_GLOBAL(db_global_init))					\
243		(void)semTake(DB_GLOBAL(db_global_lock), WAIT_FOREVER);	\
244	else {								\
245		taskLock();						\
246		if (DB_GLOBAL(db_global_init)) {			\
247			taskUnlock();					\
248			(void)semTake(DB_GLOBAL(db_global_lock),	\
249			    WAIT_FOREVER);				\
250			continue;					\
251		}							\
252		DB_GLOBAL(db_global_lock) =				\
253		    semBCreate(SEM_Q_FIFO, SEM_EMPTY);			\
254		if (DB_GLOBAL(db_global_lock) != NULL)			\
255			DB_GLOBAL(db_global_init) = 1;			\
256		taskUnlock();						\
257	}								\
258} while (DB_GLOBAL(db_global_init) == 0)
259#define	DB_END_SINGLE_THREAD	(void)semGive(DB_GLOBAL(db_global_lock))
260#endif
261
262/*
263 * Single-threading defaults to a no-op.
264 */
265#ifndef DB_BEGIN_SINGLE_THREAD
266#define	DB_BEGIN_SINGLE_THREAD
267#endif
268#ifndef DB_END_SINGLE_THREAD
269#define	DB_END_SINGLE_THREAD
270#endif
271
272#if defined(__cplusplus)
273}
274#endif
275
276#include "dbinc_auto/mutex_ext.h"
277#endif /* !_DB_MUTEX_H_ */
278