/* * pg_advisory_xact_lock_shared(int4, int4) - acquire xact scoped * share lock on 2 int4 keys */ Datum pg_advisory_xact_lock_shared_int4(PG_FUNCTION_ARGS) { int32 key1 = PG_GETARG_INT32(0); int32 key2 = PG_GETARG_INT32(1); LOCKTAG tag; SET_LOCKTAG_INT32(tag, key1, key2); (void) LockAcquire(&tag, ShareLock, false, false); PG_RETURN_VOID(); }
/* * pg_advisory_xact_lock_shared(int8) - acquire xact scoped * share lock on an int8 key */ Datum pg_advisory_xact_lock_shared_int8(PG_FUNCTION_ARGS) { int64 key = PG_GETARG_INT64(0); LOCKTAG tag; PreventAdvisoryLocksInParallelMode(); SET_LOCKTAG_INT64(tag, key); (void) LockAcquire(&tag, ShareLock, false, false); PG_RETURN_VOID(); }
/* * VirtualXactLockTableWait * * Waits until the lock on the given VXID is released, which shows that * the top-level transaction owning the VXID has ended. */ void VirtualXactLockTableWait(VirtualTransactionId vxid) { LOCKTAG tag; Assert(VirtualTransactionIdIsValid(vxid)); SET_LOCKTAG_VIRTUALTRANSACTION(tag, vxid); (void) LockAcquire(&tag, ShareLock, false, false); LockRelease(&tag, ShareLock, false); }
/* * LockTuple * * Obtain a tuple-level lock. This is used in a less-than-intuitive fashion * because we can't afford to keep a separate lock in shared memory for every * tuple. See heap_lock_tuple before using this! */ void LockTuple(Relation relation, ItemPointer tid, LOCKMODE lockmode) { LOCKTAG tag; SET_LOCKTAG_TUPLE(tag, relation->rd_lockInfo.lockRelId.dbId, relation->rd_lockInfo.lockRelId.relId, ItemPointerGetBlockNumber(tid), ItemPointerGetOffsetNumber(tid)); (void) LockAcquire(&tag, lockmode, false, false); }
/* * LockShardResource acquires a lock needed to modify data on a remote shard. * This task may be assigned to multiple backends at the same time, so the lock * manages any concurrency issues associated with shard file fetching and DML * command execution. */ void LockShardResource(uint64 shardId, LOCKMODE lockmode) { LOCKTAG tag; const bool sessionLock = false; const bool dontWait = false; AssertArg(shardId != INVALID_SHARD_ID); SET_LOCKTAG_SHARD_RESOURCE(tag, MyDatabaseId, shardId); (void) LockAcquire(&tag, lockmode, sessionLock, dontWait); }
/* * ConditionalLockPage * * As above, but only lock if we can get the lock without blocking. * Returns TRUE iff the lock was acquired. */ bool ConditionalLockPage(Relation relation, BlockNumber blkno, LOCKMODE lockmode) { LOCKTAG tag; MemSet(&tag, 0, sizeof(tag)); tag.relId = relation->rd_lockInfo.lockRelId.relId; tag.dbId = relation->rd_lockInfo.lockRelId.dbId; tag.objId.blkno = blkno; return LockAcquire(LockTableId, &tag, GetCurrentTransactionId(), lockmode, true); }
/* * ConditionalLockTuple * * As above, but only lock if we can get the lock without blocking. * Returns TRUE iff the lock was acquired. */ bool ConditionalLockTuple(Relation relation, ItemPointer tid, LOCKMODE lockmode) { LOCKTAG tag; SET_LOCKTAG_TUPLE(tag, relation->rd_lockInfo.lockRelId.dbId, relation->rd_lockInfo.lockRelId.relId, ItemPointerGetBlockNumber(tid), ItemPointerGetOffsetNumber(tid)); return (LockAcquire(&tag, lockmode, false, true) != LOCKACQUIRE_NOT_AVAIL); }
/* * pg_try_advisory_lock(int8) - acquire exclusive lock on an int8 key, no wait * * Returns true if successful, false if lock not available */ Datum pg_try_advisory_lock_int8(PG_FUNCTION_ARGS) { int64 key = PG_GETARG_INT64(0); LOCKTAG tag; LockAcquireResult res; SET_LOCKTAG_INT64(tag, key); res = LockAcquire(&tag, ExclusiveLock, true, true); PG_RETURN_BOOL(res != LOCKACQUIRE_NOT_AVAIL); }
/* * update hash entry with state */ static int FaultInjector_UpdateHashEntry( FaultInjectorEntry_s *entry) { FaultInjectorEntry_s *entryLocal; bool exists; int status = STATUS_OK; LockAcquire(); entryLocal = FaultInjector_InsertHashEntry(entry->faultInjectorIdentifier, &exists); /* entry should be found since fault has not been injected yet */ Assert(entryLocal != NULL); if (!exists) { LockRelease(); status = STATUS_ERROR; ereport(WARNING, (errmsg("could not update fault injection hash entry with fault injection status, " "no entry found, " "fault name:'%s' fault type:'%s' ", FaultInjectorIdentifierEnumToString[entry->faultInjectorIdentifier], FaultInjectorTypeEnumToString[entry->faultInjectorType]))); goto exit; } if (entry->faultInjectorType == FaultInjectorTypeResume) { entryLocal->faultInjectorType = FaultInjectorTypeResume; } else { entryLocal->faultInjectorState = entry->faultInjectorState; entryLocal->occurrence = entry->occurrence; } LockRelease(); ereport(DEBUG1, (errmsg("LOG(fault injector): update fault injection hash entry " "identifier:'%s' state:'%s' occurrence:'%d' ", FaultInjectorIdentifierEnumToString[entry->faultInjectorIdentifier], FaultInjectorStateEnumToString[entryLocal->faultInjectorState], entry->occurrence))); exit: return status; }
/* * XactLockTableInsert * * Insert a lock showing that the given transaction ID is running --- * this is done during xact startup. The lock can then be used to wait * for the transaction to finish. * * We need no corresponding unlock function, since the lock will always * be released implicitly at transaction commit/abort, never any other way. */ void XactLockTableInsert(TransactionId xid) { LOCKTAG tag; MemSet(&tag, 0, sizeof(tag)); tag.relId = XactLockTableId; tag.dbId = InvalidOid; /* xids are globally unique */ tag.objId.xid = xid; if (!LockAcquire(LockTableId, &tag, xid, ExclusiveLock, false)) elog(ERROR, "LockAcquire failed"); }
/* * LockPage * * Obtain a page-level lock. This is currently used by some index access * methods to lock index pages. For heap relations, it is used only with * blkno == 0 to signify locking the relation for extension. */ void LockPage(Relation relation, BlockNumber blkno, LOCKMODE lockmode) { LOCKTAG tag; MemSet(&tag, 0, sizeof(tag)); tag.relId = relation->rd_lockInfo.lockRelId.relId; tag.dbId = relation->rd_lockInfo.lockRelId.dbId; tag.objId.blkno = blkno; if (!LockAcquire(LockTableId, &tag, GetCurrentTransactionId(), lockmode, false)) elog(ERROR, "LockAcquire failed"); }
/* * LockRelationForSession * * This routine grabs a session-level lock on the target relation. The * session lock persists across transaction boundaries. It will be removed * when UnlockRelationForSession() is called, or if an ereport(ERROR) occurs, * or if the backend exits. * * Note that one should also grab a transaction-level lock on the rel * in any transaction that actually uses the rel, to ensure that the * relcache entry is up to date. */ void LockRelationForSession(LockRelId *relid, LOCKMODE lockmode) { LOCKTAG tag; MemSet(&tag, 0, sizeof(tag)); tag.relId = relid->relId; tag.dbId = relid->dbId; tag.objId.blkno = InvalidBlockNumber; if (!LockAcquire(LockTableId, &tag, InvalidTransactionId, lockmode, false)) elog(ERROR, "LockAcquire failed"); }
/* * TryLockShardDistributionMetadata tries to grab a lock for distribution * metadata related to the specified shard, returning false if the lock * is currently taken. Any locks acquired using this method are released * at transaction end. */ bool TryLockShardDistributionMetadata(int64 shardId, LOCKMODE lockMode) { LOCKTAG tag; const bool sessionLock = false; const bool dontWait = true; bool lockAcquired = false; SET_LOCKTAG_SHARD_METADATA_RESOURCE(tag, MyDatabaseId, shardId); lockAcquired = LockAcquire(&tag, lockMode, sessionLock, dontWait); return lockAcquired; }
/* * pg_advisory_lock_shared(int4, int4) - acquire share lock on 2 int4 keys */ Datum pg_advisory_lock_shared_int4(PG_FUNCTION_ARGS) { int32 key1 = PG_GETARG_INT32(0); int32 key2 = PG_GETARG_INT32(1); LOCKTAG tag; PreventAdvisoryLocksInParallelMode(); SET_LOCKTAG_INT32(tag, key1, key2); (void) LockAcquire(&tag, ShareLock, true, false); PG_RETURN_VOID(); }
/* * pg_try_advisory_xact_lock_shared(int4, int4) - acquire xact scoped * share lock on 2 int4 keys, no wait * * Returns true if successful, false if lock not available */ Datum pg_try_advisory_xact_lock_shared_int4(PG_FUNCTION_ARGS) { int32 key1 = PG_GETARG_INT32(0); int32 key2 = PG_GETARG_INT32(1); LOCKTAG tag; LockAcquireResult res; SET_LOCKTAG_INT32(tag, key1, key2); res = LockAcquire(&tag, ShareLock, false, true); PG_RETURN_BOOL(res != LOCKACQUIRE_NOT_AVAIL); }
/* * LockSharedObjectForSession * * Obtain a session-level lock on a shared-across-databases object. * See LockRelationIdForSession for notes about session-level locks. */ void LockSharedObjectForSession(Oid classid, Oid objid, uint16 objsubid, LOCKMODE lockmode) { LOCKTAG tag; SET_LOCKTAG_OBJECT(tag, InvalidOid, classid, objid, objsubid); (void) LockAcquire(&tag, lockmode, true, false); }
/* * LockDatabaseObject * * Obtain a lock on a general object of the current database. Don't use * this for shared objects (such as tablespaces). It's unwise to apply it * to relations, also, since a lock taken this way will NOT conflict with * locks taken via LockRelation and friends. */ void LockDatabaseObject(Oid classid, Oid objid, uint16 objsubid, LOCKMODE lockmode) { LOCKTAG tag; SET_LOCKTAG_OBJECT(tag, MyDatabaseId, classid, objid, objsubid); (void) LockAcquire(&tag, lockmode, false, false); }
/* * pg_try_advisory_xact_lock_shared(int8) - acquire xact scoped * share lock on an int8 key, no wait * * Returns true if successful, false if lock not available */ Datum pg_try_advisory_xact_lock_shared_int8(PG_FUNCTION_ARGS) { int64 key = PG_GETARG_INT64(0); LOCKTAG tag; LockAcquireResult res; PreventAdvisoryLocksInParallelMode(); SET_LOCKTAG_INT64(tag, key); res = LockAcquire(&tag, ShareLock, false, true); PG_RETURN_BOOL(res != LOCKACQUIRE_NOT_AVAIL); }
/* * pg_try_advisory_xact_lock(int4, int4) - acquire xact scoped * exclusive lock on 2 int4 keys, no wait * * Returns true if successful, false if lock not available */ Datum pg_try_advisory_xact_lock_int4(PG_FUNCTION_ARGS) { int32 key1 = PG_GETARG_INT32(0); int32 key2 = PG_GETARG_INT32(1); LOCKTAG tag; LockAcquireResult res; PreventAdvisoryLocksInParallelMode(); SET_LOCKTAG_INT32(tag, key1, key2); res = LockAcquire(&tag, ExclusiveLock, false, true); PG_RETURN_BOOL(res != LOCKACQUIRE_NOT_AVAIL); }
/* * ConditionalVirtualXactLockTableWait * * As above, but only lock if we can get the lock without blocking. * Returns TRUE if the lock was acquired. */ bool ConditionalVirtualXactLockTableWait(VirtualTransactionId vxid) { LOCKTAG tag; Assert(VirtualTransactionIdIsValid(vxid)); SET_LOCKTAG_VIRTUALTRANSACTION(tag, vxid); if (LockAcquire(&tag, ShareLock, false, true) == LOCKACQUIRE_NOT_AVAIL) return false; LockRelease(&tag, ShareLock, false); return true; }
/* * LockSharedObject * * Obtain a lock on a shared-across-databases object. */ void LockSharedObject(Oid classid, Oid objid, uint16 objsubid, LOCKMODE lockmode) { LOCKTAG tag; SET_LOCKTAG_OBJECT(tag, InvalidOid, classid, objid, objsubid); (void) LockAcquire(&tag, lockmode, false, false); /* Make sure syscaches are up-to-date with any changes we waited for */ AcceptInvalidationMessages(); }
/* * XactLockTableWait * * Wait for the specified transaction to commit or abort. If an operation * is specified, an error context callback is set up. If 'oper' is passed as * None, no error context callback is set up. * * Note that this does the right thing for subtransactions: if we wait on a * subtransaction, we will exit as soon as it aborts or its top parent commits. * It takes some extra work to ensure this, because to save on shared memory * the XID lock of a subtransaction is released when it ends, whether * successfully or unsuccessfully. So we have to check if it's "still running" * and if so wait for its parent. */ void XactLockTableWait(TransactionId xid, Relation rel, ItemPointer ctid, XLTW_Oper oper) { LOCKTAG tag; XactLockTableWaitInfo info; ErrorContextCallback callback; /* * If an operation is specified, set up our verbose error context * callback. */ if (oper != XLTW_None) { Assert(RelationIsValid(rel)); Assert(ItemPointerIsValid(ctid)); info.rel = rel; info.ctid = ctid; info.oper = oper; callback.callback = XactLockTableWaitErrorCb; callback.arg = &info; callback.previous = error_context_stack; error_context_stack = &callback; } for (;;) { Assert(TransactionIdIsValid(xid)); Assert(!TransactionIdEquals(xid, GetTopTransactionIdIfAny())); SET_LOCKTAG_TRANSACTION(tag, xid); (void) LockAcquire(&tag, ShareLock, false, false); LockRelease(&tag, ShareLock, false); if (!TransactionIdIsInProgress(xid)) break; xid = SubTransGetParent(xid); } if (oper != XLTW_None) error_context_stack = callback.previous; }
/* * LockRelation * * This is a convenience routine for acquiring an additional lock on an * already-open relation. Never try to do "relation_open(foo, NoLock)" * and then lock with this. */ void LockRelation(Relation relation, LOCKMODE lockmode) { LOCKTAG tag; LockAcquireResult res; SET_LOCKTAG_RELATION(tag, relation->rd_lockInfo.lockRelId.dbId, relation->rd_lockInfo.lockRelId.relId); res = LockAcquire(&tag, lockmode, false, false); /* * Now that we have the lock, check for invalidation messages; see notes * in LockRelationOid. */ if (res != LOCKACQUIRE_ALREADY_HELD) AcceptInvalidationMessages(); }
/* * SpeculativeInsertionLockAcquire * * Insert a lock showing that the given transaction ID is inserting a tuple, * but hasn't yet decided whether it's going to keep it. The lock can then be * used to wait for the decision to go ahead with the insertion, or aborting * it. * * The token is used to distinguish multiple insertions by the same * transaction. It is returned to caller. */ uint32 SpeculativeInsertionLockAcquire(TransactionId xid) { LOCKTAG tag; speculativeInsertionToken++; /* * Check for wrap-around. Zero means no token is held, so don't use that. */ if (speculativeInsertionToken == 0) speculativeInsertionToken = 1; SET_LOCKTAG_SPECULATIVE_INSERTION(tag, xid, speculativeInsertionToken); (void) LockAcquire(&tag, ExclusiveLock, false, false); return speculativeInsertionToken; }
/* * XactLockTableWait * * Wait for the specified transaction to commit or abort. * * Note that this does the right thing for subtransactions: if we wait on a * subtransaction, we will exit as soon as it aborts or its top parent commits. * It takes some extra work to ensure this, because to save on shared memory * the XID lock of a subtransaction is released when it ends, whether * successfully or unsuccessfully. So we have to check if it's "still running" * and if so wait for its parent. */ void XactLockTableWait(TransactionId xid) { LOCKTAG tag; for (;;) { Assert(TransactionIdIsValid(xid)); Assert(!TransactionIdEquals(xid, GetTopTransactionIdIfAny())); SET_LOCKTAG_TRANSACTION(tag, xid); (void) LockAcquire(&tag, ShareLock, false, false); LockRelease(&tag, ShareLock, false); if (!TransactionIdIsInProgress(xid)) break; xid = SubTransGetParent(xid); } }
/* * LockRelationOid * * Lock a relation given only its OID. This should generally be used * before attempting to open the relation's relcache entry. */ void LockRelationOid(Oid relid, LOCKMODE lockmode) { LOCKTAG tag; LockAcquireResult res; SetLocktagRelationOid(&tag, relid); res = LockAcquire(&tag, lockmode, false, false); /* * Now that we have the lock, check for invalidation messages, so that we * will update or flush any stale relcache entry before we try to use it. * We can skip this in the not-uncommon case that we already had the same * type of lock being requested, since then no one else could have * modified the relcache entry in an undesirable way. (In the case where * our own xact modifies the rel, the relcache update happens via * CommandCounterIncrement, not here.) */ if (res != LOCKACQUIRE_ALREADY_HELD) AcceptInvalidationMessages(); }
/* * ConditionalLockRelationOid * * As above, but only lock if we can get the lock without blocking. * Returns TRUE iff the lock was acquired. * * NOTE: we do not currently need conditional versions of all the * LockXXX routines in this file, but they could easily be added if needed. */ bool ConditionalLockRelationOid(Oid relid, LOCKMODE lockmode) { LOCKTAG tag; LockAcquireResult res; SetLocktagRelationOid(&tag, relid); res = LockAcquire(&tag, lockmode, false, true); if (res == LOCKACQUIRE_NOT_AVAIL) return false; /* * Now that we have the lock, check for invalidation messages; see notes * in LockRelationOid. */ if (res != LOCKACQUIRE_ALREADY_HELD) AcceptInvalidationMessages(); return true; }
/* * ConditionalXactLockTableWait * * As above, but only lock if we can get the lock without blocking. * Returns TRUE if the lock was acquired. */ bool ConditionalXactLockTableWait(TransactionId xid) { LOCKTAG tag; for (;;) { Assert(TransactionIdIsValid(xid)); Assert(!TransactionIdEquals(xid, GetTopTransactionIdIfAny())); SET_LOCKTAG_TRANSACTION(tag, xid); if (LockAcquire(&tag, ShareLock, false, true) == LOCKACQUIRE_NOT_AVAIL) return false; LockRelease(&tag, ShareLock, false); if (!TransactionIdIsInProgress(xid)) break; xid = SubTransGetParent(xid); } return true; }
/* * LockRelation */ void LockRelation(Relation relation, LOCKMODE lockmode) { LOCKTAG tag; MemSet(&tag, 0, sizeof(tag)); tag.relId = relation->rd_lockInfo.lockRelId.relId; tag.dbId = relation->rd_lockInfo.lockRelId.dbId; tag.objId.blkno = InvalidBlockNumber; if (!LockAcquire(LockTableId, &tag, GetCurrentTransactionId(), lockmode, false)) elog(ERROR, "LockAcquire failed"); /* * Check to see if the relcache entry has been invalidated while we * were waiting to lock it. If so, rebuild it, or ereport() trying. * Increment the refcount to ensure that RelationFlushRelation will * rebuild it and not just delete it. */ RelationIncrementReferenceCount(relation); AcceptInvalidationMessages(); RelationDecrementReferenceCount(relation); }
/* * MultiAcquire -- acquire multi level lock at requested level * * Returns: TRUE if lock is set, FALSE if not * Side Effects: */ bool MultiAcquire(LockTableId tableId, LOCKTAG *tag, LOCKT lockt, LOCK_LEVEL level) { LOCKT locks[N_LEVELS]; int i,status; LOCKTAG xxTag, *tmpTag = &xxTag; int retStatus = TRUE; /* * Three levels implemented. If we set a low level (e.g. Tuple) * lock, we must set INTENT locks on the higher levels. The * intent lock detects conflicts between the low level lock * and an existing high level lock. For example, setting a * write lock on a tuple in a relation is disallowed if there * is an existing read lock on the entire relation. The * write lock would set a WRITE + INTENT lock on the relation * and that lock would conflict with the read. */ switch (level) { case RELN_LEVEL: locks[0] = lockt; locks[1] = NO_LOCK; locks[2] = NO_LOCK; break; case PAGE_LEVEL: locks[0] = lockt + INTENT; locks[1] = lockt; locks[2] = NO_LOCK; break; case TUPLE_LEVEL: locks[0] = lockt + INTENT; locks[1] = lockt + INTENT; locks[2] = lockt; break; default: elog(WARN,"MultiAcquire: bad lock level"); return(FALSE); } /* * construct a new tag as we go. Always loop through all levels, * but if we arent' seting a low level lock, locks[i] is set to * NO_LOCK for the lower levels. Always start from the highest * level and go to the lowest level. */ memset(tmpTag,0,sizeof(*tmpTag)); tmpTag->relId = tag->relId; tmpTag->dbId = tag->dbId; for (i=0;i<N_LEVELS;i++) { if (locks[i] != NO_LOCK) { switch (i) { case RELN_LEVEL: /* ------------- * Set the block # and offset to invalid * ------------- */ BlockIdSet(&(tmpTag->tupleId.ip_blkid), InvalidBlockNumber); tmpTag->tupleId.ip_posid = InvalidOffsetNumber; break; case PAGE_LEVEL: /* ------------- * Copy the block #, set the offset to invalid * ------------- */ BlockIdCopy(&(tmpTag->tupleId.ip_blkid), &(tag->tupleId.ip_blkid)); tmpTag->tupleId.ip_posid = InvalidOffsetNumber; break; case TUPLE_LEVEL: /* -------------- * Copy the entire tuple id. * -------------- */ ItemPointerCopy(&tmpTag->tupleId, &tag->tupleId); break; } status = LockAcquire(tableId, tmpTag, locks[i]); if (! status) { /* failed for some reason. Before returning we have * to release all of the locks we just acquired. * MultiRelease(xx,xx,xx, i) means release starting from * the last level lock we successfully acquired */ retStatus = FALSE; (void) MultiRelease(tableId, tag, lockt, i); /* now leave the loop. Don't try for any more locks */ break; } } } return(retStatus); }