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_deadlock.c 10.32 (Sleepycat) 4/26/98";
14 #ifndef NO_SYSTEM_INCLUDES
15 #include <sys/types.h>
25 #include "common_ext.h"
27 #define ISSET_MAP(M, N) (M[(N) / 32] & (1 << (N) % 32))
29 #define CLEAR_MAP(M, N) { \
31 for (__i = 0; __i < (N); __i++) \
35 #define SET_MAP(M, B) (M[(B) / 32] |= (1 << ((B) % 32)))
36 #define CLR_MAP(M, B) (M[(B) / 32] &= ~(1 << ((B) % 32)))
38 #define OR_MAP(D, S, N) { \
40 for (__i = 0; __i < (N); __i++) \
43 #define BAD_KILLID 0xffffffff
52 static int __dd_abort
__P((DB_ENV
*, locker_info
*));
54 __P((DB_ENV
*, u_int32_t
**, u_int32_t
*, locker_info
**));
56 *__dd_find
__P((u_int32_t
*, locker_info
*, u_int32_t
));
59 static void __dd_debug
__P((DB_ENV
*, locker_info
*, u_int32_t
*, u_int32_t
));
63 lock_detect(lt
, flags
, atype
)
65 u_int32_t flags
, atype
;
69 u_int32_t
*bitmap
, *deadlock
, i
, killid
, nentries
, nlockers
;
72 /* Validate arguments. */
74 __db_fchk(lt
->dbenv
, "lock_detect", flags
, DB_LOCK_CONFLICT
)) != 0)
77 /* Check if a detector run is necessary. */
79 if (LF_ISSET(DB_LOCK_CONFLICT
)) {
80 /* Make a pass every time a lock waits. */
82 do_pass
= dbenv
->lk_info
->region
->need_dd
!= 0;
83 UNLOCK_LOCKREGION(lt
);
89 /* Build the waits-for bitmap. */
90 if ((ret
= __dd_build(dbenv
, &bitmap
, &nlockers
, &idmap
)) != 0)
96 if (dbenv
->db_verbose
!= 0)
97 __dd_debug(dbenv
, idmap
, bitmap
, nlockers
);
99 /* Find a deadlock. */
100 deadlock
= __dd_find(bitmap
, idmap
, nlockers
);
101 nentries
= ALIGN(nlockers
, 32) / 32;
103 if (deadlock
!= NULL
) {
108 * Find the first bit set in the current
109 * array and then look for a lower tid in
112 for (i
= 0; i
< nlockers
; i
++)
113 if (ISSET_MAP(deadlock
, i
))
116 if (killid
== BAD_KILLID
) {
118 "warning: could not find locker to abort");
123 * The oldest transaction has the lowest
126 for (i
= killid
+ 1; i
< nlockers
; i
++)
127 if (ISSET_MAP(deadlock
, i
) &&
128 idmap
[i
].id
< idmap
[killid
].id
)
131 case DB_LOCK_DEFAULT
:
134 * We are trying to calculate the id of the
135 * locker whose entry is indicated by deadlock.
137 killid
= (deadlock
- bitmap
) / nentries
;
139 case DB_LOCK_YOUNGEST
:
141 * Find the first bit set in the current
142 * array and then look for a lower tid in
145 for (i
= 0; i
< nlockers
; i
++)
146 if (ISSET_MAP(deadlock
, i
))
149 if (killid
== BAD_KILLID
) {
151 "warning: could not find locker to abort");
155 * The youngest transaction has the highest
158 for (i
= killid
+ 1; i
< nlockers
; i
++)
159 if (ISSET_MAP(deadlock
, i
) &&
160 idmap
[i
].id
> idmap
[killid
].id
)
168 /* Kill the locker with lockid idmap[killid]. */
169 if (dbenv
->db_verbose
!= 0 && killid
!= BAD_KILLID
)
170 __db_err(dbenv
, "Aborting locker %lx",
171 (u_long
)idmap
[killid
].id
);
173 if (killid
!= BAD_KILLID
&&
174 (ret
= __dd_abort(dbenv
, &idmap
[killid
])) != 0)
176 "warning: unable to abort locker %lx",
177 (u_long
)idmap
[killid
].id
);
186 * ========================================================================
190 __dd_build(dbenv
, bmp
, nlockers
, idmap
)
192 u_int32_t
**bmp
, *nlockers
;
195 struct __db_lock
*lp
;
197 DB_LOCKOBJ
*op
, *lo
, *lockerp
;
199 locker_info
*id_array
;
200 u_int32_t
*bitmap
, count
, *entryp
, i
, id
, nentries
, *tmpmap
;
206 * We'll check how many lockers there are, add a few more in for
207 * good measure and then allocate all the structures. Then we'll
208 * verify that we have enough room when we go back in and get the
209 * mutex the second time.
212 retry
: count
= lt
->region
->nlockers
;
213 lt
->region
->need_dd
= 0;
214 UNLOCK_LOCKREGION(lt
);
221 if (dbenv
->db_verbose
)
222 __db_err(dbenv
, "%lu lockers", (u_long
)count
);
225 nentries
= ALIGN(count
, 32) / 32;
227 * Allocate enough space for a count by count bitmap matrix.
230 * We can probably save the malloc's between iterations just
231 * reallocing if necessary because count grew by too much.
233 if ((bitmap
= (u_int32_t
*)__db_calloc((size_t)count
,
234 sizeof(u_int32_t
) * nentries
)) == NULL
) {
235 __db_err(dbenv
, "%s", strerror(ENOMEM
));
240 (u_int32_t
*)__db_calloc(sizeof(u_int32_t
), nentries
)) == NULL
) {
241 __db_err(dbenv
, "%s", strerror(ENOMEM
));
246 if ((id_array
= (locker_info
*)__db_calloc((size_t)count
,
247 sizeof(locker_info
))) == NULL
) {
248 __db_err(dbenv
, "%s", strerror(ENOMEM
));
255 * Now go back in and actually fill in the matrix.
258 if (lt
->region
->nlockers
> count
) {
266 * First we go through and assign each locker a deadlock detector id.
267 * Note that we fill in the idmap in the next loop since that's the
268 * only place where we conveniently have both the deadlock id and the
271 for (id
= 0, i
= 0; i
< lt
->region
->table_size
; i
++)
272 for (op
= SH_TAILQ_FIRST(<
->hashtab
[i
], __db_lockobj
);
273 op
!= NULL
; op
= SH_TAILQ_NEXT(op
, links
, __db_lockobj
))
274 if (op
->type
== DB_LOCK_LOCKER
)
277 * We go through the hash table and find each object. For each object,
278 * we traverse the waiters list and add an entry in the waitsfor matrix
279 * for each waiter/holder combination.
281 for (i
= 0; i
< lt
->region
->table_size
; i
++) {
282 for (op
= SH_TAILQ_FIRST(<
->hashtab
[i
], __db_lockobj
);
283 op
!= NULL
; op
= SH_TAILQ_NEXT(op
, links
, __db_lockobj
)) {
284 if (op
->type
!= DB_LOCK_OBJTYPE
)
286 CLEAR_MAP(tmpmap
, nentries
);
289 * First we go through and create a bit map that
290 * represents all the holders of this object.
292 for (lp
= SH_TAILQ_FIRST(&op
->holders
, __db_lock
);
294 lp
= SH_TAILQ_NEXT(lp
, links
, __db_lock
)) {
295 if (__lock_getobj(lt
, lp
->holder
,
296 NULL
, DB_LOCK_LOCKER
, &lockerp
) != 0) {
298 "warning unable to find object");
301 id_array
[lockerp
->dd_id
].id
= lp
->holder
;
302 id_array
[lockerp
->dd_id
].valid
= 1;
305 * If the holder has already been aborted, then
306 * we should ignore it for now.
308 if (lp
->status
== DB_LSTAT_HELD
)
309 SET_MAP(tmpmap
, lockerp
->dd_id
);
313 * Next, for each waiter, we set its row in the matrix
314 * equal to the map of holders we set up above.
317 lp
= SH_TAILQ_FIRST(&op
->waiters
, __db_lock
);
320 lp
= SH_TAILQ_NEXT(lp
, links
, __db_lock
)) {
321 if (__lock_getobj(lt
, lp
->holder
,
322 NULL
, DB_LOCK_LOCKER
, &lockerp
) != 0) {
324 "warning unable to find object");
327 id_array
[lockerp
->dd_id
].id
= lp
->holder
;
328 id_array
[lockerp
->dd_id
].valid
= 1;
331 * If the transaction is pending abortion, then
332 * ignore it on this iteration.
334 if (lp
->status
!= DB_LSTAT_WAITING
)
337 entryp
= bitmap
+ (nentries
* lockerp
->dd_id
);
338 OR_MAP(entryp
, tmpmap
, nentries
);
340 * If this is the first waiter on the queue,
341 * then we remove the waitsfor relationship
342 * with oneself. However, if it's anywhere
343 * else on the queue, then we have to keep
344 * it and we have an automatic deadlock.
347 CLR_MAP(entryp
, lockerp
->dd_id
);
352 /* Now for each locker; record its last lock. */
353 for (id
= 0; id
< count
; id
++) {
354 if (!id_array
[id
].valid
)
356 if (__lock_getobj(lt
,
357 id_array
[id
].id
, NULL
, DB_LOCK_LOCKER
, &lockerp
) != 0) {
359 "No locks for locker %lu", (u_long
)id_array
[id
].id
);
362 lp
= SH_LIST_FIRST(&lockerp
->heldby
, __db_lock
);
364 id_array
[id
].last_lock
= LOCK_TO_OFFSET(lt
, lp
);
365 lo
= (DB_LOCKOBJ
*)((u_int8_t
*)lp
+ lp
->obj
);
366 pptr
= SH_DBT_PTR(&lo
->lockobj
);
367 if (lo
->lockobj
.size
>= sizeof(db_pgno_t
))
368 memcpy(&id_array
[id
].pgno
, pptr
,
371 id_array
[id
].pgno
= 0;
375 /* Pass complete, reset the deadlock detector bit. */
376 lt
->region
->need_dd
= 0;
377 UNLOCK_LOCKREGION(lt
);
380 * Now we can release everything except the bitmap matrix that we
391 __dd_find(bmp
, idmap
, nlockers
)
392 u_int32_t
*bmp
, nlockers
;
395 u_int32_t i
, j
, nentries
, *mymap
, *tmpmap
;
398 * For each locker, OR in the bits from the lockers on which that
401 nentries
= ALIGN(nlockers
, 32) / 32;
402 for (mymap
= bmp
, i
= 0; i
< nlockers
; i
++, mymap
+= nentries
) {
405 for (j
= 0; j
< nlockers
; j
++) {
406 if (ISSET_MAP(mymap
, j
)) {
407 /* Find the map for this bit. */
408 tmpmap
= bmp
+ (nentries
* j
);
409 OR_MAP(mymap
, tmpmap
, nentries
);
410 if (ISSET_MAP(mymap
, i
))
419 __dd_abort(dbenv
, info
)
423 struct __db_lock
*lockp
;
425 DB_LOCKOBJ
*lockerp
, *sh_obj
;
431 /* Find the locker's last lock. */
433 __lock_getobj(lt
, info
->id
, NULL
, DB_LOCK_LOCKER
, &lockerp
)) != 0)
436 lockp
= SH_LIST_FIRST(&lockerp
->heldby
, __db_lock
);
437 if (LOCK_TO_OFFSET(lt
, lockp
) != info
->last_lock
||
438 lockp
== NULL
|| lockp
->status
!= DB_LSTAT_WAITING
)
441 /* Abort lock, take it off list, and wake up this lock. */
442 lockp
->status
= DB_LSTAT_ABORTED
;
443 lt
->region
->ndeadlocks
++;
444 SH_LIST_REMOVE(lockp
, locker_links
, __db_lock
);
445 sh_obj
= (DB_LOCKOBJ
*)((u_int8_t
*)lockp
+ lockp
->obj
);
446 SH_TAILQ_REMOVE(&sh_obj
->waiters
, lockp
, links
, __db_lock
);
447 (void)__db_mutex_unlock(&lockp
->mutex
, lt
->reginfo
.fd
);
451 out
: UNLOCK_LOCKREGION(lt
);
457 __dd_debug(dbenv
, idmap
, bitmap
, nlockers
)
460 u_int32_t
*bitmap
, nlockers
;
462 u_int32_t i
, j
, *mymap
, nentries
;
465 __db_err(dbenv
, "Waitsfor array");
466 __db_err(dbenv
, "waiter\twaiting on");
468 * Allocate space to print 10 bytes per item waited on.
470 if ((msgbuf
= (char *)__db_malloc((nlockers
+ 1) * 10 + 64)) == NULL
) {
471 __db_err(dbenv
, "%s", strerror(ENOMEM
));
475 nentries
= ALIGN(nlockers
, 32) / 32;
476 for (mymap
= bitmap
, i
= 0; i
< nlockers
; i
++, mymap
+= nentries
) {
479 sprintf(msgbuf
, /* Waiter. */
480 "%lx/%lu:\t", (u_long
)idmap
[i
].id
, (u_long
)idmap
[i
].pgno
);
481 for (j
= 0; j
< nlockers
; j
++)
482 if (ISSET_MAP(mymap
, j
))
483 sprintf(msgbuf
, "%s %lx", msgbuf
,
484 (u_long
)idmap
[j
].id
);
485 (void)sprintf(msgbuf
,
486 "%s %lu", msgbuf
, (u_long
)idmap
[i
].last_lock
);
487 __db_err(dbenv
, msgbuf
);