Header And Logo

PostgreSQL
| The world's most advanced open source database.

Data Structures | Defines | Typedefs | Functions | Variables

multixact.c File Reference

#include "postgres.h"
#include "access/multixact.h"
#include "access/slru.h"
#include "access/transam.h"
#include "access/twophase.h"
#include "access/twophase_rmgr.h"
#include "access/xact.h"
#include "catalog/pg_type.h"
#include "commands/dbcommands.h"
#include "funcapi.h"
#include "miscadmin.h"
#include "pg_trace.h"
#include "storage/lmgr.h"
#include "storage/pmsignal.h"
#include "storage/procarray.h"
#include "utils/builtins.h"
#include "utils/memutils.h"
#include "utils/snapmgr.h"
Include dependency graph for multixact.c:

Go to the source code of this file.

Data Structures

struct  MultiXactStateData
struct  mXactCacheEnt
struct  mxtruncinfo

Defines

#define MULTIXACT_OFFSETS_PER_PAGE   (BLCKSZ / sizeof(MultiXactOffset))
#define MultiXactIdToOffsetPage(xid)   ((xid) / (MultiXactOffset) MULTIXACT_OFFSETS_PER_PAGE)
#define MultiXactIdToOffsetEntry(xid)   ((xid) % (MultiXactOffset) MULTIXACT_OFFSETS_PER_PAGE)
#define MXACT_MEMBER_BITS_PER_XACT   8
#define MXACT_MEMBER_FLAGS_PER_BYTE   1
#define MXACT_MEMBER_XACT_BITMASK   ((1 << MXACT_MEMBER_BITS_PER_XACT) - 1)
#define MULTIXACT_FLAGBYTES_PER_GROUP   4
#define MULTIXACT_MEMBERS_PER_MEMBERGROUP   (MULTIXACT_FLAGBYTES_PER_GROUP * MXACT_MEMBER_FLAGS_PER_BYTE)
#define MULTIXACT_MEMBERGROUP_SIZE   (sizeof(TransactionId) * MULTIXACT_MEMBERS_PER_MEMBERGROUP + MULTIXACT_FLAGBYTES_PER_GROUP)
#define MULTIXACT_MEMBERGROUPS_PER_PAGE   (BLCKSZ / MULTIXACT_MEMBERGROUP_SIZE)
#define MULTIXACT_MEMBERS_PER_PAGE   (MULTIXACT_MEMBERGROUPS_PER_PAGE * MULTIXACT_MEMBERS_PER_MEMBERGROUP)
#define MXOffsetToMemberPage(xid)   ((xid) / (TransactionId) MULTIXACT_MEMBERS_PER_PAGE)
#define MXOffsetToFlagsOffset(xid)
#define MXOffsetToFlagsBitShift(xid)
#define MXOffsetToMemberOffset(xid)
#define MultiXactOffsetCtl   (&MultiXactOffsetCtlData)
#define MultiXactMemberCtl   (&MultiXactMemberCtlData)
#define MaxOldestSlot   (MaxBackends + max_prepared_xacts)
#define debug_elog2(a, b)
#define debug_elog3(a, b, c)
#define debug_elog4(a, b, c, d)
#define debug_elog5(a, b, c, d, e)
#define debug_elog6(a, b, c, d, e, f)
#define SHARED_MULTIXACT_STATE_SIZE

Typedefs

typedef struct MultiXactStateData MultiXactStateData
typedef struct mXactCacheEnt mXactCacheEnt
typedef struct mxtruncinfo mxtruncinfo

Functions

static void MultiXactIdSetOldestVisible (void)
static MultiXactId CreateMultiXactId (int nmembers, MultiXactMember *members)
static void RecordNewMultiXact (MultiXactId multi, MultiXactOffset offset, int nmembers, MultiXactMember *members)
static MultiXactId GetNewMultiXactId (int nmembers, MultiXactOffset *offset)
static int mxactMemberComparator (const void *arg1, const void *arg2)
static MultiXactId mXactCacheGetBySet (int nmembers, MultiXactMember *members)
static int mXactCacheGetById (MultiXactId multi, MultiXactMember **members)
static void mXactCachePut (MultiXactId multi, int nmembers, MultiXactMember *members)
static char * mxstatus_to_string (MultiXactStatus status)
static int ZeroMultiXactOffsetPage (int pageno, bool writeXlog)
static int ZeroMultiXactMemberPage (int pageno, bool writeXlog)
static bool MultiXactOffsetPagePrecedes (int page1, int page2)
static bool MultiXactMemberPagePrecedes (int page1, int page2)
static bool MultiXactOffsetPrecedes (MultiXactOffset offset1, MultiXactOffset offset2)
static void ExtendMultiXactOffset (MultiXactId multi)
static void ExtendMultiXactMember (MultiXactOffset offset, int nmembers)
static void WriteMZeroPageXlogRec (int pageno, uint8 info)
MultiXactId MultiXactIdCreate (TransactionId xid1, MultiXactStatus status1, TransactionId xid2, MultiXactStatus status2)
MultiXactId MultiXactIdExpand (MultiXactId multi, TransactionId xid, MultiXactStatus status)
bool MultiXactIdIsRunning (MultiXactId multi)
void MultiXactIdSetOldestMember (void)
MultiXactId ReadNextMultiXactId (void)
int GetMultiXactIdMembers (MultiXactId multi, MultiXactMember **members, bool allow_old)
char * mxid_to_string (MultiXactId multi, int nmembers, MultiXactMember *members)
void AtEOXact_MultiXact (void)
void AtPrepare_MultiXact (void)
void PostPrepare_MultiXact (TransactionId xid)
void multixact_twophase_recover (TransactionId xid, uint16 info, void *recdata, uint32 len)
void multixact_twophase_postcommit (TransactionId xid, uint16 info, void *recdata, uint32 len)
void multixact_twophase_postabort (TransactionId xid, uint16 info, void *recdata, uint32 len)
Size MultiXactShmemSize (void)
void MultiXactShmemInit (void)
void BootStrapMultiXact (void)
void StartupMultiXact (void)
void ShutdownMultiXact (void)
void MultiXactGetCheckptMulti (bool is_shutdown, MultiXactId *nextMulti, MultiXactOffset *nextMultiOffset, MultiXactId *oldestMulti, Oid *oldestMultiDB)
void CheckPointMultiXact (void)
void MultiXactSetNextMXact (MultiXactId nextMulti, MultiXactOffset nextMultiOffset)
void SetMultiXactIdLimit (MultiXactId oldest_datminmxid, Oid oldest_datoid)
void MultiXactAdvanceNextMXact (MultiXactId minMulti, MultiXactOffset minMultiOffset)
void MultiXactAdvanceOldest (MultiXactId oldestMulti, Oid oldestMultiDB)
MultiXactId GetOldestMultiXactId (void)
static bool SlruScanDirCbFindEarliest (SlruCtl ctl, char *filename, int segpage, void *data)
void TruncateMultiXact (MultiXactId oldestMXact)
bool MultiXactIdPrecedes (MultiXactId multi1, MultiXactId multi2)
void multixact_redo (XLogRecPtr lsn, XLogRecord *record)
Datum pg_get_multixact_members (PG_FUNCTION_ARGS)

Variables

static SlruCtlData MultiXactOffsetCtlData
static SlruCtlData MultiXactMemberCtlData
static MultiXactStateDataMultiXactState
static MultiXactIdOldestMemberMXactId
static MultiXactIdOldestVisibleMXactId
static mXactCacheEntMXactCache = NULL
static MemoryContext MXactContext = NULL

Define Documentation

#define debug_elog2 (   a,
  b 
)
#define debug_elog3 (   a,
  b,
  c 
)
#define debug_elog4 (   a,
  b,
  c,
  d 
)
#define debug_elog5 (   a,
  b,
  c,
  d,
  e 
)

Definition at line 282 of file multixact.c.

Referenced by MultiXactIdExpand().

#define debug_elog6 (   a,
  b,
  c,
  d,
  e,
  f 
)

Definition at line 283 of file multixact.c.

Referenced by MultiXactGetCheckptMulti().

#define MaxOldestSlot   (MaxBackends + max_prepared_xacts)

Definition at line 236 of file multixact.c.

#define MULTIXACT_FLAGBYTES_PER_GROUP   4

Definition at line 124 of file multixact.c.

#define MULTIXACT_MEMBERGROUP_SIZE   (sizeof(TransactionId) * MULTIXACT_MEMBERS_PER_MEMBERGROUP + MULTIXACT_FLAGBYTES_PER_GROUP)

Definition at line 128 of file multixact.c.

#define MULTIXACT_MEMBERGROUPS_PER_PAGE   (BLCKSZ / MULTIXACT_MEMBERGROUP_SIZE)

Definition at line 130 of file multixact.c.

#define MULTIXACT_MEMBERS_PER_MEMBERGROUP   (MULTIXACT_FLAGBYTES_PER_GROUP * MXACT_MEMBER_FLAGS_PER_BYTE)

Definition at line 125 of file multixact.c.

#define MULTIXACT_MEMBERS_PER_PAGE   (MULTIXACT_MEMBERGROUPS_PER_PAGE * MULTIXACT_MEMBERS_PER_MEMBERGROUP)

Definition at line 131 of file multixact.c.

Referenced by ExtendMultiXactMember().

#define MULTIXACT_OFFSETS_PER_PAGE   (BLCKSZ / sizeof(MultiXactOffset))

Definition at line 99 of file multixact.c.

#define MultiXactIdToOffsetEntry (   xid  )     ((xid) % (MultiXactOffset) MULTIXACT_OFFSETS_PER_PAGE)
#define MultiXactIdToOffsetPage (   xid  )     ((xid) / (MultiXactOffset) MULTIXACT_OFFSETS_PER_PAGE)
#define MultiXactMemberCtl   (&MultiXactMemberCtlData)
#define MultiXactOffsetCtl   (&MultiXactOffsetCtlData)
#define MXACT_MEMBER_BITS_PER_XACT   8

Definition at line 119 of file multixact.c.

#define MXACT_MEMBER_FLAGS_PER_BYTE   1

Definition at line 120 of file multixact.c.

#define MXACT_MEMBER_XACT_BITMASK   ((1 << MXACT_MEMBER_BITS_PER_XACT) - 1)

Definition at line 121 of file multixact.c.

Referenced by GetMultiXactIdMembers().

#define MXOffsetToFlagsBitShift (   xid  ) 
Value:
(((xid) % (TransactionId) MULTIXACT_MEMBERS_PER_MEMBERGROUP) * \
     MXACT_MEMBER_BITS_PER_XACT)

Definition at line 142 of file multixact.c.

Referenced by ExtendMultiXactMember(), GetMultiXactIdMembers(), and RecordNewMultiXact().

#define MXOffsetToFlagsOffset (   xid  ) 
Value:
((((xid) / (TransactionId) MULTIXACT_MEMBERS_PER_MEMBERGROUP) % \
      (TransactionId) MULTIXACT_MEMBERGROUPS_PER_PAGE) * \
     (TransactionId) MULTIXACT_MEMBERGROUP_SIZE)

Definition at line 138 of file multixact.c.

Referenced by ExtendMultiXactMember(), GetMultiXactIdMembers(), RecordNewMultiXact(), and StartupMultiXact().

#define MXOffsetToMemberOffset (   xid  ) 
Value:

Definition at line 147 of file multixact.c.

Referenced by GetMultiXactIdMembers(), RecordNewMultiXact(), and StartupMultiXact().

#define MXOffsetToMemberPage (   xid  )     ((xid) / (TransactionId) MULTIXACT_MEMBERS_PER_PAGE)
#define SHARED_MULTIXACT_STATE_SIZE
Value:

