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