1/*-
2 * See the file LICENSE for redistribution information.
3 *
4 * Copyright (c) 1999,2008 Oracle.  All rights reserved.
5 *
6 * $Id: mut_alloc.c,v 12.23 2008/02/27 17:00:33 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 --
16 *	Allocate a mutex from the mutex region.
17 *
18 * PUBLIC: int __mutex_alloc __P((ENV *, int, u_int32_t, db_mutex_t *));
19 */
20int
21__mutex_alloc(env, alloc_id, flags, indxp)
22	ENV *env;
23	int alloc_id;
24	u_int32_t flags;
25	db_mutex_t *indxp;
26{
27	int ret;
28
29	/* The caller may depend on us to initialize. */
30	*indxp = MUTEX_INVALID;
31
32	/*
33	 * If this is not an application lock, and we've turned off locking,
34	 * or the ENV handle isn't thread-safe, and this is a thread lock
35	 * or the environment isn't multi-process by definition, there's no
36	 * need to mutex at all.
37	 */
38	if (alloc_id != MTX_APPLICATION &&
39	    (F_ISSET(env->dbenv, DB_ENV_NOLOCKING) ||
40	    (!F_ISSET(env, ENV_THREAD) &&
41	    (LF_ISSET(DB_MUTEX_PROCESS_ONLY) ||
42	    F_ISSET(env, ENV_PRIVATE)))))
43		return (0);
44
45	/* Private environments never share mutexes. */
46	if (F_ISSET(env, ENV_PRIVATE))
47		LF_SET(DB_MUTEX_PROCESS_ONLY);
48
49	/*
50	 * If we have a region in which to allocate the mutexes, lock it and
51	 * do the allocation.
52	 */
53	if (MUTEX_ON(env))
54		return (__mutex_alloc_int(env, 1, alloc_id, flags, indxp));
55
56	/*
57	 * We have to allocate some number of mutexes before we have a region
58	 * in which to allocate them.  We handle this by saving up the list of
59	 * flags and allocating them as soon as we have a handle.
60	 *
61	 * The list of mutexes to alloc is maintained in pairs: first the
62	 * alloc_id argument, second the flags passed in by the caller.
63	 */
64	if (env->mutex_iq == NULL) {
65		env->mutex_iq_max = 50;
66		if ((ret = __os_calloc(env, env->mutex_iq_max,
67		    sizeof(env->mutex_iq[0]), &env->mutex_iq)) != 0)
68			return (ret);
69	} else if (env->mutex_iq_next == env->mutex_iq_max - 1) {
70		env->mutex_iq_max *= 2;
71		if ((ret = __os_realloc(env,
72		    env->mutex_iq_max * sizeof(env->mutex_iq[0]),
73		    &env->mutex_iq)) != 0)
74			return (ret);
75	}
76	*indxp = env->mutex_iq_next + 1;	/* Correct for MUTEX_INVALID. */
77	env->mutex_iq[env->mutex_iq_next].alloc_id = alloc_id;
78	env->mutex_iq[env->mutex_iq_next].flags = flags;
79	++env->mutex_iq_next;
80
81	return (0);
82}
83
84/*
85 * __mutex_alloc_int --
86 *	Internal routine to allocate a mutex.
87 *
88 * PUBLIC: int __mutex_alloc_int
89 * PUBLIC:	__P((ENV *, int, int, u_int32_t, db_mutex_t *));
90 */
91int
92__mutex_alloc_int(env, locksys, alloc_id, flags, indxp)
93	ENV *env;
94	int locksys, alloc_id;
95	u_int32_t flags;
96	db_mutex_t *indxp;
97{
98	DB_ENV *dbenv;
99	DB_MUTEX *mutexp;
100	DB_MUTEXMGR *mtxmgr;
101	DB_MUTEXREGION *mtxregion;
102	int ret;
103
104	dbenv = env->dbenv;
105	mtxmgr = env->mutex_handle;
106	mtxregion = mtxmgr->reginfo.primary;
107	ret = 0;
108
109	/*
110	 * If we're not initializing the mutex region, then lock the region to
111	 * allocate new mutexes.  Drop the lock before initializing the mutex,
112	 * mutex initialization may require a system call.
113	 */
114	if (locksys)
115		MUTEX_SYSTEM_LOCK(env);
116
117	if (mtxregion->mutex_next == MUTEX_INVALID) {
118		__db_errx(env,
119		    "unable to allocate memory for mutex; resize mutex region");
120		if (locksys)
121			MUTEX_SYSTEM_UNLOCK(env);
122		return (ENOMEM);
123	}
124
125	*indxp = mtxregion->mutex_next;
126	mutexp = MUTEXP_SET(*indxp);
127	DB_ASSERT(env,
128	    ((uintptr_t)mutexp & (dbenv->mutex_align - 1)) == 0);
129	mtxregion->mutex_next = mutexp->mutex_next_link;
130
131	--mtxregion->stat.st_mutex_free;
132	++mtxregion->stat.st_mutex_inuse;
133	if (mtxregion->stat.st_mutex_inuse > mtxregion->stat.st_mutex_inuse_max)
134		mtxregion->stat.st_mutex_inuse_max =
135		    mtxregion->stat.st_mutex_inuse;
136	if (locksys)
137		MUTEX_SYSTEM_UNLOCK(env);
138
139	/* Initialize the mutex. */
140	memset(mutexp, 0, sizeof(*mutexp));
141	F_SET(mutexp, DB_MUTEX_ALLOCATED |
142	    LF_ISSET(DB_MUTEX_LOGICAL_LOCK | DB_MUTEX_PROCESS_ONLY));
143
144	/*
145	 * If the mutex is associated with a single process, set the process
146	 * ID.  If the application ever calls DbEnv::failchk, we'll need the
147	 * process ID to know if the mutex is still in use.
148	 */
149	if (LF_ISSET(DB_MUTEX_PROCESS_ONLY))
150		dbenv->thread_id(dbenv, &mutexp->pid, NULL);
151
152#ifdef HAVE_STATISTICS
153	mutexp->alloc_id = alloc_id;
154#else
155	COMPQUIET(alloc_id, 0);
156#endif
157
158	if ((ret = __mutex_init(env, *indxp, flags)) != 0)
159		(void)__mutex_free_int(env, locksys, indxp);
160
161	return (ret);
162}
163
164/*
165 * __mutex_free --
166 *	Free a mutex.
167 *
168 * PUBLIC: int __mutex_free __P((ENV *, db_mutex_t *));
169 */
170int
171__mutex_free(env, indxp)
172	ENV *env;
173	db_mutex_t *indxp;
174{
175	/*
176	 * There is no explicit ordering in how the regions are cleaned up
177	 * up and/or discarded when an environment is destroyed (either a
178	 * private environment is closed or a public environment is removed).
179	 * The way we deal with mutexes is to clean up all remaining mutexes
180	 * when we close the mutex environment (because we have to be able to
181	 * do that anyway, after a crash), which means we don't have to deal
182	 * with region cleanup ordering on normal environment destruction.
183	 * All that said, what it really means is we can get here without a
184	 * mpool region.  It's OK, the mutex has been, or will be, destroyed.
185	 *
186	 * If the mutex has never been configured, we're done.
187	 */
188	if (!MUTEX_ON(env) || *indxp == MUTEX_INVALID)
189		return (0);
190
191	return (__mutex_free_int(env, 1, indxp));
192}
193
194/*
195 * __mutex_free_int --
196 *	Internal routine to free a mutex.
197 *
198 * PUBLIC: int __mutex_free_int __P((ENV *, int, db_mutex_t *));
199 */
200int
201__mutex_free_int(env, locksys, indxp)
202	ENV *env;
203	int locksys;
204	db_mutex_t *indxp;
205{
206	DB_MUTEX *mutexp;
207	DB_MUTEXMGR *mtxmgr;
208	DB_MUTEXREGION *mtxregion;
209	db_mutex_t mutex;
210	int ret;
211
212	mutex = *indxp;
213	*indxp = MUTEX_INVALID;
214
215	mtxmgr = env->mutex_handle;
216	mtxregion = mtxmgr->reginfo.primary;
217	mutexp = MUTEXP_SET(mutex);
218
219	DB_ASSERT(env, F_ISSET(mutexp, DB_MUTEX_ALLOCATED));
220	F_CLR(mutexp, DB_MUTEX_ALLOCATED);
221
222	ret = __mutex_destroy(env, mutex);
223
224	if (locksys)
225		MUTEX_SYSTEM_LOCK(env);
226
227	/* Link the mutex on the head of the free list. */
228	mutexp->mutex_next_link = mtxregion->mutex_next;
229	mtxregion->mutex_next = mutex;
230	++mtxregion->stat.st_mutex_free;
231	--mtxregion->stat.st_mutex_inuse;
232
233	if (locksys)
234		MUTEX_SYSTEM_UNLOCK(env);
235
236	return (ret);
237}
238