Referenced by MultiXactShmemInit().


Typedef Documentation

typedef struct mXactCacheEnt mXactCacheEnt
typedef struct mxtruncinfo mxtruncinfo

Function Documentation

void AtEOXact_MultiXact ( void   ) 

Definition at line 1455 of file multixact.c.

References MyBackendId, OldestMemberMXactId, and OldestVisibleMXactId.

Referenced by AbortTransaction(), and CommitTransaction().

{
    /*
     * Reset our OldestMemberMXactId and OldestVisibleMXactId values, both of
     * which should only be valid while within a transaction.
     *
     * We assume that storing a MultiXactId is atomic and so we need not take
     * MultiXactGenLock to do this.
     */
    OldestMemberMXactId[MyBackendId] = InvalidMultiXactId;
    OldestVisibleMXactId[MyBackendId] = InvalidMultiXactId;

    /*
     * Discard the local MultiXactId cache.  Since MXactContext was created as
     * a child of TopTransactionContext, we needn't delete it explicitly.
     */
    MXactContext = NULL;
    MXactCache = NULL;
}

void AtPrepare_MultiXact ( void   ) 
void BootStrapMultiXact ( void   ) 

Definition at line 1654 of file multixact.c.

References Assert, LW_EXCLUSIVE, LWLockAcquire(), LWLockRelease(), MultiXactMemberControlLock, MultiXactMemberCtl, MultiXactOffsetControlLock, MultiXactOffsetCtl, SimpleLruWritePage(), ZeroMultiXactMemberPage(), and ZeroMultiXactOffsetPage().

Referenced by BootStrapXLOG().

{
    int         slotno;

    LWLockAcquire(MultiXactOffsetControlLock, LW_EXCLUSIVE);

    /* Create and zero the first page of the offsets log */
    slotno = ZeroMultiXactOffsetPage(0, false);

    /* Make sure it's written out */
    SimpleLruWritePage(MultiXactOffsetCtl, slotno);
    Assert(!MultiXactOffsetCtl->shared->page_dirty[slotno]);

    LWLockRelease(MultiXactOffsetControlLock);

    LWLockAcquire(MultiXactMemberControlLock, LW_EXCLUSIVE);

    /* Create and zero the first page of the members log */
    slotno = ZeroMultiXactMemberPage(0, false);

    /* Make sure it's written out */
    SimpleLruWritePage(MultiXactMemberCtl, slotno);
    Assert(!MultiXactMemberCtl->shared->page_dirty[slotno]);

    LWLockRelease(MultiXactMemberControlLock);
}

void CheckPointMultiXact ( void   ) 

Definition at line 1847 of file multixact.c.

References MultiXactMemberCtl, MultiXactOffsetCtl, and SimpleLruFlush().

Referenced by CheckPointGuts().

{
    TRACE_POSTGRESQL_MULTIXACT_CHECKPOINT_START(true);

    /* Flush dirty MultiXact pages to disk */
    SimpleLruFlush(MultiXactOffsetCtl, true);
    SimpleLruFlush(MultiXactMemberCtl, true);

    TRACE_POSTGRESQL_MULTIXACT_CHECKPOINT_DONE(true);
}

static MultiXactId CreateMultiXactId ( int  nmembers,
MultiXactMember members 
) [static]

Definition at line 672 of file multixact.c.

References XLogRecData::buffer, XLogRecData::data, DEBUG2, debug_elog2, debug_elog3, END_CRIT_SECTION, GetNewMultiXactId(), InvalidMultiXactId, XLogRecData::len, xl_multixact_create::mid, xl_multixact_create::moff, mXactCacheEnt::multi, MultiXactIdIsValid, mXactCacheGetBySet(), mXactCachePut(), mxid_to_string(), XLogRecData::next, xl_multixact_create::nmembers, RecordNewMultiXact(), XLOG_MULTIXACT_CREATE_ID, and XLogInsert().

Referenced by MultiXactIdCreate(), and MultiXactIdExpand().

{
    MultiXactId multi;
    MultiXactOffset offset;
    XLogRecData rdata[2];
    xl_multixact_create xlrec;

    debug_elog3(DEBUG2, "Create: %s",
                mxid_to_string(InvalidMultiXactId, nmembers, members));

    /*
     * See if the same set of members already exists in our cache; if so, just
     * re-use that MultiXactId.  (Note: it might seem that looking in our
     * cache is insufficient, and we ought to search disk to see if a
     * duplicate definition already exists.  But since we only ever create
     * MultiXacts containing our own XID, in most cases any such MultiXacts
     * were in fact created by us, and so will be in our cache.  There are
     * corner cases where someone else added us to a MultiXact without our
     * knowledge, but it's not worth checking for.)
     */
    multi = mXactCacheGetBySet(nmembers, members);
    if (MultiXactIdIsValid(multi))
    {
        debug_elog2(DEBUG2, "Create: in cache!");
        return multi;
    }

    /*
     * Assign the MXID and offsets range to use, and make sure there is space
     * in the OFFSETs and MEMBERs files.  NB: this routine does
     * START_CRIT_SECTION().
     */
    multi = GetNewMultiXactId(nmembers, &offset);

    /*
     * Make an XLOG entry describing the new MXID.
     *
     * Note: we need not flush this XLOG entry to disk before proceeding. The
     * only way for the MXID to be referenced from any data page is for
     * heap_lock_tuple() to have put it there, and heap_lock_tuple() generates
     * an XLOG record that must follow ours.  The normal LSN interlock between
     * the data page and that XLOG record will ensure that our XLOG record
     * reaches disk first.  If the SLRU members/offsets data reaches disk
     * sooner than the XLOG record, we do not care because we'll overwrite it
     * with zeroes unless the XLOG record is there too; see notes at top of
     * this file.
     */
    xlrec.mid = multi;
    xlrec.moff = offset;
    xlrec.nmembers = nmembers;

    /*
     * XXX Note: there's a lot of padding space in MultiXactMember.  We could
     * find a more compact representation of this Xlog record -- perhaps all the
     * status flags in one XLogRecData, then all the xids in another one?  Not
     * clear that it's worth the trouble though.
     */
    rdata[0].data = (char *) (&xlrec);
    rdata[0].len = SizeOfMultiXactCreate;
    rdata[0].buffer = InvalidBuffer;
    rdata[0].next = &(rdata[1]);

    rdata[1].data = (char *) members;
    rdata[1].len = nmembers * sizeof(MultiXactMember);
    rdata[1].buffer = InvalidBuffer;
    rdata[1].next = NULL;

    (void) XLogInsert(RM_MULTIXACT_ID, XLOG_MULTIXACT_CREATE_ID, rdata);

    /* Now enter the information into the OFFSETs and MEMBERs logs */
    RecordNewMultiXact(multi, offset, nmembers, members);

    /* Done with critical section */
    END_CRIT_SECTION();

    /* Store the new MultiXactId in the local cache, too */
    mXactCachePut(multi, nmembers, members);

    debug_elog2(DEBUG2, "Create: all done");

    return multi;
}

static void ExtendMultiXactMember ( MultiXactOffset  offset,
int  nmembers 
) [static]

Definition at line 2076 of file multixact.c.

References LW_EXCLUSIVE, LWLockAcquire(), LWLockRelease(), MULTIXACT_MEMBERS_PER_PAGE, MultiXactMemberControlLock, MXOffsetToFlagsBitShift, MXOffsetToFlagsOffset, MXOffsetToMemberPage, and ZeroMultiXactMemberPage().

Referenced by GetNewMultiXactId().

{
    /*
     * It's possible that the members span more than one page of the members
     * file, so we loop to ensure we consider each page.  The coding is not
     * optimal if the members span several pages, but that seems unusual
     * enough to not worry much about.
     */
    while (nmembers > 0)
    {
        int         flagsoff;
        int         flagsbit;
        int         difference;

        /*
         * Only zero when at first entry of a page.
         */
        flagsoff = MXOffsetToFlagsOffset(offset);
        flagsbit = MXOffsetToFlagsBitShift(offset);
        if (flagsoff == 0 && flagsbit == 0)
        {
            int         pageno;

            pageno = MXOffsetToMemberPage(offset);

            LWLockAcquire(MultiXactMemberControlLock, LW_EXCLUSIVE);

            /* Zero the page and make an XLOG entry about it */
            ZeroMultiXactMemberPage(pageno, true);

            LWLockRelease(MultiXactMemberControlLock);
        }

        /* Advance to next page (OK if nmembers goes negative) */
        difference = MULTIXACT_MEMBERS_PER_PAGE - offset % MULTIXACT_MEMBERS_PER_PAGE;
        offset += difference;
        nmembers -= difference;
    }
}

static void ExtendMultiXactOffset ( MultiXactId  multi  )  [static]

Definition at line 2046 of file multixact.c.

References FirstMultiXactId, LW_EXCLUSIVE, LWLockAcquire(), LWLockRelease(), MultiXactIdToOffsetEntry, MultiXactIdToOffsetPage, MultiXactOffsetControlLock, and ZeroMultiXactOffsetPage().

Referenced by GetNewMultiXactId().

{
    int         pageno;

    /*
     * No work except at first MultiXactId of a page.  But beware: just after
     * wraparound, the first MultiXactId of page zero is FirstMultiXactId.
     */
    if (MultiXactIdToOffsetEntry(multi) != 0 &&
        multi != FirstMultiXactId)
        return;

    pageno = MultiXactIdToOffsetPage(multi);

    LWLockAcquire(MultiXactOffsetControlLock, LW_EXCLUSIVE);

    /* Zero the page and make an XLOG entry about it */
    ZeroMultiXactOffsetPage(pageno, true);

    LWLockRelease(MultiXactOffsetControlLock);
}

int GetMultiXactIdMembers ( MultiXactId  multi,
MultiXactMember **  members,
bool  allow_old 
)

Definition at line 1030 of file multixact.c.

References Assert, DEBUG1, DEBUG2, debug_elog3, ereport, errcode(), errmsg(), ERROR, FirstMultiXactId, i, LW_EXCLUSIVE, LW_SHARED, LWLockAcquire(), LWLockRelease(), MultiXactGenLock, MultiXactIdIsValid, MultiXactIdPrecedes(), MultiXactIdSetOldestVisible(), MultiXactIdToOffsetEntry, MultiXactIdToOffsetPage, MultiXactMemberControlLock, MultiXactMemberCtl, MultiXactOffsetControlLock, MultiXactOffsetCtl, MXACT_MEMBER_XACT_BITMASK, mXactCacheGetById(), mXactCachePut(), mxid_to_string(), MXOffsetToFlagsBitShift, MXOffsetToFlagsOffset, MXOffsetToMemberOffset, MXOffsetToMemberPage, MultiXactStateData::nextMXact, MultiXactStateData::nextOffset, MultiXactStateData::oldestMultiXactId, palloc(), pg_usleep(), SimpleLruReadPage(), MultiXactMember::status, TransactionIdIsValid, and MultiXactMember::xid.

Referenced by Do_MultiXactIdWait(), GetMultiXactIdHintBits(), heap_lock_tuple(), MultiXactIdExpand(), MultiXactIdGetUpdateXid(), MultiXactIdIsRunning(), pg_get_multixact_members(), and pgrowlocks().

