Even if a flag is modified only by the backend owning the transaction, it's
not safe to modify it without a lock. Another backend might be setting or
clearing a different flag in the flags field concurrently, and that
operation might be lost because setting or clearing a bit in a word is not
atomic.
Make did-write flag a simple backend-private boolean variable, because it
was only set or tested in the owning backend (except when committing a
prepared transaction, but it's not worthwhile to optimize for the case of a
read-only prepared transaction). This also eliminates the need to add
locking where that flag is set.
Also, set the did-write flag when doing DDL operations like DROP TABLE or
TRUNCATE -- that was missed earlier.
/*
* Keep a pointer to the currently-running serializable transaction (if any)
/*
* Keep a pointer to the currently-running serializable transaction (if any)
- * for quick reference.
- * TODO SSI: Remove volatile qualifier and the then-unnecessary casts?
+ * for quick reference. Also, remember if we have written anything that could
+ * cause a rw-conflict.
-static volatile SERIALIZABLEXACT *MySerializableXact = InvalidSerializableXact;
+static SERIALIZABLEXACT *MySerializableXact = InvalidSerializableXact;
+static bool MyXactDidWrite = false;
if (MySerializableXact == InvalidSerializableXact)
return snapshot; /* no concurrent r/w xacts; it's safe */
if (MySerializableXact == InvalidSerializableXact)
return snapshot; /* no concurrent r/w xacts; it's safe */
- MySerializableXact->flags |= SXACT_FLAG_DEFERRABLE_WAITING;
+ LWLockAcquire(SerializableXactHashLock, LW_EXCLUSIVE);
/*
* Wait for concurrent transactions to finish. Stop early if one of
* them marked us as conflicted.
*/
/*
* Wait for concurrent transactions to finish. Stop early if one of
* them marked us as conflicted.
*/
+ MySerializableXact->flags |= SXACT_FLAG_DEFERRABLE_WAITING;
while (!(SHMQueueEmpty((SHM_QUEUE *)
&MySerializableXact->possibleUnsafeConflicts) ||
SxactIsROUnsafe(MySerializableXact)))
while (!(SHMQueueEmpty((SHM_QUEUE *)
&MySerializableXact->possibleUnsafeConflicts) ||
SxactIsROUnsafe(MySerializableXact)))
+ {
+ LWLockRelease(SerializableXactHashLock);
+ LWLockAcquire(SerializableXactHashLock, LW_EXCLUSIVE);
+ }
MySerializableXact->flags &= ~SXACT_FLAG_DEFERRABLE_WAITING;
MySerializableXact->flags &= ~SXACT_FLAG_DEFERRABLE_WAITING;
if (!SxactIsROUnsafe(MySerializableXact))
if (!SxactIsROUnsafe(MySerializableXact))
+ {
+ LWLockRelease(SerializableXactHashLock);
+ }
+
+ LWLockRelease(SerializableXactHashLock);
/* else, need to retry... */
ereport(DEBUG2,
/* else, need to retry... */
ereport(DEBUG2,
}
MySerializableXact = sxact;
}
MySerializableXact = sxact;
+ MyXactDidWrite = false; /* haven't written anything yet */
LWLockRelease(SerializableXactHashLock);
LWLockRelease(SerializableXactHashLock);
if (MySerializableXact == InvalidSerializableXact)
return;
if (MySerializableXact == InvalidSerializableXact)
return;
- /* This should only be done once per transaction. */
- Assert(MySerializableXact->topXid == InvalidTransactionId);
-
/* We should have a valid XID and be at the top level. */
Assert(TransactionIdIsValid(xid));
/* We should have a valid XID and be at the top level. */
Assert(TransactionIdIsValid(xid));
+ LWLockAcquire(SerializableXactHashLock, LW_EXCLUSIVE);
+
+ /* This should only be done once per transaction. */
+ Assert(MySerializableXact->topXid == InvalidTransactionId);
+
MySerializableXact->topXid = xid;
sxidtag.xid = xid;
MySerializableXact->topXid = xid;
sxidtag.xid = xid;
- LWLockAcquire(SerializableXactHashLock, LW_EXCLUSIVE);
sxid = (SERIALIZABLEXID *) hash_search(SerializableXidHash,
&sxidtag,
HASH_ENTER, &found);
sxid = (SERIALIZABLEXID *) hash_search(SerializableXidHash,
&sxidtag,
HASH_ENTER, &found);
Assert(!found);
/* Initialize the structure. */
Assert(!found);
/* Initialize the structure. */
- sxid->myXact = (SERIALIZABLEXACT *) MySerializableXact;
+ sxid->myXact = MySerializableXact;
LWLockRelease(SerializableXactHashLock);
}
LWLockRelease(SerializableXactHashLock);
}
PREDICATELOCK *predlock;
LWLockAcquire(SerializablePredicateLockListLock, LW_SHARED);
PREDICATELOCK *predlock;
LWLockAcquire(SerializablePredicateLockListLock, LW_SHARED);
- sxact = (SERIALIZABLEXACT *) MySerializableXact;
+ sxact = MySerializableXact;
predlock = (PREDICATELOCK *)
SHMQueueNext(&(sxact->predicateLocks),
&(sxact->predicateLocks),
predlock = (PREDICATELOCK *)
SHMQueueNext(&(sxact->predicateLocks),
&(sxact->predicateLocks),
locallock->childLocks = 0;
/* Actually create the lock */
locallock->childLocks = 0;
/* Actually create the lock */
- CreatePredicateLock(targettag, targettaghash,
- (SERIALIZABLEXACT *) MySerializableXact);
+ CreatePredicateLock(targettag, targettaghash, MySerializableXact);
/*
* Lock has been acquired. Check whether it should be promoted to a
/*
* Lock has been acquired. Check whether it should be promoted to a
Assert(IsolationIsSerializable());
/* We'd better not already be on the cleanup list. */
Assert(IsolationIsSerializable());
/* We'd better not already be on the cleanup list. */
- Assert(!SxactIsOnFinishedList((SERIALIZABLEXACT *) MySerializableXact));
+ Assert(!SxactIsOnFinishedList(MySerializableXact));
topLevelIsDeclaredReadOnly = SxactIsReadOnly(MySerializableXact);
topLevelIsDeclaredReadOnly = SxactIsReadOnly(MySerializableXact);
MySerializableXact->flags |= SXACT_FLAG_COMMITTED;
MySerializableXact->commitSeqNo = ++(PredXact->LastSxactCommitSeqNo);
/* Recognize implicit read-only transaction (commit without write). */
MySerializableXact->flags |= SXACT_FLAG_COMMITTED;
MySerializableXact->commitSeqNo = ++(PredXact->LastSxactCommitSeqNo);
/* Recognize implicit read-only transaction (commit without write). */
- if (!(MySerializableXact->flags & SXACT_FLAG_DID_WRITE))
MySerializableXact->flags |= SXACT_FLAG_READ_ONLY;
}
else
MySerializableXact->flags |= SXACT_FLAG_READ_ONLY;
}
else
/* Mark conflicted if necessary. */
if (isCommit
/* Mark conflicted if necessary. */
if (isCommit
- && (MySerializableXact->flags & SXACT_FLAG_DID_WRITE)
&& SxactHasConflictOut(MySerializableXact)
&& (MySerializableXact->SeqNo.earliestOutConflictCommit
<= roXact->SeqNo.lastCommitBeforeSnapshot))
&& SxactHasConflictOut(MySerializableXact)
&& (MySerializableXact->SeqNo.earliestOutConflictCommit
<= roXact->SeqNo.lastCommitBeforeSnapshot))
(SHM_QUEUE *) &(MySerializableXact->finishedLink));
if (!isCommit)
(SHM_QUEUE *) &(MySerializableXact->finishedLink));
if (!isCommit)
- ReleaseOneSerializableXact((SERIALIZABLEXACT *) MySerializableXact,
- false, false);
+ ReleaseOneSerializableXact(MySerializableXact, false, false);
LWLockRelease(SerializableFinishedListLock);
LWLockRelease(SerializableFinishedListLock);
ClearOldPredicateLocks();
MySerializableXact = InvalidSerializableXact;
ClearOldPredicateLocks();
MySerializableXact = InvalidSerializableXact;
+ MyXactDidWrite = false;
/* Delete per-transaction lock table */
if (LocalPredicateLockHash != NULL)
/* Delete per-transaction lock table */
if (LocalPredicateLockHash != NULL)
- if (RWConflictExists((SERIALIZABLEXACT *) MySerializableXact, sxact))
+ if (RWConflictExists(MySerializableXact, sxact))
{
/* We don't want duplicate conflict records in the list. */
LWLockRelease(SerializableXactHashLock);
{
/* We don't want duplicate conflict records in the list. */
LWLockRelease(SerializableXactHashLock);
* Flag the conflict. But first, if this conflict creates a dangerous
* structure, ereport an error.
*/
* Flag the conflict. But first, if this conflict creates a dangerous
* structure, ereport an error.
*/
- FlagRWConflict((SERIALIZABLEXACT *) MySerializableXact, sxact);
+ FlagRWConflict(MySerializableXact, sxact);
LWLockRelease(SerializableXactHashLock);
}
LWLockRelease(SerializableXactHashLock);
}
&& (!SxactIsCommitted(sxact)
|| TransactionIdPrecedes(GetTransactionSnapshot()->xmin,
sxact->finishedBefore))
&& (!SxactIsCommitted(sxact)
|| TransactionIdPrecedes(GetTransactionSnapshot()->xmin,
sxact->finishedBefore))
- && !RWConflictExists(sxact, (SERIALIZABLEXACT *) MySerializableXact))
+ && !RWConflictExists(sxact, MySerializableXact))
{
LWLockRelease(SerializableXactHashLock);
LWLockAcquire(SerializableXactHashLock, LW_EXCLUSIVE);
{
LWLockRelease(SerializableXactHashLock);
LWLockAcquire(SerializableXactHashLock, LW_EXCLUSIVE);
&& (!SxactIsCommitted(sxact)
|| TransactionIdPrecedes(GetTransactionSnapshot()->xmin,
sxact->finishedBefore))
&& (!SxactIsCommitted(sxact)
|| TransactionIdPrecedes(GetTransactionSnapshot()->xmin,
sxact->finishedBefore))
- && !RWConflictExists(sxact,
- (SERIALIZABLEXACT *) MySerializableXact))
+ && !RWConflictExists(sxact, MySerializableXact))
- FlagRWConflict(sxact, (SERIALIZABLEXACT *) MySerializableXact);
+ FlagRWConflict(sxact, MySerializableXact);
}
LWLockRelease(SerializableXactHashLock);
}
LWLockRelease(SerializableXactHashLock);
errdetail("Cancelled on identification as a pivot, during conflict in checking."),
errhint("The transaction might succeed if retried.")));
errdetail("Cancelled on identification as a pivot, during conflict in checking."),
errhint("The transaction might succeed if retried.")));
- MySerializableXact->flags |= SXACT_FLAG_DID_WRITE;
+ /*
+ * We're doing a write which might cause rw-conflicts now or later.
+ * Memorize that fact.
+ */
+ MyXactDidWrite = true;
/*
* It is important that we check for locks from the finest granularity to
/*
* It is important that we check for locks from the finest granularity to
if (SkipSerialization(relation))
return;
if (SkipSerialization(relation))
return;
+ /*
+ * We're doing a write which might cause rw-conflicts now or later.
+ * Memorize that fact.
+ */
+ MyXactDidWrite = true;
+
Assert(relation->rd_index == NULL); /* not an index relation */
dbId = relation->rd_node.dbNode;
Assert(relation->rd_index == NULL); /* not an index relation */
dbId = relation->rd_node.dbNode;
offsetof(PREDICATELOCK, targetLink));
if (predlock->tag.myXact != MySerializableXact
offsetof(PREDICATELOCK, targetLink));
if (predlock->tag.myXact != MySerializableXact
- && !RWConflictExists(predlock->tag.myXact,
- (SERIALIZABLEXACT *) MySerializableXact))
- FlagRWConflict(predlock->tag.myXact,
- (SERIALIZABLEXACT *) MySerializableXact);
+ && !RWConflictExists(predlock->tag.myXact, MySerializableXact))
+ {
+ FlagRWConflict(predlock->tag.myXact, MySerializableXact);
+ }
predlock = nextpredlock;
}
predlock = nextpredlock;
}
TwoPhasePredicateXactRecord *xactRecord;
TwoPhasePredicateLockRecord *lockRecord;
TwoPhasePredicateXactRecord *xactRecord;
TwoPhasePredicateLockRecord *lockRecord;
- sxact = (SERIALIZABLEXACT *) MySerializableXact;
+ sxact = MySerializableXact;
xactRecord = &(record.data.xactRecord);
lockRecord = &(record.data.lockRecord);
xactRecord = &(record.data.xactRecord);
lockRecord = &(record.data.lockRecord);
LocalPredicateLockHash = NULL;
MySerializableXact = InvalidSerializableXact;
LocalPredicateLockHash = NULL;
MySerializableXact = InvalidSerializableXact;
+ MyXactDidWrite = false;
/* Release its locks */
MySerializableXact = sxid->myXact;
/* Release its locks */
MySerializableXact = sxid->myXact;
+ MyXactDidWrite = true; /* conservatively assume that we wrote
+ * something */
ReleasePredicateLocks(isCommit);
}
ReleasePredicateLocks(isCommit);
}
*/
#define SXACT_FLAG_CONFLICT_OUT 0x00000004
#define SXACT_FLAG_READ_ONLY 0x00000008
*/
#define SXACT_FLAG_CONFLICT_OUT 0x00000004
#define SXACT_FLAG_READ_ONLY 0x00000008
-#define SXACT_FLAG_DID_WRITE 0x00000010
-#define SXACT_FLAG_MARKED_FOR_DEATH 0x00000020
-#define SXACT_FLAG_DEFERRABLE_WAITING 0x00000040
-#define SXACT_FLAG_RO_SAFE 0x00000080
-#define SXACT_FLAG_RO_UNSAFE 0x00000100
-#define SXACT_FLAG_SUMMARY_CONFLICT_IN 0x00000200
-#define SXACT_FLAG_SUMMARY_CONFLICT_OUT 0x00000400
-#define SXACT_FLAG_PREPARED 0x00000800
+#define SXACT_FLAG_MARKED_FOR_DEATH 0x00000010
+#define SXACT_FLAG_DEFERRABLE_WAITING 0x00000020
+#define SXACT_FLAG_RO_SAFE 0x00000040
+#define SXACT_FLAG_RO_UNSAFE 0x00000080
+#define SXACT_FLAG_SUMMARY_CONFLICT_IN 0x00000100
+#define SXACT_FLAG_SUMMARY_CONFLICT_OUT 0x00000200
+#define SXACT_FLAG_PREPARED 0x00000400
/*
* The following types are used to provide an ad hoc list for holding
/*
* The following types are used to provide an ad hoc list for holding