summaryrefslogtreecommitdiff
path: root/src/dbinc/mutex.h
blob: b699142ce6e317c8c01b34c6a8beb18f5961d718 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
/*
 * See the file LICENSE for redistribution information.
 *
 * Copyright (c) 1996, 2012 Oracle and/or its affiliates.  All rights reserved.
 *
 * $Id$
 */

#ifndef _DB_MUTEX_H_
#define	_DB_MUTEX_H_

#ifdef HAVE_MUTEX_SUPPORT
/* The inlined trylock calls need access to the details of mutexes. */
#define	LOAD_ACTUAL_MUTEX_CODE
#include "dbinc/mutex_int.h"

#ifndef HAVE_SHARED_LATCHES
 #error "Shared latches are required in DB 4.8 and above"
#endif
#endif

#if defined(__cplusplus)
extern "C" {
#endif

/*
 * By default, spin 50 times per processor if fail to acquire a test-and-set
 * mutex, we have anecdotal evidence it's a reasonable value.
 */
#define	MUTEX_SPINS_PER_PROCESSOR	50

/*
 * Mutexes are represented by unsigned, 32-bit integral values.  As the
 * OOB value is 0, mutexes can be initialized by zero-ing out the memory
 * in which they reside.
 */
#define	MUTEX_INVALID	0

/*
 * We track mutex allocations by ID.
 */
#define	MTX_APPLICATION		 1
#define	MTX_ATOMIC_EMULATION	 2
#define	MTX_DB_HANDLE		 3
#define	MTX_ENV_DBLIST		 4
#define	MTX_ENV_EXCLDBLIST	 5
#define	MTX_ENV_HANDLE		 6
#define	MTX_ENV_REGION		 7
#define	MTX_LOCK_REGION		 8
#define	MTX_LOGICAL_LOCK	 9
#define	MTX_LOG_FILENAME	10
#define	MTX_LOG_FLUSH		11
#define	MTX_LOG_HANDLE		12
#define	MTX_LOG_REGION		13
#define	MTX_MPOOLFILE_HANDLE	14
#define	MTX_MPOOL_BH		15
#define	MTX_MPOOL_FH		16
#define	MTX_MPOOL_FILE_BUCKET	17
#define	MTX_MPOOL_HANDLE	18
#define	MTX_MPOOL_HASH_BUCKET	19
#define	MTX_MPOOL_REGION	20
#define	MTX_MUTEX_REGION	21
#define	MTX_MUTEX_TEST		22
#define	MTX_REP_CHKPT		23
#define	MTX_REP_DATABASE	24
#define	MTX_REP_DIAG		25
#define	MTX_REP_EVENT		26
#define	MTX_REP_REGION		27
#define	MTX_REP_START		28
#define	MTX_REP_WAITER		29
#define	MTX_REPMGR		30
#define	MTX_SEQUENCE		31
#define	MTX_TWISTER		32
#define	MTX_TCL_EVENTS		33
#define	MTX_TXN_ACTIVE		34
#define	MTX_TXN_CHKPT		35
#define	MTX_TXN_COMMIT		36
#define	MTX_TXN_MVCC		37
#define	MTX_TXN_REGION		38

#define	MTX_MAX_ENTRY		38

/* The following macros are defined on some platforms, e.g. QNX. */
#undef __mutex_init
#undef __mutex_lock
#undef __mutex_timedlock
#undef __mutex_unlock
#undef __mutex_destroy
#undef __mutex_trylock

/* Redirect mutex calls to the correct functions. */
#if !defined(HAVE_MUTEX_HYBRID) && (					\
    defined(HAVE_MUTEX_PTHREADS) ||					\
    defined(HAVE_MUTEX_SOLARIS_LWP) ||					\
    defined(HAVE_MUTEX_UI_THREADS))
#define	__mutex_init(a, b, c)		__db_pthread_mutex_init(a, b, c)
#define	__mutex_lock(a, b)		__db_pthread_mutex_lock(a, b, 0)
#define	__mutex_timedlock(a, b, c)	__db_pthread_mutex_lock(a, b, c)
#define	__mutex_unlock(a, b)		__db_pthread_mutex_unlock(a, b)
#define	__mutex_destroy(a, b)		__db_pthread_mutex_destroy(a, b)
#define	__mutex_trylock(a, b)		__db_pthread_mutex_trylock(a, b)
/*
 * These trylock versions do not support DB_ENV_FAILCHK. Callers which loop
 * checking mutexes which are held by dead processes or threads might spin.
 * These have ANSI-style definitions because this file can be included by
 * C++ files, and extern "C" affects linkage only, not argument typing.
 */
