Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commite90ceea

Browse files
committed
Avoid bogus TwoPhaseState locking sequences
The optimized code in728bd99 contains a few invalid lockingsequences. To wit, the original code would try to acquire an lwlockthat it already holds. Avoid this by moving lock acquisitions tohigher-level code, and install appropriate assertions in low-level thatthe correct mode is held.Authors: Michael Paquier, Álvaro HerreraReported-By: chuanting wangBug: #14680Discussion:https://postgr.es/m/20170531033228.1487.10124@wrigleys.postgresql.org
1 parent0d9bdbc commite90ceea

File tree

2 files changed

+83
-72
lines changed

2 files changed

+83
-72
lines changed

‎src/backend/access/transam/twophase.c

Lines changed: 47 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,10 @@ typedef struct TwoPhaseStateData
195195
staticTwoPhaseStateData*TwoPhaseState;
196196

197197
/*
198-
* Global transaction entry currently locked by us, if any.
198+
* Global transaction entry currently locked by us, if any. Note that any
199+
* access to the entry pointed to by this variable must be protected by
200+
* TwoPhaseStateLock, though obviously the pointer itself doesn't need to be
201+
* (since it's just local memory).
199202
*/
200203
staticGlobalTransactionMyLockedGxact=NULL;
201204

@@ -338,18 +341,13 @@ AtAbort_Twophase(void)
338341
* resources held by the transaction yet. In those cases, the in-memory
339342
* state can be wrong, but it's too late to back out.
340343
*/
344+
LWLockAcquire(TwoPhaseStateLock,LW_EXCLUSIVE);
341345
if (!MyLockedGxact->valid)
342-
{
343346
RemoveGXact(MyLockedGxact);
344-
}
345347
else
346-
{
347-
LWLockAcquire(TwoPhaseStateLock,LW_EXCLUSIVE);
348-
349348
MyLockedGxact->locking_backend=InvalidBackendId;
349+
LWLockRelease(TwoPhaseStateLock);
350350

351-
LWLockRelease(TwoPhaseStateLock);
352-
}
353351
MyLockedGxact=NULL;
354352
}
355353