{
    int         pageno;
    int         prev_pageno;
    int         entryno;
    int         slotno;
    MultiXactOffset *offptr;
    MultiXactOffset offset;
    int         length;
    int         truelength;
    int         i;
    MultiXactId oldestMXact;
    MultiXactId nextMXact;
    MultiXactId tmpMXact;
    MultiXactOffset nextOffset;
    MultiXactMember *ptr;

    debug_elog3(DEBUG2, "GetMembers: asked for %u", multi);

    Assert(MultiXactIdIsValid(multi));

    /* See if the MultiXactId is in the local cache */
    length = mXactCacheGetById(multi, members);
    if (length >= 0)
    {
        debug_elog3(DEBUG2, "GetMembers: found %s in the cache",
                    mxid_to_string(multi, length, *members));
        return length;
    }

    /* Set our OldestVisibleMXactId[] entry if we didn't already */
    MultiXactIdSetOldestVisible();

    /*
     * We check known limits on MultiXact before resorting to the SLRU area.
     *
     * An ID older than MultiXactState->oldestMultiXactId cannot possibly be
     * useful; it should have already been frozen by vacuum.  We've truncated
     * the on-disk structures anyway.  Returning the wrong values could lead to
     * an incorrect visibility result.  However, to support pg_upgrade we need
     * to allow an empty set to be returned regardless, if the caller is
     * willing to accept it; the caller is expected to check that it's an
     * allowed condition (such as ensuring that the infomask bits set on the
     * tuple are consistent with the pg_upgrade scenario).  If the caller is
     * expecting this to be called only on recently created multis, then we
     * raise an error.
     *
     * Conversely, an ID >= nextMXact shouldn't ever be seen here; if it is
     * seen, it implies undetected ID wraparound has occurred.  This raises
     * a hard error.
     *
     * Shared lock is enough here since we aren't modifying any global state.
     * Acquire it just long enough to grab the current counter values.  We may
     * need both nextMXact and nextOffset; see below.
     */
    LWLockAcquire(MultiXactGenLock, LW_SHARED);

    oldestMXact = MultiXactState->oldestMultiXactId;
    nextMXact = MultiXactState->nextMXact;
    nextOffset = MultiXactState->nextOffset;

    LWLockRelease(MultiXactGenLock);

    if (MultiXactIdPrecedes(multi, oldestMXact))
    {
        ereport(allow_old ? DEBUG1 : ERROR,
                (errcode(ERRCODE_INTERNAL_ERROR),
                 errmsg("MultiXactId %u does no longer exist -- apparent wraparound",
                        multi)));
        return -1;
    }

    if (!MultiXactIdPrecedes(multi, nextMXact))
        ereport(ERROR,
                (errcode(ERRCODE_INTERNAL_ERROR),
                 errmsg("MultiXactId %u has not been created yet -- apparent wraparound",
                        multi)));

    /*
     * Find out the offset at which we need to start reading MultiXactMembers
     * and the number of members in the multixact.  We determine the latter as
     * the difference between this multixact's starting offset and the next
     * one's.  However, there are some corner cases to worry about:
     *
     * 1. This multixact may be the latest one created, in which case there is
     * no next one to look at.  In this case the nextOffset value we just
     * saved is the correct endpoint.
     *
     * 2. The next multixact may still be in process of being filled in: that
     * is, another process may have done GetNewMultiXactId but not yet written
     * the offset entry for that ID.  In that scenario, it is guaranteed that
     * the offset entry for that multixact exists (because GetNewMultiXactId
     * won't release MultiXactGenLock until it does) but contains zero
     * (because we are careful to pre-zero offset pages). Because
     * GetNewMultiXactId will never return zero as the starting offset for a
     * multixact, when we read zero as the next multixact's offset, we know we
     * have this case.  We sleep for a bit and try again.
     *
     * 3. Because GetNewMultiXactId increments offset zero to offset one to
     * handle case #2, there is an ambiguity near the point of offset
     * wraparound.  If we see next multixact's offset is one, is that our
     * multixact's actual endpoint, or did it end at zero with a subsequent
     * increment?  We handle this using the knowledge that if the zero'th
     * member slot wasn't filled, it'll contain zero, and zero isn't a valid
     * transaction ID so it can't be a multixact member.  Therefore, if we
     * read a zero from the members array, just ignore it.
     *
     * This is all pretty messy, but the mess occurs only in infrequent corner
     * cases, so it seems better than holding the MultiXactGenLock for a long
     * time on every multixact creation.
     */
retry:
    LWLockAcquire(MultiXactOffsetControlLock, LW_EXCLUSIVE);

    pageno = MultiXactIdToOffsetPage(multi);
    entryno = MultiXactIdToOffsetEntry(multi);

    slotno = SimpleLruReadPage(MultiXactOffsetCtl, pageno, true, multi);
    offptr = (MultiXactOffset *) MultiXactOffsetCtl->shared->page_buffer[slotno];
    offptr += entryno;
    offset = *offptr;

    Assert(offset != 0);

    /*
     * Use the same increment rule as GetNewMultiXactId(), that is, don't
     * handle wraparound explicitly until needed.
     */
    tmpMXact = multi + 1;

    if (nextMXact == tmpMXact)
    {
        /* Corner case 1: there is no next multixact */
        length = nextOffset - offset;
    }
    else
    {
        MultiXactOffset nextMXOffset;

        /* handle wraparound if needed */
        if (tmpMXact < FirstMultiXactId)
            tmpMXact = FirstMultiXactId;

        prev_pageno = pageno;

        pageno = MultiXactIdToOffsetPage(tmpMXact);
        entryno = MultiXactIdToOffsetEntry(tmpMXact);

        if (pageno != prev_pageno)
            slotno = SimpleLruReadPage(MultiXactOffsetCtl, pageno, true, tmpMXact);

        offptr = (MultiXactOffset *) MultiXactOffsetCtl->shared->page_buffer[slotno];
        offptr += entryno;
        nextMXOffset = *offptr;

        if (nextMXOffset == 0)
        {
            /* Corner case 2: next multixact is still being filled in */
            LWLockRelease(MultiXactOffsetControlLock);
            pg_usleep(1000L);
            goto retry;
        }

        length = nextMXOffset - offset;
    }

    LWLockRelease(MultiXactOffsetControlLock);

    ptr = (MultiXactMember *) palloc(length * sizeof(MultiXactMember));
    *members = ptr;

    /* Now get the members themselves. */
    LWLockAcquire(MultiXactMemberControlLock, LW_EXCLUSIVE);

    truelength = 0;
    prev_pageno = -1;
    for (i = 0; i < length; i++, offset++)
    {
        TransactionId *xactptr;
        uint32     *flagsptr;
        int         flagsoff;
        int         bshift;
        int         memberoff;

        pageno = MXOffsetToMemberPage(offset);
        memberoff = MXOffsetToMemberOffset(offset);

        if (pageno != prev_pageno)
        {
            slotno = SimpleLruReadPage(MultiXactMemberCtl, pageno, true, multi);
            prev_pageno = pageno;
        }

        xactptr = (TransactionId *)
            (MultiXactMemberCtl->shared->page_buffer[slotno] + memberoff);

        if (!TransactionIdIsValid(*xactptr))
        {
            /* Corner case 3: we must be looking at unused slot zero */
            Assert(offset == 0);
            continue;
        }

        flagsoff = MXOffsetToFlagsOffset(offset);
        bshift = MXOffsetToFlagsBitShift(offset);
        flagsptr = (uint32 *) (MultiXactMemberCtl->shared->page_buffer[slotno] + flagsoff);

        ptr[truelength].xid = *xactptr;
        ptr[truelength].status = (*flagsptr >> bshift) & MXACT_MEMBER_XACT_BITMASK;
        truelength++;
    }

    LWLockRelease(MultiXactMemberControlLock);

    /*
     * Copy the result into the local cache.
     */
    mXactCachePut(multi, truelength, ptr);

    debug_elog3(DEBUG2, "GetMembers: no cache for %s",
                mxid_to_string(multi, truelength, ptr));
    return truelength;
}

static MultiXactId GetNewMultiXactId ( int  nmembers,
MultiXactOffset offset 
) [static]

Definition at line 856 of file multixact.c.

References Assert, DEBUG2, debug_elog3, debug_elog4, elog, ereport, errcode(), errhint(), errmsg(), ERROR, ExtendMultiXactMember(), ExtendMultiXactOffset(), FirstMultiXactId, get_database_name(), IsUnderPostmaster, LW_EXCLUSIVE, LWLockAcquire(), LWLockRelease(), MultiXactStateData::multiStopLimit, MultiXactStateData::multiVacLimit, MultiXactStateData::multiWarnLimit, MultiXactStateData::multiWrapLimit, MultiXactGenLock, MultiXactIdIsValid, MultiXactIdPrecedes(), MyBackendId, MultiXactStateData::nextMXact, MultiXactStateData::nextOffset, OldestMemberMXactId, MultiXactStateData::oldestMultiXactDB, PMSIGNAL_START_AUTOVAC_LAUNCHER, RecoveryInProgress(), SendPostmasterSignal(), START_CRIT_SECTION, and WARNING.

Referenced by CreateMultiXactId().