static inline int __db_pthread_mutex_trylock(ENV *env, db_mutex_t mutex)
{
	int ret;
	DB_MUTEX *mutexp;
	if (!MUTEX_ON(env) || F_ISSET(env->dbenv, DB_ENV_NOLOCKING))
		return (0);
	mutexp = MUTEXP_SET(env, mutex);
#ifdef HAVE_SHARED_LATCHES
	if (F_ISSET(mutexp, DB_MUTEX_SHARED))
		ret = pthread_rwlock_trywrlock(&mutexp->u.rwlock);
	else
#endif
		ret = pthread_mutex_trylock(&mutexp->u.m.mutex);
	if (ret == EBUSY)
		ret = DB_LOCK_NOTGRANTED;
	else if (ret == 0) {
		F_SET(mutexp, DB_MUTEX_LOCKED);
		env->dbenv->thread_id(env->dbenv, &mutexp->pid, &mutexp->tid);
		STAT_INC(env,
		    mutex, set_nowait, mutexp->mutex_set_nowait, mutex);
	}
	return (ret);
}
#ifdef HAVE_SHARED_LATCHES
#define	__mutex_rdlock(a, b)		__db_pthread_mutex_readlock(a, b)
#define	__mutex_tryrdlock(a, b)		__db_pthread_mutex_tryreadlock(a, b)
static inline int __db_pthread_mutex_tryreadlock(ENV *env, db_mutex_t mutex)
{
	int ret;
	DB_MUTEX *mutexp;
	if (!MUTEX_ON(env) || F_ISSET(env->dbenv, DB_ENV_NOLOCKING))
		return (0);
	mutexp = MUTEXP_SET(env, mutex);
	if (F_ISSET(mutexp, DB_MUTEX_SHARED))
		ret = pthread_rwlock_tryrdlock(&mutexp->u.rwlock);
	else
		return (EINVAL);
	if (ret == EBUSY)
		ret = DB_LOCK_NOTGRANTED;
#ifdef HAVE_STATISTICS
	if (ret == 0)
		STAT_INC(env,
		    mutex, set_rd_nowait, mutexp->mutex_set_nowait, mutex);
#endif
	return (ret);
}
#endif
#elif defined(HAVE_MUTEX_WIN32) || defined(HAVE_MUTEX_WIN32_GCC)
#define	__mutex_init(a, b, c)		__db_win32_mutex_init(a, b, c)
#define	__mutex_lock(a, b)		__db_win32_mutex_lock(a, b, 0)
#define	__mutex_timedlock(a, b, c)	__db_win32_mutex_lock(a, b, c)
#define	__mutex_trylock(a, b)		__db_win32_mutex_trylock(a, b)
#define	__mutex_unlock(a, b)		__db_win32_mutex_unlock(a, b)
#define	__mutex_destroy(a, b)		__db_win32_mutex_destroy(a, b)
#ifdef HAVE_SHARED_LATCHES
#define	__mutex_rdlock(a, b)		__db_win32_mutex_readlock(a, b)
#define	__mutex_tryrdlock(a, b)		__db_win32_mutex_tryreadlock(a, b)
#endif
#elif defined(HAVE_MUTEX_FCNTL)
#define	__mutex_init(a, b, c)		__db_fcntl_mutex_init(a, b, c)
#define	__mutex_lock(a, b)		__db_fcntl_mutex_lock(a, b, 0)
#define	__mutex_timedlock(a, b, c)	__db_fcntl_lock(a, b, c)
#define	__mutex_trylock(a, b)		__db_fcntl_mutex_trylock(a, b)
#define	__mutex_unlock(a, b)		__db_fcntl_mutex_unlock(a, b)
#define	__mutex_destroy(a, b)		__db_fcntl_mutex_destroy(a, b)
#else
#define	__mutex_init(a, b, c)		__db_tas_mutex_init(a, b, c)
#define	__mutex_lock(a, b)		__db_tas_mutex_lock(a, b, 0)
#define	__mutex_timedlock(a, b, c)	__db_tas_mutex_lock(a, b, c)
#define	__mutex_trylock(a, b)		__db_tas_mutex_trylock(a, b)
#define	__mutex_unlock(a, b)		__db_tas_mutex_unlock(a, b)
#define	__mutex_destroy(a, b)		__db_tas_mutex_destroy(a, b)
#if defined(HAVE_SHARED_LATCHES)
#define	__mutex_rdlock(a, b)		__db_tas_mutex_readlock(a, b)
#define	__mutex_tryrdlock(a,b)		__db_tas_mutex_tryreadlock(a, b)
#endif
#endif

/*
 * When there is no method to get a shared latch, fall back to
 * implementing __mutex_rdlock() as getting an exclusive one.
 * This occurs either when !HAVE_SHARED_LATCHES or HAVE_MUTEX_FCNTL.
 */
#ifndef __mutex_rdlock
#define	__mutex_rdlock(a, b)		__mutex_lock(a, b)
#endif
#ifndef __mutex_tryrdlock
#define	__mutex_tryrdlock(a, b)		__mutex_trylock(a, b)
#endif

