@@ -270,7 +270,10 @@ static struct xllist
270
270
static TwoPhaseStateData * TwoPhaseState ;
271
271
272
272
/*
273
- * Global transaction entry currently locked by us, if any.
273
+ * Global transaction entry currently locked by us, if any. Note that any
274
+ * access to the entry pointed to by this variable must be protected by
275
+ * TwoPhaseStateLock, though obviously the pointer itself doesn't need to be
276
+ * (since it's just local memory).
274
277
*/
275
278
static GlobalTransaction MyLockedGxact = NULL ;
276
279
@@ -418,18 +421,13 @@ AtAbort_Twophase(void)
418
421
* resources held by the transaction yet. In those cases, the in-memory
419
422
* state can be wrong, but it's too late to back out.
420
423
*/
424
+ LWLockAcquire (TwoPhaseStateLock , LW_EXCLUSIVE );
421
425
if (!MyLockedGxact -> valid )
422
- {
423
426
RemoveGXact (MyLockedGxact );
424
- }
425
427
else
426
- {
427
- LWLockAcquire (TwoPhaseStateLock , LW_EXCLUSIVE );
428
-
429
428
MyLockedGxact -> locking_pid = InvalidBackendId ;
429
+ LWLockRelease (TwoPhaseStateLock );
430
430
431
- LWLockRelease (TwoPhaseStateLock );
432
- }
433
431
MyLockedGxact = NULL ;
434
432
}
435
433
@@ -534,6 +532,8 @@ MarkAsPreparingGuts(GlobalTransaction gxact, TransactionId xid, const char *gid,
534
532
PGXACT * pgxact ;
535
533
int i ;
536
534
535
+ Assert (LWLockHeldByMeInMode (TwoPhaseStateLock , LW_EXCLUSIVE ));
536
+
537
537
Assert (gxact != NULL );
538
538
proc = & ProcGlobal -> allProcs [gxact -> pgprocno ];
539
539
pgxact = & ProcGlobal -> allPgXact [gxact -> pgprocno ];
@@ -611,15 +611,19 @@ GXactLoadSubxactData(GlobalTransaction gxact, int nsubxacts,
611
611
/*
612
612
* MarkAsPrepared
613
613
* Mark the GXACT as fully valid, and enter it into the global ProcArray.
614
+ *
615
+ * lock_held indicates whether caller already holds TwoPhaseStateLock.
614
616
*/
615
617
static void
616
- MarkAsPrepared (GlobalTransaction gxact )
618
+ MarkAsPrepared (GlobalTransaction gxact , bool lock_held )
617
619
{
618
620
/* Lock here may be overkill, but I'm not convinced of that ... */
619
- LWLockAcquire (TwoPhaseStateLock , LW_EXCLUSIVE );
621
+ if (!lock_held )
622
+ LWLockAcquire (TwoPhaseStateLock , LW_EXCLUSIVE );
620
623
Assert (!gxact -> valid );
621
624
gxact -> valid = true;
622
- LWLockRelease (TwoPhaseStateLock );
625
+ if (!lock_held )
626
+ LWLockRelease (TwoPhaseStateLock );
623
627
624
628
/*
625
629
* Put it into the global ProcArray so TransactionIdIsInProgress considers
@@ -714,7 +718,7 @@ RemoveGXact(GlobalTransaction gxact)
714
718
{
715
719
int i ;
716
720
717
- LWLockAcquire ( TwoPhaseStateLock , LW_EXCLUSIVE );
721
+ Assert ( LWLockHeldByMeInMode ( TwoPhaseStateLock , LW_EXCLUSIVE ) );
718
722
719
723
for (i = 0 ; i < TwoPhaseState -> numPrepXacts ; i ++ )
720
724
{
@@ -728,15 +732,10 @@ RemoveGXact(GlobalTransaction gxact)
728
732
gxact -> next = TwoPhaseState -> freeGXacts ;
729
733
TwoPhaseState -> freeGXacts = gxact ;
730
734
731
- gxact -> locking_pid = InvalidBackendId ;
732
- LWLockRelease (TwoPhaseStateLock );
733
-
734
735
return ;
735
736
}
736
737
}
737
738
738
- LWLockRelease (TwoPhaseStateLock );
739
-
740
739
elog (ERROR , "failed to find %p in GlobalTransaction array" , gxact );
741
740
}
742
741
@@ -1261,7 +1260,7 @@ EndPrepare(GlobalTransaction gxact)
1261
1260
* the xact crashed. Instead we have a window where the same XID appears
1262
1261
* twice in ProcArray, which is OK.
1263
1262
*/
1264
- MarkAsPrepared (gxact );
1263
+ MarkAsPrepared (gxact , false );
1265
1264
1266
1265
/*
1267
1266
* Now we can mark ourselves as out of the commit critical section: a
@@ -1688,7 +1687,9 @@ FinishPreparedTransaction(const char *gid, bool isCommit)
1688
1687
if (gxact -> ondisk )
1689
1688
RemoveTwoPhaseFile (xid , true);
1690
1689
1690
+ LWLockAcquire (TwoPhaseStateLock , LW_EXCLUSIVE );
1691
1691
RemoveGXact (gxact );
1692
+ LWLockRelease (TwoPhaseStateLock );
1692
1693
MyLockedGxact = NULL ;
1693
1694
1694
1695
pfree (buf );
@@ -1917,6 +1918,7 @@ restoreTwoPhaseData(void)
1917
1918
struct dirent * clde ;
1918
1919
1919
1920
cldir = AllocateDir (TWOPHASE_DIR );
1921
+ LWLockAcquire (TwoPhaseStateLock , LW_EXCLUSIVE );
1920
1922
while ((clde = ReadDir (cldir , TWOPHASE_DIR )) != NULL )
1921
1923
{
1922
1924
if (strlen (clde -> d_name ) == 2 * sizeof (TransactionId ) &&
@@ -1936,6 +1938,7 @@ restoreTwoPhaseData(void)
1936
1938
PrepareRedoAdd (buf , InvalidXLogRecPtr , InvalidXLogRecPtr );
1937
1939
}
1938
1940
}
1941
+ LWLockRelease (TwoPhaseStateLock );
1939
1942
FreeDir (cldir );
1940
1943
}
1941
1944
@@ -1977,7 +1980,7 @@ PrescanPreparedTransactions(TransactionId **xids_p, int *nxids_p)
1977
1980
int allocsize = 0 ;
1978
1981
int i ;
1979
1982
1980
- LWLockAcquire (TwoPhaseStateLock , LW_SHARED );
1983
+ LWLockAcquire (TwoPhaseStateLock , LW_EXCLUSIVE );
1981
1984
for (i = 0 ; i < TwoPhaseState -> numPrepXacts ; i ++ )
1982
1985
{
1983
1986
TransactionId xid ;
@@ -2055,7 +2058,7 @@ StandbyRecoverPreparedTransactions(bool overwriteOK)
2055
2058
{
2056
2059
int i ;
2057
2060
2058
- LWLockAcquire (TwoPhaseStateLock , LW_SHARED );
2061
+ LWLockAcquire (TwoPhaseStateLock , LW_EXCLUSIVE );
2059
2062
for (i = 0 ; i < TwoPhaseState -> numPrepXacts ; i ++ )
2060
2063
{
2061
2064
TransactionId xid ;
@@ -2082,16 +2085,22 @@ StandbyRecoverPreparedTransactions(bool overwriteOK)
2082
2085
* Scan the shared memory entries of TwoPhaseState and reload the state for
2083
2086
* each prepared transaction (reacquire locks, etc).
2084
2087
*
2085
- * This is run during database startup.
2088
+ * This is run at the end of recovery, but before we allow backends to write
2089
+ * WAL.
2090
+ *
2091
+ * At the end of recovery the way we take snapshots will change. We now need
2092
+ * to mark all running transactions with their full SubTransSetParent() info
2093
+ * to allow normal snapshots to work correctly if snapshots overflow.
2094
+ * We do this here because by definition prepared transactions are the only
2095
+ * type of write transaction still running, so this is necessary and
2096
+ * complete.
2086
2097
*/
2087
2098
void
2088
2099
RecoverPreparedTransactions (void )
2089
2100
{
2090
2101
int i ;
2091
2102
2092
- /*
2093
- * Don't need a lock in the recovery phase.
2094
- */
2103
+ LWLockAcquire (TwoPhaseStateLock , LW_EXCLUSIVE );
2095
2104
for (i = 0 ; i < TwoPhaseState -> numPrepXacts ; i ++ )
2096
2105
{
2097
2106
TransactionId xid ;
@@ -2114,7 +2123,7 @@ RecoverPreparedTransactions(void)
2114
2123
continue ;
2115
2124
2116
2125
ereport (LOG ,
2117
- (errmsg ("recovering prepared transaction %u from shared memory" , xid )));
2126
+ (errmsg ("recovering prepared transaction " XID_FMT " from shared memory" , xid )));
2118
2127
2119
2128
hdr = (TwoPhaseFileHeader * ) buf ;
2120
2129
Assert (TransactionIdEquals (hdr -> xid , xid ));
@@ -2151,21 +2160,20 @@ RecoverPreparedTransactions(void)
2151
2160
* it was added in redo and already has a shmem entry for
2152
2161
* it.
2153
2162
*/
2154
- LWLockAcquire (TwoPhaseStateLock , LW_EXCLUSIVE );
2155
2163
MarkAsPreparingGuts (gxact , xid , gid ,
2156
2164
hdr -> prepared_at ,
2157
2165
hdr -> owner , hdr -> database );
2158
2166
2159
2167
/* recovered, so reset the flag for entries generated by redo */
2160
2168
gxact -> inredo = false;
2161
2169
2162
- LWLockRelease (TwoPhaseStateLock );
2163
-
2164
2170
GXactLoadSubxactData (gxact , hdr -> nsubxacts , subxids );
2165
- MarkAsPrepared (gxact );
2171
+ MarkAsPrepared (gxact , true);
2172
+
2173
+ LWLockRelease (TwoPhaseStateLock );
2166
2174
2167
2175
/*
2168
- * Recover other state (notably locks) using resource managers
2176
+ * Recover other state (notably locks) using resource managers.
2169
2177
*/
2170
2178
ProcessRecords (bufptr , xid , twophase_recover_callbacks );
2171
2179
@@ -2185,7 +2193,11 @@ RecoverPreparedTransactions(void)
2185
2193
PostPrepare_Twophase ();
2186
2194
2187
2195
pfree (buf );
2196
+
2197
+ LWLockAcquire (TwoPhaseStateLock , LW_EXCLUSIVE );
2188
2198
}
2199
+
2200
+ LWLockRelease (TwoPhaseStateLock );
2189
2201
}
2190
2202
2191
2203
/*
@@ -2216,6 +2228,8 @@ ProcessTwoPhaseBuffer(TransactionId xid,
2216
2228
TwoPhaseFileHeader * hdr ;
2217
2229
int i ;
2218
2230
2231
+ Assert (LWLockHeldByMeInMode (TwoPhaseStateLock , LW_EXCLUSIVE ));
2232
+
2219
2233
if (!fromdisk )
2220
2234
Assert (prepare_start_lsn != InvalidXLogRecPtr );
2221
2235
@@ -2230,15 +2244,15 @@ ProcessTwoPhaseBuffer(TransactionId xid,
2230
2244
if (fromdisk )
2231
2245
{
2232
2246
ereport (WARNING ,
2233
- (errmsg ("removing stale two-phase state file for \"%u \"" ,
2247
+ (errmsg ("removing stale two-phase state file for \"" XID_FMT " \"" ,
2234
2248
xid )));
2235
2249
RemoveTwoPhaseFile (xid , true);
2236
2250
}
2237
2251
else
2238
2252
{
2239
2253
ereport (WARNING ,
2240
- (errmsg ("removing stale two-phase state from"
2241
- " shared memory for \"%u\"" , xid )));
2254
+ (errmsg ("removing stale two-phase state from shared memory for \"" XID_FMT "\"" ,
2255
+ xid )));
2242
2256
PrepareRedoRemove (xid , true);
2243
2257
}
2244
2258
return NULL ;
@@ -2250,14 +2264,14 @@ ProcessTwoPhaseBuffer(TransactionId xid,
2250
2264
if (fromdisk )
2251
2265
{
2252
2266
ereport (WARNING ,
2253
- (errmsg ("removing future two-phase state file for \"%u \"" ,
2267
+ (errmsg ("removing future two-phase state file for \"" XID_FMT " \"" ,
2254
2268
xid )));
2255
2269
RemoveTwoPhaseFile (xid , true);
2256
2270
}
2257
2271
else
2258
2272
{
2259
2273
ereport (WARNING ,
2260
- (errmsg ("removing future two-phase state from memory for \"%u \"" ,
2274
+ (errmsg ("removing future two-phase state from memory for \"" XID_FMT " \"" ,
2261
2275
xid )));
2262
2276
PrepareRedoRemove (xid , true);
2263
2277
}
@@ -2271,7 +2285,7 @@ ProcessTwoPhaseBuffer(TransactionId xid,
2271
2285
if (buf == NULL )
2272
2286
{
2273
2287
ereport (WARNING ,
2274
- (errmsg ("removing corrupt two-phase state file for \"%u \"" ,
2288
+ (errmsg ("removing corrupt two-phase state file for \"" XID_FMT " \"" ,
2275
2289
xid )));
2276
2290
RemoveTwoPhaseFile (xid , true);
2277
2291
return NULL ;
@@ -2290,14 +2304,14 @@ ProcessTwoPhaseBuffer(TransactionId xid,
2290
2304
if (fromdisk )
2291
2305
{
2292
2306
ereport (WARNING ,
2293
- (errmsg ("removing corrupt two-phase state file for \"%u \"" ,
2307
+ (errmsg ("removing corrupt two-phase state file for \"" XID_FMT " \"" ,
2294
2308
xid )));
2295
2309
RemoveTwoPhaseFile (xid , true);
2296
2310
}
2297
2311
else
2298
2312
{
2299
2313
ereport (WARNING ,
2300
- (errmsg ("removing corrupt two-phase state from memory for \"%u \"" ,
2314
+ (errmsg ("removing corrupt two-phase state from memory for \"" XID_FMT " \"" ,
2301
2315
xid )));
2302
2316
PrepareRedoRemove (xid , true);
2303
2317
}
@@ -2565,6 +2579,7 @@ PrepareRedoAdd(char *buf, XLogRecPtr start_lsn, XLogRecPtr end_lsn)
2565
2579
const char * gid ;
2566
2580
GlobalTransaction gxact ;
2567
2581
2582
+ Assert (LWLockHeldByMeInMode (TwoPhaseStateLock , LW_EXCLUSIVE ));
2568
2583
Assert (RecoveryInProgress ());
2569
2584
2570
2585
bufptr = buf + MAXALIGN (sizeof (TwoPhaseFileHeader ));
@@ -2581,7 +2596,6 @@ PrepareRedoAdd(char *buf, XLogRecPtr start_lsn, XLogRecPtr end_lsn)
2581
2596
* that it got added in the redo phase
2582
2597
*/
2583
2598
2584
- LWLockAcquire (TwoPhaseStateLock , LW_EXCLUSIVE );
2585
2599
/* Get a free gxact from the freelist */
2586
2600
if (TwoPhaseState -> freeGXacts == NULL )
2587
2601
ereport (ERROR ,
@@ -2607,27 +2621,27 @@ PrepareRedoAdd(char *buf, XLogRecPtr start_lsn, XLogRecPtr end_lsn)
2607
2621
Assert (TwoPhaseState -> numPrepXacts < max_prepared_xacts );
2608
2622
TwoPhaseState -> prepXacts [TwoPhaseState -> numPrepXacts ++ ] = gxact ;
2609
2623
2610
- LWLockRelease (TwoPhaseStateLock );
2611
-
2612
- elog (DEBUG2 , "Adding 2PC data to shared memory %u" , gxact -> xid );
2624
+ elog (DEBUG2 , "added 2PC data in shared memory for transaction " XID_FMT , gxact -> xid );
2613
2625
}
2614
2626
2615
2627
/*
2616
2628
* PrepareRedoRemove
2617
2629
*
2618
- * Remove the corresponding gxact entry from TwoPhaseState. Also
2619
- * remove the 2PC file if a prepared transaction was saved via
2620
- * an earlier checkpoint.
2630
+ * Remove the corresponding gxact entry from TwoPhaseState. Also remove
2631
+ * the 2PC file if a prepared transaction was saved via an earlier checkpoint.
2632
+ *
2633
+ * Caller must hold TwoPhaseStateLock in exclusive mode, because TwoPhaseState
2634
+ * is updated.
2621
2635
*/
2622
2636
void
2623
2637
PrepareRedoRemove (TransactionId xid , bool giveWarning )
2624
2638
{
2625
2639
GlobalTransaction gxact = NULL ;
2626
2640
int i ;
2627
2641
2642
+ Assert (LWLockHeldByMeInMode (TwoPhaseStateLock , LW_EXCLUSIVE ));
2628
2643
Assert (RecoveryInProgress ());
2629
2644
2630
- LWLockAcquire (TwoPhaseStateLock , LW_SHARED );
2631
2645
for (i = 0 ; i < TwoPhaseState -> numPrepXacts ; i ++ )
2632
2646
{
2633
2647
gxact = TwoPhaseState -> prepXacts [i ];
@@ -2638,7 +2652,6 @@ PrepareRedoRemove(TransactionId xid, bool giveWarning)
2638
2652
break ;
2639
2653
}
2640
2654
}
2641
- LWLockRelease (TwoPhaseStateLock );
2642
2655
2643
2656
/*
2644
2657
* Just leave if there is nothing, this is expected during WAL replay.
@@ -2649,7 +2662,7 @@ PrepareRedoRemove(TransactionId xid, bool giveWarning)
2649
2662
/*
2650
2663
* And now we can clean up any files we may have left.
2651
2664
*/
2652
- elog (DEBUG2 , "Removing 2PC data from shared memory %u" , xid );
2665
+ elog (DEBUG2 , "removing 2PC data for transaction " XID_FMT , xid );
2653
2666
if (gxact -> ondisk )
2654
2667
RemoveTwoPhaseFile (xid , giveWarning );
2655
2668
RemoveGXact (gxact );
0 commit comments