1/*-
2 * See the file LICENSE for redistribution information.
3 *
4 * Copyright (c) 2005,2008 Oracle.  All rights reserved.
5 *
6 * $Id: lock_failchk.c,v 12.18 2008/01/08 20:58:40 bostic Exp $
7 */
8
9#include "db_config.h"
10
11#include "db_int.h"
12#include "dbinc/lock.h"
13#include "dbinc/txn.h"
14
15/*
16 * __lock_failchk --
17 *	Check for locks held by dead threads of control and release
18 *	read locks.  If any write locks were held by dead non-trasnactional
19 *	lockers then we must abort and run recovery.  Otherwise we release
20 *	read locks for lockers owned by dead threads.  Write locks for
21 *	dead transactional lockers will be freed when we abort the transaction.
22 *
23 * PUBLIC: int __lock_failchk __P((ENV *));
24 */
25int
26__lock_failchk(env)
27	ENV *env;
28{
29	DB_ENV *dbenv;
30	DB_LOCKER *lip;
31	DB_LOCKREGION *lrp;
32	DB_LOCKREQ request;
33	DB_LOCKTAB *lt;
34	u_int32_t i;
35	int ret;
36	char buf[DB_THREADID_STRLEN];
37
38	dbenv = env->dbenv;
39	lt = env->lk_handle;
40	lrp = lt->reginfo.primary;
41
42retry:	LOCK_LOCKERS(env, lrp);
43
44	ret = 0;
45	for (i = 0; i < lrp->locker_t_size; i++)
46		SH_TAILQ_FOREACH(lip, &lt->locker_tab[i], links, __db_locker) {
47			/*
48			 * Skip lockers that have no locks.  Check the heldby
49			 * list rather then nlocks since a lock may be PENDING.
50			 * If the locker is transactional, we can ignore it if
51			 * it has no read lock or has no locks at all;
52			 * __txn_failchk aborts any transactional lockers.
53			 */
54			if (SH_LIST_EMPTY(&lip->heldby) ||
55			     (lip->id >= TXN_MINIMUM &&
56			     lip->nlocks == lip->nwrites))
57				continue;
58
59			/* If the locker is still alive, it's not a problem. */
60			if (dbenv->is_alive(dbenv, lip->pid, lip->tid, 0))
61				continue;
62
63			/*
64			 * We can only deal with read locks.  If a
65			 * non-transactional locker holds write locks we
66			 * have to assume a Berkeley DB operation was
67			 * interrupted with only 1-of-N pages modified.
68			 */
69			if (lip->id < TXN_MINIMUM && lip->nwrites != 0) {
70				ret = __db_failed(env,
71				     "locker has write locks",
72				     lip->pid, lip->tid);
73				break;
74			}
75
76			/*
77			 * Discard the locker and its read locks.
78			 */
79			__db_msg(env, "Freeing read locks for locker %#lx: %s",
80			    (u_long)lip->id, dbenv->thread_id_string(
81			    dbenv, lip->pid, lip->tid, buf));
82			UNLOCK_LOCKERS(env, lrp);
83			memset(&request, 0, sizeof(request));
84			request.op = DB_LOCK_PUT_READ;
85			if ((ret = __lock_vec(env,
86			    lip, 0, &request, 1, NULL)) != 0)
87				return (ret);
88
89			/*
90			 * This locker is most likely referenced by a cursor
91			 * which is owned by a dead thread.  Normally the
92			 * cursor would be available for other threads
93			 * but we assume the dead thread will never release
94			 * it.
95			 */
96			if (lip->id < TXN_MINIMUM &&
97			    (ret = __lock_freefamilylocker(lt, lip)) != 0)
98				return (ret);
99			goto retry;
100		}
101
102	UNLOCK_LOCKERS(env, lrp);
103
104	return (ret);
105}
106