📄 lock.c
字号:
LWLockAcquire(partitionLock, LW_EXCLUSIVE); /* * We don't need to re-find the lock or proclock, since we kept their * addresses in the locallock table, and they couldn't have been removed * while we were holding a lock on them. */ lock = locallock->lock; LOCK_PRINT("LockRelease: found", lock, lockmode); proclock = locallock->proclock; PROCLOCK_PRINT("LockRelease: found", proclock); /* * Double-check that we are actually holding a lock of the type we want to * release. */ if (!(proclock->holdMask & LOCKBIT_ON(lockmode))) { PROCLOCK_PRINT("LockRelease: WRONGTYPE", proclock); LWLockRelease(partitionLock); elog(WARNING, "you don't own a lock of type %s", lockMethodTable->lockModeNames[lockmode]); RemoveLocalLock(locallock); return FALSE; } /* * Do the releasing. CleanUpLock will waken any now-wakable waiters. */ wakeupNeeded = UnGrantLock(lock, lockmode, proclock, lockMethodTable); CleanUpLock(lock, proclock, lockMethodTable, locallock->hashcode, wakeupNeeded); LWLockRelease(partitionLock); RemoveLocalLock(locallock); return TRUE;}/* * LockReleaseAll -- Release all locks of the specified lock method that * are held by the current process. * * Well, not necessarily *all* locks. The available behaviors are: * allLocks == true: release all locks including session locks. * allLocks == false: release all non-session locks. */voidLockReleaseAll(LOCKMETHODID lockmethodid, bool allLocks){ HASH_SEQ_STATUS status; LockMethod lockMethodTable; int i, numLockModes; LOCALLOCK *locallock; LOCK *lock; PROCLOCK *proclock; int partition; if (lockmethodid <= 0 || lockmethodid >= lengthof(LockMethods)) elog(ERROR, "unrecognized lock method: %d", lockmethodid); lockMethodTable = LockMethods[lockmethodid];#ifdef LOCK_DEBUG if (*(lockMethodTable->trace_flag)) elog(LOG, "LockReleaseAll: lockmethod=%d", lockmethodid);#endif numLockModes = lockMethodTable->numLockModes; /* * First we run through the locallock table and get rid of unwanted * entries, then we scan the process's proclocks and get rid of those. 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); 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); } /* * Now, scan each lock partition separately. */ for (partition = 0; partition < NUM_LOCK_PARTITIONS; partition++) { LWLockId partitionLock = FirstLockMgrLock + partition; SHM_QUEUE *procLocks = &(MyProc->myProcLocks[partition]); proclock = (PROCLOCK *) SHMQueueNext(procLocks, procLocks, offsetof(PROCLOCK, procLink)); if (!proclock) continue; /* needn't examine this partition */ LWLockAcquire(partitionLock, LW_EXCLUSIVE); 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.myProc == MyProc); lock = proclock->tag.myLock; /* 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(lock, proclock, lockMethodTable, LockTagHashCode(&lock->tag), wakeupNeeded); next_item: proclock = nextplock; } /* loop over PROCLOCKs within this partition */ LWLockRelease(partitionLock); } /* loop over partitions */#ifdef LOCK_DEBUG if (*(lockMethodTable->trace_flag)) elog(LOG, "LockReleaseAll done");#endif}/* * LockReleaseCurrentOwner * Release all locks belonging to CurrentResourceOwner */voidLockReleaseCurrentOwner(void){ HASH_SEQ_STATUS status; LOCALLOCK *locallock; LOCALLOCKOWNER *lockOwners; int i; hash_seq_init(&status, LockMethodLocalHash); while ((locallock = (LOCALLOCK *) hash_seq_search(&status)) != NULL) { /* Ignore items that must be nontransactional */ if (!LockMethods[LOCALLOCK_LOCKMETHOD(*locallock)]->transactional) 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(&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); while ((locallock = (LOCALLOCK *) hash_seq_search(&status)) != NULL) { int i; int ic = -1; int ip = -1; /* Ignore items that must be nontransactional */ if (!LockMethods[LOCALLOCK_LOCKMETHOD(*locallock)]->transactional) 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]; } }}/* * GetLockConflicts * Get an array of VirtualTransactionIds of xacts currently holding locks * that would conflict with the specified lock/lockmode. * xacts merely awaiting such a lock are NOT reported. * * The result array is palloc'd and is terminated with an invalid VXID. * * Of course, the result could be out of date by the time it's returned, * so use of this function has to be thought about carefully. * * Note we never include the current xact's vxid in the result array, * since an xact never blocks itself. Also, prepared transactions are * ignored, which is a bit more debatable but is appropriate for current * uses of the result. */VirtualTransactionId *GetLockConflicts(const LOCKTAG *locktag, LOCKMODE lockmode){ VirtualTransactionId *vxids; LOCKMETHODID lockmethodid = locktag->locktag_lockmethodid; LockMethod lockMethodTable; LOCK *lock; LOCKMASK conflictMask; SHM_QUEUE *procLocks; PROCLOCK *proclock; uint32 hashcode; LWLockId partitionLock; int count = 0; if (lockmethodid <= 0 || lockmethodid >= lengthof(LockMethods)) elog(ERROR, "unrecognized lock method: %d", lockmethodid); lockMethodTable = LockMethods[lockmethodid]; if (lockmode <= 0 || lockmode > lockMethodTable->numLockModes) elog(ERROR, "unrecognized lock mode: %d", lockmode); /* * Allocate memory to store results, and fill with InvalidVXID. We only * need enough space for MaxBackends + a terminator, since prepared xacts * don't count. */ vxids = (VirtualTransactionId *) palloc0(sizeof(VirtualTransactionId) * (MaxBackends + 1)); /* * Look up the lock object matching the tag. */ hashcode = LockTagHashCode(locktag); partitionLock = LockHashPartitionLock(hashcode); LWLockAcquire(partitionLock, LW_SHARED); lock = (LOCK *) hash_search_with_hash_value(LockMethodLockHash, (void *) locktag, hashcode, HASH_FIND, NULL); if (!lock) { /* * If the lock object doesn't exist, there is nothing holding a lock * on this lockable object. */ LWLockRelease(partitionLock); return vxids; } /* * Examine each existing holder (or awaiter) of the lock. */ conflictMask = lockMethodTable->conflictTab[lockmode]; procLocks = &(lock->procLocks); proclock = (PROCLOCK *) SHMQueueNext(procLocks, procLocks, offsetof(PROCLOCK, lockLink)); while (proclock) { if (conflictMask & proclock->holdMask) { PGPROC *proc = proclock->tag.myProc; /* A backend never blocks itself */ if (proc != MyProc) { VirtualTransactionId vxid; GET_VXID_FROM_PGPROC(vxid, *proc); /* * If we see an invalid VXID, then either the xact has already * committed (or aborted), or it's a prepared xact. In either * case we may ignore it. */ if (VirtualTransactionIdIsValid(vxid)) vxids[count++] = vxid; } } proclock = (PROCLOCK *) SHMQueueNext(procLocks, &proclock->lockLink, offsetof(PROCLOCK, lockLink)); } LWLockRelease(partitionLock); if (count > MaxBackends) /* should never happen */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -