@@ -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