📄 lock.c
字号:
LOCKREGION(dbenv, lt); ret = __lock_put_nolock(dbenv, lock, &run_dd, 0); UNLOCKREGION(dbenv, lt); /* * Only run the lock detector if put told us to AND we are running * in auto-detect mode. If we are not running in auto-detect, then * a call to lock_detect here will 0 the need_dd bit, but will not * actually abort anything. */ if (ret == 0 && run_dd) (void)dbenv->lock_detect(dbenv, 0, ((DB_LOCKREGION *)lt->reginfo.primary)->detect, NULL); return (ret);}static int__lock_put_nolock(dbenv, lock, runp, flags) DB_ENV *dbenv; DB_LOCK *lock; int *runp; u_int32_t flags;{ struct __db_lock *lockp; DB_LOCKREGION *region; DB_LOCKTAB *lt; int ret; /* Check if locks have been globally turned off. */ if (F_ISSET(dbenv, DB_ENV_NOLOCKING)) return (0); lt = dbenv->lk_handle; region = lt->reginfo.primary; lockp = (struct __db_lock *)R_ADDR(<->reginfo, lock->off); LOCK_INIT(*lock); if (lock->gen != lockp->gen) { __db_err(dbenv, __db_lock_invalid, "DB_LOCK->lock_put"); return (EINVAL); } ret = __lock_put_internal(lt, lockp, lock->ndx, flags | DB_LOCK_UNLINK | DB_LOCK_FREE); *runp = 0; if (ret == 0 && region->need_dd && region->detect != DB_LOCK_NORUN) *runp = 1; return (ret);}/* * __lock_downgrade -- * Used to downgrade locks. Currently this is used in two places, * 1) by the concurrent access product to downgrade write locks * back to iwrite locks and 2) to downgrade write-handle locks to read-handle * locks at the end of an open/create. * * PUBLIC: int __lock_downgrade __P((DB_ENV *, * PUBLIC: DB_LOCK *, db_lockmode_t, u_int32_t)); */int__lock_downgrade(dbenv, lock, new_mode, flags) DB_ENV *dbenv; DB_LOCK *lock; db_lockmode_t new_mode; u_int32_t flags;{ struct __db_lock *lockp; DB_LOCKER *sh_locker; DB_LOCKOBJ *obj; DB_LOCKREGION *region; DB_LOCKTAB *lt; u_int32_t indx; int ret; COMPQUIET(flags, 0); PANIC_CHECK(dbenv); ret = 0; /* Check if locks have been globally turned off. */ if (F_ISSET(dbenv, DB_ENV_NOLOCKING)) return (0); lt = dbenv->lk_handle; region = lt->reginfo.primary; LOCKREGION(dbenv, lt); lockp = (struct __db_lock *)R_ADDR(<->reginfo, lock->off); if (lock->gen != lockp->gen) { __db_err(dbenv, __db_lock_invalid, "lock_downgrade"); ret = EINVAL; goto out; } LOCKER_LOCK(lt, region, lockp->holder, indx); if ((ret = __lock_getlocker(lt, lockp->holder, indx, 0, &sh_locker)) != 0 || sh_locker == NULL) { if (ret == 0) ret = EINVAL; __db_err(dbenv, __db_locker_invalid); goto out; } if (IS_WRITELOCK(lockp->mode) && !IS_WRITELOCK(new_mode)) sh_locker->nwrites--; if (new_mode == DB_LOCK_WWRITE) F_SET(sh_locker, DB_LOCKER_DIRTY); lockp->mode = new_mode; /* Get the object associated with this lock. */ obj = (DB_LOCKOBJ *)((u_int8_t *)lockp + lockp->obj); (void)__lock_promote(lt, obj, LF_ISSET(DB_LOCK_NOWAITERS));out: UNLOCKREGION(dbenv, lt); return (ret);}static int__lock_put_internal(lt, lockp, obj_ndx, flags) DB_LOCKTAB *lt; struct __db_lock *lockp; u_int32_t obj_ndx, flags;{ DB_LOCKOBJ *sh_obj; DB_LOCKREGION *region; int ret, state_changed; region = lt->reginfo.primary; ret = state_changed = 0; if (!OBJ_LINKS_VALID(lockp)) { /* * Someone removed this lock while we were doing a release * by locker id. We are trying to free this lock, but it's * already been done; all we need to do is return it to the * free list. */ lockp->status = DB_LSTAT_FREE; SH_TAILQ_INSERT_HEAD( ®ion->free_locks, lockp, links, __db_lock); region->stat.st_nlocks--; return (0); } if (LF_ISSET(DB_LOCK_DOALL)) region->stat.st_nreleases += lockp->refcount; else region->stat.st_nreleases++; if (!LF_ISSET(DB_LOCK_DOALL) && lockp->refcount > 1) { lockp->refcount--; return (0); } /* Increment generation number. */ lockp->gen++; /* Get the object associated with this lock. */ sh_obj = (DB_LOCKOBJ *)((u_int8_t *)lockp + lockp->obj); /* Remove this lock from its holders/waitlist. */ if (lockp->status != DB_LSTAT_HELD && lockp->status != DB_LSTAT_PENDING) __lock_remove_waiter(lt, sh_obj, lockp, DB_LSTAT_FREE); else { SH_TAILQ_REMOVE(&sh_obj->holders, lockp, links, __db_lock); lockp->links.stqe_prev = -1; } if (LF_ISSET(DB_LOCK_NOPROMOTE)) state_changed = 0; else state_changed = __lock_promote(lt, sh_obj, LF_ISSET(DB_LOCK_REMOVE | DB_LOCK_NOWAITERS)); if (LF_ISSET(DB_LOCK_UNLINK)) ret = __lock_checklocker(lt, lockp, lockp->holder, flags); /* Check if object should be reclaimed. */ if (SH_TAILQ_FIRST(&sh_obj->holders, __db_lock) == NULL && SH_TAILQ_FIRST(&sh_obj->waiters, __db_lock) == NULL) { HASHREMOVE_EL(lt->obj_tab, obj_ndx, __db_lockobj, links, sh_obj); if (sh_obj->lockobj.size > sizeof(sh_obj->objdata)) __db_shalloc_free(lt->reginfo.addr, SH_DBT_PTR(&sh_obj->lockobj)); SH_TAILQ_INSERT_HEAD( ®ion->free_objs, sh_obj, links, __db_lockobj); region->stat.st_nobjects--; state_changed = 1; } /* Free lock. */ if (!LF_ISSET(DB_LOCK_UNLINK) && LF_ISSET(DB_LOCK_FREE)) { lockp->status = DB_LSTAT_FREE; SH_TAILQ_INSERT_HEAD( ®ion->free_locks, lockp, links, __db_lock); region->stat.st_nlocks--; } /* * If we did not promote anyone; we need to run the deadlock * detector again. */ if (state_changed == 0) region->need_dd = 1; return (ret);}/* * Utility functions; listed alphabetically. *//* * __lock_checklocker -- * If a locker has no more locks, then we can free the object. * Return a boolean indicating whether we freed the object or not. * * Must be called without the locker's lock set. */static int__lock_checklocker(lt, lockp, locker, flags) DB_LOCKTAB *lt; struct __db_lock *lockp; u_int32_t locker, flags;{ DB_ENV *dbenv; DB_LOCKER *sh_locker; DB_LOCKREGION *region; u_int32_t indx; int ret; dbenv = lt->dbenv; region = lt->reginfo.primary; ret = 0; LOCKER_LOCK(lt, region, locker, indx); /* If the locker's list is NULL, free up the locker. */ if ((ret = __lock_getlocker(lt, locker, indx, 0, &sh_locker)) != 0 || sh_locker == NULL) { if (ret == 0) ret = EINVAL; __db_err(dbenv, __db_locker_invalid); goto freelock; } if (F_ISSET(sh_locker, DB_LOCKER_DELETED)) { LF_CLR(DB_LOCK_FREE); if (!LF_ISSET(DB_LOCK_IGNOREDEL)) goto freelock; } if (LF_ISSET(DB_LOCK_UNLINK)) { SH_LIST_REMOVE(lockp, locker_links, __db_lock); if (lockp->status == DB_LSTAT_HELD) { sh_locker->nlocks--; if (IS_WRITELOCK(lockp->mode)) sh_locker->nwrites--; } } if (SH_LIST_FIRST(&sh_locker->heldby, __db_lock) == NULL && LF_ISSET(DB_LOCK_FREE_LOCKER)) __lock_freelocker( lt, region, sh_locker, indx);freelock: if (LF_ISSET(DB_LOCK_FREE)) { lockp->status = DB_LSTAT_FREE; SH_TAILQ_INSERT_HEAD( ®ion->free_locks, lockp, links, __db_lock); region->stat.st_nlocks--; } return (ret);}/* * __lock_addfamilylocker * Put a locker entry in for a child transaction. * * PUBLIC: int __lock_addfamilylocker __P((DB_ENV *, u_int32_t, u_int32_t)); */int__lock_addfamilylocker(dbenv, pid, id) DB_ENV *dbenv; u_int32_t pid, id;{ DB_LOCKER *lockerp, *mlockerp; DB_LOCKREGION *region; DB_LOCKTAB *lt; u_int32_t ndx; int ret; lt = dbenv->lk_handle; region = lt->reginfo.primary; LOCKREGION(dbenv, lt); /* get/create the parent locker info */ LOCKER_LOCK(lt, region, pid, ndx); if ((ret = __lock_getlocker(dbenv->lk_handle, pid, ndx, 1, &mlockerp)) != 0) goto err; /* * We assume that only one thread can manipulate * a single transaction family. * Therefore the master locker cannot go away while * we manipulate it, nor can another child in the * family be created at the same time. */ LOCKER_LOCK(lt, region, id, ndx); if ((ret = __lock_getlocker(dbenv->lk_handle, id, ndx, 1, &lockerp)) != 0) goto err; /* Point to our parent. */ lockerp->parent_locker = R_OFFSET(<->reginfo, mlockerp); /* See if this locker is the family master. */ if (mlockerp->master_locker == INVALID_ROFF) lockerp->master_locker = R_OFFSET(<->reginfo, mlockerp); else { lockerp->master_locker = mlockerp->master_locker; mlockerp = R_ADDR(<->reginfo, mlockerp->master_locker); } /* * Link the child at the head of the master's list. * The guess is when looking for deadlock that * the most recent child is the one thats blocked. */ SH_LIST_INSERT_HEAD( &mlockerp->child_locker, lockerp, child_link, __db_locker);err: UNLOCKREGION(dbenv, lt); return (ret);}/* * __lock_freefamilylocker * Remove a locker from the hash table and its family. * * This must be called without the locker bucket locked. * * PUBLIC: int __lock_freefamilylocker __P((DB_LOCKTAB *, u_int32_t)); */int__lock_freefamilylocker(lt, locker) DB_LOCKTAB *lt; u_int32_t locker;{ DB_ENV *dbenv; DB_LOCKER *sh_locker; DB_LOCKREGION *region; u_int32_t indx; int ret; dbenv = lt->dbenv; region = lt->reginfo.primary; LOCKREGION(dbenv, lt); LOCKER_LOCK(lt, region, locker, indx); if ((ret = __lock_getlocker(lt, locker, indx, 0, &sh_locker)) != 0 || sh_locker == NULL) goto freelock; if (SH_LIST_FIRST(&sh_locker->heldby, __db_lock) != NULL) { ret = EINVAL; __db_err(dbenv, "Freeing locker with locks"); goto freelock; } /* If this is part of a family, we must fix up its links. */ if (sh_locker->master_locker != INVALID_ROFF) SH_LIST_REMOVE(sh_locker, child_link, __db_locker); __lock_freelocker(lt, region, sh_locker, indx);freelock: UNLOCKREGION(dbenv, lt); return (ret);}/* * __lock_freelocker * common code for deleting a locker. * * This must be called with the locker bucket locked. */static void__lock_freelocker(lt, region, sh_locker, indx) DB_LOCKTAB *lt; DB_LOCKREGION *region; DB_LOCKER *sh_locker; u_int32_t indx;{ HASHREMOVE_EL( lt->locker_tab, indx, __db_locker, links, sh_locker); SH_TAILQ_INSERT_HEAD( ®ion->free_lockers, sh_locker, links, __db_locker); SH_TAILQ_REMOVE(®ion->lockers, sh_locker, ulinks, __db_locker); region->stat.st_nlockers--;}/* * __lock_set_timeout * -- set timeout values in shared memory. * This is called from the transaction system. * We either set the time that this tranaction expires or the * amount of time that a lock for this transaction is permitted * to wait. * * PUBLIC: int __lock_set_timeout __P(( DB_ENV *, * PUBLIC: u_int32_t, db_timeout_t, u_int32_t)); */int__lock_set_timeout(dbenv, locker, timeout, op) DB_ENV *dbenv; u_int32_t locker; db_timeout_t timeout; u_int32_t op;{ DB_LOCKER *sh_locker; DB_LOCKREGION *region; DB_LOCKTAB *lt; u_int32_t locker_ndx; int ret; lt = dbenv->lk_handle; region = lt->reginfo.primary; LOCKREGION(dbenv, lt); LOCKER_LOCK(lt, region, locker, locker_ndx); ret = __lock_getlocker(lt, locker, locker_ndx, 1, &sh_locker); UNLOCKREGION(dbenv, lt); if (ret != 0) return (ret); if (op == DB_SET_TXN_TIMEOUT) { if (timeout == 0) LOCK_SET_TIME_INVALID(&sh_locker->tx_expire); else __lock_expires(dbenv, &sh_locker->tx_expire, timeout); } else if (op == DB_SET_LOCK_TIMEOUT) { sh_locker->lk_timeout = timeout; F_SET(sh_locker, DB_LOCKER_TIMEOUT); } else if (op == DB_SET_TXN_NOW) { LOCK_SET_TIME_INVALID(&sh_locker->tx_expire); __lock_expires(dbenv, &sh_locker->tx_expire, 0); sh_locker->lk_expire = sh_locker->tx_expire; } else return (EINVAL); return (0);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -