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#include "db_config.h"
10
11#include "db_int.h"
12
13/*
14 * __mutex_alloc_pp --
15 *	Allocate a mutex, application method.
16 *
17 * PUBLIC: int __mutex_alloc_pp __P((DB_ENV *, u_int32_t, db_mutex_t *));
18 */
19int
20__mutex_alloc_pp(dbenv, flags, indxp)
21	DB_ENV *dbenv;
22	u_int32_t flags;
23	db_mutex_t *indxp;
24{
25	DB_THREAD_INFO *ip;
26	ENV *env;
27	int ret;
28
29	env = dbenv->env;
30
31	if ((ret = __db_fchk(env, "DB_ENV->mutex_alloc",
32	    flags, DB_MUTEX_PROCESS_ONLY | DB_MUTEX_SELF_BLOCK)) != 0)
33		return (ret);
34
35	ENV_ENTER(env, ip);
36	ret = __mutex_alloc(env, MTX_APPLICATION, flags, indxp);
37	ENV_LEAVE(env, ip);
38
39	return (ret);
40}
41
42/*
43 * __mutex_free_pp --
44 *	Destroy a mutex, application method.
45 *
46 * PUBLIC: int __mutex_free_pp __P((DB_ENV *, db_mutex_t));
47 */
48int
49__mutex_free_pp(dbenv, indx)
50	DB_ENV *dbenv;
51	db_mutex_t indx;
52{
53	DB_THREAD_INFO *ip;
54	ENV *env;
55	int ret;
56
57	env = dbenv->env;
58
59	if (indx == MUTEX_INVALID)
60		return (EINVAL);
61
62	/*
63	 * Internally Berkeley DB passes around the db_mutex_t address on
64	 * free, because we want to make absolutely sure the slot gets
65	 * overwritten with MUTEX_INVALID.  We don't export MUTEX_INVALID,
66	 * so we don't export that part of the API, either.
67	 */
68	ENV_ENTER(env, ip);
69	ret = __mutex_free(env, &indx);
70	ENV_LEAVE(env, ip);
71
72	return (ret);
73}
74
75/*
76 * __mutex_lock --
77 *	Lock a mutex, application method.
78 *
79 * PUBLIC: int __mutex_lock_pp __P((DB_ENV *, db_mutex_t));
80 */
81int
82__mutex_lock_pp(dbenv, indx)
83	DB_ENV *dbenv;
84	db_mutex_t indx;
85{
86	DB_THREAD_INFO *ip;
87	ENV *env;
88	int ret;
89
90	env = dbenv->env;
91
92	if (indx == MUTEX_INVALID)
93		return (EINVAL);
94
95	ENV_ENTER(env, ip);
96	ret = __mutex_lock(env, indx);
97	ENV_LEAVE(env, ip);
98	return (ret);
99}
100
101/*
102 * __mutex_unlock --
103 *	Unlock a mutex, application method.
104 *
105 * PUBLIC: int __mutex_unlock_pp __P((DB_ENV *, db_mutex_t));
106 */
107int
108__mutex_unlock_pp(dbenv, indx)
109	DB_ENV *dbenv;
110	db_mutex_t indx;
111{
112	DB_THREAD_INFO *ip;
113	ENV *env;
114	int ret;
115
116	env = dbenv->env;
117
118	if (indx == MUTEX_INVALID)
119		return (EINVAL);
120
121	ENV_ENTER(env, ip);
122	ret = __mutex_unlock(env, indx);
123	ENV_LEAVE(env, ip);
124	return (ret);
125}
126
127/*
128 * __mutex_get_align --
129 *	DB_ENV->mutex_get_align.
130 *
131 * PUBLIC: int __mutex_get_align __P((DB_ENV *, u_int32_t *));
132 */
133int
134__mutex_get_align(dbenv, alignp)
135	DB_ENV *dbenv;
136	u_int32_t *alignp;
137{
138	ENV *env;
139
140	env = dbenv->env;
141
142	if (MUTEX_ON(env)) {
143		/* Cannot be set after open, no lock required to read. */
144		*alignp = ((DB_MUTEXREGION *)
145		    env->mutex_handle->reginfo.primary)->stat.st_mutex_align;
146	} else
147		*alignp = dbenv->mutex_align;
148	return (0);
149}
150
151/*
152 * __mutex_set_align --
153 *	DB_ENV->mutex_set_align.
154 *
155 * PUBLIC: int __mutex_set_align __P((DB_ENV *, u_int32_t));
156 */
157int
158__mutex_set_align(dbenv, align)
159	DB_ENV *dbenv;
160	u_int32_t align;
161{
162	ENV *env;
163
164	env = dbenv->env;
165
166	ENV_ILLEGAL_AFTER_OPEN(env, "DB_ENV->set_mutex_align");
167
168	if (align == 0 || !POWER_OF_TWO(align)) {
169		__db_errx(env,
170    "DB_ENV->mutex_set_align: alignment value must be a non-zero power-of-two");
171		return (EINVAL);
172	}
173
174	dbenv->mutex_align = align;
175	return (0);
176}
177
178/*
179 * __mutex_get_increment --
180 *	DB_ENV->mutex_get_increment.
181 *
182 * PUBLIC: int __mutex_get_increment __P((DB_ENV *, u_int32_t *));
183 */
184int
185__mutex_get_increment(dbenv, incrementp)
186	DB_ENV *dbenv;
187	u_int32_t *incrementp;
188{
189	/*
190	 * We don't maintain the increment in the region (it just makes
191	 * no sense).  Return whatever we have configured on this handle,
192	 * nobody is ever going to notice.
193	 */
194	*incrementp = dbenv->mutex_inc;
195	return (0);
196}
197
198/*
199 * __mutex_set_increment --
200 *	DB_ENV->mutex_set_increment.
201 *
202 * PUBLIC: int __mutex_set_increment __P((DB_ENV *, u_int32_t));
203 */
204int
205__mutex_set_increment(dbenv, increment)
206	DB_ENV *dbenv;
207	u_int32_t increment;
208{
209	ENV *env;
210
211	env = dbenv->env;
212
213	ENV_ILLEGAL_AFTER_OPEN(env, "DB_ENV->set_mutex_increment");
214
215	dbenv->mutex_cnt = 0;
216	dbenv->mutex_inc = increment;
217	return (0);
218}
219
220/*
221 * __mutex_get_max --
222 *	DB_ENV->mutex_get_max.
223 *
224 * PUBLIC: int __mutex_get_max __P((DB_ENV *, u_int32_t *));
225 */
226int
227__mutex_get_max(dbenv, maxp)
228	DB_ENV *dbenv;
229	u_int32_t *maxp;
230{
231	ENV *env;
232
233	env = dbenv->env;
234
235	if (MUTEX_ON(env)) {
236		/* Cannot be set after open, no lock required to read. */
237		*maxp = ((DB_MUTEXREGION *)
238		    env->mutex_handle->reginfo.primary)->stat.st_mutex_cnt;
239	} else
240		*maxp = dbenv->mutex_cnt;
241	return (0);
242}
243
244/*
245 * __mutex_set_max --
246 *	DB_ENV->mutex_set_max.
247 *
248 * PUBLIC: int __mutex_set_max __P((DB_ENV *, u_int32_t));
249 */
250int
251__mutex_set_max(dbenv, max)
252	DB_ENV *dbenv;
253	u_int32_t max;
254{
255	ENV *env;
256
257	env = dbenv->env;
258
259	ENV_ILLEGAL_AFTER_OPEN(env, "DB_ENV->set_mutex_max");
260
261	dbenv->mutex_cnt = max;
262	dbenv->mutex_inc = 0;
263	return (0);
264}
265
266/*
267 * __mutex_get_tas_spins --
268 *	DB_ENV->mutex_get_tas_spins.
269 *
270 * PUBLIC: int __mutex_get_tas_spins __P((DB_ENV *, u_int32_t *));
271 */
272int
273__mutex_get_tas_spins(dbenv, tas_spinsp)
274	DB_ENV *dbenv;
275	u_int32_t *tas_spinsp;
276{
277	ENV *env;
278
279	env = dbenv->env;
280
281	if (MUTEX_ON(env)) {
282		/* Cannot be set after open, no lock required to read. */
283		*tas_spinsp = ((DB_MUTEXREGION *)env->
284		    mutex_handle->reginfo.primary)->stat.st_mutex_tas_spins;
285	} else
286		*tas_spinsp = dbenv->mutex_tas_spins;
287	return (0);
288}
289
290/*
291 * __mutex_set_tas_spins --
292 *	DB_ENV->mutex_set_tas_spins.
293 *
294 * PUBLIC: int __mutex_set_tas_spins __P((DB_ENV *, u_int32_t));
295 */
296int
297__mutex_set_tas_spins(dbenv, tas_spins)
298	DB_ENV *dbenv;
299	u_int32_t tas_spins;
300{
301	ENV *env;
302
303	env = dbenv->env;
304
305	/*
306	 * Bound the value -- less than 1 makes no sense, greater than 1M
307	 * makes no sense.
308	 */
309	if (tas_spins == 0)
310		tas_spins = 1;
311	else if (tas_spins > 1000000)
312		tas_spins = 1000000;
313
314	/*
315	 * There's a theoretical race here, but I'm not interested in locking
316	 * the test-and-set spin count.  The worst possibility is a thread
317	 * reads out a bad spin count and spins until it gets the lock, but
318	 * that's awfully unlikely.
319	 */
320	if (MUTEX_ON(env))
321		((DB_MUTEXREGION *)env->mutex_handle
322		    ->reginfo.primary)->stat.st_mutex_tas_spins = tas_spins;
323	else
324		dbenv->mutex_tas_spins = tas_spins;
325	return (0);
326}
327
328#if !defined(HAVE_ATOMIC_SUPPORT) && defined(HAVE_MUTEX_SUPPORT)
329/*
330 * Provide atomic operations for platforms which have mutexes yet do not have
331 * native atomic operations configured. They are emulated by protected the
332 * operation with a mutex.  The address of the atomic value selects which
333 * mutex to use.
334 */
335/*
336 * atomic_get_mutex -
337 *	Map an address to the mutex to use to atomically modify it
338 */
339static inline db_mutex_t atomic_get_mutex(env, v)
340	ENV *env;
341	db_atomic_t *v;
342{
343	u_int	index;
344	DB_MUTEXREGION *mtxreg;
345
346	if (!MUTEX_ON(env))
347		return (MUTEX_INVALID);
348	index = (u_int)(((uintptr_t) (v)) >> 6) % MAX_ATOMIC_MUTEXES;
349	mtxreg = (DB_MUTEXREGION *)env->mutex_handle->reginfo.primary;
350	return (mtxreg->mtx_atomic[index]);
351}
352
353/*
354 * __atomic_inc
355 *	Use a mutex to provide an atomic increment function
356 *
357 * PUBLIC: #if !defined(HAVE_ATOMIC_SUPPORT) && defined(HAVE_MUTEX_SUPPORT)
358 * PUBLIC: atomic_value_t __atomic_inc __P((ENV *, db_atomic_t *));
359 * PUBLIC: #endif
360 */
361atomic_value_t
362__atomic_inc(env, v)
363	ENV *env;
364	db_atomic_t *v;
365{
366	db_mutex_t mtx;
367	int ret;
368
369	mtx = atomic_get_mutex(env, v);
370	MUTEX_LOCK(env, mtx);
371	ret = ++v->value;
372	MUTEX_UNLOCK(env, mtx);
373
374	return (ret);
375}
376
377/*
378 * __atomic_dec
379 *	Use a mutex to provide an atomic decrement function
380 *
381 * PUBLIC: #if !defined(HAVE_ATOMIC_SUPPORT) && defined(HAVE_MUTEX_SUPPORT)
382 * PUBLIC: atomic_value_t __atomic_dec __P((ENV *, db_atomic_t *));
383 * PUBLIC: #endif
384 */
385atomic_value_t
386__atomic_dec(env, v)
387	ENV *env;
388	db_atomic_t *v;
389{
390	db_mutex_t mtx;
391	int ret;
392
393	mtx = atomic_get_mutex(env, v);
394	MUTEX_LOCK(env, mtx);
395	ret = --v->value;
396	MUTEX_UNLOCK(env, mtx);
397
398	return (ret);
399}
400
401/*
402 * atomic_compare_exchange
403 *	Use a mutex to provide an atomic decrement function
404 *
405 * PRIVATE: int atomic_compare_exchange
406 * PRIVATE:     __P((ENV *, db_atomic_t *, atomic_value_t, atomic_value_t));
407 *	Returns 1 if the *v was equal to oldval, else 0
408 *
409 *	Side Effect:
410 *		Sets the value to newval if and only if returning 1
411 */
412int
413atomic_compare_exchange(env, v, oldval, newval)
414	ENV *env;
415	db_atomic_t *v;
416	atomic_value_t oldval;
417	atomic_value_t newval;
418{
419	db_mutex_t mtx;
420	int ret;
421
422	if (atomic_read(v) != oldval)
423		return (0);
424
425	mtx = atomic_get_mutex(env, v);
426	MUTEX_LOCK(env, mtx);
427	ret = atomic_read(v) == oldval;
428	if (ret)
429		atomic_init(v, newval);
430	MUTEX_UNLOCK(env, mtx);
431
432	return (ret);
433}
434#endif
435