1/*-
2 * See the file LICENSE for redistribution information.
3 *
4 * Copyright (c) 1996,2008 Oracle.  All rights reserved.
5 *
6 * $Id: mut_method.c,v 12.17 2008/01/08 20:58:43 bostic Exp $
7 */
8
9#include "db_config.h"
10
11#include "db_int.h"
12#include "dbinc/mutex_int.h"
13
14/*
15 * __mutex_alloc_pp --
16 *	Allocate a mutex, application method.
17 *
18 * PUBLIC: int __mutex_alloc_pp __P((DB_ENV *, u_int32_t, db_mutex_t *));
19 */
20int
21__mutex_alloc_pp(dbenv, flags, indxp)
22	DB_ENV *dbenv;
23	u_int32_t flags;
24	db_mutex_t *indxp;
25{
26	DB_THREAD_INFO *ip;
27	ENV *env;
28	int ret;
29
30	env = dbenv->env;
31
32	switch (flags) {
33	case 0:
34	case DB_MUTEX_PROCESS_ONLY:
35	case DB_MUTEX_SELF_BLOCK:
36		break;
37	default:
38		return (__db_ferr(env, "DB_ENV->mutex_alloc", 0));
39	}
40
41	ENV_ENTER(env, ip);
42	ret = __mutex_alloc(env, MTX_APPLICATION, flags, indxp);
43	ENV_LEAVE(env, ip);
44
45	return (ret);
46}
47
48/*
49 * __mutex_free_pp --
50 *	Destroy a mutex, application method.
51 *
52 * PUBLIC: int __mutex_free_pp __P((DB_ENV *, db_mutex_t));
53 */
54int
55__mutex_free_pp(dbenv, indx)
56	DB_ENV *dbenv;
57	db_mutex_t indx;
58{
59	DB_THREAD_INFO *ip;
60	ENV *env;
61	int ret;
62
63	env = dbenv->env;
64
65	if (indx == MUTEX_INVALID)
66		return (EINVAL);
67
68	/*
69	 * Internally Berkeley DB passes around the db_mutex_t address on
70	 * free, because we want to make absolutely sure the slot gets
71	 * overwritten with MUTEX_INVALID.  We don't export MUTEX_INVALID,
72	 * so we don't export that part of the API, either.
73	 */
74	ENV_ENTER(env, ip);
75	ret = __mutex_free(env, &indx);
76	ENV_LEAVE(env, ip);
77
78	return (ret);
79}
80
81/*
82 * __mutex_lock --
83 *	Lock a mutex, application method.
84 *
85 * PUBLIC: int __mutex_lock_pp __P((DB_ENV *, db_mutex_t));
86 */
87int
88__mutex_lock_pp(dbenv, indx)
89	DB_ENV *dbenv;
90	db_mutex_t indx;
91{
92	DB_THREAD_INFO *ip;
93	ENV *env;
94	int ret;
95
96	env = dbenv->env;
97
98	if (indx == MUTEX_INVALID)
99		return (EINVAL);
100
101	ENV_ENTER(env, ip);
102	ret = __mutex_lock(env, indx);
103	ENV_LEAVE(env, ip);
104	return (ret);
105}
106
107/*
108 * __mutex_unlock --
109 *	Unlock a mutex, application method.
110 *
111 * PUBLIC: int __mutex_unlock_pp __P((DB_ENV *, db_mutex_t));
112 */
113int
114__mutex_unlock_pp(dbenv, indx)
115	DB_ENV *dbenv;
116	db_mutex_t indx;
117{
118	DB_THREAD_INFO *ip;
119	ENV *env;
120	int ret;
121
122	env = dbenv->env;
123
124	if (indx == MUTEX_INVALID)
125		return (EINVAL);
126
127	ENV_ENTER(env, ip);
128	ret = __mutex_unlock(env, indx);
129	ENV_LEAVE(env, ip);
130	return (ret);
131}
132
133/*
134 * __mutex_get_align --
135 *	DB_ENV->mutex_get_align.
136 *
137 * PUBLIC: int __mutex_get_align __P((DB_ENV *, u_int32_t *));
138 */
139int
140__mutex_get_align(dbenv, alignp)
141	DB_ENV *dbenv;
142	u_int32_t *alignp;
143{
144	ENV *env;
145
146	env = dbenv->env;
147
148	if (MUTEX_ON(env)) {
149		/* Cannot be set after open, no lock required to read. */
150		*alignp = ((DB_MUTEXREGION *)
151		    env->mutex_handle->reginfo.primary)->stat.st_mutex_align;
152	} else
153		*alignp = dbenv->mutex_align;
154	return (0);
155}
156
157/*
158 * __mutex_set_align --
159 *	DB_ENV->mutex_set_align.
160 *
161 * PUBLIC: int __mutex_set_align __P((DB_ENV *, u_int32_t));
162 */
163int
164__mutex_set_align(dbenv, align)
165	DB_ENV *dbenv;
166	u_int32_t align;
167{
168	ENV *env;
169
170	env = dbenv->env;
171
172	ENV_ILLEGAL_AFTER_OPEN(env, "DB_ENV->set_mutex_align");
173
174	if (align == 0 || !POWER_OF_TWO(align)) {
175		__db_errx(env,
176    "DB_ENV->mutex_set_align: alignment value must be a non-zero power-of-two");
177		return (EINVAL);
178	}
179
180	dbenv->mutex_align = align;
181	return (0);
182}
183
184/*
185 * __mutex_get_increment --
186 *	DB_ENV->mutex_get_increment.
187 *
188 * PUBLIC: int __mutex_get_increment __P((DB_ENV *, u_int32_t *));
189 */
190int
191__mutex_get_increment(dbenv, incrementp)
192	DB_ENV *dbenv;
193	u_int32_t *incrementp;
194{
195	/*
196	 * We don't maintain the increment in the region (it just makes
197	 * no sense).  Return whatever we have configured on this handle,
198	 * nobody is ever going to notice.
199	 */
200	*incrementp = dbenv->mutex_inc;
201	return (0);
202}
203
204/*
205 * __mutex_set_increment --
206 *	DB_ENV->mutex_set_increment.
207 *
208 * PUBLIC: int __mutex_set_increment __P((DB_ENV *, u_int32_t));
209 */
210int
211__mutex_set_increment(dbenv, increment)
212	DB_ENV *dbenv;
213	u_int32_t increment;
214{
215	ENV *env;
216
217	env = dbenv->env;
218
219	ENV_ILLEGAL_AFTER_OPEN(env, "DB_ENV->set_mutex_increment");
220
221	dbenv->mutex_cnt = 0;
222	dbenv->mutex_inc = increment;
223	return (0);
224}
225
226/*
227 * __mutex_get_max --
228 *	DB_ENV->mutex_get_max.
229 *
230 * PUBLIC: int __mutex_get_max __P((DB_ENV *, u_int32_t *));
231 */
232int
233__mutex_get_max(dbenv, maxp)
234	DB_ENV *dbenv;
235	u_int32_t *maxp;
236{
237	ENV *env;
238
239	env = dbenv->env;
240
241	if (MUTEX_ON(env)) {
242		/* Cannot be set after open, no lock required to read. */
243		*maxp = ((DB_MUTEXREGION *)
244		    env->mutex_handle->reginfo.primary)->stat.st_mutex_cnt;
245	} else
246		*maxp = dbenv->mutex_cnt;
247	return (0);
248}
249
250/*
251 * __mutex_set_max --
252 *	DB_ENV->mutex_set_max.
253 *
254 * PUBLIC: int __mutex_set_max __P((DB_ENV *, u_int32_t));
255 */
256int
257__mutex_set_max(dbenv, max)
258	DB_ENV *dbenv;
259	u_int32_t max;
260{
261	ENV *env;
262
263	env = dbenv->env;
264
265	ENV_ILLEGAL_AFTER_OPEN(env, "DB_ENV->set_mutex_max");
266
267	dbenv->mutex_cnt = max;
268	dbenv->mutex_inc = 0;
269	return (0);
270}
271
272/*
273 * __mutex_get_tas_spins --
274 *	DB_ENV->mutex_get_tas_spins.
275 *
276 * PUBLIC: int __mutex_get_tas_spins __P((DB_ENV *, u_int32_t *));
277 */
278int
279__mutex_get_tas_spins(dbenv, tas_spinsp)
280	DB_ENV *dbenv;
281	u_int32_t *tas_spinsp;
282{
283	ENV *env;
284
285	env = dbenv->env;
286
287	if (MUTEX_ON(env)) {
288		/* Cannot be set after open, no lock required to read. */
289		*tas_spinsp = ((DB_MUTEXREGION *)env->
290		    mutex_handle->reginfo.primary)->stat.st_mutex_tas_spins;
291	} else
292		*tas_spinsp = dbenv->mutex_tas_spins;
293	return (0);
294}
295
296/*
297 * __mutex_set_tas_spins --
298 *	DB_ENV->mutex_set_tas_spins.
299 *
300 * PUBLIC: int __mutex_set_tas_spins __P((DB_ENV *, u_int32_t));
301 */
302int
303__mutex_set_tas_spins(dbenv, tas_spins)
304	DB_ENV *dbenv;
305	u_int32_t tas_spins;
306{
307	ENV *env;
308
309	env = dbenv->env;
310
311	/*
312	 * Bound the value -- less than 1 makes no sense, greater than 1M
313	 * makes no sense.
314	 */
315	if (tas_spins == 0)
316		tas_spins = 1;
317	else if (tas_spins > 1000000)
318		tas_spins = 1000000;
319
320	/*
321	 * There's a theoretical race here, but I'm not interested in locking
322	 * the test-and-set spin count.  The worst possibility is a thread
323	 * reads out a bad spin count and spins until it gets the lock, but
324	 * that's awfully unlikely.
325	 */
326	if (MUTEX_ON(env))
327		((DB_MUTEXREGION *)env->mutex_handle
328		    ->reginfo.primary)->stat.st_mutex_tas_spins = tas_spins;
329	else
330		dbenv->mutex_tas_spins = tas_spins;
331	return (0);
332}
333