lock.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*-
* See the file LICENSE for redistribution information.
*
* Copyright (c) 1996, 1997, 1998
* Sleepycat Software. All rights reserved.
*/
#include "config.h"
#ifndef lint
#endif /* not lint */
#ifndef NO_SYSTEM_INCLUDES
#include <errno.h>
#include <string.h>
#endif
#include "db_int.h"
#include "shqueue.h"
#include "db_page.h"
#include "db_shash.h"
#include "lock.h"
#include "db_am.h"
#include "txn_auto.h"
#include "txn_ext.h"
#include "common_ext.h"
static void __lock_remove_waiter
int
DB_LOCKTAB *lt;
{
return (0);
}
int
DB_LOCKTAB *lt;
int nlist;
{
return (__lock_vec_internal(lt,
}
int
DB_LOCKTAB *lt;
int nlist;
{
return (__lock_vec_internal(lt,
}
static int
DB_LOCKTAB *lt;
int nlist;
{
/* Validate arguments. */
if ((ret =
return (ret);
return (ret);
}
ret = 0;
case DB_LOCK_GET:
if (ret == 0) {
}
break;
case DB_LOCK_INHERIT:
/* Find the locker. */
break;
break;
}
break;
/*
* Traverse all the locks held by this locker. Remove
* the locks from the locker's list and put them on the
* parent's list.
*/
}
break;
case DB_LOCK_PUT:
break;
}
break;
case DB_LOCK_PUT_ALL:
/* Find the locker. */
break;
break;
}
break;
case DB_LOCK_PUT_OBJ:
/* Look up the object in the hash table. */
break;
}
/*
* Release waiters first, because they won't cause
* anyone else to be awakened. If we release the
* lockers first, all the waiters get awakened
* needlessly.
*/
}
}
/* Now free the object. */
break;
#ifdef DEBUG
case DB_LOCK_DUMP:
/* Find the locker. */
break;
}
if (ret == 0) {
}
break;
#endif
default:
break;
}
}
run_dd = 1;
} else
run_dd = 0;
return (ret);
}
int
DB_LOCKTAB *lt;
{
int ret;
/* Validate arguments. */
return (ret);
if (LF_ISSET(DB_LOCK_UPGRADE))
if (!LF_ISSET(DB_LOCK_UPGRADE))
}
}
return (ret);
}
int
DB_LOCKTAB *lt;
{
int ret;
/* Validate arguments. */
return (ret);
if (LF_ISSET(DB_LOCK_UPGRADE))
if (!LF_ISSET(DB_LOCK_UPGRADE))
}
}
return (ret);
}
int
DB_LOCKTAB *lt;
{
return (ret);
else {
}
run_dd = 1;
} else
run_dd = 0;
return (ret);
}
static int
DB_LOCKTAB *lt;
int do_all;
{
int state_changed;
return (EINVAL);
}
if (do_all)
else
return (0);
}
/* Get the object associated with this lock. */
/* Remove lock from locker list. */
else
/* Check if object should be reclaimed. */
state_changed = 1;
}
/* Free lock. */
/*
* If we did not promote anyone; we need to run the deadlock
* detector again.
*/
if (state_changed == 0)
return (0);
}
static int
DB_LOCKTAB *lt;
{
/*
* Check that lock mode is valid.
*/
return (EINVAL);
}
/* Allocate a new lock. Optimize for the common case of a grant. */
return (ret);
}
/* Optimize for common case of granting a lock. */
return (ret);
/* Now make new lock point to object */
/*
* Now we have a lock and an object and we need to see if we should
* grant the lock. We use a FIFO ordering so we can only grant a
* new lock if it does not conflict with anyone on the holders list
* OR anyone on the waiters list. The reason that we don't grant if
* there's a conflict is that this can lead to starvation (a writer
* waiting on a popularly read item will never be granted). The
* downside of this is that a waiting reader can prevent an upgrade
* from reader to writer, which is not uncommon.
*
* There is one exception to the no-conflict rule. If a lock is held
* by the requesting locker AND the new lock does not conflict with
* any other holders, then we grant the lock. The most common place
* this happens is when the holder has a WRITE lock and a READ lock
* request comes in for the same locker. If we do not grant the read
* lock, then we guarantee deadlock.
*
* In case of conflict, we put the new lock on the end of the waiters
* list, unless we are upgrading in which case the locker goes on the
* front of the list.
*/
ihold = 0;
if (LF_ISSET(DB_LOCK_UPGRADE))
goto upgrade;
/*
* Lock is held, so we can increment the
* reference count and return this lock.
*/
return (0);
} else
ihold = 1;
break;
}
/*
* If we are upgrading, then there are two scenarios. Either
* we had no conflicts, so we can do the upgrade. Or, there
* is a conflict and we should wait at the HEAD of the waiters
* list.
*/
if (LF_ISSET(DB_LOCK_UPGRADE)) {
goto upgrade;
/* There was a conflict, wait. */
goto wait;
}
break;
}
else if (!(flags & DB_LOCK_NOWAIT))
else {
/* Free the lock and return an error. */
return (DB_LOCK_NOTGRANTED);
}
/*
* Now, insert the lock onto its locker's list. If the locker does
* not currently hold any locks, there's no reason to run a deadlock
* detector, save that information.
*/
if ((ret =
return (ret);
/*
* This is really a blocker for the process, so initialize it
* set. That way the current process will block when it tries
* to get it and the waking process will release it.
*/
lrp->nconflicts++;
/*
* We are about to wait; must release the region mutex. Then,
* when we wakeup, we need to reacquire the region mutex before
* continuing.
*/
/*
* We are about to wait; before waiting, see if the deadlock
* detector should be run.
*/
/*
* If this lock errored due to a deadlock, then
* we have waiters that require promotion.
*/
/* Return to free list. */
case DB_LSTAT_ABORTED:
break;
case DB_LSTAT_NOGRANT:
break;
default:
break;
}
} else if (LF_ISSET(DB_LOCK_UPGRADE)) {
/*
* The lock that was just granted got put on the
* holders list. Since we're upgrading some other
* lock, we've got to remove it here.
*/
goto upgrade;
} else
}
return (ret);
/*
* This was an upgrade, so return the new lock to the free list and
* upgrade the mode.
*/
return (0);
}
/*
* __lock_is_locked --
*
* PUBLIC: int __lock_is_locked
* PUBLIC: __P((DB_LOCKTAB *, u_int32_t, DBT *, db_lockmode_t));
*/
int
DB_LOCKTAB *lt;
{
/* Look up the object in the hash table. */
return (0);
return (1);
}
return (0);
}
/*
* __lock_printlock --
*
* PUBLIC: void __lock_printlock __P((DB_LOCKTAB *, struct __db_lock *, int));
*/
void
DB_LOCKTAB *lt;
int ispgno;
{
case DB_LOCK_IREAD:
mode = "IREAD";
break;
case DB_LOCK_IWR:
mode = "IWR";
break;
case DB_LOCK_IWRITE:
mode = "IWRITE";
break;
case DB_LOCK_NG:
mode = "NG";
break;
case DB_LOCK_READ:
mode = "READ";
break;
case DB_LOCK_WRITE:
mode = "WRITE";
break;
default:
mode = "UNKNOWN";
break;
}
case DB_LSTAT_ABORTED:
status = "ABORT";
break;
case DB_LSTAT_ERR:
status = "ERROR";
break;
case DB_LSTAT_FREE:
status = "FREE";
break;
case DB_LSTAT_HELD:
status = "HELD";
break;
case DB_LSTAT_NOGRANT:
status = "NONE";
break;
case DB_LSTAT_WAITING:
status = "WAIT";
break;
case DB_LSTAT_PENDING:
status = "PENDING";
break;
default:
status = "UNKNOWN";
break;
}
printf("\t%lx\t%s\t%lu\t%s\t",
if (ispgno) {
/* Assume this is a DBT lock. */
} else {
printf("\n");
}
}
/*
* PUBLIC: int __lock_getobj __P((DB_LOCKTAB *,
* PUBLIC: u_int32_t, const DBT *, u_int32_t type, DB_LOCKOBJ **));
*/
int
DB_LOCKTAB *lt;
DB_LOCKOBJ **objp;
{
int ret;
void *p, *src;
/* Look up the object in the hash table. */
if (type == DB_LOCK_OBJTYPE) {
} else {
}
/*
* If we found the object, then we can just return it. If
* we didn't find the object, then we need to create it.
*/
/* Create new object and then insert it into hash table. */
if ((sh_obj =
return (ret);
}
/*
* If we can fit this object in the structure, do so instead
* of shalloc-ing space for it.
*/
else
if ((ret =
DB_LOCK_MEM, obj_size)) != 0)
return (ret);
/* Reacquire the head of the list. */
}
if (type == DB_LOCK_LOCKER)
else
if (type == DB_LOCK_LOCKER)
}
return (0);
}
/*
* Any lock on the waitlist has a process waiting for it. Therefore, we
* can't return the lock to the freelist immediately. Instead, we can
* remove the lock from the list of waiters, set the status field of the
* lock, and then let the process waking up return the lock to the
* free list.
*/
static void
DB_LOCKTAB *lt;
{
/* Wake whoever is waiting on this lock. */
}
static void
DB_LOCKTAB *lt;
int do_remove;
{
if (do_remove)
/* if the locker list is NULL, free up the object. */
}
}
static void
DB_LOCKTAB *lt;
{
}
/*
* __lock_downgrade --
* Used by the concurrent access product to downgrade write locks
* back to iwrite locks.
*
* PUBLIC: int __lock_downgrade __P((DB_LOCKTAB *,
* PUBLIC: DB_LOCK, db_lockmode_t, u_int32_t));
*/
int
DB_LOCKTAB *lt;
{
int ret;
/* Get the object associated with this lock. */
}
return (ret);
}
/*
* __lock_promote --
*
* Look through the waiters and holders lists and decide which (if any)
* locks can be promoted. Promote any that are eligible.
*/
static int
DB_LOCKTAB *lt;
{
int state_changed, waiter_is_txn;
/*
* We need to do lock promotion. We also need to determine if
* we're going to need to run the deadlock detector again. If
* we release locks, and there are waiters, but no one gets promoted,
* then we haven't fundamentally changed the lockmgr state, so
* we may still have a deadlock and we have to run again. However,
* if there were no waiters, or we actually promoted someone, then
* we are OK and we don't have to run it immediately.
*
* During promotion, we look for state changes so we can return
* this information to the caller.
*/
lp_w = next_waiter) {
!(waiter_is_txn &&
TXN_IS_HOLDING(lp_h) &&
break;
}
break;
/* No conflict, promote the waiting lock. */
/* Wake up waiter. */
state_changed = 1;
}
return (state_changed);
}
static int
{
DB_TXN *t;
return (0);
return (1);
return (0);
}