1/*-
2 * See the file LICENSE for redistribution information.
3 *
4 * Copyright (c) 2005-2009 Oracle.  All rights reserved.
5 *
6 * $Id$
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			 * If the locker is transactional, we can ignore it if
49			 * it has no read locks or has no locks at all.  Check
50			 * the heldby list rather then nlocks since a lock may
51			 * be PENDING.  __txn_failchk aborts any transactional
52			 * lockers.  Non-transactional lockers progress to
53			 * is_alive test.
54			 */
55			if ((lip->id >= TXN_MINIMUM) &&
56			     (SH_LIST_EMPTY(&lip->heldby) ||
57			     lip->nlocks == lip->nwrites))
58				continue;
59
60			/* If the locker is still alive, it's not a problem. */
61			if (dbenv->is_alive(dbenv, lip->pid, lip->tid, 0))
62				continue;
63
64			/*
65			 * We can only deal with read locks.  If a
66			 * non-transactional locker holds write locks we
67			 * have to assume a Berkeley DB operation was
68			 * interrupted with only 1-of-N pages modified.
69			 */
70			if (lip->id < TXN_MINIMUM && lip->nwrites != 0) {
71				ret = __db_failed(env,
72				     "locker has write locks",
73				     lip->pid, lip->tid);
74				break;
75			}
76
77			/*
78			 * Discard the locker and its read locks.
79			 */
80			if (!SH_LIST_EMPTY(&lip->heldby)) {
81				__db_msg(env,
82				    "Freeing read locks for locker %#lx: %s",
83			    	    (u_long)lip->id, dbenv->thread_id_string(
84			    	    dbenv, lip->pid, lip->tid, buf));
85				UNLOCK_LOCKERS(env, lrp);
86				memset(&request, 0, sizeof(request));
87				request.op = DB_LOCK_PUT_READ;
88				if ((ret = __lock_vec(env,
89			    	    lip, 0, &request, 1, NULL)) != 0)
90					return (ret);
91			}
92			else
93				UNLOCK_LOCKERS(env, lrp);
94
95			/*
96			 * This locker is most likely referenced by a cursor
97			 * which is owned by a dead thread.  Normally the
98			 * cursor would be available for other threads
99			 * but we assume the dead thread will never release
100			 * it.
101			 */
102			if (lip->id < TXN_MINIMUM &&
103			    (ret = __lock_freefamilylocker(lt, lip)) != 0)
104				return (ret);
105			goto retry;
106		}
107
108	UNLOCK_LOCKERS(env, lrp);
109
110	return (ret);
111}
112