@@ -454,6 +452,8 @@ MarkAsPreparingGuts(GlobalTransaction gxact, TransactionId xid, const char *gid,
454452
PGXACT*pgxact;
455453
inti;
456454

455+
Assert(LWLockHeldByMeInMode(TwoPhaseStateLock,LW_EXCLUSIVE));
456+
457457
Assert(gxact!=NULL);
458458
proc=&ProcGlobal->allProcs[gxact->pgprocno];
459459
pgxact=&ProcGlobal->allPgXact[gxact->pgprocno];
@@ -530,15 +530,19 @@ GXactLoadSubxactData(GlobalTransaction gxact, int nsubxacts,
530530
/*
531531
* MarkAsPrepared
532532
*Mark the GXACT as fully valid, and enter it into the global ProcArray.
533+
*
534+
* lock_held indicates whether caller already holds TwoPhaseStateLock.
533535
*/
534536
staticvoid
535-
MarkAsPrepared(GlobalTransactiongxact)
537+
MarkAsPrepared(GlobalTransactiongxact,boollock_held)
536538
{
537539
/* Lock here may be overkill, but I'm not convinced of that ... */
538-
LWLockAcquire(TwoPhaseStateLock,LW_EXCLUSIVE);
540+
if (!lock_held)
541+
LWLockAcquire(TwoPhaseStateLock,LW_EXCLUSIVE);
539542
Assert(!gxact->valid);
540543
gxact->valid= true;
541-
LWLockRelease(TwoPhaseStateLock);
544+
if (!lock_held)
545+
LWLockRelease(TwoPhaseStateLock);
542546

543547
/*
544548
* Put it into the global ProcArray so TransactionIdIsInProgress considers
@@ -632,7 +636,7 @@ RemoveGXact(GlobalTransaction gxact)
632636
{
633637
inti;
634638

635-
LWLockAcquire(TwoPhaseStateLock,LW_EXCLUSIVE);
639+
Assert(LWLockHeldByMeInMode(TwoPhaseStateLock,LW_EXCLUSIVE));
636640

637641
for (i=0;i<TwoPhaseState->numPrepXacts;i++)
638642
{
@@ -646,14 +650,10 @@ RemoveGXact(GlobalTransaction gxact)
646650
gxact->next=TwoPhaseState->freeGXacts;
647651
TwoPhaseState->freeGXacts=gxact;
648652

649-
LWLockRelease(TwoPhaseStateLock);
650-
651653
return;
652654
}
653655
}
654656

655-
LWLockRelease(TwoPhaseStateLock);
656-
657657
elog(ERROR,"failed to find %p in GlobalTransaction array",gxact);
658658
}
659659

@@ -1127,7 +1127,7 @@ EndPrepare(GlobalTransaction gxact)
11271127
* the xact crashed. Instead we have a window where the same XID appears
11281128
* twice in ProcArray, which is OK.
11291129
*/
1130-
MarkAsPrepared(gxact);
1130+
MarkAsPrepared(gxact, false);
11311131

11321132
/*
11331133
* Now we can mark ourselves as out of the commit critical section: a
@@ -1506,7 +1506,9 @@ FinishPreparedTransaction(const char *gid, bool isCommit)
15061506
if (gxact->ondisk)
15071507
RemoveTwoPhaseFile(xid, true);
15081508

1509+
LWLockAcquire(TwoPhaseStateLock,LW_EXCLUSIVE);
15091510
RemoveGXact(gxact);
1511+
LWLockRelease(TwoPhaseStateLock);
15101512
MyLockedGxact=NULL;
15111513

15121514
pfree(buf);
@@ -1734,6 +1736,7 @@ restoreTwoPhaseData(void)
17341736
structdirent*clde;
17351737

17361738
cldir=AllocateDir(TWOPHASE_DIR);
1739+
LWLockAcquire(TwoPhaseStateLock,LW_EXCLUSIVE);
17371740
while ((clde=ReadDir(cldir,TWOPHASE_DIR))!=NULL)
17381741
{
17391742
if (strlen(clde->d_name)==8&&
@@ -1752,6 +1755,7 @@ restoreTwoPhaseData(void)
17521755
PrepareRedoAdd(buf,InvalidXLogRecPtr,InvalidXLogRecPtr);
17531756
}
17541757
}
1758+
LWLockRelease(TwoPhaseStateLock);
17551759
FreeDir(cldir);
17561760
}
17571761

@@ -1792,7 +1796,7 @@ PrescanPreparedTransactions(TransactionId **xids_p, int *nxids_p)
17921796
intallocsize=0;
17931797
inti;
17941798

1795-
LWLockAcquire(TwoPhaseStateLock,LW_SHARED);
1799+
LWLockAcquire(TwoPhaseStateLock,LW_EXCLUSIVE);
17961800
for (i=0;i<TwoPhaseState->numPrepXacts;i++)
17971801
{
17981802
TransactionIdxid;
@@ -1867,7 +1871,7 @@ StandbyRecoverPreparedTransactions(void)
18671871
{
18681872
inti;
18691873

1870-
LWLockAcquire(TwoPhaseStateLock,LW_SHARED);
1874+
LWLockAcquire(TwoPhaseStateLock,LW_EXCLUSIVE);
18711875
for (i=0;i<TwoPhaseState->numPrepXacts;i++)
18721876
{
18731877
TransactionIdxid;
@@ -1893,7 +1897,8 @@ StandbyRecoverPreparedTransactions(void)
18931897
* Scan the shared memory entries of TwoPhaseState and reload the state for
18941898
* each prepared transaction (reacquire locks, etc).
18951899
*
1896-
* This is run during database startup.
1900+
* This is run at the end of recovery, but before we allow backends to write
1901+
* WAL.
18971902
*
18981903
* At the end of recovery the way we take snapshots will change. We now need
18991904
* to mark all running transactions with their full SubTransSetParent() info
@@ -1907,9 +1912,7 @@ RecoverPreparedTransactions(void)
19071912
{
19081913
inti;
19091914

1910-
/*
1911-
* Don't need a lock in the recovery phase.
1912-
*/
1915+
LWLockAcquire(TwoPhaseStateLock,LW_EXCLUSIVE);
19131916
for (i=0;i<TwoPhaseState->numPrepXacts;i++)
19141917
{
19151918
TransactionIdxid;
@@ -1955,21 +1958,20 @@ RecoverPreparedTransactions(void)
19551958
* Recreate its GXACT and dummy PGPROC. But, check whether it was
19561959
* added in redo and already has a shmem entry for it.
19571960
*/
1958-
LWLockAcquire(TwoPhaseStateLock,LW_EXCLUSIVE);
19591961
MarkAsPreparingGuts(gxact,xid,gid,
19601962
hdr->prepared_at,
19611963
hdr->owner,hdr->database);
19621964

19631965
/* recovered, so reset the flag for entries generated by redo */
19641966
gxact->inredo= false;
19651967

1966-
LWLockRelease(TwoPhaseStateLock);
1967-
19681968
GXactLoadSubxactData(gxact,hdr->nsubxacts,subxids);
1969-
MarkAsPrepared(gxact);
1969+
MarkAsPrepared(gxact, true);
1970+
1971+
LWLockRelease(TwoPhaseStateLock);
19701972

19711973
/*
1972-
* Recover other state (notably locks) using resource managers
1974+
* Recover other state (notably locks) using resource managers.
19731975
*/
19741976
ProcessRecords(bufptr,xid,twophase_recover_callbacks);
19751977

@@ -1988,7 +1990,11 @@ RecoverPreparedTransactions(void)
19881990
PostPrepare_Twophase();
19891991

19901992
pfree(buf);
1993+
1994+
LWLockAcquire(TwoPhaseStateLock,LW_EXCLUSIVE);
19911995
}
1996+
1997+
LWLockRelease(TwoPhaseStateLock);
19921998
}
19931999

19942000
/*
@@ -2014,6 +2020,8 @@ ProcessTwoPhaseBuffer(TransactionId xid,
20142020
TwoPhaseFileHeader*hdr;
20152021
inti;
20162022

2023+
Assert(LWLockHeldByMeInMode(TwoPhaseStateLock,LW_EXCLUSIVE));
2024+
20172025
if (!fromdisk)
20182026
Assert(prepare_start_lsn!=InvalidXLogRecPtr);
20192027

@@ -2030,8 +2038,8 @@ ProcessTwoPhaseBuffer(TransactionId xid,
20302038
else
20312039
{
20322040
ereport(WARNING,
2033-
(errmsg("removing stale two-phase state from"
2034-
" shared memory for \"%u\"",xid)));
2041+
(errmsg("removing stale two-phase state from shared memory for \"%u\"",
2042+
xid)));
20352043
PrepareRedoRemove(xid, true);
20362044
}
20372045
returnNULL;
@@ -2308,6 +2316,7 @@ PrepareRedoAdd(char *buf, XLogRecPtr start_lsn, XLogRecPtr end_lsn)
23082316
constchar*gid;
23092317
GlobalTransactiongxact;
23102318

2319+
Assert(LWLockHeldByMeInMode(TwoPhaseStateLock,LW_EXCLUSIVE));
23112320
Assert(RecoveryInProgress());
23122321

23132322
bufptr=buf+MAXALIGN(sizeof(TwoPhaseFileHeader));
@@ -2324,7 +2333,6 @@ PrepareRedoAdd(char *buf, XLogRecPtr start_lsn, XLogRecPtr end_lsn)
23242333
* that it got added in the redo phase
23252334
*/
23262335

2327-
LWLockAcquire(TwoPhaseStateLock,LW_EXCLUSIVE);
23282336
/* Get a free gxact from the freelist */
23292337
if (TwoPhaseState->freeGXacts==NULL)
23302338
ereport(ERROR,
@@ -2350,17 +2358,17 @@ PrepareRedoAdd(char *buf, XLogRecPtr start_lsn, XLogRecPtr end_lsn)
23502358
Assert(TwoPhaseState->numPrepXacts<max_prepared_xacts);
23512359
TwoPhaseState->prepXacts[TwoPhaseState->numPrepXacts++]=gxact;
23522360

2353-
LWLockRelease(TwoPhaseStateLock);
2354-
2355-
elog(DEBUG2,"Adding 2PC data to shared memory %u",gxact->xid);
2361+
elog(DEBUG2,"added 2PC data in shared memory for transaction %u",gxact->xid);
23562362
}
23572363

23582364
/*
23592365
* PrepareRedoRemove
23602366
*
2361-
* Remove the corresponding gxact entry from TwoPhaseState. Also
2362-
* remove the 2PC file if a prepared transaction was saved via
2363-
* an earlier checkpoint.
2367+
* Remove the corresponding gxact entry from TwoPhaseState. Also remove
2368+
* the 2PC file if a prepared transaction was saved via an earlier checkpoint.
2369+
*
2370+
* Caller must hold TwoPhaseStateLock in exclusive mode, because TwoPhaseState
2371+
* is updated.
23642372
*/
23652373
void
23662374
PrepareRedoRemove(TransactionIdxid,boolgiveWarning)
@@ -2369,9 +2377,9 @@ PrepareRedoRemove(TransactionId xid, bool giveWarning)
23692377
inti;
23702378
boolfound= false;
23712379

2380+
Assert(LWLockHeldByMeInMode(TwoPhaseStateLock,LW_EXCLUSIVE));
23722381
Assert(RecoveryInProgress());
23732382

2374-
LWLockAcquire(TwoPhaseStateLock,LW_SHARED);
23752383
for (i=0;i<TwoPhaseState->numPrepXacts;i++)
23762384
{
23772385
gxact=TwoPhaseState->prepXacts[i];
@@ -2383,7 +2391,6 @@ PrepareRedoRemove(TransactionId xid, bool giveWarning)
23832391
break;
23842392
}
23852393
}
2386-
LWLockRelease(TwoPhaseStateLock);
23872394

23882395
/*
23892396
* Just leave if there is nothing, this is expected during WAL replay.
@@ -2394,7 +2401,7 @@ PrepareRedoRemove(TransactionId xid, bool giveWarning)
23942401
/*
23952402
* And now we can clean up any files we may have left.
23962403
*/
2397-
elog(DEBUG2,"Removing 2PC datafrom shared memory %u",xid);
2404+
elog(DEBUG2,"removing 2PC datafor transaction %u",xid);
23982405
if (gxact->ondisk)
23992406
RemoveTwoPhaseFile(xid,giveWarning);
24002407
RemoveGXact(gxact);

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp