/* Finally wait for each such transaction to complete */ foreach(lc, holders) { VirtualTransactionId *lockholders = lfirst(lc); while (VirtualTransactionIdIsValid(*lockholders)) { VirtualXactLock(*lockholders, true); lockholders++; } }
/* * VirtualXactLockTableInsert * * Insert a lock showing that the given virtual transaction ID is running --- * this is done at main transaction start when its VXID is assigned. * The lock can then be used to wait for the transaction to finish. */ void VirtualXactLockTableInsert(VirtualTransactionId vxid) { LOCKTAG tag; Assert(VirtualTransactionIdIsValid(vxid)); SET_LOCKTAG_VIRTUALTRANSACTION(tag, vxid); (void) LockAcquire(&tag, ExclusiveLock, false, false); }
/* * 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); }
/* * 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; }
/* * This is the main executioner for any query backend that conflicts with * recovery processing. Judgement has already been passed on it within * a specific rmgr. Here we just issue the orders to the procs. The procs * then throw the required error as instructed. */ static void ResolveRecoveryConflictWithVirtualXIDs(VirtualTransactionId *waitlist, ProcSignalReason reason) { TimestampTz waitStart; char *new_status; /* Fast exit, to avoid a kernel call if there's no work to be done. */ if (!VirtualTransactionIdIsValid(*waitlist)) return; waitStart = GetCurrentTimestamp(); new_status = NULL; /* we haven't changed the ps display */ while (VirtualTransactionIdIsValid(*waitlist)) { /* reset standbyWait_us for each xact we wait for */ standbyWait_us = STANDBY_INITIAL_WAIT_US; /* wait until the virtual xid is gone */ while (!ConditionalVirtualXactLockTableWait(*waitlist)) { /* * Report via ps if we have been waiting for more than 500 msec * (should that be configurable?) */ if (update_process_title && new_status == NULL && TimestampDifferenceExceeds(waitStart, GetCurrentTimestamp(), 500)) { const char *old_status; int len; old_status = get_ps_display(&len); new_status = (char *) palloc(len + 8 + 1); memcpy(new_status, old_status, len); strcpy(new_status + len, " waiting"); set_ps_display(new_status, false); new_status[len] = '\0'; /* truncate off " waiting" */ } /* Is it time to kill it? */ if (WaitExceedsMaxStandbyDelay()) { pid_t pid; /* * Now find out who to throw out of the balloon. */ Assert(VirtualTransactionIdIsValid(*waitlist)); pid = CancelVirtualTransaction(*waitlist, reason); /* * Wait a little bit for it to die so that we avoid flooding * an unresponsive backend when system is heavily loaded. */ if (pid != 0) pg_usleep(5000L); } } /* The virtual transaction is gone now, wait for the next one */ waitlist++; } /* Reset ps display if we changed it */ if (new_status) { set_ps_display(new_status, false); pfree(new_status); } }