@@ -2787,8 +2787,12 @@ FastPathGetRelationLockEntry(LOCALLOCK *locallock)
2787
2787
*
2788
2788
* The result array is palloc'd and is terminated with an invalid VXID.
2789
2789
*
2790
- * Of course, the result could be out of date by the time it's returned,
2791
- * so use of this function has to be thought about carefully.
2790
+ * Of course, the result could be out of date by the time it's returned, so
2791
+ * use of this function has to be thought about carefully. Similarly, a
2792
+ * PGPROC with no "lxid" will be considered non-conflicting regardless of any
2793
+ * lock it holds. Existing callers don't care about a locker after that
2794
+ * locker's pg_xact updates complete. CommitTransaction() clears "lxid" after
2795
+ * pg_xact updates and before releasing locks.
2792
2796
*
2793
2797
* Note we never include the current xact's vxid in the result array,
2794
2798
* since an xact never blocks itself.
@@ -4405,37 +4409,80 @@ VirtualXactLockTableCleanup(void)
4405
4409
}
4406
4410
}
4407
4411
4412
+ /*
4413
+ * XactLockForVirtualXact
4414
+ *
4415
+ * If TransactionIdIsValid(xid), this is essentially XactLockTableWait(xid,
4416
+ * NULL, NULL, XLTW_None) or ConditionalXactLockTableWait(xid). Unlike those
4417
+ * functions, it assumes "xid" is never a subtransaction and that "xid" is
4418
+ * prepared, committed, or aborted.
4419
+ *
4420
+ * If !TransactionIdIsValid(xid), this locks every prepared XID having been
4421
+ * known as "vxid" before its PREPARE TRANSACTION.
4422
+ */
4423
+ static bool
4424
+ XactLockForVirtualXact(VirtualTransactionId vxid,
4425
+ TransactionId xid, bool wait)
4426
+ {
4427
+ bool more = false;
4428
+
4429
+ /* There is no point to wait for 2PCs if you have no 2PCs. */
4430
+ if (max_prepared_xacts == 0)
4431
+ return true;
4432
+
4433
+ do
4434
+ {
4435
+ LockAcquireResult lar;
4436
+ LOCKTAG tag;
4437
+
4438
+ /* Clear state from previous iterations. */
4439
+ if (more)
4440
+ {
4441
+ xid = InvalidTransactionId;
4442
+ more = false;
4443
+ }
4444
+
4445
+ /* If we have no xid, try to find one. */
4446
+ if (!TransactionIdIsValid(xid))
4447
+ xid = TwoPhaseGetXidByVirtualXID(vxid, &more);
4448
+ if (!TransactionIdIsValid(xid))
4449
+ {
4450
+ Assert(!more);
4451
+ return true;
4452
+ }
4453
+
4454
+ /* Check or wait for XID completion. */
4455
+ SET_LOCKTAG_TRANSACTION(tag, xid);
4456
+ lar = LockAcquire(&tag, ShareLock, false, !wait);
4457
+ if (lar == LOCKACQUIRE_NOT_AVAIL)
4458
+ return false;
4459
+ LockRelease(&tag, ShareLock, false);
4460
+ } while (more);
4461
+
4462
+ return true;
4463
+ }
4464
+
4408
4465
/*
4409
4466
* VirtualXactLock
4410
4467
*
4411
- * If wait = true, wait until the given VXID has been released, and then
4412
- * return true.
4468
+ * If wait = true, wait as long as the given VXID or any XID acquired by the
4469
+ * same transaction is still running. Then, return true.
4413
4470
*
4414
- * If wait = false, just check whether the VXID is still running, and return
4415
- * true or false.
4471
+ * If wait = false, just check whether that VXID or one of those XIDs is still
4472
+ * running, and return true or false.
4416
4473
*/
4417
4474
bool
4418
4475
VirtualXactLock(VirtualTransactionId vxid, bool wait)
4419
4476
{
4420
4477
LOCKTAG tag;
4421
4478
PGPROC *proc;
4479
+ TransactionId xid = InvalidTransactionId;
4422
4480
4423
4481
Assert(VirtualTransactionIdIsValid(vxid));
4424
4482
4425
- if (VirtualTransactionIdIsPreparedXact(vxid))
4426
- {
4427
- LockAcquireResult lar;
4428
-
4429
- /*
4430
- * Prepared transactions don't hold vxid locks. The
4431
- * LocalTransactionId is always a normal, locked XID.
4432
- */
4433
- SET_LOCKTAG_TRANSACTION(tag, vxid.localTransactionId);
4434
- lar = LockAcquire(&tag, ShareLock, false, !wait);
4435
- if (lar != LOCKACQUIRE_NOT_AVAIL)
4436
- LockRelease(&tag, ShareLock, false);
4437
- return lar != LOCKACQUIRE_NOT_AVAIL;
4438
- }
4483
+ if (VirtualTransactionIdIsRecoveredPreparedXact(vxid))
4484
+ /* no vxid lock; localTransactionId is a normal, locked XID */
4485
+ return XactLockForVirtualXact(vxid, vxid.localTransactionId, wait);
4439
4486
4440
4487
SET_LOCKTAG_VIRTUALTRANSACTION(tag, vxid);
4441
4488
@@ -4449,7 +4496,7 @@ VirtualXactLock(VirtualTransactionId vxid, bool wait)
4449
4496
*/
4450
4497
proc = BackendIdGetProc(vxid.backendId);
4451
4498
if (proc == NULL)
4452
- return true ;
4499
+ return XactLockForVirtualXact(vxid, InvalidTransactionId, wait) ;
4453
4500
4454
4501
/*
4455
4502
* We must acquire this lock before checking the backendId and lxid
@@ -4458,12 +4505,12 @@ VirtualXactLock(VirtualTransactionId vxid, bool wait)
4458
4505
*/
4459
4506
LWLockAcquire(&proc->backendLock, LW_EXCLUSIVE);
4460
4507
4461
- /* If the transaction has ended, our work here is done. */
4462
4508
if (proc->backendId != vxid.backendId
4463
4509
|| proc->fpLocalTransactionId != vxid.localTransactionId)
4464
4510
{
4511
+ /* VXID ended */
4465
4512
LWLockRelease(&proc->backendLock);
4466
- return true ;
4513
+ return XactLockForVirtualXact(vxid, InvalidTransactionId, wait) ;
4467
4514
}
4468
4515
4469
4516
/*
@@ -4510,14 +4557,24 @@ VirtualXactLock(VirtualTransactionId vxid, bool wait)
4510
4557
proc->fpVXIDLock = false;
4511
4558
}
4512
4559
4560
+ /*
4561
+ * If the proc has an XID now, we'll avoid a TwoPhaseGetXidByVirtualXID()
4562
+ * search. The proc might have assigned this XID but not yet locked it,
4563
+ * in which case the proc will lock this XID before releasing the VXID.
4564
+ * The backendLock critical section excludes VirtualXactLockTableCleanup(),
4565
+ * so we won't save an XID of a different VXID. It doesn't matter whether
4566
+ * we save this before or after setting up the primary lock table entry.
4567
+ */
4568
+ xid = ProcGlobal->allPgXact[proc->pgprocno].xid;
4569
+
4513
4570
/* Done with proc->fpLockBits */
4514
4571
LWLockRelease(&proc->backendLock);
4515
4572
4516
4573
/* Time to wait. */
4517
4574
(void) LockAcquire(&tag, ShareLock, false, false);
4518
4575
4519
4576
LockRelease(&tag, ShareLock, false);
4520
- return true ;
4577
+ return XactLockForVirtualXact(vxid, xid, wait) ;
4521
4578
}
4522
4579
4523
4580
/*
0 commit comments