/*
 * Lock/unlock a mutex.  If the mutex was never required, the thread of
 * control can proceed without it.
 *
 * We never fail to acquire or release a mutex without panicing.  Simplify
 * the macros to always return a panic value rather than saving the actual
 * return value of the mutex routine.
 */
#ifdef HAVE_MUTEX_SUPPORT
#define	MUTEX_LOCK(env, mutex) do {					\
	if ((mutex) != MUTEX_INVALID &&					\
	    __mutex_lock(env, mutex) != 0)				\
		return (DB_RUNRECOVERY);				\
} while (0)

/*
 * Always check the return value of MUTEX_TRYLOCK()!  Expect 0 on success,
 * or DB_LOCK_NOTGRANTED, or possibly DB_RUNRECOVERY for failchk.
 */
#define	MUTEX_TRYLOCK(env, mutex)					\
	(((mutex) == MUTEX_INVALID) ? 0 : __mutex_trylock(env, mutex))

/*
 * Acquire a DB_MUTEX_SHARED "mutex" in shared mode.
 */
#define	MUTEX_READLOCK(env, mutex) do {					\
	if ((mutex) != MUTEX_INVALID &&					\
	    __mutex_rdlock(env, mutex) != 0)				\
		return (DB_RUNRECOVERY);				\
} while (0)
#define	MUTEX_TRY_READLOCK(env, mutex)					\
	((mutex) != MUTEX_INVALID ? __mutex_tryrdlock(env, mutex) : 0)

#define	MUTEX_UNLOCK(env, mutex) do {					\
	if ((mutex) != MUTEX_INVALID &&					\
	    __mutex_unlock(env, mutex) != 0)				\
		return (DB_RUNRECOVERY);				\
} while (0)

#define	MUTEX_WAIT(env, mutex, duration) do {			      \
	int __ret;						      \
	if ((mutex) != MUTEX_INVALID &&				      \
	    (__ret = __mutex_timedlock(env, mutex, duration)) != 0 && \
	    __ret != DB_TIMEOUT)				      \
		return (DB_RUNRECOVERY);			      \
} while (0)
#else
/*
 * There are calls to lock/unlock mutexes outside of #ifdef's -- replace
 * the call with something the compiler can discard, but which will make
 * if-then-else blocks work correctly.
 */
#define	MUTEX_LOCK(env, mutex)		(mutex) = (mutex)
#define	MUTEX_TRYLOCK(env, mutex)	(mutex) = (mutex)
#define	MUTEX_READLOCK(env, mutex)	(mutex) = (mutex)
#define	MUTEX_TRY_READLOCK(env, mutex)	(mutex) = (mutex)
#define	MUTEX_UNLOCK(env, mutex)	(mutex) = (mutex)
#define	MUTEX_REQUIRED(env, mutex)	(mutex) = (mutex)
#define	MUTEX_REQUIRED_READ(env, mutex)	(mutex) = (mutex)
#define	MUTEX_WAIT(env, mutex, duration) (mutex) = (mutex)
#endif

/*
 * Berkeley DB ports may require single-threading at places in the code.
 */
#ifdef HAVE_MUTEX_VXWORKS
#include "taskLib.h"
/*
 * Use the taskLock() mutex to eliminate a race where two tasks are
 * trying to initialize the global lock at the same time.
 */
#define	DB_BEGIN_SINGLE_THREAD do {					\
	if (DB_GLOBAL(db_global_init))					\
		(void)semTake(DB_GLOBAL(db_global_lock), WAIT_FOREVER);	\
	else {								\
		taskLock();						\
		if (DB_GLOBAL(db_global_init)) {			\
			taskUnlock();					\
			(void)semTake(DB_GLOBAL(db_global_lock),	\
			    WAIT_FOREVER);				\
			continue;					\
		}							\
		DB_GLOBAL(db_global_lock) =				\
		    semBCreate(SEM_Q_FIFO, SEM_EMPTY);			\
		if (DB_GLOBAL(db_global_lock) != NULL)			\
			DB_GLOBAL(db_global_init) = 1;			\
		taskUnlock();						\
	}								\
} while (DB_GLOBAL(db_global_init) == 0)
#define	DB_END_SINGLE_THREAD	(void)semGive(DB_GLOBAL(db_global_lock))
#endif

/*
 * Single-threading defaults to a no-op.
 */
#ifndef DB_BEGIN_SINGLE_THREAD
#define	DB_BEGIN_SINGLE_THREAD
#endif
#ifndef DB_END_SINGLE_THREAD
#define	DB_END_SINGLE_THREAD
#endif

#if defined(__cplusplus)
}
#endif

#include "dbinc_auto/mutex_ext.h"
#endif /* !_DB_MUTEX_H_ */