/*- * See the file LICENSE for redistribution information. * * Copyright (c) 2005,2008 Oracle. All rights reserved. * * $Id: lock_failchk.c,v 12.18 2008/01/08 20:58:40 bostic Exp $ */ #include "db_config.h" #include "db_int.h" #include "dbinc/lock.h" #include "dbinc/txn.h" /* * __lock_failchk -- * Check for locks held by dead threads of control and release * read locks. If any write locks were held by dead non-trasnactional * lockers then we must abort and run recovery. Otherwise we release * read locks for lockers owned by dead threads. Write locks for * dead transactional lockers will be freed when we abort the transaction. * * PUBLIC: int __lock_failchk __P((ENV *)); */ int __lock_failchk(env) ENV *env; { DB_ENV *dbenv; DB_LOCKER *lip; DB_LOCKREGION *lrp; DB_LOCKREQ request; DB_LOCKTAB *lt; u_int32_t i; int ret; char buf[DB_THREADID_STRLEN]; dbenv = env->dbenv; lt = env->lk_handle; lrp = lt->reginfo.primary; retry: LOCK_LOCKERS(env, lrp); ret = 0; for (i = 0; i < lrp->locker_t_size; i++) SH_TAILQ_FOREACH(lip, <->locker_tab[i], links, __db_locker) { /* * Skip lockers that have no locks. Check the heldby * list rather then nlocks since a lock may be PENDING. * If the locker is transactional, we can ignore it if * it has no read lock or has no locks at all; * __txn_failchk aborts any transactional lockers. */ if (SH_LIST_EMPTY(&lip->heldby) || (lip->id >= TXN_MINIMUM && lip->nlocks == lip->nwrites)) continue; /* If the locker is still alive, it's not a problem. */ if (dbenv->is_alive(dbenv, lip->pid, lip->tid, 0)) continue; /* * We can only deal with read locks. If a * non-transactional locker holds write locks we * have to assume a Berkeley DB operation was * interrupted with only 1-of-N pages modified. */ if (lip->id < TXN_MINIMUM && lip->nwrites != 0) { ret = __db_failed(env, "locker has write locks", lip->pid, lip->tid); break; } /* * Discard the locker and its read locks. */ __db_msg(env, "Freeing read locks for locker %#lx: %s", (u_long)lip->id, dbenv->thread_id_string( dbenv, lip->pid, lip->tid, buf)); UNLOCK_LOCKERS(env, lrp); memset(&request, 0, sizeof(request)); request.op = DB_LOCK_PUT_READ; if ((ret = __lock_vec(env, lip, 0, &request, 1, NULL)) != 0) return (ret); /* * This locker is most likely referenced by a cursor * which is owned by a dead thread. Normally the * cursor would be available for other threads * but we assume the dead thread will never release * it. */ if (lip->id < TXN_MINIMUM && (ret = __lock_freefamilylocker(lt, lip)) != 0) return (ret); goto retry; } UNLOCK_LOCKERS(env, lrp); return (ret); }