{
    MultiXactId result;
    MultiXactOffset nextOffset;

    debug_elog3(DEBUG2, "GetNew: for %d xids", nmembers);

    /* MultiXactIdSetOldestMember() must have been called already */
    Assert(MultiXactIdIsValid(OldestMemberMXactId[MyBackendId]));

    /* safety check, we should never get this far in a HS slave */
    if (RecoveryInProgress())
        elog(ERROR, "cannot assign MultiXactIds during recovery");

    LWLockAcquire(MultiXactGenLock, LW_EXCLUSIVE);

    /* Handle wraparound of the nextMXact counter */
    if (MultiXactState->nextMXact < FirstMultiXactId)
        MultiXactState->nextMXact = FirstMultiXactId;

    /* Assign the MXID */
    result = MultiXactState->nextMXact;

    /*----------
     * Check to see if it's safe to assign another MultiXactId.  This protects
     * against catastrophic data loss due to multixact wraparound.  The basic
     * rules are:
     *
     * If we're past multiVacLimit, start trying to force autovacuum cycles.
     * If we're past multiWarnLimit, start issuing warnings.
     * If we're past multiStopLimit, refuse to create new MultiXactIds.
     *
     * Note these are pretty much the same protections in GetNewTransactionId.
     *----------
     */
    if (!MultiXactIdPrecedes(result, MultiXactState->multiVacLimit))
    {
        /*
         * For safety's sake, we release MultiXactGenLock while sending
         * signals, warnings, etc.  This is not so much because we care about
         * preserving concurrency in this situation, as to avoid any
         * possibility of deadlock while doing get_database_name(). First,
         * copy all the shared values we'll need in this path.
         */
        MultiXactId multiWarnLimit = MultiXactState->multiWarnLimit;
        MultiXactId multiStopLimit = MultiXactState->multiStopLimit;
        MultiXactId multiWrapLimit = MultiXactState->multiWrapLimit;
        Oid         oldest_datoid = MultiXactState->oldestMultiXactDB;

        LWLockRelease(MultiXactGenLock);

        /*
         * To avoid swamping the postmaster with signals, we issue the autovac
         * request only once per 64K transaction starts.  This still gives
         * plenty of chances before we get into real trouble.
         */
        if (IsUnderPostmaster && (result % 65536) == 0)
            SendPostmasterSignal(PMSIGNAL_START_AUTOVAC_LAUNCHER);

        if (IsUnderPostmaster &&
            !MultiXactIdPrecedes(result, multiStopLimit))
        {
            char       *oldest_datname = get_database_name(oldest_datoid);

            /* complain even if that DB has disappeared */
            if (oldest_datname)
                ereport(ERROR,
                        (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
                         errmsg("database is not accepting commands that generate new MultiXactIds to avoid wraparound data loss in database \"%s\"",
                                oldest_datname),
                         errhint("Execute a database-wide VACUUM in that database.\n"
                                 "You might also need to commit or roll back old prepared transactions.")));
            else
                ereport(ERROR,
                        (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
                         errmsg("database is not accepting commands that generate new MultiXactIds to avoid wraparound data loss in database with OID %u",
                                oldest_datoid),
                         errhint("Execute a database-wide VACUUM in that database.\n"
                                 "You might also need to commit or roll back old prepared transactions.")));
        }
        else if (!MultiXactIdPrecedes(result, multiWarnLimit))
        {
            char       *oldest_datname = get_database_name(oldest_datoid);

            /* complain even if that DB has disappeared */
            if (oldest_datname)
                ereport(WARNING,
                        (errmsg("database \"%s\" must be vacuumed before %u more MultiXactIds are used",
                                oldest_datname,
                                multiWrapLimit - result),
                         errhint("Execute a database-wide VACUUM in that database.\n"
                                 "You might also need to commit or roll back old prepared transactions.")));
            else
                ereport(WARNING,
                        (errmsg("database with OID %u must be vacuumed before %u more MultiXactIds are used",
                                oldest_datoid,
                                multiWrapLimit - result),
                         errhint("Execute a database-wide VACUUM in that database.\n"
                                 "You might also need to commit or roll back old prepared transactions.")));
        }

        /* Re-acquire lock and start over */
        LWLockAcquire(MultiXactGenLock, LW_EXCLUSIVE);
        result = MultiXactState->nextMXact;
        if (result < FirstMultiXactId)
            result = FirstMultiXactId;
    }

    /* Make sure there is room for the MXID in the file.  */
    ExtendMultiXactOffset(result);

    /*
     * Reserve the members space, similarly to above.  Also, be careful not to
     * return zero as the starting offset for any multixact. See
     * GetMultiXactIdMembers() for motivation.
     */
    nextOffset = MultiXactState->nextOffset;
    if (nextOffset == 0)
    {
        *offset = 1;
        nmembers++;             /* allocate member slot 0 too */
    }
    else
        *offset = nextOffset;

    ExtendMultiXactMember(nextOffset, nmembers);

    /*
     * Critical section from here until caller has written the data into the
     * just-reserved SLRU space; we don't want to error out with a partly
     * written MultiXact structure.  (In particular, failing to write our
     * start offset after advancing nextMXact would effectively corrupt the
     * previous MultiXact.)
     */
    START_CRIT_SECTION();

    /*
     * Advance counters.  As in GetNewTransactionId(), this must not happen
     * until after file extension has succeeded!
     *
     * We don't care about MultiXactId wraparound here; it will be handled by
     * the next iteration.  But note that nextMXact may be InvalidMultiXactId
     * or the first value on a segment-beginning page after this routine exits,
     * so anyone else looking at the variable must be prepared to deal with
     * either case.  Similarly, nextOffset may be zero, but we won't use that
     * as the actual start offset of the next multixact.
     */
    (MultiXactState->nextMXact)++;

    MultiXactState->nextOffset += nmembers;

    LWLockRelease(MultiXactGenLock);

    debug_elog4(DEBUG2, "GetNew: returning %u offset %u", result, *offset);
    return result;
}

MultiXactId GetOldestMultiXactId ( void   ) 

Definition at line 2128 of file multixact.c.

References FirstMultiXactId, i, LW_SHARED, LWLockAcquire(), LWLockRelease(), MultiXactGenLock, MultiXactIdIsValid, MultiXactIdPrecedes(), MultiXactStateData::nextMXact, OldestMemberMXactId, and OldestVisibleMXactId.

Referenced by AddNewRelationTuple(), ExecuteTruncate(), vac_update_datfrozenxid(), and vacuum_set_xid_limits().

{
    MultiXactId     oldestMXact;
    MultiXactId     nextMXact;
    int             i;

    /*
     * This is the oldest valid value among all the OldestMemberMXactId[] and
     * OldestVisibleMXactId[] entries, or nextMXact if none are valid.
     */
    LWLockAcquire(MultiXactGenLock, LW_SHARED);

    /*
     * We have to beware of the possibility that nextMXact is in the
     * wrapped-around state.  We don't fix the counter itself here, but we
     * must be sure to use a valid value in our calculation.
     */
    nextMXact = MultiXactState->nextMXact;
    if (nextMXact < FirstMultiXactId)
        nextMXact = FirstMultiXactId;

    oldestMXact = nextMXact;
    for (i = 1; i <= MaxOldestSlot; i++)
    {
        MultiXactId thisoldest;

        thisoldest = OldestMemberMXactId[i];
        if (MultiXactIdIsValid(thisoldest) &&
            MultiXactIdPrecedes(thisoldest, oldestMXact))
            oldestMXact = thisoldest;
        thisoldest = OldestVisibleMXactId[i];
        if (MultiXactIdIsValid(thisoldest) &&
            MultiXactIdPrecedes(thisoldest, oldestMXact))
            oldestMXact = thisoldest;
    }

    LWLockRelease(MultiXactGenLock);

    return oldestMXact;
}

void multixact_redo ( XLogRecPtr  lsn,
XLogRecord record 
)

Definition at line 2343 of file multixact.c.

References Assert, elog, i, LW_EXCLUSIVE, LWLockAcquire(), LWLockRelease(), xl_multixact_create::members, xl_multixact_create::mid, xl_multixact_create::moff, MultiXactAdvanceNextMXact(), MultiXactMemberControlLock, MultiXactMemberCtl, MultiXactOffsetControlLock, MultiXactOffsetCtl, VariableCacheData::nextXid, xl_multixact_create::nmembers, PANIC, RecordNewMultiXact(), ShmemVariableCache, SimpleLruWritePage(), TransactionIdAdvance, TransactionIdFollowsOrEquals(), TransactionIdPrecedes(), MultiXactMember::xid, XidGenLock, XLogRecord::xl_info, XLogRecord::xl_xid, XLOG_MULTIXACT_CREATE_ID, XLOG_MULTIXACT_ZERO_MEM_PAGE, XLOG_MULTIXACT_ZERO_OFF_PAGE, XLogRecGetData, XLR_BKP_BLOCK_MASK, ZeroMultiXactMemberPage(), and ZeroMultiXactOffsetPage().

{
    uint8       info = record->xl_info & ~XLR_INFO_MASK;

    /* Backup blocks are not used in multixact records */
    Assert(!(record->xl_info & XLR_BKP_BLOCK_MASK));

    if (info == XLOG_MULTIXACT_ZERO_OFF_PAGE)
    {
        int         pageno;
        int         slotno;

        memcpy(&pageno, XLogRecGetData(record), sizeof(int));

        LWLockAcquire(MultiXactOffsetControlLock, LW_EXCLUSIVE);

        slotno = ZeroMultiXactOffsetPage(pageno, false);
        SimpleLruWritePage(MultiXactOffsetCtl, slotno);
        Assert(!MultiXactOffsetCtl->shared->page_dirty[slotno]);

        LWLockRelease(MultiXactOffsetControlLock);
    }
    else if (info == XLOG_MULTIXACT_ZERO_MEM_PAGE)
    {
        int         pageno;
        int         slotno;

        memcpy(&pageno, XLogRecGetData(record), sizeof(int));

        LWLockAcquire(MultiXactMemberControlLock, LW_EXCLUSIVE);

        slotno = ZeroMultiXactMemberPage(pageno, false);
        SimpleLruWritePage(MultiXactMemberCtl, slotno);
        Assert(!MultiXactMemberCtl->shared->page_dirty[slotno]);

        LWLockRelease(MultiXactMemberControlLock);
    }
    else if (info == XLOG_MULTIXACT_CREATE_ID)
    {
        xl_multixact_create *xlrec =
            (xl_multixact_create *) XLogRecGetData(record);
        TransactionId max_xid;
        int         i;

        /* Store the data back into the SLRU files */
        RecordNewMultiXact(xlrec->mid, xlrec->moff, xlrec->nmembers,
                           xlrec->members);

        /* Make sure nextMXact/nextOffset are beyond what this record has */
        MultiXactAdvanceNextMXact(xlrec->mid + 1,
                                  xlrec->moff + xlrec->nmembers);

        /*
         * Make sure nextXid is beyond any XID mentioned in the record. This
         * should be unnecessary, since any XID found here ought to have other
         * evidence in the XLOG, but let's be safe.
         */
        max_xid = record->xl_xid;
        for (i = 0; i < xlrec->nmembers; i++)
        {
            if (TransactionIdPrecedes(max_xid, xlrec->members[i].xid))
                max_xid = xlrec->members[i].xid;
        }

        /*
         * We don't expect anyone else to modify nextXid, hence startup
         * process doesn't need to hold a lock while checking this. We still
         * acquire the lock to modify it, though.
         */
        if (TransactionIdFollowsOrEquals(max_xid,
                                         ShmemVariableCache->nextXid))
        {
            LWLockAcquire(XidGenLock, LW_EXCLUSIVE);
            ShmemVariableCache->nextXid = max_xid;
            TransactionIdAdvance(ShmemVariableCache->nextXid);
            LWLockRelease(XidGenLock);
        }
    }
    else
        elog(PANIC, "multixact_redo: unknown op code %u", info);
}

void multixact_twophase_postabort ( TransactionId  xid,
uint16  info,
void *  recdata,
uint32  len 
)

Definition at line 1582 of file multixact.c.

References multixact_twophase_postcommit().

{
    multixact_twophase_postcommit(xid, info, recdata, len);
}

void multixact_twophase_postcommit ( TransactionId  xid,
uint16  info,
void *  recdata,
uint32  len 
)

Definition at line 1567 of file multixact.c.

References Assert, OldestMemberMXactId, and TwoPhaseGetDummyBackendId().

Referenced by multixact_twophase_postabort().

{
    BackendId   dummyBackendId = TwoPhaseGetDummyBackendId(xid);

    Assert(len == sizeof(MultiXactId));

    OldestMemberMXactId[dummyBackendId] = InvalidMultiXactId;
}

void multixact_twophase_recover ( TransactionId  xid,
uint16  info,
void *  recdata,
uint32  len 
)

Definition at line 1546 of file multixact.c.

References Assert, OldestMemberMXactId, and TwoPhaseGetDummyBackendId().

{
    BackendId   dummyBackendId = TwoPhaseGetDummyBackendId(xid);
    MultiXactId oldestMember;

    /*
     * Get the oldest member XID from the state file record, and set it in the
     * OldestMemberMXactId slot reserved for this prepared transaction.
     */
    Assert(len == sizeof(MultiXactId));
    oldestMember = *((MultiXactId *) recdata);

    OldestMemberMXactId[dummyBackendId] = oldestMember;
}

void MultiXactAdvanceNextMXact ( MultiXactId  minMulti,
MultiXactOffset  minMultiOffset 
)
void MultiXactAdvanceOldest ( MultiXactId  oldestMulti,
Oid  oldestMultiDB 
)

Definition at line 2031 of file multixact.c.

References MultiXactIdPrecedes(), MultiXactStateData::oldestMultiXactId, and SetMultiXactIdLimit().

Referenced by vac_truncate_clog(), and xlog_redo().

{
    if (MultiXactIdPrecedes(MultiXactState->oldestMultiXactId, oldestMulti))
        SetMultiXactIdLimit(oldestMulti, oldestMultiDB);
}

void MultiXactGetCheckptMulti ( bool  is_shutdown,
MultiXactId nextMulti,
MultiXactOffset nextMultiOffset,
MultiXactId oldestMulti,
Oid oldestMultiDB 
)

Definition at line 1825 of file multixact.c.

References DEBUG2, debug_elog6, LW_SHARED, LWLockAcquire(), LWLockRelease(), MultiXactGenLock, MultiXactStateData::nextMXact, MultiXactStateData::nextOffset, MultiXactStateData::oldestMultiXactDB, and MultiXactStateData::oldestMultiXactId.

Referenced by CreateCheckPoint().

{
    LWLockAcquire(MultiXactGenLock, LW_SHARED);
    *nextMulti = MultiXactState->nextMXact;
    *nextMultiOffset = MultiXactState->nextOffset;
    *oldestMulti = MultiXactState->oldestMultiXactId;
    *oldestMultiDB = MultiXactState->oldestMultiXactDB;
    LWLockRelease(MultiXactGenLock);

    debug_elog6(DEBUG2,
                "MultiXact: checkpoint is nextMulti %u, nextOffset %u, oldestMulti %u in DB %u",
                *nextMulti, *nextMultiOffset, *oldestMulti, *oldestMultiDB);
}

MultiXactId MultiXactIdCreate ( TransactionId  xid1,
MultiXactStatus  status1,
TransactionId  xid2,
MultiXactStatus  status2 
)

Definition at line 324 of file multixact.c.

References Assert, AssertArg, CreateMultiXactId(), DEBUG2, debug_elog3, mXactCacheEnt::members, mxid_to_string(), MultiXactMember::status, TransactionIdEquals, TransactionIdIsValid, and MultiXactMember::xid.

Referenced by compute_new_xmax_infomask().

{
    MultiXactId newMulti;
    MultiXactMember members[2];

    AssertArg(TransactionIdIsValid(xid1));
    AssertArg(TransactionIdIsValid(xid2));

    Assert(!TransactionIdEquals(xid1, xid2) || (status1 != status2));

    /*
     * Note: unlike MultiXactIdExpand, we don't bother to check that both XIDs
     * are still running.  In typical usage, xid2 will be our own XID and the
     * caller just did a check on xid1, so it'd be wasted effort.
     */

    members[0].xid = xid1;
    members[0].status = status1;
    members[1].xid = xid2;
    members[1].status = status2;

    newMulti = CreateMultiXactId(2, members);

    debug_elog3(DEBUG2, "Create: %s",
                mxid_to_string(newMulti, 2, members));

    return newMulti;
}

MultiXactId MultiXactIdExpand ( MultiXactId  multi,
TransactionId  xid,
MultiXactStatus  status 
)

Definition at line 374 of file multixact.c.

References AssertArg, CreateMultiXactId(), DEBUG2, debug_elog3, debug_elog4, debug_elog5, GetMultiXactIdMembers(), i, mXactCacheEnt::members, MultiXactIdIsValid, mxstatus_to_string(), mXactCacheEnt::nmembers, palloc(), pfree(), MultiXactMember::status, TransactionIdDidCommit(), TransactionIdEquals, TransactionIdIsInProgress(), TransactionIdIsValid, and MultiXactMember::xid.

Referenced by compute_new_xmax_infomask().

{
    MultiXactId newMulti;
    MultiXactMember *members;
    MultiXactMember *newMembers;
    int         nmembers;
    int         i;
    int         j;

    AssertArg(MultiXactIdIsValid(multi));
    AssertArg(TransactionIdIsValid(xid));

    debug_elog5(DEBUG2, "Expand: received multi %u, xid %u status %s",
                multi, xid, mxstatus_to_string(status));

    /*
     * Note: we don't allow for old multis here.  The reason is that the
     * only caller of this function does a check that the multixact is
     * no longer running.
     */
    nmembers = GetMultiXactIdMembers(multi, &members, false);

    if (nmembers < 0)
    {
        MultiXactMember     member;

        /*
         * The MultiXactId is obsolete.  This can only happen if all the
         * MultiXactId members stop running between the caller checking and
         * passing it to us.  It would be better to return that fact to the
         * caller, but it would complicate the API and it's unlikely to happen
         * too often, so just deal with it by creating a singleton MultiXact.
         */
        member.xid = xid;
        member.status = status;
        newMulti = CreateMultiXactId(1, &member);

        debug_elog4(DEBUG2, "Expand: %u has no members, create singleton %u",
                    multi, newMulti);
        return newMulti;
    }

    /*
     * If the TransactionId is already a member of the MultiXactId with the
     * same status, just return the existing MultiXactId.
     */
    for (i = 0; i < nmembers; i++)
    {
        if (TransactionIdEquals(members[i].xid, xid) &&
            (members[i].status == status))
        {
            debug_elog4(DEBUG2, "Expand: %u is already a member of %u",
                        xid, multi);
            pfree(members);
            return multi;
        }
    }

    /*
     * Determine which of the members of the MultiXactId are still of interest.
     * This is any running transaction, and also any transaction that grabbed
     * something stronger than just a lock and was committed.  (An update that
     * aborted is of no interest here.)
     *
     * (Removing dead members is just an optimization, but a useful one.
     * Note we have the same race condition here as above: j could be 0 at the
     * end of the loop.)
     */
    newMembers = (MultiXactMember *)
        palloc(sizeof(MultiXactMember) * (nmembers + 1));

    for (i = 0, j = 0; i < nmembers; i++)
    {
        if (TransactionIdIsInProgress(members[i].xid) ||
            ((members[i].status > MultiXactStatusForUpdate) &&
             TransactionIdDidCommit(members[i].xid)))
        {
            newMembers[j].xid = members[i].xid;
            newMembers[j++].status = members[i].status;
        }
    }

    newMembers[j].xid = xid;
    newMembers[j++].status = status;
    newMulti = CreateMultiXactId(j, newMembers);

    pfree(members);
    pfree(newMembers);

    debug_elog3(DEBUG2, "Expand: returning new multi %u", newMulti);

    return newMulti;
}

bool MultiXactIdIsRunning ( MultiXactId  multi  ) 

Definition at line 480 of file multixact.c.

References DEBUG2, debug_elog2, debug_elog3, debug_elog4, GetMultiXactIdMembers(), i, mXactCacheEnt::members, mXactCacheEnt::nmembers, pfree(), TransactionIdIsCurrentTransactionId(), and TransactionIdIsInProgress().

Referenced by compute_new_xmax_infomask(), HeapTupleSatisfiesUpdate(), and HeapTupleSatisfiesVacuum().

{
    MultiXactMember *members;
    int         nmembers;
    int         i;

    debug_elog3(DEBUG2, "IsRunning %u?", multi);

    /*
     * "false" here means we assume our callers have checked that the given
     * multi cannot possibly come from a pg_upgraded database.
     */
    nmembers = GetMultiXactIdMembers(multi, &members, false);

    if (nmembers < 0)
    {
        debug_elog2(DEBUG2, "IsRunning: no members");
        return false;
    }

    /*
     * Checking for myself is cheap compared to looking in shared memory;
     * return true if any live subtransaction of the current top-level
     * transaction is a member.
     *
     * This is not needed for correctness, it's just a fast path.
     */
    for (i = 0; i < nmembers; i++)
    {
        if (TransactionIdIsCurrentTransactionId(members[i].xid))
        {
            debug_elog3(DEBUG2, "IsRunning: I (%d) am running!", i);
            pfree(members);
            return true;
        }
    }

    /*
     * This could be made faster by having another entry point in procarray.c,
     * walking the PGPROC array only once for all the members.  But in most
     * cases nmembers should be small enough that it doesn't much matter.
     */
    for (i = 0; i < nmembers; i++)
    {
        if (TransactionIdIsInProgress(members[i].xid))
        {
            debug_elog4(DEBUG2, "IsRunning: member %d (%u) is running",
                        i, members[i].xid);
            pfree(members);
            return true;
        }
    }

    pfree(members);

    debug_elog3(DEBUG2, "IsRunning: %u is not running", multi);

    return false;
}

bool MultiXactIdPrecedes ( MultiXactId  multi1,
MultiXactId  multi2 
)
void MultiXactIdSetOldestMember ( void   ) 

Definition at line 554 of file multixact.c.

References DEBUG2, debug_elog4, FirstMultiXactId, LW_EXCLUSIVE, LWLockAcquire(), LWLockRelease(), MultiXactGenLock, MultiXactIdIsValid, MyBackendId, MultiXactStateData::nextMXact, and OldestMemberMXactId.

Referenced by heap_delete(), heap_lock_tuple(), heap_lock_updated_tuple(), and heap_update().

{
    if (!MultiXactIdIsValid(OldestMemberMXactId[MyBackendId]))
    {
        MultiXactId nextMXact;

        /*
         * You might think we don't need to acquire a lock here, since
         * fetching and storing of TransactionIds is probably atomic, but in
         * fact we do: suppose we pick up nextMXact and then lose the CPU for
         * a long time.  Someone else could advance nextMXact, and then
         * another someone else could compute an OldestVisibleMXactId that
         * would be after the value we are going to store when we get control
         * back.  Which would be wrong.
         */
        LWLockAcquire(MultiXactGenLock, LW_EXCLUSIVE);

        /*
         * We have to beware of the possibility that nextMXact is in the
         * wrapped-around state.  We don't fix the counter itself here, but we
         * must be sure to store a valid value in our array entry.
         */
        nextMXact = MultiXactState->nextMXact;
        if (nextMXact < FirstMultiXactId)
            nextMXact = FirstMultiXactId;

        OldestMemberMXactId[MyBackendId] = nextMXact;

        LWLockRelease(MultiXactGenLock);

        debug_elog4(DEBUG2, "MultiXact: setting OldestMember[%d] = %u",
                    MyBackendId, nextMXact);
    }
}

static void MultiXactIdSetOldestVisible ( void   )  [static]

Definition at line 606 of file multixact.c.

References DEBUG2, debug_elog4, FirstMultiXactId, i, LW_EXCLUSIVE, LWLockAcquire(), LWLockRelease(), MultiXactGenLock, MultiXactIdIsValid, MultiXactIdPrecedes(), MyBackendId, MultiXactStateData::nextMXact, OldestMemberMXactId, and OldestVisibleMXactId.

Referenced by GetMultiXactIdMembers().

{
    if (!MultiXactIdIsValid(OldestVisibleMXactId[MyBackendId]))
    {
        MultiXactId oldestMXact;
        int         i;

        LWLockAcquire(MultiXactGenLock, LW_EXCLUSIVE);

        /*
         * We have to beware of the possibility that nextMXact is in the
         * wrapped-around state.  We don't fix the counter itself here, but we
         * must be sure to store a valid value in our array entry.
         */
        oldestMXact = MultiXactState->nextMXact;
        if (oldestMXact < FirstMultiXactId)
            oldestMXact = FirstMultiXactId;

        for (i = 1; i <= MaxOldestSlot; i++)
        {
            MultiXactId thisoldest = OldestMemberMXactId[i];

            if (MultiXactIdIsValid(thisoldest) &&
                MultiXactIdPrecedes(thisoldest, oldestMXact))
                oldestMXact = thisoldest;
        }

        OldestVisibleMXactId[MyBackendId] = oldestMXact;

        LWLockRelease(MultiXactGenLock);

        debug_elog4(DEBUG2, "MultiXact: setting OldestVisible[%d] = %u",
                    MyBackendId, oldestMXact);
    }
}

static bool MultiXactMemberPagePrecedes ( int  page1,
int  page2 
) [static]

Definition at line 2287 of file multixact.c.

References MultiXactOffsetPrecedes().

{
    MultiXactOffset offset1;
    MultiXactOffset offset2;

    offset1 = ((MultiXactOffset) page1) * MULTIXACT_MEMBERS_PER_PAGE;
    offset2 = ((MultiXactOffset) page2) * MULTIXACT_MEMBERS_PER_PAGE;

    return MultiXactOffsetPrecedes(offset1, offset2);
}

