📄 lock.c
字号:
while ((locallock = (LOCALLOCK *) hash_seq_search(&status)) != NULL) { if (locallock->proclock == NULL || locallock->lock == NULL) { /* * We must've run out of shared memory while trying to set up this * lock. Just forget the local entry. */ Assert(locallock->nLocks == 0); RemoveLocalLock(locallock); continue; } /* Ignore items that are not of the lockmethod to be removed */ if (LOCALLOCK_LOCKMETHOD(*locallock) != lockmethodid) continue; /* * If we are asked to release all locks, we can just zap the entry. * Otherwise, must scan to see if there are session locks. We assume * there is at most one lockOwners entry for session locks. */ if (!allLocks) { LOCALLOCKOWNER *lockOwners = locallock->lockOwners; /* If it's above array position 0, move it down to 0 */ for (i = locallock->numLockOwners - 1; i > 0; i--) { if (lockOwners[i].owner == NULL) { lockOwners[0] = lockOwners[i]; break; } } if (locallock->numLockOwners > 0 && lockOwners[0].owner == NULL && lockOwners[0].nLocks > 0) { /* Fix the locallock to show just the session locks */ locallock->nLocks = lockOwners[0].nLocks; locallock->numLockOwners = 1; /* We aren't deleting this locallock, so done */ continue; } } /* Mark the proclock to show we need to release this lockmode */ if (locallock->nLocks > 0) locallock->proclock->releaseMask |= LOCKBIT_ON(locallock->tag.mode); /* And remove the locallock hashtable entry */ RemoveLocalLock(locallock); } LWLockAcquire(masterLock, LW_EXCLUSIVE); proclock = (PROCLOCK *) SHMQueueNext(procLocks, procLocks, offsetof(PROCLOCK, procLink)); while (proclock) { bool wakeupNeeded = false; PROCLOCK *nextplock; /* Get link first, since we may unlink/delete this proclock */ nextplock = (PROCLOCK *) SHMQueueNext(procLocks, &proclock->procLink, offsetof(PROCLOCK, procLink)); Assert(proclock->tag.proc == MAKE_OFFSET(MyProc)); lock = (LOCK *) MAKE_PTR(proclock->tag.lock); /* Ignore items that are not of the lockmethod to be removed */ if (LOCK_LOCKMETHOD(*lock) != lockmethodid) goto next_item; /* * In allLocks mode, force release of all locks even if locallock * table had problems */ if (allLocks) proclock->releaseMask = proclock->holdMask; else Assert((proclock->releaseMask & ~proclock->holdMask) == 0); /* * Ignore items that have nothing to be released, unless they have * holdMask == 0 and are therefore recyclable */ if (proclock->releaseMask == 0 && proclock->holdMask != 0) goto next_item; PROCLOCK_PRINT("LockReleaseAll", proclock); LOCK_PRINT("LockReleaseAll", lock, 0); Assert(lock->nRequested >= 0); Assert(lock->nGranted >= 0); Assert(lock->nGranted <= lock->nRequested); Assert((proclock->holdMask & ~lock->grantMask) == 0); /* * Release the previously-marked lock modes */ for (i = 1; i <= numLockModes; i++) { if (proclock->releaseMask & LOCKBIT_ON(i)) wakeupNeeded |= UnGrantLock(lock, i, proclock, lockMethodTable); } Assert((lock->nRequested >= 0) && (lock->nGranted >= 0)); Assert(lock->nGranted <= lock->nRequested); LOCK_PRINT("LockReleaseAll: updated", lock, 0); proclock->releaseMask = 0; /* CleanUpLock will wake up waiters if needed. */ CleanUpLock(lockmethodid, lock, proclock, wakeupNeeded);next_item: proclock = nextplock; } LWLockRelease(masterLock);#ifdef LOCK_DEBUG if (lockmethodid == USER_LOCKMETHOD ? Trace_userlocks : Trace_locks) elog(LOG, "LockReleaseAll done");#endif}/* * LockReleaseCurrentOwner * Release all locks belonging to CurrentResourceOwner * * Only DEFAULT_LOCKMETHOD locks can belong to a resource owner. */voidLockReleaseCurrentOwner(void){ HASH_SEQ_STATUS status; LOCALLOCK *locallock; LOCALLOCKOWNER *lockOwners; int i; hash_seq_init(&status, LockMethodLocalHash[DEFAULT_LOCKMETHOD]); while ((locallock = (LOCALLOCK *) hash_seq_search(&status)) != NULL) { /* Ignore items that must be nontransactional */ if (LOCALLOCK_LOCKMETHOD(*locallock) != DEFAULT_LOCKMETHOD) continue; /* Scan to see if there are any locks belonging to current owner */ lockOwners = locallock->lockOwners; for (i = locallock->numLockOwners - 1; i >= 0; i--) { if (lockOwners[i].owner == CurrentResourceOwner) { Assert(lockOwners[i].nLocks > 0); if (lockOwners[i].nLocks < locallock->nLocks) { /* * We will still hold this lock after forgetting this * ResourceOwner. */ locallock->nLocks -= lockOwners[i].nLocks; /* compact out unused slot */ locallock->numLockOwners--; if (i < locallock->numLockOwners) lockOwners[i] = lockOwners[locallock->numLockOwners]; } else { Assert(lockOwners[i].nLocks == locallock->nLocks); /* We want to call LockRelease just once */ lockOwners[i].nLocks = 1; locallock->nLocks = 1; if (!LockRelease(DEFAULT_LOCKMETHOD, &locallock->tag.lock, locallock->tag.mode, false)) elog(WARNING, "LockReleaseCurrentOwner: failed??"); } break; } } }}/* * LockReassignCurrentOwner * Reassign all locks belonging to CurrentResourceOwner to belong * to its parent resource owner */voidLockReassignCurrentOwner(void){ ResourceOwner parent = ResourceOwnerGetParent(CurrentResourceOwner); HASH_SEQ_STATUS status; LOCALLOCK *locallock; LOCALLOCKOWNER *lockOwners; Assert(parent != NULL); hash_seq_init(&status, LockMethodLocalHash[DEFAULT_LOCKMETHOD]); while ((locallock = (LOCALLOCK *) hash_seq_search(&status)) != NULL) { int i; int ic = -1; int ip = -1; /* Ignore items that must be nontransactional */ if (LOCALLOCK_LOCKMETHOD(*locallock) != DEFAULT_LOCKMETHOD) continue; /* * Scan to see if there are any locks belonging to current owner or * its parent */ lockOwners = locallock->lockOwners; for (i = locallock->numLockOwners - 1; i >= 0; i--) { if (lockOwners[i].owner == CurrentResourceOwner) ic = i; else if (lockOwners[i].owner == parent) ip = i; } if (ic < 0) continue; /* no current locks */ if (ip < 0) { /* Parent has no slot, so just give it child's slot */ lockOwners[ic].owner = parent; } else { /* Merge child's count with parent's */ lockOwners[ip].nLocks += lockOwners[ic].nLocks; /* compact out unused slot */ locallock->numLockOwners--; if (ic < locallock->numLockOwners) lockOwners[ic] = lockOwners[locallock->numLockOwners]; } }}/* * AtPrepare_Locks * Do the preparatory work for a PREPARE: make 2PC state file records * for all locks currently held. * * User locks are non-transactional and are therefore ignored. * * There are some special cases that we error out on: we can't be holding * any session locks (should be OK since only VACUUM uses those) and we * can't be holding any locks on temporary objects (since that would mess * up the current backend if it tries to exit before the prepared xact is * committed). */voidAtPrepare_Locks(void){ LOCKMETHODID lockmethodid = DEFAULT_LOCKMETHOD; HASH_SEQ_STATUS status; LOCALLOCK *locallock; /* * We don't need to touch shared memory for this --- all the necessary * state information is in the locallock table. */ hash_seq_init(&status, LockMethodLocalHash[lockmethodid]); while ((locallock = (LOCALLOCK *) hash_seq_search(&status)) != NULL) { TwoPhaseLockRecord record; LOCALLOCKOWNER *lockOwners = locallock->lockOwners; int i; /* Ignore items that are not of the lockmethod to be processed */ if (LOCALLOCK_LOCKMETHOD(*locallock) != lockmethodid) continue; /* Ignore it if we don't actually hold the lock */ if (locallock->nLocks <= 0) continue; /* Scan to verify there are no session locks */ for (i = locallock->numLockOwners - 1; i >= 0; i--) { /* elog not ereport since this should not happen */ if (lockOwners[i].owner == NULL) elog(ERROR, "cannot PREPARE when session locks exist"); } /* Can't handle it if the lock is on a temporary object */ if (locallock->isTempObject) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot PREPARE a transaction that has operated on temporary tables"))); /* * Create a 2PC record. */ memcpy(&(record.locktag), &(locallock->tag.lock), sizeof(LOCKTAG)); record.lockmode = locallock->tag.mode; RegisterTwoPhaseRecord(TWOPHASE_RM_LOCK_ID, 0, &record, sizeof(TwoPhaseLockRecord)); }}/* * PostPrepare_Locks * Clean up after successful PREPARE * * Here, we want to transfer ownership of our locks to a dummy PGPROC * that's now associated with the prepared transaction, and we want to * clean out the corresponding entries in the LOCALLOCK table. * * Note: by removing the LOCALLOCK entries, we are leaving dangling * pointers in the transaction's resource owner. This is OK at the * moment since resowner.c doesn't try to free locks retail at a toplevel * transaction commit or abort. We could alternatively zero out nLocks * and leave the LOCALLOCK entries to be garbage-collected by LockReleaseAll, * but that probably costs more cycles. */voidPostPrepare_Locks(TransactionId xid){ PGPROC *newproc = TwoPhaseGetDummyProc(xid); LOCKMETHODID lockmethodid = DEFAULT_LOCKMETHOD; HASH_SEQ_STATUS status; SHM_QUEUE *procLocks = &(MyProc->procLocks); LWLockId masterLock; LockMethod lockMethodTable; int numLockModes; LOCALLOCK *locallock; PROCLOCK *proclock; PROCLOCKTAG proclocktag; bool found; LOCK *lock; /* This is a critical section: any error means big trouble */ START_CRIT_SECTION(); lockMethodTable = LockMethods[lockmethodid]; if (!lockMethodTable) elog(ERROR, "unrecognized lock method: %d", lockmethodid); numLockModes = lockMethodTable->numLockModes; masterLock = lockMethodTable->masterLock; /* * First we run through the locallock table and get rid of unwanted * entries, then we scan the process's proclocks and transfer them to the * target proc. * * We do this separately because we may have multiple locallock entries * pointing to the same proclock, and we daren't end up with any dangling * pointers. */ hash_seq_init(&status, LockMethodLocalHash[lockmethodid]); while ((locallock = (LOCALLOCK *) hash_seq_search(&status)) != NULL) { if (locallock->proclock == NULL || locallock->lock == NULL) { /* * We must've run out of shared memory while trying to set up this * lock. Just forget the local entry. */ Assert(locallock->nLocks == 0); RemoveLocalLock(locallock); continue; } /* Ignore items that are not of the lockmethod to be removed */ if (LOCALLOCK_LOCKMETHOD(*locallock) != lockmethodid) continue; /* We already checked there are no session locks */ /* Mark the proclock to show we need to release this lockmode */ if (locallock->nLocks > 0) locallock->proclock->releaseMask |= LOCKBIT_ON(locallock->tag.mode); /* And remove the locallock hashtable entry */ RemoveLocalLock(locallock); } LWLockAcquire(masterLock, LW_EXCLUSIVE); proclock = (PROCLOCK *) SHMQueueNext(procLocks, procLocks, offsetof(PROCLOCK, procLink)); while (proclock) { PROCLOCK *nextplock; LOCKMASK holdMask; PROCLOCK *newproclock; /* Get link first, since we may unlink/delete this proclock */ nextplock = (PROCLOCK *) SHMQueueNext(procLocks, &proclock->procLink, offsetof(PROCLOCK, procLink)); Assert(proclock->tag.proc == MAKE_OFFSET(MyProc)); lock = (LOCK *) MAKE_PTR(proclock->tag.lock); /* Ignore items that are not of the lockmethod to be removed */ if (LOCK_LOCKMETHOD(*lock) != lockmethodid) goto next_item; PROCLOCK_PRINT("PostPrepare_Locks", proclock); LOCK_PRINT("PostPrepare_Locks", lock, 0); Assert(lock->nRequested >= 0); Assert(lock->nGranted >= 0); Assert(lock->nGranted <= lock->nRequested); Assert((proclock->holdMask & ~lock->grantMask) == 0); /* * Since there were no session locks, we should be releasing all locks */ if (proclock->releaseMask != proclock->holdMask) elog(PANIC, "we seem to have dropped a bit somewhere"); holdMask = proclock->holdMask; /* * We cannot simply modify proclock->tag.proc to reassign ownership of * the lock, because that's part of the hash key and the proclock * would then be in the wrong hash chain. So, unlink and delete the * old proclock; create a new one with the right contents; and link it * into place. We do it in this order to be certain we won't run out * of shared memory (the way dynahash.c works, the deleted object is * certain to be available for reallocation). */ SHMQueueDelete(&proclock->lockLink); SHMQueueDelete(&proclock->procLink); if (!hash_search(LockMethodProcLockHash[lockmethodid], (void *) &(proclock->tag), HASH_REMOVE, NULL)) elog(PANIC, "proclock table corrupted"); /* * Create the hash key for the new proclock table. */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -