/*
* Per-backend data starts here. We have two arrays stored in the area
- * immediately following the MultiXactStateData struct. Each is indexed by
- * ProcNumber.
- *
- * In both arrays, there's a slot for all normal backends
- * (0..MaxBackends-1) followed by a slot for max_prepared_xacts prepared
- * transactions.
+ * immediately following the MultiXactStateData struct:
*
* OldestMemberMXactId[k] is the oldest MultiXactId each backend's current
* transaction(s) could possibly be a member of, or InvalidMultiXactId
* member of a MultiXact, and that MultiXact would have to be created
* during or after the lock acquisition.)
*
+ * In the OldestMemberMXactId array, there's a slot for all normal
+ * backends (0..MaxBackends-1) followed by a slot for max_prepared_xacts
+ * prepared transactions.
+ *
* OldestVisibleMXactId[k] is the oldest MultiXactId each backend's
* current transaction(s) think is potentially live, or InvalidMultiXactId
* when not in a transaction or not in a transaction that's paid any
* than its own OldestVisibleMXactId[] setting; this is necessary because
* the relevant SLRU data can be concurrently truncated away.
*
+ * In the OldestVisibleMXactId array, there's a slot for all normal
+ * backends (0..MaxBackends-1) only. No slots for prepared transactions.
+ *
* The oldest valid value among all of the OldestMemberMXactId[] and
* OldestVisibleMXactId[] entries is considered by vacuum as the earliest
* possible value still having any live member transaction -- OldestMxact.
} MultiXactStateData;
/*
- * Size of OldestMemberMXactId and OldestVisibleMXactId arrays.
+ * Sizes of OldestMemberMXactId and OldestVisibleMXactId arrays.
*/
-#define MaxOldestSlot (MaxBackends + max_prepared_xacts)
+#define NumMemberSlots (MaxBackends + max_prepared_xacts)
+#define NumVisibleSlots MaxBackends
/* Pointers to the state data in shared memory */
static MultiXactStateData *MultiXactState;
static MultiXactId *OldestVisibleMXactId;
+static inline MultiXactId *
+MyOldestMemberMXactIdSlot(void)
+{
+ /*
+ * The first MaxBackends entries in the OldestMemberMXactId array are
+ * reserved for regular backends. MyProcNumber should index into one of
+ * them.
+ */
+ Assert(MyProcNumber >= 0 && MyProcNumber < MaxBackends);
+ return &OldestMemberMXactId[MyProcNumber];
+}
+
+static inline MultiXactId *
+PreparedXactOldestMemberMXactIdSlot(ProcNumber procno)
+{
+ int prepared_xact_idx;
+
+ Assert(procno >= FIRST_PREPARED_XACT_PROC_NUMBER);
+ prepared_xact_idx = procno - FIRST_PREPARED_XACT_PROC_NUMBER;
+
+ /*
+ * The first MaxBackends entries in the OldestMemberMXactId array are
+ * reserved for regular backends. Prepared xacts come after them.
+ */
+ Assert(MaxBackends + prepared_xact_idx < NumMemberSlots);
+ return &OldestMemberMXactId[MaxBackends + prepared_xact_idx];
+}
+
+static inline MultiXactId *
+MyOldestVisibleMXactIdSlot(void)
+{
+ Assert(MyProcNumber >= 0 && MyProcNumber < NumVisibleSlots);
+ return &OldestVisibleMXactId[MyProcNumber];
+}
+
/*
* Definitions for the backend-local MultiXactId cache.
*
Assert(!TransactionIdEquals(xid1, xid2) || (status1 != status2));
/* MultiXactIdSetOldestMember() must have been called already. */
- Assert(MultiXactIdIsValid(OldestMemberMXactId[MyProcNumber]));
+ Assert(MultiXactIdIsValid(*MyOldestMemberMXactIdSlot()));
/*
* Note: unlike MultiXactIdExpand, we don't bother to check that both XIDs
Assert(TransactionIdIsValid(xid));
/* MultiXactIdSetOldestMember() must have been called already. */
- Assert(MultiXactIdIsValid(OldestMemberMXactId[MyProcNumber]));
+ Assert(MultiXactIdIsValid(*MyOldestMemberMXactIdSlot()));
debug_elog5(DEBUG2, "Expand: received multi %u, xid %u status %s",
multi, xid, mxstatus_to_string(status));
void
MultiXactIdSetOldestMember(void)
{
- if (!MultiXactIdIsValid(OldestMemberMXactId[MyProcNumber]))
+ if (!MultiXactIdIsValid(*MyOldestMemberMXactIdSlot()))
{
MultiXactId nextMXact;
nextMXact = MultiXactState->nextMXact;
- OldestMemberMXactId[MyProcNumber] = nextMXact;
+ *MyOldestMemberMXactIdSlot() = nextMXact;
LWLockRelease(MultiXactGenLock);
static void
MultiXactIdSetOldestVisible(void)
{
- if (!MultiXactIdIsValid(OldestVisibleMXactId[MyProcNumber]))
+ if (!MultiXactIdIsValid(*MyOldestVisibleMXactIdSlot()))
{
MultiXactId oldestMXact;
int i;
LWLockAcquire(MultiXactGenLock, LW_EXCLUSIVE);
oldestMXact = MultiXactState->nextMXact;
- for (i = 0; i < MaxOldestSlot; i++)
+ for (i = 0; i < NumMemberSlots; i++)
{
MultiXactId thisoldest = OldestMemberMXactId[i];
oldestMXact = thisoldest;
}
- OldestVisibleMXactId[MyProcNumber] = oldestMXact;
+ *MyOldestVisibleMXactIdSlot() = oldestMXact;
LWLockRelease(MultiXactGenLock);
* multi. It cannot possibly still be running.
*/
if (isLockOnly &&
- MultiXactIdPrecedes(multi, OldestVisibleMXactId[MyProcNumber]))
+ MultiXactIdPrecedes(multi, *MyOldestVisibleMXactIdSlot()))
{
debug_elog2(DEBUG2, "GetMembers: a locker-only multi is too old");
*members = NULL;
* We assume that storing a MultiXactId is atomic and so we need not take
* MultiXactGenLock to do this.
*/
- OldestMemberMXactId[MyProcNumber] = InvalidMultiXactId;
- OldestVisibleMXactId[MyProcNumber] = InvalidMultiXactId;
+ *MyOldestMemberMXactIdSlot() = InvalidMultiXactId;
+ *MyOldestVisibleMXactIdSlot() = InvalidMultiXactId;
/*
* Discard the local MultiXactId cache. Since MXactContext was created as
void
AtPrepare_MultiXact(void)
{
- MultiXactId myOldestMember = OldestMemberMXactId[MyProcNumber];
+ MultiXactId myOldestMember = *MyOldestMemberMXactIdSlot();
if (MultiXactIdIsValid(myOldestMember))
RegisterTwoPhaseRecord(TWOPHASE_RM_MULTIXACT_ID, 0,
* Transfer our OldestMemberMXactId value to the slot reserved for the
* prepared transaction.
*/
- myOldestMember = OldestMemberMXactId[MyProcNumber];
+ myOldestMember = *MyOldestMemberMXactIdSlot();
if (MultiXactIdIsValid(myOldestMember))
{
ProcNumber dummyProcNumber = TwoPhaseGetDummyProcNumber(fxid, false);
*/
LWLockAcquire(MultiXactGenLock, LW_EXCLUSIVE);
- OldestMemberMXactId[dummyProcNumber] = myOldestMember;
- OldestMemberMXactId[MyProcNumber] = InvalidMultiXactId;
+ *PreparedXactOldestMemberMXactIdSlot(dummyProcNumber) = myOldestMember;
+ *MyOldestMemberMXactIdSlot() = InvalidMultiXactId;
LWLockRelease(MultiXactGenLock);
}
* We assume that storing a MultiXactId is atomic and so we need not take
* MultiXactGenLock to do this.
*/
- OldestVisibleMXactId[MyProcNumber] = InvalidMultiXactId;
+ *MyOldestVisibleMXactIdSlot() = InvalidMultiXactId;
/*
* Discard the local MultiXactId cache like in AtEOXact_MultiXact.
Assert(len == sizeof(MultiXactId));
oldestMember = *((MultiXactId *) recdata);
- OldestMemberMXactId[dummyProcNumber] = oldestMember;
+ *PreparedXactOldestMemberMXactIdSlot(dummyProcNumber) = oldestMember;
}
/*
Assert(len == sizeof(MultiXactId));
- OldestMemberMXactId[dummyProcNumber] = InvalidMultiXactId;
+ *PreparedXactOldestMemberMXactIdSlot(dummyProcNumber) = InvalidMultiXactId;
}
/*
}
/*
- * Initialization of shared memory for MultiXact. We use two SLRU areas,
- * thus double memory. Also, reserve space for the shared MultiXactState
- * struct and the per-backend MultiXactId arrays (two of those, too).
+ * Initialization of shared memory for MultiXact.
+ *
+ * MultiXactSharedStateShmemSize() calculates the size of the MultiXactState
+ * struct, and the two per-backend MultiXactId arrays. They are carved out of
+ * the same allocation. MultiXactShmemSize() additionally includes the memory
+ * needed for the two SLRU areas.
*/
+static Size
+MultiXactSharedStateShmemSize(void)
+{
+ Size size;
+
+ size = offsetof(MultiXactStateData, perBackendXactIds);
+ size = add_size(size,
+ mul_size(sizeof(MultiXactId), NumMemberSlots));
+ size = add_size(size,
+ mul_size(sizeof(MultiXactId), NumVisibleSlots));
+ return size;
+}
+
Size
MultiXactShmemSize(void)
{
Size size;
- /* We need 2*MaxOldestSlot perBackendXactIds[] entries */
-#define SHARED_MULTIXACT_STATE_SIZE \
- add_size(offsetof(MultiXactStateData, perBackendXactIds), \
- mul_size(sizeof(MultiXactId) * 2, MaxOldestSlot))
-
- size = SHARED_MULTIXACT_STATE_SIZE;
+ size = MultiXactSharedStateShmemSize();
size = add_size(size, SimpleLruShmemSize(multixact_offset_buffers, 0));
size = add_size(size, SimpleLruShmemSize(multixact_member_buffers, 0));
/* Initialize our shared state struct */
MultiXactState = ShmemInitStruct("Shared MultiXact State",
- SHARED_MULTIXACT_STATE_SIZE,
+ MultiXactSharedStateShmemSize(),
&found);
if (!IsUnderPostmaster)
{
Assert(!found);
/* Make sure we zero out the per-backend state */
- MemSet(MultiXactState, 0, SHARED_MULTIXACT_STATE_SIZE);
+ MemSet(MultiXactState, 0, MultiXactSharedStateShmemSize());
}
else
Assert(found);
* Set up array pointers.
*/
OldestMemberMXactId = MultiXactState->perBackendXactIds;
- OldestVisibleMXactId = OldestMemberMXactId + MaxOldestSlot;
+ OldestVisibleMXactId = OldestMemberMXactId + NumMemberSlots;
}
/*
GetOldestMultiXactId(void)
{
MultiXactId oldestMXact;
- int i;
/*
* This is the oldest valid value among all the OldestMemberMXactId[] and
*/
LWLockAcquire(MultiXactGenLock, LW_SHARED);
oldestMXact = MultiXactState->nextMXact;
- for (i = 0; i < MaxOldestSlot; i++)
+ for (int i = 0; i < NumMemberSlots; i++)
{
MultiXactId thisoldest;
if (MultiXactIdIsValid(thisoldest) &&
MultiXactIdPrecedes(thisoldest, oldestMXact))
oldestMXact = thisoldest;
+ }
+ for (int i = 0; i < NumVisibleSlots; i++)
+ {
+ MultiXactId thisoldest;
+
thisoldest = OldestVisibleMXactId[i];
if (MultiXactIdIsValid(thisoldest) &&
MultiXactIdPrecedes(thisoldest, oldestMXact))