static bool MultiXactOffsetPagePrecedes ( int  page1,
int  page2 
) [static]

Definition at line 2269 of file multixact.c.

References MultiXactIdPrecedes().

{
    MultiXactId multi1;
    MultiXactId multi2;

    multi1 = ((MultiXactId) page1) * MULTIXACT_OFFSETS_PER_PAGE;
    multi1 += FirstMultiXactId;
    multi2 = ((MultiXactId) page2) * MULTIXACT_OFFSETS_PER_PAGE;
    multi2 += FirstMultiXactId;

    return MultiXactIdPrecedes(multi1, multi2);
}

static bool MultiXactOffsetPrecedes ( MultiXactOffset  offset1,
MultiXactOffset  offset2 
) [static]

Definition at line 2316 of file multixact.c.

Referenced by MultiXactAdvanceNextMXact(), and MultiXactMemberPagePrecedes().

{
    int32       diff = (int32) (offset1 - offset2);

    return (diff < 0);
}

void MultiXactSetNextMXact ( MultiXactId  nextMulti,
MultiXactOffset  nextMultiOffset 
)

Definition at line 1867 of file multixact.c.

References DEBUG2, debug_elog4, LW_EXCLUSIVE, LWLockAcquire(), LWLockRelease(), MultiXactGenLock, MultiXactStateData::nextMXact, and MultiXactStateData::nextOffset.

Referenced by BootStrapXLOG(), StartupXLOG(), and xlog_redo().

{
    debug_elog4(DEBUG2, "MultiXact: setting next multi to %u offset %u",
                nextMulti, nextMultiOffset);
    LWLockAcquire(MultiXactGenLock, LW_EXCLUSIVE);
    MultiXactState->nextMXact = nextMulti;
    MultiXactState->nextOffset = nextMultiOffset;
    LWLockRelease(MultiXactGenLock);
}

void MultiXactShmemInit ( void   ) 

Definition at line 1610 of file multixact.c.

References Assert, DEBUG2, debug_elog2, IsUnderPostmaster, MemSet, MultiXactMemberControlLock, MultiXactMemberCtl, MultiXactOffsetControlLock, MultiXactOffsetCtl, NUM_MXACTMEMBER_BUFFERS, NUM_MXACTOFFSET_BUFFERS, OldestMemberMXactId, OldestVisibleMXactId, MultiXactStateData::perBackendXactIds, SHARED_MULTIXACT_STATE_SIZE, ShmemInitStruct(), and SimpleLruInit().

Referenced by CreateSharedMemoryAndSemaphores().

{
    bool        found;

    debug_elog2(DEBUG2, "Shared Memory Init for MultiXact");

    MultiXactOffsetCtl->PagePrecedes = MultiXactOffsetPagePrecedes;
    MultiXactMemberCtl->PagePrecedes = MultiXactMemberPagePrecedes;

    SimpleLruInit(MultiXactOffsetCtl,
                  "MultiXactOffset Ctl", NUM_MXACTOFFSET_BUFFERS, 0,
                  MultiXactOffsetControlLock, "pg_multixact/offsets");
    SimpleLruInit(MultiXactMemberCtl,
                  "MultiXactMember Ctl", NUM_MXACTMEMBER_BUFFERS, 0,
                  MultiXactMemberControlLock, "pg_multixact/members");

    /* Initialize our shared state struct */
    MultiXactState = ShmemInitStruct("Shared MultiXact State",
                                     SHARED_MULTIXACT_STATE_SIZE,
                                     &found);
    if (!IsUnderPostmaster)
    {
        Assert(!found);

        /* Make sure we zero out the per-backend state */
        MemSet(MultiXactState, 0, SHARED_MULTIXACT_STATE_SIZE);
    }
    else
        Assert(found);

    /*
     * Set up array pointers.  Note that perBackendXactIds[0] is wasted space
     * since we only use indexes 1..MaxOldestSlot in each array.
     */
    OldestMemberMXactId = MultiXactState->perBackendXactIds;
    OldestVisibleMXactId = OldestMemberMXactId + MaxOldestSlot;
}

Size MultiXactShmemSize ( void   ) 

Definition at line 1594 of file multixact.c.

References add_size(), NUM_MXACTMEMBER_BUFFERS, NUM_MXACTOFFSET_BUFFERS, and SimpleLruShmemSize().

Referenced by CreateSharedMemoryAndSemaphores().

{
    Size        size;

#define SHARED_MULTIXACT_STATE_SIZE \
    add_size(sizeof(MultiXactStateData), \
             mul_size(sizeof(MultiXactId) * 2, MaxOldestSlot))

    size = SHARED_MULTIXACT_STATE_SIZE;
    size = add_size(size, SimpleLruShmemSize(NUM_MXACTOFFSET_BUFFERS, 0));
    size = add_size(size, SimpleLruShmemSize(NUM_MXACTMEMBER_BUFFERS, 0));

    return size;
}

static int mXactCacheGetById ( MultiXactId  multi,
MultiXactMember **  members 
) [static]

Definition at line 1332 of file multixact.c.

References DEBUG2, debug_elog2, debug_elog3, mXactCacheEnt::members, mXactCacheEnt::multi, mxid_to_string(), mXactCacheEnt::next, mXactCacheEnt::nmembers, and palloc().

Referenced by GetMultiXactIdMembers().

{
    mXactCacheEnt *entry;

    debug_elog3(DEBUG2, "CacheGet: looking for %u", multi);

    for (entry = MXactCache; entry != NULL; entry = entry->next)
    {
        if (entry->multi == multi)
        {
            MultiXactMember *ptr;
            Size        size;

            size = sizeof(MultiXactMember) * entry->nmembers;
            ptr = (MultiXactMember *) palloc(size);
            *members = ptr;

            memcpy(ptr, entry->members, size);

            debug_elog3(DEBUG2, "CacheGet: found %s",
                        mxid_to_string(multi, entry->nmembers, entry->members));
            return entry->nmembers;
        }
    }

    debug_elog2(DEBUG2, "CacheGet: not found");
    return -1;
}

static MultiXactId mXactCacheGetBySet ( int  nmembers,
MultiXactMember members 
) [static]

Definition at line 1293 of file multixact.c.

References DEBUG2, debug_elog2, debug_elog3, InvalidMultiXactId, mXactCacheEnt::members, memcmp(), mXactCacheEnt::multi, mxactMemberComparator(), mxid_to_string(), mXactCacheEnt::next, mXactCacheEnt::nmembers, and qsort.

Referenced by CreateMultiXactId().

{
    mXactCacheEnt *entry;

    debug_elog3(DEBUG2, "CacheGet: looking for %s",
                mxid_to_string(InvalidMultiXactId, nmembers, members));

    /* sort the array so comparison is easy */
    qsort(members, nmembers, sizeof(MultiXactMember), mxactMemberComparator);

    for (entry = MXactCache; entry != NULL; entry = entry->next)
    {
        if (entry->nmembers != nmembers)
            continue;

        /*
         * We assume the cache entries are sorted, and that the unused bits in
         * "status" are zeroed.
         */
        if (memcmp(members, entry->members, nmembers * sizeof(MultiXactMember)) == 0)
        {
            debug_elog3(DEBUG2, "CacheGet: found %u", entry->multi);
            return entry->multi;
        }
    }

    debug_elog2(DEBUG2, "CacheGet: not found :-(");
    return InvalidMultiXactId;
}

static void mXactCachePut ( MultiXactId  multi,
int  nmembers,
MultiXactMember members 
) [static]

Definition at line 1366 of file multixact.c.

References ALLOCSET_SMALL_INITSIZE, ALLOCSET_SMALL_MAXSIZE, ALLOCSET_SMALL_MINSIZE, AllocSetContextCreate(), DEBUG2, debug_elog2, debug_elog3, mXactCacheEnt::members, MemoryContextAlloc(), mXactCacheEnt::multi, mxactMemberComparator(), mxid_to_string(), mXactCacheEnt::next, mXactCacheEnt::nmembers, NULL, offsetof, qsort, and TopTransactionContext.

Referenced by CreateMultiXactId(), and GetMultiXactIdMembers().

{
    mXactCacheEnt *entry;

    debug_elog3(DEBUG2, "CachePut: storing %s",
                mxid_to_string(multi, nmembers, members));

    if (MXactContext == NULL)
    {
        /* The cache only lives as long as the current transaction */
        debug_elog2(DEBUG2, "CachePut: initializing memory context");
        MXactContext = AllocSetContextCreate(TopTransactionContext,
                                             "MultiXact Cache Context",
                                             ALLOCSET_SMALL_MINSIZE,
                                             ALLOCSET_SMALL_INITSIZE,
                                             ALLOCSET_SMALL_MAXSIZE);
    }

    entry = (mXactCacheEnt *)
        MemoryContextAlloc(MXactContext,
                           offsetof(mXactCacheEnt, members) +
                           nmembers * sizeof(MultiXactMember));

    entry->multi = multi;
    entry->nmembers = nmembers;
    memcpy(entry->members, members, nmembers * sizeof(MultiXactMember));

    /* mXactCacheGetBySet assumes the entries are sorted, so sort them */
    qsort(entry->members, nmembers, sizeof(MultiXactMember), mxactMemberComparator);

    entry->next = MXactCache;
    MXactCache = entry;
}

static int mxactMemberComparator ( const void *  arg1,
const void *  arg2 
) [static]

Definition at line 1263 of file multixact.c.

References MultiXactMember::status, and MultiXactMember::xid.

Referenced by mXactCacheGetBySet(), and mXactCachePut().

{
    MultiXactMember member1 = *(const MultiXactMember *) arg1;
    MultiXactMember member2 = *(const MultiXactMember *) arg2;

    if (member1.xid > member2.xid)
        return 1;
    if (member1.xid < member2.xid)
        return -1;
    if (member1.status > member2.status)
        return 1;
    if (member1.status < member2.status)
        return -1;
    return 0;
}

char* mxid_to_string ( MultiXactId  multi,
int  nmembers,
MultiXactMember members 
)

Definition at line 1424 of file multixact.c.

References appendStringInfo(), appendStringInfoChar(), buf, StringInfoData::data, i, initStringInfo(), MemoryContextStrdup(), mxstatus_to_string(), NULL, pfree(), status(), and TopMemoryContext.

Referenced by CreateMultiXactId(), GetMultiXactIdMembers(), MultiXactIdCreate(), mXactCacheGetById(), mXactCacheGetBySet(), and mXactCachePut().

{
    static char    *str = NULL;
    StringInfoData  buf;
    int         i;

    if (str != NULL)
        pfree(str);

    initStringInfo(&buf);

    appendStringInfo(&buf, "%u %d[%u (%s)", multi, nmembers, members[0].xid,
                     mxstatus_to_string(members[0].status));

    for (i = 1; i < nmembers; i++)
        appendStringInfo(&buf, ", %u (%s)", members[i].xid,
                         mxstatus_to_string(members[i].status));

    appendStringInfoChar(&buf, ']');
    str = MemoryContextStrdup(TopMemoryContext, buf.data);
    pfree(buf.data);
    return str;
}

static char * mxstatus_to_string ( MultiXactStatus  status  )  [static]

Definition at line 1401 of file multixact.c.

References elog, ERROR, MultiXactStatusForKeyShare, MultiXactStatusForNoKeyUpdate, MultiXactStatusForShare, MultiXactStatusForUpdate, MultiXactStatusNoKeyUpdate, and MultiXactStatusUpdate.

Referenced by MultiXactIdExpand(), mxid_to_string(), and pg_get_multixact_members().

{
    switch (status)
    {
        case MultiXactStatusForKeyShare:
            return "keysh";
        case MultiXactStatusForShare:
            return "sh";
        case MultiXactStatusForNoKeyUpdate:
            return "fornokeyupd";
        case MultiXactStatusForUpdate:
            return "forupd";
        case MultiXactStatusNoKeyUpdate:
            return "nokeyupd";
        case MultiXactStatusUpdate:
            return "upd";
        default:
            elog(ERROR, "unrecognized multixact status %d", status);
            return "";
    }
}

Datum pg_get_multixact_members ( PG_FUNCTION_ARGS   ) 

Definition at line 2426 of file multixact.c.

References FuncCallContext::attinmeta, BuildTupleFromCStrings(), CreateTemplateTupleDesc(), ereport, errcode(), errmsg(), ERROR, FirstMultiXactId, GetMultiXactIdMembers(), HeapTupleGetDatum, MemoryContextSwitchTo(), FuncCallContext::multi_call_memory_ctx, mxstatus_to_string(), palloc(), pfree(), PG_GETARG_UINT32, SRF_FIRSTCALL_INIT, SRF_IS_FIRSTCALL, SRF_PERCALL_SETUP, SRF_RETURN_DONE, SRF_RETURN_NEXT, TEXTOID, TupleDescGetAttInMetadata(), TupleDescInitEntry(), FuncCallContext::user_fctx, values, and XIDOID.

{
    typedef struct
    {
        MultiXactMember *members;
        int             nmembers;
        int             iter;
    } mxact;
    MultiXactId     mxid = PG_GETARG_UINT32(0);
    mxact          *multi;
    FuncCallContext *funccxt;

    if (mxid < FirstMultiXactId)
        ereport(ERROR,
                (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                 errmsg("invalid MultiXactId: %u", mxid)));

    if (SRF_IS_FIRSTCALL())
    {
        MemoryContext oldcxt;
        TupleDesc   tupdesc;

        funccxt = SRF_FIRSTCALL_INIT();
        oldcxt = MemoryContextSwitchTo(funccxt->multi_call_memory_ctx);

        multi = palloc(sizeof(mxact));
        /* no need to allow for old values here */
        multi->nmembers = GetMultiXactIdMembers(mxid, &multi->members, false);
        multi->iter = 0;

        tupdesc = CreateTemplateTupleDesc(2, false);
        TupleDescInitEntry(tupdesc, (AttrNumber) 1, "xid",
                           XIDOID, -1, 0);
        TupleDescInitEntry(tupdesc, (AttrNumber) 2, "mode",
                           TEXTOID, -1, 0);

        funccxt->attinmeta = TupleDescGetAttInMetadata(tupdesc);
        funccxt->user_fctx = multi;

        MemoryContextSwitchTo(oldcxt);
    }

    funccxt = SRF_PERCALL_SETUP();
    multi = (mxact *) funccxt->user_fctx;

    while (multi->iter < multi->nmembers)
    {
        HeapTuple   tuple;
        char       *values[2];

        values[0] = palloc(32);
        sprintf(values[0], "%u", multi->members[multi->iter].xid);
        values[1] = mxstatus_to_string(multi->members[multi->iter].status);

        tuple = BuildTupleFromCStrings(funccxt->attinmeta, values);

        multi->iter++;
        pfree(values[0]);
        SRF_RETURN_NEXT(funccxt, HeapTupleGetDatum(tuple));
    }

    if (multi->nmembers > 0)
        pfree(multi->members);
    pfree(multi);

    SRF_RETURN_DONE(funccxt);
}

void PostPrepare_MultiXact ( TransactionId  xid  ) 

Definition at line 1497 of file multixact.c.

References LW_EXCLUSIVE, LWLockAcquire(), LWLockRelease(), MultiXactGenLock, MultiXactIdIsValid, MyBackendId, OldestMemberMXactId, OldestVisibleMXactId, and TwoPhaseGetDummyBackendId().

Referenced by PrepareTransaction().

{
    MultiXactId myOldestMember;

    /*
     * Transfer our OldestMemberMXactId value to the slot reserved for the
     * prepared transaction.
     */
    myOldestMember = OldestMemberMXactId[MyBackendId];
    if (MultiXactIdIsValid(myOldestMember))
    {
        BackendId   dummyBackendId = TwoPhaseGetDummyBackendId(xid);

        /*
         * Even though storing MultiXactId is atomic, acquire lock to make
         * sure others see both changes, not just the reset of the slot of the
         * current backend. Using a volatile pointer might suffice, but this
         * isn't a hot spot.
         */
        LWLockAcquire(MultiXactGenLock, LW_EXCLUSIVE);

        OldestMemberMXactId[dummyBackendId] = myOldestMember;
        OldestMemberMXactId[MyBackendId] = InvalidMultiXactId;

        LWLockRelease(MultiXactGenLock);
    }

    /*
     * We don't need to transfer OldestVisibleMXactId value, because the
     * transaction is not going to be looking at any more multixacts once it's
     * prepared.
     *
     * We assume that storing a MultiXactId is atomic and so we need not take
     * MultiXactGenLock to do this.
     */
    OldestVisibleMXactId[MyBackendId] = InvalidMultiXactId;

    /*
     * Discard the local MultiXactId cache like in AtEOX_MultiXact
     */
    MXactContext = NULL;
    MXactCache = NULL;
}

MultiXactId ReadNextMultiXactId ( void   ) 

Definition at line 647 of file multixact.c.

References FirstMultiXactId, LW_SHARED, LWLockAcquire(), LWLockRelease(), MultiXactGenLock, and MultiXactStateData::nextMXact.

Referenced by ATRewriteTables(), AutoVacWorkerMain(), do_start_worker(), and ExecRefreshMatView().

{
    MultiXactId     mxid;

    /* XXX we could presumably do this without a lock. */
    LWLockAcquire(MultiXactGenLock, LW_SHARED);
    mxid = MultiXactState->nextMXact;
    LWLockRelease(MultiXactGenLock);

    if (mxid < FirstMultiXactId)
        mxid = FirstMultiXactId;

    return mxid;
}

static void RecordNewMultiXact ( MultiXactId  multi,
MultiXactOffset  offset,
int  nmembers,
MultiXactMember members 
) [static]

Definition at line 762 of file multixact.c.

References Assert, i, LW_EXCLUSIVE, LWLockAcquire(), LWLockRelease(), MultiXactIdToOffsetEntry, MultiXactIdToOffsetPage, MultiXactMemberControlLock, MultiXactMemberCtl, MultiXactOffsetControlLock, MultiXactOffsetCtl, MultiXactStatusUpdate, MXOffsetToFlagsBitShift, MXOffsetToFlagsOffset, MXOffsetToMemberOffset, MXOffsetToMemberPage, SimpleLruReadPage(), MultiXactMember::status, status(), and MultiXactMember::xid.

Referenced by CreateMultiXactId(), and multixact_redo().

{
    int         pageno;
    int         prev_pageno;
    int         entryno;
    int         slotno;
    MultiXactOffset *offptr;
    int         i;

    LWLockAcquire(MultiXactOffsetControlLock, LW_EXCLUSIVE);

    pageno = MultiXactIdToOffsetPage(multi);
    entryno = MultiXactIdToOffsetEntry(multi);

    /*
     * Note: we pass the MultiXactId to SimpleLruReadPage as the "transaction"
     * to complain about if there's any I/O error.  This is kinda bogus, but
     * since the errors will always give the full pathname, it should be clear
     * enough that a MultiXactId is really involved.  Perhaps someday we'll
     * take the trouble to generalize the slru.c error reporting code.
     */
    slotno = SimpleLruReadPage(MultiXactOffsetCtl, pageno, true, multi);
    offptr = (MultiXactOffset *) MultiXactOffsetCtl->shared->page_buffer[slotno];
    offptr += entryno;

    *offptr = offset;

    MultiXactOffsetCtl->shared->page_dirty[slotno] = true;

    /* Exchange our lock */
    LWLockRelease(MultiXactOffsetControlLock);

    LWLockAcquire(MultiXactMemberControlLock, LW_EXCLUSIVE);

    prev_pageno = -1;

    for (i = 0; i < nmembers; i++, offset++)
    {
        TransactionId *memberptr;
        uint32     *flagsptr;
        uint32      flagsval;
        int         bshift;
        int         flagsoff;
        int         memberoff;

        Assert(members[i].status <= MultiXactStatusUpdate);

        pageno = MXOffsetToMemberPage(offset);
        memberoff = MXOffsetToMemberOffset(offset);
        flagsoff = MXOffsetToFlagsOffset(offset);
        bshift = MXOffsetToFlagsBitShift(offset);

        if (pageno != prev_pageno)
        {
            slotno = SimpleLruReadPage(MultiXactMemberCtl, pageno, true, multi);
            prev_pageno = pageno;
        }

        memberptr = (TransactionId *)
            (MultiXactMemberCtl->shared->page_buffer[slotno] + memberoff);

        *memberptr = members[i].xid;

        flagsptr = (uint32 *)
            (MultiXactMemberCtl->shared->page_buffer[slotno] + flagsoff);

        flagsval = *flagsptr;
        flagsval &= ~(((1 << MXACT_MEMBER_BITS_PER_XACT) - 1) << bshift);
        flagsval |= (members[i].status << bshift);
        *flagsptr = flagsval;

        MultiXactMemberCtl->shared->page_dirty[slotno] = true;
    }

    LWLockRelease(MultiXactMemberControlLock);
}

void SetMultiXactIdLimit ( MultiXactId  oldest_datminmxid,
Oid  oldest_datoid 
)

Definition at line 1884 of file multixact.c.

References Assert, DEBUG1, ereport, errhint(), errmsg(), FirstMultiXactId, get_database_name(), InRecovery, IsTransactionState(), IsUnderPostmaster, LW_EXCLUSIVE, LWLockAcquire(), LWLockRelease(), MaxMultiXactId, MultiXactStateData::multiStopLimit, MultiXactStateData::multiVacLimit, MultiXactStateData::multiWarnLimit, MultiXactStateData::multiWrapLimit, MultiXactGenLock, MultiXactIdIsValid, MultiXactIdPrecedes(), MultiXactStateData::nextMXact, MultiXactStateData::oldestMultiXactDB, MultiXactStateData::oldestMultiXactId, PMSIGNAL_START_AUTOVAC_LAUNCHER, SendPostmasterSignal(), and WARNING.

Referenced by BootStrapXLOG(), MultiXactAdvanceOldest(), StartupXLOG(), and xlog_redo().

