2 * See the file LICENSE for redistribution information.
4 * Copyright (c) 1996, 1997, 1998
5 * Sleepycat Software. All rights reserved.
11 static const char sccsid
[] = "@(#)lock_region.c 10.15 (Sleepycat) 6/2/98";
14 #ifndef NO_SYSTEM_INCLUDES
15 #include <sys/types.h>
26 #include "common_ext.h"
28 static u_int32_t __lock_count_locks
__P((DB_LOCKREGION
*));
29 static u_int32_t __lock_count_objs
__P((DB_LOCKREGION
*));
30 static void __lock_dump_locker
__P((DB_LOCKTAB
*, DB_LOCKOBJ
*, FILE *));
31 static void __lock_dump_object
__P((DB_LOCKTAB
*, DB_LOCKOBJ
*, FILE *));
32 static const char *__lock_dump_status
__P((db_status_t
));
33 static void __lock_reset_region
__P((DB_LOCKTAB
*));
34 static int __lock_tabinit
__P((DB_ENV
*, DB_LOCKREGION
*));
37 lock_open(path
, flags
, mode
, dbenv
, ltp
)
45 u_int32_t lock_modes
, maxlocks
, regflags
;
48 /* Validate arguments. */
50 #define OKFLAGS (DB_CREATE | DB_THREAD)
52 #define OKFLAGS (DB_CREATE)
54 if ((ret
= __db_fchk(dbenv
, "lock_open", flags
, OKFLAGS
)) != 0)
57 /* Create the lock table structure. */
58 if ((lt
= (DB_LOCKTAB
*)__db_calloc(1, sizeof(DB_LOCKTAB
))) == NULL
) {
59 __db_err(dbenv
, "%s", strerror(ENOMEM
));
64 /* Grab the values that we need to compute the region size. */
65 lock_modes
= DB_LOCK_RW_N
;
66 maxlocks
= DB_LOCK_DEFAULT_N
;
67 regflags
= REGION_SIZEDEF
;
69 if (dbenv
->lk_modes
!= 0) {
70 lock_modes
= dbenv
->lk_modes
;
73 if (dbenv
->lk_max
!= 0) {
74 maxlocks
= dbenv
->lk_max
;
79 /* Join/create the lock region. */
80 lt
->reginfo
.dbenv
= dbenv
;
81 lt
->reginfo
.appname
= DB_APP_NONE
;
83 lt
->reginfo
.path
= NULL
;
85 if ((lt
->reginfo
.path
= (char *)__db_strdup(path
)) == NULL
)
87 lt
->reginfo
.file
= DB_DEFAULT_LOCK_FILE
;
88 lt
->reginfo
.mode
= mode
;
90 LOCK_REGION_SIZE(lock_modes
, maxlocks
, __db_tablesize(maxlocks
));
91 lt
->reginfo
.dbflags
= flags
;
92 lt
->reginfo
.addr
= NULL
;
94 lt
->reginfo
.flags
= regflags
;
96 if ((ret
= __db_rattach(<
->reginfo
)) != 0)
99 /* Now set up the pointer to the region. */
100 lt
->region
= lt
->reginfo
.addr
;
102 /* Initialize the region if we created it. */
103 if (F_ISSET(<
->reginfo
, REGION_CREATED
)) {
104 lt
->region
->maxlocks
= maxlocks
;
105 lt
->region
->nmodes
= lock_modes
;
106 if ((ret
= __lock_tabinit(dbenv
, lt
->region
)) != 0)
109 /* Check for an unexpected region. */
110 if (lt
->region
->magic
!= DB_LOCKMAGIC
) {
112 "lock_open: %s: bad magic number", path
);
118 /* Check for automatic deadlock detection. */
119 if (dbenv
!= NULL
&& dbenv
->lk_detect
!= DB_LOCK_NORUN
) {
120 if (lt
->region
->detect
!= DB_LOCK_NORUN
&&
121 dbenv
->lk_detect
!= DB_LOCK_DEFAULT
&&
122 lt
->region
->detect
!= dbenv
->lk_detect
) {
124 "lock_open: incompatible deadlock detector mode");
128 if (lt
->region
->detect
== DB_LOCK_NORUN
)
129 lt
->region
->detect
= dbenv
->lk_detect
;
132 /* Set up remaining pointers into region. */
133 lt
->conflicts
= (u_int8_t
*)lt
->region
+ sizeof(DB_LOCKREGION
);
135 (DB_HASHTAB
*)((u_int8_t
*)lt
->region
+ lt
->region
->hash_off
);
136 lt
->mem
= (void *)((u_int8_t
*)lt
->region
+ lt
->region
->mem_off
);
138 UNLOCK_LOCKREGION(lt
);
142 err
: if (lt
->reginfo
.addr
!= NULL
) {
143 UNLOCK_LOCKREGION(lt
);
144 (void)__db_rdetach(<
->reginfo
);
145 if (F_ISSET(<
->reginfo
, REGION_CREATED
))
146 (void)lock_unlink(path
, 1, dbenv
);
149 if (lt
->reginfo
.path
!= NULL
)
150 FREES(lt
->reginfo
.path
);
151 FREE(lt
, sizeof(*lt
));
157 * Initialize the lock region.
160 __lock_tabinit(dbenv
, lrp
)
164 struct __db_lock
*lp
;
165 struct lock_header
*tq_head
;
166 struct obj_header
*obj_head
;
168 u_int32_t i
, nelements
;
169 const u_int8_t
*conflicts
;
172 conflicts
= dbenv
== NULL
|| dbenv
->lk_conflicts
== NULL
?
173 db_rw_conflicts
: dbenv
->lk_conflicts
;
175 lrp
->table_size
= __db_tablesize(lrp
->maxlocks
);
176 lrp
->magic
= DB_LOCKMAGIC
;
177 lrp
->version
= DB_LOCKVERSION
;
180 * These fields (lrp->maxlocks, lrp->nmodes) are initialized
181 * in the caller, since we had to grab those values to size
185 lrp
->detect
= DB_LOCK_NORUN
;
186 lrp
->numobjs
= lrp
->maxlocks
;
188 lrp
->mem_bytes
= ALIGN(STRING_SIZE(lrp
->maxlocks
), sizeof(size_t));
189 lrp
->increment
= lrp
->hdr
.size
/ 2;
196 * As we write the region, we've got to maintain the alignment
197 * for the structures that follow each chunk. This information
198 * ends up being encapsulated both in here as well as in the
199 * lock.h file for the XXX_SIZE macros.
201 /* Initialize conflict matrix. */
202 curaddr
= (u_int8_t
*)lrp
+ sizeof(DB_LOCKREGION
);
203 memcpy(curaddr
, conflicts
, lrp
->nmodes
* lrp
->nmodes
);
204 curaddr
+= lrp
->nmodes
* lrp
->nmodes
;
207 * Initialize hash table.
209 curaddr
= (u_int8_t
*)ALIGNP(curaddr
, LOCK_HASH_ALIGN
);
210 lrp
->hash_off
= curaddr
- (u_int8_t
*)lrp
;
211 nelements
= lrp
->table_size
;
212 __db_hashinit(curaddr
, nelements
);
213 curaddr
+= nelements
* sizeof(DB_HASHTAB
);
216 * Initialize locks onto a free list. Since locks contains mutexes,
217 * we need to make sure that each lock is aligned on a MUTEX_ALIGNMENT
220 curaddr
= (u_int8_t
*)ALIGNP(curaddr
, MUTEX_ALIGNMENT
);
221 tq_head
= &lrp
->free_locks
;
222 SH_TAILQ_INIT(tq_head
);
224 for (i
= 0; i
++ < lrp
->maxlocks
;
225 curaddr
+= ALIGN(sizeof(struct __db_lock
), MUTEX_ALIGNMENT
)) {
226 lp
= (struct __db_lock
*)curaddr
;
227 lp
->status
= DB_LSTAT_FREE
;
228 SH_TAILQ_INSERT_HEAD(tq_head
, lp
, links
, __db_lock
);
231 /* Initialize objects onto a free list. */
232 obj_head
= &lrp
->free_objs
;
233 SH_TAILQ_INIT(obj_head
);
235 for (i
= 0; i
++ < lrp
->maxlocks
; curaddr
+= sizeof(DB_LOCKOBJ
)) {
236 op
= (DB_LOCKOBJ
*)curaddr
;
237 SH_TAILQ_INSERT_HEAD(obj_head
, op
, links
, __db_lockobj
);
241 * Initialize the string space; as for all shared memory allocation
242 * regions, this requires size_t alignment, since we store the
243 * lengths of malloc'd areas in the area.
245 curaddr
= (u_int8_t
*)ALIGNP(curaddr
, sizeof(size_t));
246 lrp
->mem_off
= curaddr
- (u_int8_t
*)lrp
;
247 __db_shalloc_init(curaddr
, lrp
->mem_bytes
);
257 if ((ret
= __db_rdetach(<
->reginfo
)) != 0)
260 if (lt
->reginfo
.path
!= NULL
)
261 FREES(lt
->reginfo
.path
);
262 FREE(lt
, sizeof(*lt
));
268 lock_unlink(path
, force
, dbenv
)
276 memset(®info
, 0, sizeof(reginfo
));
277 reginfo
.dbenv
= dbenv
;
278 reginfo
.appname
= DB_APP_NONE
;
279 if (path
!= NULL
&& (reginfo
.path
= (char *)__db_strdup(path
)) == NULL
)
281 reginfo
.file
= DB_DEFAULT_LOCK_FILE
;
282 ret
= __db_runlink(®info
, force
);
283 if (reginfo
.path
!= NULL
)
289 * __lock_validate_region --
290 * Called at every interface to verify if the region has changed size,
291 * and if so, to remap the region in and reset the process' pointers.
293 * PUBLIC: int __lock_validate_region __P((DB_LOCKTAB *));
296 __lock_validate_region(lt
)
301 if (lt
->reginfo
.size
== lt
->region
->hdr
.size
)
304 /* Detach/reattach the region. */
305 if ((ret
= __db_rreattach(<
->reginfo
, lt
->region
->hdr
.size
)) != 0)
308 /* Reset region information. */
309 lt
->region
= lt
->reginfo
.addr
;
310 __lock_reset_region(lt
);
316 * __lock_grow_region --
317 * We have run out of space; time to grow the region.
319 * PUBLIC: int __lock_grow_region __P((DB_LOCKTAB *, int, size_t));
322 __lock_grow_region(lt
, which
, howmuch
)
327 struct __db_lock
*newl
;
328 struct lock_header
*lock_head
;
329 struct obj_header
*obj_head
;
332 float lock_ratio
, obj_ratio
;
333 size_t incr
, oldsize
, used
, usedmem
;
334 u_int32_t i
, newlocks
, newmem
, newobjs
, usedlocks
, usedobjs
;
339 oldsize
= lrp
->hdr
.size
;
340 incr
= lrp
->increment
;
342 /* Figure out how much of each sort of space we have. */
343 usedmem
= lrp
->mem_bytes
- __db_shalloc_count(lt
->mem
);
344 usedobjs
= lrp
->numobjs
- __lock_count_objs(lrp
);
345 usedlocks
= lrp
->maxlocks
- __lock_count_locks(lrp
);
348 * Figure out what fraction of the used space belongs to each
349 * different type of "thing" in the region. Then partition the
350 * new space up according to this ratio.
353 usedlocks
* ALIGN(sizeof(struct __db_lock
), MUTEX_ALIGNMENT
) +
354 usedobjs
* sizeof(DB_LOCKOBJ
);
356 lock_ratio
= usedlocks
*
357 ALIGN(sizeof(struct __db_lock
), MUTEX_ALIGNMENT
) / (float)used
;
358 obj_ratio
= usedobjs
* sizeof(DB_LOCKOBJ
) / (float)used
;
360 newlocks
= (u_int32_t
)(lock_ratio
*
361 incr
/ ALIGN(sizeof(struct __db_lock
), MUTEX_ALIGNMENT
));
362 newobjs
= (u_int32_t
)(obj_ratio
* incr
/ sizeof(DB_LOCKOBJ
));
364 (newobjs
* sizeof(DB_LOCKOBJ
) +
365 newlocks
* ALIGN(sizeof(struct __db_lock
), MUTEX_ALIGNMENT
));
368 * Make sure we allocate enough memory for the object being
375 incr
+= newlocks
* sizeof(struct __db_lock
);
381 incr
+= newobjs
* sizeof(DB_LOCKOBJ
);
385 if (newmem
< howmuch
* 2) {
386 incr
+= howmuch
* 2 - newmem
;
387 newmem
= howmuch
* 2;
392 newmem
+= ALIGN(incr
, sizeof(size_t)) - incr
;
393 incr
= ALIGN(incr
, sizeof(size_t));
396 * Since we are going to be allocating locks at the beginning of the
397 * new chunk, we need to make sure that the chunk is MUTEX_ALIGNMENT
398 * aligned. We did not guarantee this when we created the region, so
399 * we may need to pad the old region by extra bytes to ensure this
402 incr
+= ALIGN(oldsize
, MUTEX_ALIGNMENT
) - oldsize
;
405 "Growing lock region: %lu locks %lu objs %lu bytes",
406 (u_long
)newlocks
, (u_long
)newobjs
, (u_long
)newmem
);
408 if ((ret
= __db_rgrow(<
->reginfo
, oldsize
+ incr
)) != 0)
410 lt
->region
= lt
->reginfo
.addr
;
411 __lock_reset_region(lt
);
413 /* Update region parameters. */
415 lrp
->increment
= incr
<< 1;
416 lrp
->maxlocks
+= newlocks
;
417 lrp
->numobjs
+= newobjs
;
418 lrp
->mem_bytes
+= newmem
;
420 curaddr
= (u_int8_t
*)lrp
+ oldsize
;
421 curaddr
= (u_int8_t
*)ALIGNP(curaddr
, MUTEX_ALIGNMENT
);
423 /* Put new locks onto the free list. */
424 lock_head
= &lrp
->free_locks
;
425 for (i
= 0; i
++ < newlocks
;
426 curaddr
+= ALIGN(sizeof(struct __db_lock
), MUTEX_ALIGNMENT
)) {
427 newl
= (struct __db_lock
*)curaddr
;
428 SH_TAILQ_INSERT_HEAD(lock_head
, newl
, links
, __db_lock
);
431 /* Put new objects onto the free list. */
432 obj_head
= &lrp
->free_objs
;
433 for (i
= 0; i
++ < newobjs
; curaddr
+= sizeof(DB_LOCKOBJ
)) {
434 op
= (DB_LOCKOBJ
*)curaddr
;
435 SH_TAILQ_INSERT_HEAD(obj_head
, op
, links
, __db_lockobj
);
438 *((size_t *)curaddr
) = newmem
- sizeof(size_t);
439 curaddr
+= sizeof(size_t);
440 __db_shalloc_free(lt
->mem
, curaddr
);
446 __lock_reset_region(lt
)
449 lt
->conflicts
= (u_int8_t
*)lt
->region
+ sizeof(DB_LOCKREGION
);
451 (DB_HASHTAB
*)((u_int8_t
*)lt
->region
+ lt
->region
->hash_off
);
452 lt
->mem
= (void *)((u_int8_t
*)lt
->region
+ lt
->region
->mem_off
);
457 * Return LOCK statistics.
460 lock_stat(lt
, gspp
, db_malloc
)
463 void *(*db_malloc
) __P((size_t));
469 if ((*gspp
= db_malloc
== NULL
?
470 (DB_LOCK_STAT
*)__db_malloc(sizeof(**gspp
)) :
471 (DB_LOCK_STAT
*)db_malloc(sizeof(**gspp
))) == NULL
)
474 /* Copy out the global statistics. */
478 (*gspp
)->st_magic
= rp
->magic
;
479 (*gspp
)->st_version
= rp
->version
;
480 (*gspp
)->st_maxlocks
= rp
->maxlocks
;
481 (*gspp
)->st_nmodes
= rp
->nmodes
;
482 (*gspp
)->st_numobjs
= rp
->numobjs
;
483 (*gspp
)->st_nlockers
= rp
->nlockers
;
484 (*gspp
)->st_nconflicts
= rp
->nconflicts
;
485 (*gspp
)->st_nrequests
= rp
->nrequests
;
486 (*gspp
)->st_nreleases
= rp
->nreleases
;
487 (*gspp
)->st_ndeadlocks
= rp
->ndeadlocks
;
488 (*gspp
)->st_region_nowait
= rp
->hdr
.lock
.mutex_set_nowait
;
489 (*gspp
)->st_region_wait
= rp
->hdr
.lock
.mutex_set_wait
;
490 (*gspp
)->st_refcnt
= rp
->hdr
.refcnt
;
491 (*gspp
)->st_regsize
= rp
->hdr
.size
;
493 UNLOCK_LOCKREGION(lt
);
499 __lock_count_locks(lrp
)
502 struct __db_lock
*newl
;
506 for (newl
= SH_TAILQ_FIRST(&lrp
->free_locks
, __db_lock
);
508 newl
= SH_TAILQ_NEXT(newl
, links
, __db_lock
))
515 __lock_count_objs(lrp
)
522 for (obj
= SH_TAILQ_FIRST(&lrp
->free_objs
, __db_lockobj
);
524 obj
= SH_TAILQ_NEXT(obj
, links
, __db_lockobj
))
530 #define LOCK_DUMP_CONF 0x001 /* Conflict matrix. */
531 #define LOCK_DUMP_FREE 0x002 /* Display lock free list. */
532 #define LOCK_DUMP_LOCKERS 0x004 /* Display lockers. */
533 #define LOCK_DUMP_MEM 0x008 /* Display region memory. */
534 #define LOCK_DUMP_OBJECTS 0x010 /* Display objects. */
535 #define LOCK_DUMP_ALL 0x01f /* Display all. */
538 * __lock_dump_region --
540 * PUBLIC: void __lock_dump_region __P((DB_LOCKTAB *, char *, FILE *));
543 __lock_dump_region(lt
, area
, fp
)
548 struct __db_lock
*lp
;
551 u_int32_t flags
, i
, j
;
554 /* Make it easy to call from the debugger. */
558 for (flags
= 0; *area
!= '\0'; ++area
)
561 LF_SET(LOCK_DUMP_ALL
);
564 LF_SET(LOCK_DUMP_CONF
);
567 LF_SET(LOCK_DUMP_FREE
);
570 LF_SET(LOCK_DUMP_LOCKERS
);
573 LF_SET(LOCK_DUMP_MEM
);
576 LF_SET(LOCK_DUMP_OBJECTS
);
582 fprintf(fp
, "%s\nLock region parameters\n", DB_LINE
);
583 fprintf(fp
, "%s: %lu, %s: %lu, %s: %lu, %s: %lu\n%s: %lu, %s: %lu\n",
584 "table size", (u_long
)lrp
->table_size
,
585 "hash_off", (u_long
)lrp
->hash_off
,
586 "increment", (u_long
)lrp
->increment
,
587 "mem_off", (u_long
)lrp
->mem_off
,
588 "mem_bytes", (u_long
)lrp
->mem_bytes
,
589 "need_dd", (u_long
)lrp
->need_dd
);
591 if (LF_ISSET(LOCK_DUMP_CONF
)) {
592 fprintf(fp
, "\n%s\nConflict matrix\n", DB_LINE
);
593 for (i
= 0; i
< lrp
->nmodes
; i
++) {
594 for (j
= 0; j
< lrp
->nmodes
; j
++)
596 (u_long
)lt
->conflicts
[i
* lrp
->nmodes
+ j
]);
601 if (LF_ISSET(LOCK_DUMP_LOCKERS
| LOCK_DUMP_OBJECTS
)) {
602 fprintf(fp
, "%s\nLock hash buckets\n", DB_LINE
);
603 for (i
= 0; i
< lrp
->table_size
; i
++) {
605 for (op
= SH_TAILQ_FIRST(<
->hashtab
[i
], __db_lockobj
);
607 op
= SH_TAILQ_NEXT(op
, links
, __db_lockobj
)) {
608 if (LF_ISSET(LOCK_DUMP_LOCKERS
) &&
609 op
->type
== DB_LOCK_LOCKER
) {
612 "Bucket %lu:\n", (u_long
)i
);
615 __lock_dump_locker(lt
, op
, fp
);
617 if (LF_ISSET(LOCK_DUMP_OBJECTS
) &&
618 op
->type
== DB_LOCK_OBJTYPE
) {
621 "Bucket %lu:\n", (u_long
)i
);
624 __lock_dump_object(lt
, op
, fp
);
630 if (LF_ISSET(LOCK_DUMP_FREE
)) {
631 fprintf(fp
, "%s\nLock free list\n", DB_LINE
);
632 for (lp
= SH_TAILQ_FIRST(&lrp
->free_locks
, __db_lock
);
634 lp
= SH_TAILQ_NEXT(lp
, links
, __db_lock
))
635 fprintf(fp
, "0x%x: %lu\t%lu\t%s\t0x%x\n", (u_int
)lp
,
636 (u_long
)lp
->holder
, (u_long
)lp
->mode
,
637 __lock_dump_status(lp
->status
), (u_int
)lp
->obj
);
639 fprintf(fp
, "%s\nObject free list\n", DB_LINE
);
640 for (op
= SH_TAILQ_FIRST(&lrp
->free_objs
, __db_lockobj
);
642 op
= SH_TAILQ_NEXT(op
, links
, __db_lockobj
))
643 fprintf(fp
, "0x%x\n", (u_int
)op
);
646 if (LF_ISSET(LOCK_DUMP_MEM
))
647 __db_shalloc_dump(lt
->mem
, fp
);
651 __lock_dump_locker(lt
, op
, fp
)
656 struct __db_lock
*lp
;
660 ptr
= SH_DBT_PTR(&op
->lockobj
);
661 memcpy(&locker
, ptr
, sizeof(u_int32_t
));
662 fprintf(fp
, "L %lx", (u_long
)locker
);
664 lp
= SH_LIST_FIRST(&op
->heldby
, __db_lock
);
669 for (; lp
!= NULL
; lp
= SH_LIST_NEXT(lp
, locker_links
, __db_lock
))
670 __lock_printlock(lt
, lp
, 0);
674 __lock_dump_object(lt
, op
, fp
)
679 struct __db_lock
*lp
;
684 ptr
= SH_DBT_PTR(&op
->lockobj
);
685 for (j
= 0; j
< op
->lockobj
.size
; ptr
++, j
++) {
687 fprintf(fp
, isprint(ch
) ? "%c" : "\\%o", ch
);
693 SH_TAILQ_FIRST(&op
->holders
, __db_lock
);
695 lp
= SH_TAILQ_NEXT(lp
, links
, __db_lock
))
696 __lock_printlock(lt
, lp
, 0);
697 lp
= SH_TAILQ_FIRST(&op
->waiters
, __db_lock
);
700 for (; lp
!= NULL
; lp
= SH_TAILQ_NEXT(lp
, links
, __db_lock
))
701 __lock_printlock(lt
, lp
, 0);
706 __lock_dump_status(status
)
710 case DB_LSTAT_ABORTED
:
718 case DB_LSTAT_NOGRANT
:
720 case DB_LSTAT_PENDING
:
722 case DB_LSTAT_WAITING
:
725 return ("unknown status");