diff options
Diffstat (limited to 'src/lock/lock_failchk.c')
-rw-r--r-- | src/lock/lock_failchk.c | 114 |
1 files changed, 114 insertions, 0 deletions
diff --git a/src/lock/lock_failchk.c b/src/lock/lock_failchk.c new file mode 100644 index 00000000..59fb010f --- /dev/null +++ b/src/lock/lock_failchk.c @@ -0,0 +1,114 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2005, 2012 Oracle and/or its affiliates. All rights reserved. + * + * $Id$ + */ + +#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) { + /* + * If the locker is transactional, we can ignore it if + * it has no read locks or has no locks at all. Check + * the heldby list rather then nlocks since a lock may + * be PENDING. __txn_failchk aborts any transactional + * lockers. Non-transactional lockers progress to + * is_alive test. + */ + if ((lip->id >= TXN_MINIMUM) && + (SH_LIST_EMPTY(&lip->heldby) || + 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, + F_ISSET(lip, DB_LOCKER_HANDLE_LOCKER) ? + DB_MUTEX_PROCESS_ONLY : 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, DB_STR_A("2052", + "locker has write locks", ""), + lip->pid, lip->tid); + break; + } + + /* + * Discard the locker and its read locks. + */ + if (!SH_LIST_EMPTY(&lip->heldby)) { + __db_msg(env, DB_STR_A("2053", + "Freeing read locks for locker %#lx: %s", + "%#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); + } + else + UNLOCK_LOCKERS(env, lrp); + + /* + * 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_freelocker(lt, lip)) != 0) + return (ret); + goto retry; + } + + UNLOCK_LOCKERS(env, lrp); + + return (ret); +} |