{
    MultiXactId multiVacLimit;
    MultiXactId multiWarnLimit;
    MultiXactId multiStopLimit;
    MultiXactId multiWrapLimit;
    MultiXactId curMulti;

    Assert(MultiXactIdIsValid(oldest_datminmxid));

    /*
     * The place where we actually get into deep trouble is halfway around
     * from the oldest potentially-existing XID/multi.  (This calculation is
     * probably off by one or two counts for Xids, because the special XIDs
     * reduce the size of the loop a little bit.  But we throw in plenty of
     * slop below, so it doesn't matter.)
     */
    multiWrapLimit = oldest_datminmxid + (MaxMultiXactId >> 1);
    if (multiWrapLimit < FirstMultiXactId)
        multiWrapLimit += FirstMultiXactId;

    /*
     * We'll refuse to continue assigning MultiXactIds once we get within 100
     * multi of data loss.
     */
    multiStopLimit = multiWrapLimit - 100;
    if (multiStopLimit < FirstMultiXactId)
        multiStopLimit -= FirstMultiXactId;

    /*
     * We'll start complaining loudly when we get within 10M multis of the stop
     * point.   This is kind of arbitrary, but if you let your gas gauge get
     * down to 1% of full, would you be looking for the next gas station?  We
     * need to be fairly liberal about this number because there are lots of
     * scenarios where most transactions are done by automatic clients that
     * won't pay attention to warnings. (No, we're not gonna make this
     * configurable.  If you know enough to configure it, you know enough to
     * not get in this kind of trouble in the first place.)
     */
    multiWarnLimit = multiStopLimit - 10000000;
    if (multiWarnLimit < FirstMultiXactId)
        multiWarnLimit -= FirstMultiXactId;

    /*
     * We'll start trying to force autovacuums when oldest_datminmxid gets
     * to be more than 200 million transactions old.
     */
    multiVacLimit = oldest_datminmxid + 200000000;
    if (multiVacLimit < FirstMultiXactId)
        multiVacLimit += FirstMultiXactId;

    /* Grab lock for just long enough to set the new limit values */
    LWLockAcquire(MultiXactGenLock, LW_EXCLUSIVE);
    MultiXactState->oldestMultiXactId = oldest_datminmxid;
    MultiXactState->oldestMultiXactDB = oldest_datoid;
    MultiXactState->multiVacLimit = multiVacLimit;
    MultiXactState->multiWarnLimit = multiWarnLimit;
    MultiXactState->multiStopLimit = multiStopLimit;
    MultiXactState->multiWrapLimit = multiWrapLimit;
    curMulti = MultiXactState->nextMXact;
    LWLockRelease(MultiXactGenLock);

    /* Log the info */
    ereport(DEBUG1,
            (errmsg("MultiXactId wrap limit is %u, limited by database with OID %u",
                    multiWrapLimit, oldest_datoid)));

    /*
     * If past the autovacuum force point, immediately signal an autovac
     * request.  The reason for this is that autovac only processes one
     * database per invocation.  Once it's finished cleaning up the oldest
     * database, it'll call here, and we'll signal the postmaster to start
     * another iteration immediately if there are still any old databases.
     */
    if (MultiXactIdPrecedes(multiVacLimit, curMulti) &&
        IsUnderPostmaster && !InRecovery)
        SendPostmasterSignal(PMSIGNAL_START_AUTOVAC_LAUNCHER);

    /* Give an immediate warning if past the wrap warn point */
    if (MultiXactIdPrecedes(multiWarnLimit, curMulti) && !InRecovery)
    {
        char       *oldest_datname;

        /*
         * We can be called when not inside a transaction, for example during
         * StartupXLOG().  In such a case we cannot do database access, so we
         * must just report the oldest DB's OID.
         *
         * Note: it's also possible that get_database_name fails and returns
         * NULL, for example because the database just got dropped.  We'll
         * still warn, even though the warning might now be unnecessary.
         */
        if (IsTransactionState())
            oldest_datname = get_database_name(oldest_datoid);
        else
            oldest_datname = NULL;

        if (oldest_datname)
            ereport(WARNING,
                    (errmsg("database \"%s\" must be vacuumed before %u more MultiXactIds are used",
                            oldest_datname,
                            multiWrapLimit - curMulti),
                     errhint("To avoid a database shutdown, execute a database-wide VACUUM in that database.\n"
                             "You might also need to commit or roll back old prepared transactions.")));
        else
            ereport(WARNING,
                    (errmsg("database with OID %u must be vacuumed before %u more MultiXactIds are used",
                            oldest_datoid,
                            multiWrapLimit - curMulti),
                     errhint("To avoid a database shutdown, execute a database-wide VACUUM in that database.\n"
                             "You might also need to commit or roll back old prepared transactions.")));
    }
}

void ShutdownMultiXact ( void   ) 

Definition at line 1812 of file multixact.c.

References MultiXactMemberCtl, MultiXactOffsetCtl, and SimpleLruFlush().

Referenced by ShutdownXLOG().

{
    /* Flush dirty MultiXact pages to disk */
    TRACE_POSTGRESQL_MULTIXACT_CHECKPOINT_START(false);
    SimpleLruFlush(MultiXactOffsetCtl, false);
    SimpleLruFlush(MultiXactMemberCtl, false);
    TRACE_POSTGRESQL_MULTIXACT_CHECKPOINT_DONE(false);
}

static bool SlruScanDirCbFindEarliest ( SlruCtl  ctl,
char *  filename,
int  segpage,
void *  data 
) [static]

Definition at line 2179 of file multixact.c.

References mxtruncinfo::earliestExistingPage, and SlruCtlData::PagePrecedes.

Referenced by TruncateMultiXact().

{
    mxtruncinfo     *trunc = (mxtruncinfo *) data;

    if (trunc->earliestExistingPage == -1 ||
        ctl->PagePrecedes(segpage, trunc->earliestExistingPage))
    {
        trunc->earliestExistingPage = segpage;
    }

    return false;   /* keep going */
}

void StartupMultiXact ( void   ) 

Definition at line 1731 of file multixact.c.

References LW_EXCLUSIVE, LWLockAcquire(), LWLockRelease(), MemSet, mXactCacheEnt::multi, MultiXactIdToOffsetEntry, MultiXactIdToOffsetPage, MultiXactMemberControlLock, MultiXactMemberCtl, MultiXactOffsetControlLock, MultiXactOffsetCtl, MXOffsetToFlagsOffset, MXOffsetToMemberOffset, MXOffsetToMemberPage, MultiXactStateData::nextMXact, MultiXactStateData::nextOffset, and SimpleLruReadPage().

Referenced by StartupXLOG().

{
    MultiXactId multi = MultiXactState->nextMXact;
    MultiXactOffset offset = MultiXactState->nextOffset;
    int         pageno;
    int         entryno;
    int         flagsoff;

    /* Clean up offsets state */
    LWLockAcquire(MultiXactOffsetControlLock, LW_EXCLUSIVE);

    /*
     * Initialize our idea of the latest page number.
     */
    pageno = MultiXactIdToOffsetPage(multi);
    MultiXactOffsetCtl->shared->latest_page_number = pageno;

    /*
     * Zero out the remainder of the current offsets page.  See notes in
     * StartupCLOG() for motivation.
     */
    entryno = MultiXactIdToOffsetEntry(multi);
    if (entryno != 0)
    {
        int         slotno;
        MultiXactOffset *offptr;

        slotno = SimpleLruReadPage(MultiXactOffsetCtl, pageno, true, multi);
        offptr = (MultiXactOffset *) MultiXactOffsetCtl->shared->page_buffer[slotno];
        offptr += entryno;

        MemSet(offptr, 0, BLCKSZ - (entryno * sizeof(MultiXactOffset)));

        MultiXactOffsetCtl->shared->page_dirty[slotno] = true;
    }

    LWLockRelease(MultiXactOffsetControlLock);

    /* And the same for members */
    LWLockAcquire(MultiXactMemberControlLock, LW_EXCLUSIVE);

    /*
     * Initialize our idea of the latest page number.
     */
    pageno = MXOffsetToMemberPage(offset);
    MultiXactMemberCtl->shared->latest_page_number = pageno;

    /*
     * Zero out the remainder of the current members page.  See notes in
     * TrimCLOG() for motivation.
     */
    flagsoff = MXOffsetToFlagsOffset(offset);
    if (flagsoff != 0)
    {
        int         slotno;
        TransactionId *xidptr;
        int         memberoff;

        memberoff = MXOffsetToMemberOffset(offset);
        slotno = SimpleLruReadPage(MultiXactMemberCtl, pageno, true, offset);
        xidptr = (TransactionId *)
            (MultiXactMemberCtl->shared->page_buffer[slotno] + memberoff);

        MemSet(xidptr, 0, BLCKSZ - memberoff);

        /*
         * Note: we don't need to zero out the flag bits in the remaining
         * members of the current group, because they are always reset before
         * writing.
         */

        MultiXactMemberCtl->shared->page_dirty[slotno] = true;
    }

    LWLockRelease(MultiXactMemberControlLock);
}

void TruncateMultiXact ( MultiXactId  oldestMXact  ) 

Definition at line 2201 of file multixact.c.

References mxtruncinfo::earliestExistingPage, LWLockRelease(), MultiXactIdPrecedes(), MultiXactIdToOffsetEntry, MultiXactIdToOffsetPage, MultiXactMemberCtl, MultiXactOffsetControlLock, MultiXactOffsetCtl, MXOffsetToMemberPage, SimpleLruReadPage_ReadOnly(), SimpleLruTruncate(), SlruScanDirCbFindEarliest(), and SlruScanDirectory().

Referenced by vac_truncate_clog().

{
    MultiXactOffset oldestOffset;
    mxtruncinfo     trunc;
    MultiXactId     earliest;

    /*
     * Note we can't just plow ahead with the truncation; it's possible that
     * there are no segments to truncate, which is a problem because we are
     * going to attempt to read the offsets page to determine where to truncate
     * the members SLRU.  So we first scan the directory to determine the
     * earliest offsets page number that we can read without error.
     */
    trunc.earliestExistingPage = -1;
    SlruScanDirectory(MultiXactOffsetCtl, SlruScanDirCbFindEarliest, &trunc);
    earliest = trunc.earliestExistingPage * MULTIXACT_OFFSETS_PER_PAGE;

    /* nothing to do */
    if (MultiXactIdPrecedes(oldestMXact, earliest))
        return;

    /*
     * First, compute the safe truncation point for MultiXactMember.
     * This is the starting offset of the multixact we were passed
     * as MultiXactOffset cutoff.
     */
    {
        int         pageno;
        int         slotno;
        int         entryno;
        MultiXactOffset *offptr;

        /* lock is acquired by SimpleLruReadPage_ReadOnly */

        pageno = MultiXactIdToOffsetPage(oldestMXact);
        entryno = MultiXactIdToOffsetEntry(oldestMXact);

        slotno = SimpleLruReadPage_ReadOnly(MultiXactOffsetCtl, pageno,
                                            oldestMXact);
        offptr = (MultiXactOffset *)
            MultiXactOffsetCtl->shared->page_buffer[slotno];
        offptr += entryno;
        oldestOffset = *offptr;

        LWLockRelease(MultiXactOffsetControlLock);
    }

    /* truncate MultiXactOffset */
    SimpleLruTruncate(MultiXactOffsetCtl,
                      MultiXactIdToOffsetPage(oldestMXact));

    /* truncate MultiXactMembers and we're done */
    SimpleLruTruncate(MultiXactMemberCtl,
                      MXOffsetToMemberPage(oldestOffset));
}

static void WriteMZeroPageXlogRec ( int  pageno,
uint8  info 
) [static]

Definition at line 2328 of file multixact.c.

References XLogRecData::buffer, XLogRecData::data, XLogRecData::len, XLogRecData::next, and XLogInsert().

Referenced by ZeroMultiXactMemberPage(), and ZeroMultiXactOffsetPage().

{
    XLogRecData rdata;

    rdata.data = (char *) (&pageno);
    rdata.len = sizeof(int);
    rdata.buffer = InvalidBuffer;
    rdata.next = NULL;
    (void) XLogInsert(RM_MULTIXACT_ID, info, &rdata);
}

static int ZeroMultiXactMemberPage ( int  pageno,
bool  writeXlog 
) [static]
static int ZeroMultiXactOffsetPage ( int  pageno,
bool  writeXlog 
) [static]

Variable Documentation

Definition at line 156 of file multixact.c.

Definition at line 155 of file multixact.c.

Definition at line 239 of file multixact.c.

mXactCacheEnt* MXactCache = NULL [static]

Definition at line 269 of file multixact.c.

MemoryContext MXactContext = NULL [static]

Definition at line 270 of file multixact.c.