2
* See the file LICENSE for redistribution information.
4
* Copyright (c) 1996-2002
5
* Sleepycat Software. All rights reserved.
11
static const char revid[] = "$Id$";
14
#ifndef NO_SYSTEM_INCLUDES
15
#include <sys/types.h>
21
#include "dbinc/db_shash.h"
22
#include "dbinc/lock.h"
24
static int __lock_init __P((DB_ENV *, DB_LOCKTAB *));
26
__lock_region_size __P((DB_ENV *));
28
#ifdef HAVE_MUTEX_SYSTEM_RESOURCES
29
static size_t __lock_region_maint __P((DB_ENV *));
33
* The conflict arrays are set up such that the row is the lock you are
34
* holding and the column is the lock that is desired.
36
#define DB_LOCK_RIW_N 9
37
static const u_int8_t db_riw_conflicts[] = {
38
/* N R W WT IW IR RIW DR WW */
39
/* N */ 0, 0, 0, 0, 0, 0, 0, 0, 0,
40
/* R */ 0, 0, 1, 0, 1, 0, 1, 0, 1,
41
/* W */ 0, 1, 1, 1, 1, 1, 1, 1, 1,
42
/* WT */ 0, 0, 0, 0, 0, 0, 0, 0, 0,
43
/* IW */ 0, 1, 1, 0, 0, 0, 0, 1, 1,
44
/* IR */ 0, 0, 1, 0, 0, 0, 0, 0, 1,
45
/* RIW */ 0, 1, 1, 0, 0, 0, 0, 1, 1,
46
/* DR */ 0, 0, 1, 0, 1, 0, 1, 0, 0,
47
/* WW */ 0, 1, 1, 0, 1, 1, 1, 0, 1
51
* This conflict array is used for concurrent db access (CDB). It uses
52
* the same locks as the db_riw_conflicts array, but adds an IW mode to
53
* be used for write cursors.
55
#define DB_LOCK_CDB_N 5
56
static const u_int8_t db_cdb_conflicts[] = {
58
/* N */ 0, 0, 0, 0, 0,
59
/* R */ 0, 0, 1, 0, 0,
60
/* W */ 0, 1, 1, 1, 1,
61
/* WT */ 0, 0, 0, 0, 0,
62
/* IW */ 0, 0, 1, 0, 1
67
* Internal version of lock_open: only called from DB_ENV->open.
69
* PUBLIC: int __lock_open __P((DB_ENV *));
75
DB_LOCKREGION *region;
80
/* Create the lock table structure. */
81
if ((ret = __os_calloc(dbenv, 1, sizeof(DB_LOCKTAB), <)) != 0)
85
/* Join/create the lock region. */
86
lt->reginfo.type = REGION_TYPE_LOCK;
87
lt->reginfo.id = INVALID_REGION_ID;
88
lt->reginfo.mode = dbenv->db_mode;
89
lt->reginfo.flags = REGION_JOIN_OK;
90
if (F_ISSET(dbenv, DB_ENV_CREATE))
91
F_SET(<->reginfo, REGION_CREATE_OK);
92
size = __lock_region_size(dbenv);
93
if ((ret = __db_r_attach(dbenv, <->reginfo, size)) != 0)
96
/* If we created the region, initialize it. */
97
if (F_ISSET(<->reginfo, REGION_CREATE))
98
if ((ret = __lock_init(dbenv, lt)) != 0)
101
/* Set the local addresses. */
102
region = lt->reginfo.primary =
103
R_ADDR(<->reginfo, lt->reginfo.rp->primary);
105
/* Check for incompatible automatic deadlock detection requests. */
106
if (dbenv->lk_detect != DB_LOCK_NORUN) {
107
if (region->detect != DB_LOCK_NORUN &&
108
dbenv->lk_detect != DB_LOCK_DEFAULT &&
109
region->detect != dbenv->lk_detect) {
111
"lock_open: incompatible deadlock detector mode");
117
* Upgrade if our caller wants automatic detection, and it
118
* was not currently being done, whether or not we created
121
if (region->detect == DB_LOCK_NORUN)
122
region->detect = dbenv->lk_detect;
126
* A process joining the region may have reset the lock and transaction
129
if (dbenv->lk_timeout != 0)
130
region->lk_timeout = dbenv->lk_timeout;
131
if (dbenv->tx_timeout != 0)
132
region->tx_timeout = dbenv->tx_timeout;
134
/* Set remaining pointers into region. */
135
lt->conflicts = (u_int8_t *)R_ADDR(<->reginfo, region->conf_off);
136
lt->obj_tab = (DB_HASHTAB *)R_ADDR(<->reginfo, region->obj_off);
137
lt->locker_tab = (DB_HASHTAB *)R_ADDR(<->reginfo, region->locker_off);
139
R_UNLOCK(dbenv, <->reginfo);
141
dbenv->lk_handle = lt;
144
err: if (lt->reginfo.addr != NULL) {
145
if (F_ISSET(<->reginfo, REGION_CREATE))
146
ret = __db_panic(dbenv, ret);
147
R_UNLOCK(dbenv, <->reginfo);
148
(void)__db_r_detach(dbenv, <->reginfo, 0);
150
__os_free(dbenv, lt);
156
* Initialize the lock region.
159
__lock_init(dbenv, lt)
163
const u_int8_t *lk_conflicts;
164
struct __db_lock *lp;
167
DB_LOCKREGION *region;
168
#ifdef HAVE_MUTEX_SYSTEM_RESOURCES
171
u_int32_t i, lk_modes;
175
if ((ret = __db_shalloc(lt->reginfo.addr,
176
sizeof(DB_LOCKREGION), 0, <->reginfo.primary)) != 0)
178
lt->reginfo.rp->primary = R_OFFSET(<->reginfo, lt->reginfo.primary);
179
region = lt->reginfo.primary;
180
memset(region, 0, sizeof(*region));
182
/* Select a conflict matrix if none specified. */
183
if (dbenv->lk_modes == 0)
184
if (CDB_LOCKING(dbenv)) {
185
lk_modes = DB_LOCK_CDB_N;
186
lk_conflicts = db_cdb_conflicts;
188
lk_modes = DB_LOCK_RIW_N;
189
lk_conflicts = db_riw_conflicts;
192
lk_modes = dbenv->lk_modes;
193
lk_conflicts = dbenv->lk_conflicts;
197
region->detect = DB_LOCK_NORUN;
198
region->lk_timeout = dbenv->lk_timeout;
199
region->tx_timeout = dbenv->tx_timeout;
200
region->locker_t_size = __db_tablesize(dbenv->lk_max_lockers);
201
region->object_t_size = __db_tablesize(dbenv->lk_max_objects);
202
memset(®ion->stat, 0, sizeof(region->stat));
203
region->stat.st_id = 0;
204
region->stat.st_cur_maxid = DB_LOCK_MAXID;
205
region->stat.st_maxlocks = dbenv->lk_max;
206
region->stat.st_maxlockers = dbenv->lk_max_lockers;
207
region->stat.st_maxobjects = dbenv->lk_max_objects;
208
region->stat.st_nmodes = lk_modes;
210
/* Allocate room for the conflict matrix and initialize it. */
212
__db_shalloc(lt->reginfo.addr, lk_modes * lk_modes, 0, &addr)) != 0)
214
memcpy(addr, lk_conflicts, lk_modes * lk_modes);
215
region->conf_off = R_OFFSET(<->reginfo, addr);
217
/* Allocate room for the object hash table and initialize it. */
218
if ((ret = __db_shalloc(lt->reginfo.addr,
219
region->object_t_size * sizeof(DB_HASHTAB), 0, &addr)) != 0)
221
__db_hashinit(addr, region->object_t_size);
222
region->obj_off = R_OFFSET(<->reginfo, addr);
224
/* Allocate room for the locker hash table and initialize it. */
225
if ((ret = __db_shalloc(lt->reginfo.addr,
226
region->locker_t_size * sizeof(DB_HASHTAB), 0, &addr)) != 0)
228
__db_hashinit(addr, region->locker_t_size);
229
region->locker_off = R_OFFSET(<->reginfo, addr);
231
#ifdef HAVE_MUTEX_SYSTEM_RESOURCES
232
maint_size = __lock_region_maint(dbenv);
233
/* Allocate room for the locker maintenance info and initialize it. */
234
if ((ret = __db_shalloc(lt->reginfo.addr,
235
sizeof(REGMAINT) + maint_size, 0, &addr)) != 0)
237
__db_maintinit(<->reginfo, addr, maint_size);
238
region->maint_off = R_OFFSET(<->reginfo, addr);
242
* Initialize locks onto a free list. Initialize and lock the mutex
243
* so that when we need to block, all we need do is try to acquire
246
SH_TAILQ_INIT(®ion->free_locks);
247
for (i = 0; i < region->stat.st_maxlocks; ++i) {
248
if ((ret = __db_shalloc(lt->reginfo.addr,
249
sizeof(struct __db_lock), MUTEX_ALIGN, &lp)) != 0)
251
lp->status = DB_LSTAT_FREE;
253
if ((ret = __db_mutex_setup(dbenv, <->reginfo, &lp->mutex,
254
MUTEX_NO_RLOCK | MUTEX_SELF_BLOCK)) != 0)
256
MUTEX_LOCK(dbenv, &lp->mutex);
257
SH_TAILQ_INSERT_HEAD(®ion->free_locks, lp, links, __db_lock);
260
/* Initialize objects onto a free list. */
261
SH_TAILQ_INIT(®ion->dd_objs);
262
SH_TAILQ_INIT(®ion->free_objs);
263
for (i = 0; i < region->stat.st_maxobjects; ++i) {
264
if ((ret = __db_shalloc(lt->reginfo.addr,
265
sizeof(DB_LOCKOBJ), 0, &op)) != 0)
267
SH_TAILQ_INSERT_HEAD(
268
®ion->free_objs, op, links, __db_lockobj);
271
/* Initialize lockers onto a free list. */
272
SH_TAILQ_INIT(®ion->lockers);
273
SH_TAILQ_INIT(®ion->free_lockers);
274
for (i = 0; i < region->stat.st_maxlockers; ++i) {
275
if ((ret = __db_shalloc(lt->reginfo.addr,
276
sizeof(DB_LOCKER), 0, &lidp)) != 0) {
277
mem_err: __db_err(dbenv,
278
"Unable to allocate memory for the lock table");
281
SH_TAILQ_INSERT_HEAD(
282
®ion->free_lockers, lidp, links, __db_locker);
289
* __lock_dbenv_refresh --
290
* Clean up after the lock system on a close or failed open. Called
291
* only from __dbenv_refresh. (Formerly called __lock_close.)
293
* PUBLIC: int __lock_dbenv_refresh __P((DB_ENV *));
296
__lock_dbenv_refresh(dbenv)
302
lt = dbenv->lk_handle;
304
/* Detach from the region. */
305
ret = __db_r_detach(dbenv, <->reginfo, 0);
307
__os_free(dbenv, lt);
309
dbenv->lk_handle = NULL;
314
* __lock_region_size --
315
* Return the region size.
318
__lock_region_size(dbenv)
324
* Figure out how much space we're going to need. This list should
325
* map one-to-one with the __db_shalloc calls in __lock_init.
328
retval += __db_shalloc_size(sizeof(DB_LOCKREGION), sizeof(db_align_t));
329
retval += __db_shalloc_size(dbenv->lk_modes * dbenv->lk_modes, sizeof(db_align_t));
330
retval += __db_shalloc_size(
331
__db_tablesize(dbenv->lk_max_lockers) * (sizeof(DB_HASHTAB)), sizeof(db_align_t));
332
retval += __db_shalloc_size(
333
__db_tablesize(dbenv->lk_max_objects) * (sizeof(DB_HASHTAB)), sizeof(db_align_t));
334
#ifdef HAVE_MUTEX_SYSTEM_RESOURCES
336
__db_shalloc_size(sizeof(REGMAINT) + __lock_region_maint(dbenv), sizeof(db_align_t));
338
retval += __db_shalloc_size(
339
sizeof(struct __db_lock), MUTEX_ALIGN) * dbenv->lk_max;
340
retval += __db_shalloc_size(
341
sizeof(DB_LOCKOBJ), sizeof(db_align_t)) * dbenv->lk_max_objects;
342
retval += __db_shalloc_size(
343
sizeof(DB_LOCKER), sizeof(db_align_t)) * dbenv->lk_max_lockers;
346
* Include 16 bytes of string space per lock. DB doesn't use it
347
* because we pre-allocate lock space for DBTs in the structure.
349
retval += __db_shalloc_size(dbenv->lk_max * 16, sizeof(size_t));
351
/* And we keep getting this wrong, let's be generous. */
352
retval += retval / 4;
357
#ifdef HAVE_MUTEX_SYSTEM_RESOURCES
359
* __lock_region_maint --
360
* Return the amount of space needed for region maintenance info.
363
__lock_region_maint(dbenv)
368
s = sizeof(DB_MUTEX *) * dbenv->lk_max;
374
* __lock_region_destroy
375
* Destroy any region maintenance info.
377
* PUBLIC: void __lock_region_destroy __P((DB_ENV *, REGINFO *));
380
__lock_region_destroy(dbenv, infop)
384
__db_shlocks_destroy(infop, (REGMAINT *)R_ADDR(infop,
385
((DB_LOCKREGION *)R_ADDR(infop, infop->rp->primary))->maint_off));
387
COMPQUIET(dbenv, NULL);
388
COMPQUIET(infop, NULL);
394
* Set the current locker ID and current maximum unused ID (for
395
* testing purposes only).
397
* PUBLIC: int __lock_id_set __P((DB_ENV *, u_int32_t, u_int32_t));
400
__lock_id_set(dbenv, cur_id, max_id)
402
u_int32_t cur_id, max_id;
405
DB_LOCKREGION *region;
407
ENV_REQUIRES_CONFIG(dbenv,
408
dbenv->lk_handle, "lock_id_set", DB_INIT_LOCK);
410
lt = dbenv->lk_handle;
411
region = lt->reginfo.primary;
412
region->stat.st_id = cur_id;
413
region->stat.st_cur_maxid = max_id;