2 * See the file LICENSE for redistribution information.
4 * Copyright (c) 1996, 1997, 1998
5 * Sleepycat Software. All rights reserved.
8 #pragma ident "%Z%%M% %I% %E% SMI"
13 static const char sccsid
[] = "@(#)lock_region.c 10.21 (Sleepycat) 10/19/98";
16 #ifndef NO_SYSTEM_INCLUDES
17 #include <sys/types.h>
28 #include "common_ext.h"
30 static u_int32_t __lock_count_locks
__P((DB_LOCKREGION
*));
31 static u_int32_t __lock_count_objs
__P((DB_LOCKREGION
*));
32 static void __lock_dump_locker
__P((DB_LOCKTAB
*, DB_LOCKOBJ
*, FILE *));
33 static void __lock_dump_object
__P((DB_LOCKTAB
*, DB_LOCKOBJ
*, FILE *));
35 __lock_dump_status
__P((db_status_t
));
36 static void __lock_reset_region
__P((DB_LOCKTAB
*));
37 static int __lock_tabinit
__P((DB_ENV
*, DB_LOCKREGION
*));
40 lock_open(path
, flags
, mode
, dbenv
, ltp
)
48 u_int32_t lock_modes
, maxlocks
, regflags
;
51 /* Validate arguments. */
53 #define OKFLAGS (DB_CREATE | DB_THREAD)
55 #define OKFLAGS (DB_CREATE)
57 if ((ret
= __db_fchk(dbenv
, "lock_open", flags
, OKFLAGS
)) != 0)
60 /* Create the lock table structure. */
61 if ((ret
= __os_calloc(1, sizeof(DB_LOCKTAB
), <
)) != 0)
65 /* Grab the values that we need to compute the region size. */
66 lock_modes
= DB_LOCK_RW_N
;
67 maxlocks
= DB_LOCK_DEFAULT_N
;
68 regflags
= REGION_SIZEDEF
;
70 if (dbenv
->lk_modes
!= 0) {
71 lock_modes
= dbenv
->lk_modes
;
74 if (dbenv
->lk_max
!= 0) {
75 maxlocks
= dbenv
->lk_max
;
80 /* Join/create the lock region. */
81 lt
->reginfo
.dbenv
= dbenv
;
82 lt
->reginfo
.appname
= DB_APP_NONE
;
84 lt
->reginfo
.path
= NULL
;
86 if ((ret
= __os_strdup(path
, <
->reginfo
.path
)) != 0)
88 lt
->reginfo
.file
= DB_DEFAULT_LOCK_FILE
;
89 lt
->reginfo
.mode
= mode
;
91 LOCK_REGION_SIZE(lock_modes
, maxlocks
, __db_tablesize(maxlocks
));
92 lt
->reginfo
.dbflags
= flags
;
93 lt
->reginfo
.addr
= NULL
;
95 lt
->reginfo
.flags
= regflags
;
97 if ((ret
= __db_rattach(<
->reginfo
)) != 0)
100 /* Now set up the pointer to the region. */
101 lt
->region
= lt
->reginfo
.addr
;
103 /* Initialize the region if we created it. */
104 if (F_ISSET(<
->reginfo
, REGION_CREATED
)) {
105 lt
->region
->maxlocks
= maxlocks
;
106 lt
->region
->nmodes
= lock_modes
;
107 if ((ret
= __lock_tabinit(dbenv
, lt
->region
)) != 0)
110 /* Check for an unexpected region. */
111 if (lt
->region
->magic
!= DB_LOCKMAGIC
) {
113 "lock_open: %s: bad magic number", path
);
119 /* Check for automatic deadlock detection. */
120 if (dbenv
!= NULL
&& dbenv
->lk_detect
!= DB_LOCK_NORUN
) {
121 if (lt
->region
->detect
!= DB_LOCK_NORUN
&&
122 dbenv
->lk_detect
!= DB_LOCK_DEFAULT
&&
123 lt
->region
->detect
!= dbenv
->lk_detect
) {
125 "lock_open: incompatible deadlock detector mode");
129 if (lt
->region
->detect
== DB_LOCK_NORUN
)
130 lt
->region
->detect
= dbenv
->lk_detect
;
133 /* Set up remaining pointers into region. */
134 lt
->conflicts
= (u_int8_t
*)lt
->region
+ sizeof(DB_LOCKREGION
);
136 (DB_HASHTAB
*)((u_int8_t
*)lt
->region
+ lt
->region
->hash_off
);
137 lt
->mem
= (void *)((u_int8_t
*)lt
->region
+ lt
->region
->mem_off
);
139 UNLOCK_LOCKREGION(lt
);
143 err
: if (lt
->reginfo
.addr
!= NULL
) {
144 UNLOCK_LOCKREGION(lt
);
145 (void)__db_rdetach(<
->reginfo
);
146 if (F_ISSET(<
->reginfo
, REGION_CREATED
))
147 (void)lock_unlink(path
, 1, dbenv
);
150 if (lt
->reginfo
.path
!= NULL
)
151 __os_freestr(lt
->reginfo
.path
);
152 __os_free(lt
, sizeof(*lt
));
158 * Panic a lock region.
160 * PUBLIC: void __lock_panic __P((DB_ENV *));
166 if (dbenv
->lk_info
!= NULL
)
167 dbenv
->lk_info
->region
->hdr
.panic
= 1;
173 * Initialize the lock region.
176 __lock_tabinit(dbenv
, lrp
)
180 struct __db_lock
*lp
;
181 struct lock_header
*tq_head
;
182 struct obj_header
*obj_head
;
184 u_int32_t i
, nelements
;
185 const u_int8_t
*conflicts
;
188 conflicts
= dbenv
== NULL
|| dbenv
->lk_conflicts
== NULL
?
189 db_rw_conflicts
: dbenv
->lk_conflicts
;
191 lrp
->table_size
= __db_tablesize(lrp
->maxlocks
);
192 lrp
->magic
= DB_LOCKMAGIC
;
193 lrp
->version
= DB_LOCKVERSION
;
196 * These fields (lrp->maxlocks, lrp->nmodes) are initialized
197 * in the caller, since we had to grab those values to size
201 lrp
->detect
= DB_LOCK_NORUN
;
202 lrp
->numobjs
= lrp
->maxlocks
;
204 lrp
->mem_bytes
= ALIGN(STRING_SIZE(lrp
->maxlocks
), sizeof(size_t));
205 lrp
->increment
= lrp
->hdr
.size
/ 2;
212 * As we write the region, we've got to maintain the alignment
213 * for the structures that follow each chunk. This information
214 * ends up being encapsulated both in here as well as in the
215 * lock.h file for the XXX_SIZE macros.
217 /* Initialize conflict matrix. */
218 curaddr
= (u_int8_t
*)lrp
+ sizeof(DB_LOCKREGION
);
219 memcpy(curaddr
, conflicts
, lrp
->nmodes
* lrp
->nmodes
);
220 curaddr
+= lrp
->nmodes
* lrp
->nmodes
;
223 * Initialize hash table.
225 curaddr
= (u_int8_t
*)ALIGNP(curaddr
, LOCK_HASH_ALIGN
);
226 lrp
->hash_off
= curaddr
- (u_int8_t
*)lrp
;
227 nelements
= lrp
->table_size
;
228 __db_hashinit(curaddr
, nelements
);
229 curaddr
+= nelements
* sizeof(DB_HASHTAB
);
232 * Initialize locks onto a free list. Since locks contains mutexes,
233 * we need to make sure that each lock is aligned on a MUTEX_ALIGNMENT
236 curaddr
= (u_int8_t
*)ALIGNP(curaddr
, MUTEX_ALIGNMENT
);
237 tq_head
= &lrp
->free_locks
;
238 SH_TAILQ_INIT(tq_head
);
240 for (i
= 0; i
++ < lrp
->maxlocks
;
241 curaddr
+= ALIGN(sizeof(struct __db_lock
), MUTEX_ALIGNMENT
)) {
242 lp
= (struct __db_lock
*)curaddr
;
243 lp
->status
= DB_LSTAT_FREE
;
244 SH_TAILQ_INSERT_HEAD(tq_head
, lp
, links
, __db_lock
);
247 /* Initialize objects onto a free list. */
248 obj_head
= &lrp
->free_objs
;
249 SH_TAILQ_INIT(obj_head
);
251 for (i
= 0; i
++ < lrp
->maxlocks
; curaddr
+= sizeof(DB_LOCKOBJ
)) {
252 op
= (DB_LOCKOBJ
*)curaddr
;
253 SH_TAILQ_INSERT_HEAD(obj_head
, op
, links
, __db_lockobj
);
257 * Initialize the string space; as for all shared memory allocation
258 * regions, this requires size_t alignment, since we store the
259 * lengths of malloc'd areas in the area.
261 curaddr
= (u_int8_t
*)ALIGNP(curaddr
, sizeof(size_t));
262 lrp
->mem_off
= curaddr
- (u_int8_t
*)lrp
;
263 __db_shalloc_init(curaddr
, lrp
->mem_bytes
);
273 LOCK_PANIC_CHECK(lt
);
275 if ((ret
= __db_rdetach(<
->reginfo
)) != 0)
278 if (lt
->reginfo
.path
!= NULL
)
279 __os_freestr(lt
->reginfo
.path
);
280 __os_free(lt
, sizeof(*lt
));
286 lock_unlink(path
, force
, dbenv
)
294 memset(®info
, 0, sizeof(reginfo
));
295 reginfo
.dbenv
= dbenv
;
296 reginfo
.appname
= DB_APP_NONE
;
297 if (path
!= NULL
&& (ret
= __os_strdup(path
, ®info
.path
)) != 0)
299 reginfo
.file
= DB_DEFAULT_LOCK_FILE
;
300 ret
= __db_runlink(®info
, force
);
301 if (reginfo
.path
!= NULL
)
302 __os_freestr(reginfo
.path
);
307 * __lock_validate_region --
308 * Called at every interface to verify if the region has changed size,
309 * and if so, to remap the region in and reset the process' pointers.
311 * PUBLIC: int __lock_validate_region __P((DB_LOCKTAB *));
314 __lock_validate_region(lt
)
319 if (lt
->reginfo
.size
== lt
->region
->hdr
.size
)
322 /* Detach/reattach the region. */
323 if ((ret
= __db_rreattach(<
->reginfo
, lt
->region
->hdr
.size
)) != 0)
326 /* Reset region information. */
327 lt
->region
= lt
->reginfo
.addr
;
328 __lock_reset_region(lt
);
334 * __lock_grow_region --
335 * We have run out of space; time to grow the region.
337 * PUBLIC: int __lock_grow_region __P((DB_LOCKTAB *, int, size_t));
340 __lock_grow_region(lt
, which
, howmuch
)
345 struct __db_lock
*newl
;
346 struct lock_header
*lock_head
;
347 struct obj_header
*obj_head
;
350 float lock_ratio
, obj_ratio
;
351 size_t incr
, oldsize
, used
, usedmem
;
352 u_int32_t i
, newlocks
, newmem
, newobjs
, usedlocks
, usedobjs
;
357 oldsize
= lrp
->hdr
.size
;
358 incr
= lrp
->increment
;
360 /* Figure out how much of each sort of space we have. */
361 usedmem
= lrp
->mem_bytes
- __db_shalloc_count(lt
->mem
);
362 usedobjs
= lrp
->numobjs
- __lock_count_objs(lrp
);
363 usedlocks
= lrp
->maxlocks
- __lock_count_locks(lrp
);
366 * Figure out what fraction of the used space belongs to each
367 * different type of "thing" in the region. Then partition the
368 * new space up according to this ratio.
371 usedlocks
* ALIGN(sizeof(struct __db_lock
), MUTEX_ALIGNMENT
) +
372 usedobjs
* sizeof(DB_LOCKOBJ
);
374 lock_ratio
= usedlocks
*
375 ALIGN(sizeof(struct __db_lock
), MUTEX_ALIGNMENT
) / (float)used
;
376 obj_ratio
= usedobjs
* sizeof(DB_LOCKOBJ
) / (float)used
;
378 newlocks
= (u_int32_t
)(lock_ratio
*
379 incr
/ ALIGN(sizeof(struct __db_lock
), MUTEX_ALIGNMENT
));
380 newobjs
= (u_int32_t
)(obj_ratio
* incr
/ sizeof(DB_LOCKOBJ
));
382 (newobjs
* sizeof(DB_LOCKOBJ
) +
383 newlocks
* ALIGN(sizeof(struct __db_lock
), MUTEX_ALIGNMENT
));
386 * Make sure we allocate enough memory for the object being
393 incr
+= newlocks
* sizeof(struct __db_lock
);
399 incr
+= newobjs
* sizeof(DB_LOCKOBJ
);
403 if (newmem
< howmuch
* 2) {
404 incr
+= howmuch
* 2 - newmem
;
405 newmem
= howmuch
* 2;
410 newmem
+= ALIGN(incr
, sizeof(size_t)) - incr
;
411 incr
= ALIGN(incr
, sizeof(size_t));
414 * Since we are going to be allocating locks at the beginning of the
415 * new chunk, we need to make sure that the chunk is MUTEX_ALIGNMENT
416 * aligned. We did not guarantee this when we created the region, so
417 * we may need to pad the old region by extra bytes to ensure this
420 incr
+= ALIGN(oldsize
, MUTEX_ALIGNMENT
) - oldsize
;
423 "Growing lock region: %lu locks %lu objs %lu bytes",
424 (u_long
)newlocks
, (u_long
)newobjs
, (u_long
)newmem
);
426 if ((ret
= __db_rgrow(<
->reginfo
, oldsize
+ incr
)) != 0)
428 lt
->region
= lt
->reginfo
.addr
;
429 __lock_reset_region(lt
);
431 /* Update region parameters. */
433 lrp
->increment
= incr
<< 1;
434 lrp
->maxlocks
+= newlocks
;
435 lrp
->numobjs
+= newobjs
;
436 lrp
->mem_bytes
+= newmem
;
438 curaddr
= (u_int8_t
*)lrp
+ oldsize
;
439 curaddr
= (u_int8_t
*)ALIGNP(curaddr
, MUTEX_ALIGNMENT
);
441 /* Put new locks onto the free list. */
442 lock_head
= &lrp
->free_locks
;
443 for (i
= 0; i
++ < newlocks
;
444 curaddr
+= ALIGN(sizeof(struct __db_lock
), MUTEX_ALIGNMENT
)) {
445 newl
= (struct __db_lock
*)curaddr
;
446 SH_TAILQ_INSERT_HEAD(lock_head
, newl
, links
, __db_lock
);
449 /* Put new objects onto the free list. */
450 obj_head
= &lrp
->free_objs
;
451 for (i
= 0; i
++ < newobjs
; curaddr
+= sizeof(DB_LOCKOBJ
)) {
452 op
= (DB_LOCKOBJ
*)curaddr
;
453 SH_TAILQ_INSERT_HEAD(obj_head
, op
, links
, __db_lockobj
);
456 *((size_t *)curaddr
) = newmem
- sizeof(size_t);
457 curaddr
+= sizeof(size_t);
458 __db_shalloc_free(lt
->mem
, curaddr
);
464 __lock_reset_region(lt
)
467 lt
->conflicts
= (u_int8_t
*)lt
->region
+ sizeof(DB_LOCKREGION
);
469 (DB_HASHTAB
*)((u_int8_t
*)lt
->region
+ lt
->region
->hash_off
);
470 lt
->mem
= (void *)((u_int8_t
*)lt
->region
+ lt
->region
->mem_off
);
475 * Return LOCK statistics.
478 lock_stat(lt
, gspp
, db_malloc
)
481 void *(*db_malloc
) __P((size_t));
488 LOCK_PANIC_CHECK(lt
);
490 if ((ret
= __os_malloc(sizeof(**gspp
), db_malloc
, gspp
)) != 0)
493 /* Copy out the global statistics. */
497 (*gspp
)->st_magic
= rp
->magic
;
498 (*gspp
)->st_version
= rp
->version
;
499 (*gspp
)->st_maxlocks
= rp
->maxlocks
;
500 (*gspp
)->st_nmodes
= rp
->nmodes
;
501 (*gspp
)->st_numobjs
= rp
->numobjs
;
502 (*gspp
)->st_nlockers
= rp
->nlockers
;
503 (*gspp
)->st_nconflicts
= rp
->nconflicts
;
504 (*gspp
)->st_nrequests
= rp
->nrequests
;
505 (*gspp
)->st_nreleases
= rp
->nreleases
;
506 (*gspp
)->st_ndeadlocks
= rp
->ndeadlocks
;
507 (*gspp
)->st_region_nowait
= rp
->hdr
.lock
.mutex_set_nowait
;
508 (*gspp
)->st_region_wait
= rp
->hdr
.lock
.mutex_set_wait
;
509 (*gspp
)->st_refcnt
= rp
->hdr
.refcnt
;
510 (*gspp
)->st_regsize
= rp
->hdr
.size
;
512 UNLOCK_LOCKREGION(lt
);
518 __lock_count_locks(lrp
)
521 struct __db_lock
*newl
;
525 for (newl
= SH_TAILQ_FIRST(&lrp
->free_locks
, __db_lock
);
527 newl
= SH_TAILQ_NEXT(newl
, links
, __db_lock
))
534 __lock_count_objs(lrp
)
541 for (obj
= SH_TAILQ_FIRST(&lrp
->free_objs
, __db_lockobj
);
543 obj
= SH_TAILQ_NEXT(obj
, links
, __db_lockobj
))
549 #define LOCK_DUMP_CONF 0x001 /* Conflict matrix. */
550 #define LOCK_DUMP_FREE 0x002 /* Display lock free list. */
551 #define LOCK_DUMP_LOCKERS 0x004 /* Display lockers. */
552 #define LOCK_DUMP_MEM 0x008 /* Display region memory. */
553 #define LOCK_DUMP_OBJECTS 0x010 /* Display objects. */
554 #define LOCK_DUMP_ALL 0x01f /* Display all. */
557 * __lock_dump_region --
559 * PUBLIC: void __lock_dump_region __P((DB_LOCKTAB *, char *, FILE *));
562 __lock_dump_region(lt
, area
, fp
)
567 struct __db_lock
*lp
;
570 u_int32_t flags
, i
, j
;
573 /* Make it easy to call from the debugger. */
577 for (flags
= 0; *area
!= '\0'; ++area
)
580 LF_SET(LOCK_DUMP_ALL
);
583 LF_SET(LOCK_DUMP_CONF
);
586 LF_SET(LOCK_DUMP_FREE
);
589 LF_SET(LOCK_DUMP_LOCKERS
);
592 LF_SET(LOCK_DUMP_MEM
);
595 LF_SET(LOCK_DUMP_OBJECTS
);
601 fprintf(fp
, "%s\nLock region parameters\n", DB_LINE
);
602 fprintf(fp
, "%s: %lu, %s: %lu, %s: %lu, %s: %lu\n%s: %lu, %s: %lu\n",
603 "table size", (u_long
)lrp
->table_size
,
604 "hash_off", (u_long
)lrp
->hash_off
,
605 "increment", (u_long
)lrp
->increment
,
606 "mem_off", (u_long
)lrp
->mem_off
,
607 "mem_bytes", (u_long
)lrp
->mem_bytes
,
608 "need_dd", (u_long
)lrp
->need_dd
);
610 if (LF_ISSET(LOCK_DUMP_CONF
)) {
611 fprintf(fp
, "\n%s\nConflict matrix\n", DB_LINE
);
612 for (i
= 0; i
< lrp
->nmodes
; i
++) {
613 for (j
= 0; j
< lrp
->nmodes
; j
++)
615 (u_long
)lt
->conflicts
[i
* lrp
->nmodes
+ j
]);
620 if (LF_ISSET(LOCK_DUMP_LOCKERS
| LOCK_DUMP_OBJECTS
)) {
621 fprintf(fp
, "%s\nLock hash buckets\n", DB_LINE
);
622 for (i
= 0; i
< lrp
->table_size
; i
++) {
624 for (op
= SH_TAILQ_FIRST(<
->hashtab
[i
], __db_lockobj
);
626 op
= SH_TAILQ_NEXT(op
, links
, __db_lockobj
)) {
627 if (LF_ISSET(LOCK_DUMP_LOCKERS
) &&
628 op
->type
== DB_LOCK_LOCKER
) {
631 "Bucket %lu:\n", (u_long
)i
);
634 __lock_dump_locker(lt
, op
, fp
);
636 if (LF_ISSET(LOCK_DUMP_OBJECTS
) &&
637 op
->type
== DB_LOCK_OBJTYPE
) {
640 "Bucket %lu:\n", (u_long
)i
);
643 __lock_dump_object(lt
, op
, fp
);
649 if (LF_ISSET(LOCK_DUMP_FREE
)) {
650 fprintf(fp
, "%s\nLock free list\n", DB_LINE
);
651 for (lp
= SH_TAILQ_FIRST(&lrp
->free_locks
, __db_lock
);
653 lp
= SH_TAILQ_NEXT(lp
, links
, __db_lock
))
654 fprintf(fp
, "0x%lx: %lu\t%lu\t%s\t0x%lx\n", (u_long
)lp
,
655 (u_long
)lp
->holder
, (u_long
)lp
->mode
,
656 __lock_dump_status(lp
->status
), (u_long
)lp
->obj
);
658 fprintf(fp
, "%s\nObject free list\n", DB_LINE
);
659 for (op
= SH_TAILQ_FIRST(&lrp
->free_objs
, __db_lockobj
);
661 op
= SH_TAILQ_NEXT(op
, links
, __db_lockobj
))
662 fprintf(fp
, "0x%lx\n", (u_long
)op
);
665 if (LF_ISSET(LOCK_DUMP_MEM
))
666 __db_shalloc_dump(lt
->mem
, fp
);
670 __lock_dump_locker(lt
, op
, fp
)
675 struct __db_lock
*lp
;
679 ptr
= SH_DBT_PTR(&op
->lockobj
);
680 memcpy(&locker
, ptr
, sizeof(u_int32_t
));
681 fprintf(fp
, "L %lx", (u_long
)locker
);
683 lp
= SH_LIST_FIRST(&op
->heldby
, __db_lock
);
688 for (; lp
!= NULL
; lp
= SH_LIST_NEXT(lp
, locker_links
, __db_lock
))
689 __lock_printlock(lt
, lp
, 0);
693 __lock_dump_object(lt
, op
, fp
)
698 struct __db_lock
*lp
;
703 ptr
= SH_DBT_PTR(&op
->lockobj
);
704 for (j
= 0; j
< op
->lockobj
.size
; ptr
++, j
++) {
706 fprintf(fp
, isprint(ch
) ? "%c" : "\\%o", ch
);
712 SH_TAILQ_FIRST(&op
->holders
, __db_lock
);
714 lp
= SH_TAILQ_NEXT(lp
, links
, __db_lock
))
715 __lock_printlock(lt
, lp
, 0);
716 lp
= SH_TAILQ_FIRST(&op
->waiters
, __db_lock
);
719 for (; lp
!= NULL
; lp
= SH_TAILQ_NEXT(lp
, links
, __db_lock
))
720 __lock_printlock(lt
, lp
, 0);
725 __lock_dump_status(status
)
729 case DB_LSTAT_ABORTED
:
737 case DB_LSTAT_NOGRANT
:
739 case DB_LSTAT_PENDING
:
741 case DB_LSTAT_WAITING
:
744 return ("unknown status");