Split the shared-memory array of PGPROC pointers out of the sinval
communication structure, and make it its own module with its own lock. This should reduce contention at least a little, and it definitely makes the code seem cleaner. Per my recent proposal.
This commit is contained in:
parent
6910032a56
commit
ee3b71f6bc
|
@ -8,7 +8,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/access/heap/heapam.c,v 1.190 2005/05/03 19:42:40 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/access/heap/heapam.c,v 1.191 2005/05/19 21:35:44 tgl Exp $
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* INTERFACE ROUTINES
|
* INTERFACE ROUTINES
|
||||||
|
@ -47,7 +47,7 @@
|
||||||
#include "catalog/catalog.h"
|
#include "catalog/catalog.h"
|
||||||
#include "catalog/namespace.h"
|
#include "catalog/namespace.h"
|
||||||
#include "miscadmin.h"
|
#include "miscadmin.h"
|
||||||
#include "storage/sinval.h"
|
#include "storage/procarray.h"
|
||||||
#include "utils/inval.h"
|
#include "utils/inval.h"
|
||||||
#include "utils/relcache.h"
|
#include "utils/relcache.h"
|
||||||
#include "pgstat.h"
|
#include "pgstat.h"
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
$PostgreSQL: pgsql/src/backend/access/transam/README,v 1.2 2004/09/16 16:58:26 tgl Exp $
|
$PostgreSQL: pgsql/src/backend/access/transam/README,v 1.3 2005/05/19 21:35:45 tgl Exp $
|
||||||
|
|
||||||
The Transaction System
|
The Transaction System
|
||||||
----------------------
|
----------------------
|
||||||
|
@ -246,7 +246,7 @@ but since we allow arbitrary nesting of subtransactions, we can't fit all Xids
|
||||||
in shared memory, so we have to store them on disk. Note, however, that for
|
in shared memory, so we have to store them on disk. Note, however, that for
|
||||||
each transaction we keep a "cache" of Xids that are known to be part of the
|
each transaction we keep a "cache" of Xids that are known to be part of the
|
||||||
transaction tree, so we can skip looking at pg_subtrans unless we know the
|
transaction tree, so we can skip looking at pg_subtrans unless we know the
|
||||||
cache has been overflowed. See storage/ipc/sinval.c for the gory details.
|
cache has been overflowed. See storage/ipc/procarray.c for the gory details.
|
||||||
|
|
||||||
slru.c is the supporting mechanism for both pg_clog and pg_subtrans. It
|
slru.c is the supporting mechanism for both pg_clog and pg_subtrans. It
|
||||||
implements the LRU policy for in-memory buffer pages. The high-level routines
|
implements the LRU policy for in-memory buffer pages. The high-level routines
|
||||||
|
|
|
@ -31,7 +31,7 @@
|
||||||
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/backend/access/transam/multixact.c,v 1.3 2005/05/07 18:14:25 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/access/transam/multixact.c,v 1.4 2005/05/19 21:35:45 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -44,7 +44,7 @@
|
||||||
#include "utils/memutils.h"
|
#include "utils/memutils.h"
|
||||||
#include "storage/backendid.h"
|
#include "storage/backendid.h"
|
||||||
#include "storage/lmgr.h"
|
#include "storage/lmgr.h"
|
||||||
#include "storage/sinval.h"
|
#include "storage/procarray.h"
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -383,8 +383,8 @@ MultiXactIdIsRunning(MultiXactId multi)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This could be made better by having a special entry point in sinval.c,
|
* This could be made faster by having another entry point in procarray.c,
|
||||||
* walking the PGPROC array only once for the whole array. But in most
|
* 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.
|
* cases nmembers should be small enough that it doesn't much matter.
|
||||||
*/
|
*/
|
||||||
for (i = 0; i < nmembers; i++)
|
for (i = 0; i < nmembers; i++)
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/backend/access/transam/subtrans.c,v 1.7 2004/12/31 21:59:29 pgsql Exp $
|
* $PostgreSQL: pgsql/src/backend/access/transam/subtrans.c,v 1.8 2005/05/19 21:35:45 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -30,7 +30,6 @@
|
||||||
|
|
||||||
#include "access/slru.h"
|
#include "access/slru.h"
|
||||||
#include "access/subtrans.h"
|
#include "access/subtrans.h"
|
||||||
#include "storage/sinval.h"
|
|
||||||
#include "utils/tqual.h"
|
#include "utils/tqual.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
* Copyright (c) 2000-2005, PostgreSQL Global Development Group
|
* Copyright (c) 2000-2005, PostgreSQL Global Development Group
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/access/transam/varsup.c,v 1.63 2005/04/13 18:54:56 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/access/transam/varsup.c,v 1.64 2005/05/19 21:35:45 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -107,7 +107,7 @@ GetNewTransactionId(bool isSubXact)
|
||||||
* nextXid are already present in PGPROC. Else we have a race
|
* nextXid are already present in PGPROC. Else we have a race
|
||||||
* condition.
|
* condition.
|
||||||
*
|
*
|
||||||
* XXX by storing xid into MyProc without acquiring SInvalLock, we are
|
* XXX by storing xid into MyProc without acquiring ProcArrayLock, we are
|
||||||
* relying on fetch/store of an xid to be atomic, else other backends
|
* relying on fetch/store of an xid to be atomic, else other backends
|
||||||
* might see a partially-set xid here. But holding both locks at once
|
* might see a partially-set xid here. But holding both locks at once
|
||||||
* would be a nasty concurrency hit (and in fact could cause a
|
* would be a nasty concurrency hit (and in fact could cause a
|
||||||
|
@ -120,8 +120,7 @@ GetNewTransactionId(bool isSubXact)
|
||||||
*
|
*
|
||||||
* A solution to the atomic-store problem would be to give each PGPROC
|
* A solution to the atomic-store problem would be to give each PGPROC
|
||||||
* its own spinlock used only for fetching/storing that PGPROC's xid
|
* its own spinlock used only for fetching/storing that PGPROC's xid
|
||||||
* and related fields. (SInvalLock would then mean primarily that
|
* and related fields.
|
||||||
* PGPROCs couldn't be added/removed while holding the lock.)
|
|
||||||
*
|
*
|
||||||
* If there's no room to fit a subtransaction XID into PGPROC, set the
|
* If there's no room to fit a subtransaction XID into PGPROC, set the
|
||||||
* cache-overflowed flag instead. This forces readers to look in
|
* cache-overflowed flag instead. This forces readers to look in
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/access/transam/xact.c,v 1.200 2005/04/28 21:47:10 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/access/transam/xact.c,v 1.201 2005/05/19 21:35:45 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -34,7 +34,7 @@
|
||||||
#include "miscadmin.h"
|
#include "miscadmin.h"
|
||||||
#include "storage/fd.h"
|
#include "storage/fd.h"
|
||||||
#include "storage/proc.h"
|
#include "storage/proc.h"
|
||||||
#include "storage/sinval.h"
|
#include "storage/procarray.h"
|
||||||
#include "storage/smgr.h"
|
#include "storage/smgr.h"
|
||||||
#include "utils/flatfiles.h"
|
#include "utils/flatfiles.h"
|
||||||
#include "utils/guc.h"
|
#include "utils/guc.h"
|
||||||
|
@ -1503,16 +1503,18 @@ CommitTransaction(void)
|
||||||
* this must be done _before_ releasing locks we hold and _after_
|
* this must be done _before_ releasing locks we hold and _after_
|
||||||
* RecordTransactionCommit.
|
* RecordTransactionCommit.
|
||||||
*
|
*
|
||||||
* LWLockAcquire(SInvalLock) is required: UPDATE with xid 0 is blocked by
|
* LWLockAcquire(ProcArrayLock) is required; consider this example:
|
||||||
* xid 1' UPDATE, xid 1 is doing commit while xid 2 gets snapshot - if
|
* UPDATE with xid 0 is blocked by xid 1's UPDATE.
|
||||||
* xid 2' GetSnapshotData sees xid 1 as running then it must see xid 0
|
* xid 1 is doing commit while xid 2 gets snapshot.
|
||||||
* as running as well or it will see two tuple versions - one deleted
|
* If xid 2's GetSnapshotData sees xid 1 as running then it must see
|
||||||
* by xid 1 and one inserted by xid 0. See notes in GetSnapshotData.
|
* xid 0 as running as well, or it will be able to see two tuple versions
|
||||||
|
* - one deleted by xid 1 and one inserted by xid 0. See notes in
|
||||||
|
* GetSnapshotData.
|
||||||
*/
|
*/
|
||||||
if (MyProc != NULL)
|
if (MyProc != NULL)
|
||||||
{
|
{
|
||||||
/* Lock SInvalLock because that's what GetSnapshotData uses. */
|
/* Lock ProcArrayLock because that's what GetSnapshotData uses. */
|
||||||
LWLockAcquire(SInvalLock, LW_EXCLUSIVE);
|
LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE);
|
||||||
MyProc->xid = InvalidTransactionId;
|
MyProc->xid = InvalidTransactionId;
|
||||||
MyProc->xmin = InvalidTransactionId;
|
MyProc->xmin = InvalidTransactionId;
|
||||||
|
|
||||||
|
@ -1520,7 +1522,7 @@ CommitTransaction(void)
|
||||||
MyProc->subxids.nxids = 0;
|
MyProc->subxids.nxids = 0;
|
||||||
MyProc->subxids.overflowed = false;
|
MyProc->subxids.overflowed = false;
|
||||||
|
|
||||||
LWLockRelease(SInvalLock);
|
LWLockRelease(ProcArrayLock);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1688,8 +1690,8 @@ AbortTransaction(void)
|
||||||
*/
|
*/
|
||||||
if (MyProc != NULL)
|
if (MyProc != NULL)
|
||||||
{
|
{
|
||||||
/* Lock SInvalLock because that's what GetSnapshotData uses. */
|
/* Lock ProcArrayLock because that's what GetSnapshotData uses. */
|
||||||
LWLockAcquire(SInvalLock, LW_EXCLUSIVE);
|
LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE);
|
||||||
MyProc->xid = InvalidTransactionId;
|
MyProc->xid = InvalidTransactionId;
|
||||||
MyProc->xmin = InvalidTransactionId;
|
MyProc->xmin = InvalidTransactionId;
|
||||||
|
|
||||||
|
@ -1697,7 +1699,7 @@ AbortTransaction(void)
|
||||||
MyProc->subxids.nxids = 0;
|
MyProc->subxids.nxids = 0;
|
||||||
MyProc->subxids.overflowed = false;
|
MyProc->subxids.overflowed = false;
|
||||||
|
|
||||||
LWLockRelease(SInvalLock);
|
LWLockRelease(ProcArrayLock);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/backend/access/transam/xlog.c,v 1.191 2005/05/10 22:27:29 momjian Exp $
|
* $PostgreSQL: pgsql/src/backend/access/transam/xlog.c,v 1.192 2005/05/19 21:35:45 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -38,7 +38,7 @@
|
||||||
#include "storage/lwlock.h"
|
#include "storage/lwlock.h"
|
||||||
#include "storage/pmsignal.h"
|
#include "storage/pmsignal.h"
|
||||||
#include "storage/proc.h"
|
#include "storage/proc.h"
|
||||||
#include "storage/sinval.h"
|
#include "storage/procarray.h"
|
||||||
#include "storage/spin.h"
|
#include "storage/spin.h"
|
||||||
#include "utils/builtins.h"
|
#include "utils/builtins.h"
|
||||||
#include "utils/guc.h"
|
#include "utils/guc.h"
|
||||||
|
@ -776,7 +776,7 @@ begin:;
|
||||||
if (MyLastRecPtr.xrecoff == 0 && !no_tran)
|
if (MyLastRecPtr.xrecoff == 0 && !no_tran)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* We do not acquire SInvalLock here because of possible deadlock.
|
* We do not acquire ProcArrayLock here because of possible deadlock.
|
||||||
* Anyone who wants to inspect other procs' logRec must acquire
|
* Anyone who wants to inspect other procs' logRec must acquire
|
||||||
* WALInsertLock, instead. A better solution would be a per-PROC
|
* WALInsertLock, instead. A better solution would be a per-PROC
|
||||||
* spinlock, but no time for that before 7.2 --- tgl 12/19/01.
|
* spinlock, but no time for that before 7.2 --- tgl 12/19/01.
|
||||||
|
@ -4887,11 +4887,11 @@ CreateCheckPoint(bool shutdown, bool force)
|
||||||
* commits after REDO point).
|
* commits after REDO point).
|
||||||
*
|
*
|
||||||
* XXX temporarily ifdef'd out to avoid three-way deadlock condition:
|
* XXX temporarily ifdef'd out to avoid three-way deadlock condition:
|
||||||
* GetUndoRecPtr needs to grab SInvalLock to ensure that it is looking
|
* GetUndoRecPtr needs to grab ProcArrayLock to ensure that it is looking
|
||||||
* at a stable set of proc records, but grabbing SInvalLock while
|
* at a stable set of proc records, but grabbing ProcArrayLock while
|
||||||
* holding WALInsertLock is no good. GetNewTransactionId may cause a
|
* holding WALInsertLock is no good. GetNewTransactionId may cause a
|
||||||
* WAL record to be written while holding XidGenLock, and
|
* WAL record to be written while holding XidGenLock, and
|
||||||
* GetSnapshotData needs to get XidGenLock while holding SInvalLock,
|
* GetSnapshotData needs to get XidGenLock while holding ProcArrayLock,
|
||||||
* so there's a risk of deadlock. Need to find a better solution. See
|
* so there's a risk of deadlock. Need to find a better solution. See
|
||||||
* pgsql-hackers discussion of 17-Dec-01.
|
* pgsql-hackers discussion of 17-Dec-01.
|
||||||
*
|
*
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/catalog/index.c,v 1.255 2005/05/11 06:24:54 neilc Exp $
|
* $PostgreSQL: pgsql/src/backend/catalog/index.c,v 1.256 2005/05/19 21:35:45 tgl Exp $
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* INTERFACE ROUTINES
|
* INTERFACE ROUTINES
|
||||||
|
@ -38,7 +38,7 @@
|
||||||
#include "miscadmin.h"
|
#include "miscadmin.h"
|
||||||
#include "optimizer/clauses.h"
|
#include "optimizer/clauses.h"
|
||||||
#include "parser/parse_expr.h"
|
#include "parser/parse_expr.h"
|
||||||
#include "storage/sinval.h"
|
#include "storage/procarray.h"
|
||||||
#include "storage/smgr.h"
|
#include "storage/smgr.h"
|
||||||
#include "utils/builtins.h"
|
#include "utils/builtins.h"
|
||||||
#include "utils/fmgroids.h"
|
#include "utils/fmgroids.h"
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/commands/dbcommands.c,v 1.156 2005/04/14 20:03:23 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/commands/dbcommands.c,v 1.157 2005/05/19 21:35:45 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -40,7 +40,7 @@
|
||||||
#include "postmaster/bgwriter.h"
|
#include "postmaster/bgwriter.h"
|
||||||
#include "storage/fd.h"
|
#include "storage/fd.h"
|
||||||
#include "storage/freespace.h"
|
#include "storage/freespace.h"
|
||||||
#include "storage/sinval.h"
|
#include "storage/procarray.h"
|
||||||
#include "utils/acl.h"
|
#include "utils/acl.h"
|
||||||
#include "utils/array.h"
|
#include "utils/array.h"
|
||||||
#include "utils/builtins.h"
|
#include "utils/builtins.h"
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/commands/vacuum.c,v 1.308 2005/05/06 17:24:53 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/commands/vacuum.c,v 1.309 2005/05/19 21:35:45 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -36,7 +36,7 @@
|
||||||
#include "executor/executor.h"
|
#include "executor/executor.h"
|
||||||
#include "miscadmin.h"
|
#include "miscadmin.h"
|
||||||
#include "storage/freespace.h"
|
#include "storage/freespace.h"
|
||||||
#include "storage/sinval.h"
|
#include "storage/procarray.h"
|
||||||
#include "storage/smgr.h"
|
#include "storage/smgr.h"
|
||||||
#include "tcop/pquery.h"
|
#include "tcop/pquery.h"
|
||||||
#include "utils/acl.h"
|
#include "utils/acl.h"
|
||||||
|
|
|
@ -31,7 +31,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/commands/vacuumlazy.c,v 1.53 2005/05/07 21:32:24 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/commands/vacuumlazy.c,v 1.54 2005/05/19 21:35:46 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -45,7 +45,6 @@
|
||||||
#include "commands/vacuum.h"
|
#include "commands/vacuum.h"
|
||||||
#include "miscadmin.h"
|
#include "miscadmin.h"
|
||||||
#include "storage/freespace.h"
|
#include "storage/freespace.h"
|
||||||
#include "storage/sinval.h"
|
|
||||||
#include "storage/smgr.h"
|
#include "storage/smgr.h"
|
||||||
#include "utils/lsyscache.h"
|
#include "utils/lsyscache.h"
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/storage/buffer/buf_init.c,v 1.72 2005/03/04 20:21:06 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/storage/buffer/buf_init.c,v 1.73 2005/05/19 21:35:46 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -115,7 +115,7 @@ InitBufferPool(void)
|
||||||
buf->flags = 0;
|
buf->flags = 0;
|
||||||
buf->usage_count = 0;
|
buf->usage_count = 0;
|
||||||
buf->refcount = 0;
|
buf->refcount = 0;
|
||||||
buf->wait_backend_id = 0;
|
buf->wait_backend_pid = 0;
|
||||||
|
|
||||||
SpinLockInit(&buf->buf_hdr_lock);
|
SpinLockInit(&buf->buf_hdr_lock);
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/storage/buffer/bufmgr.c,v 1.188 2005/03/20 22:00:53 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/storage/buffer/bufmgr.c,v 1.189 2005/05/19 21:35:46 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -825,11 +825,11 @@ UnpinBuffer(BufferDesc *buf, bool fixOwner, bool trashOK)
|
||||||
buf->refcount == 1)
|
buf->refcount == 1)
|
||||||
{
|
{
|
||||||
/* we just released the last pin other than the waiter's */
|
/* we just released the last pin other than the waiter's */
|
||||||
BackendId wait_backend_id = buf->wait_backend_id;
|
int wait_backend_pid = buf->wait_backend_pid;
|
||||||
|
|
||||||
buf->flags &= ~BM_PIN_COUNT_WAITER;
|
buf->flags &= ~BM_PIN_COUNT_WAITER;
|
||||||
UnlockBufHdr_NoHoldoff(buf);
|
UnlockBufHdr_NoHoldoff(buf);
|
||||||
ProcSendSignal(wait_backend_id);
|
ProcSendSignal(wait_backend_pid);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
UnlockBufHdr_NoHoldoff(buf);
|
UnlockBufHdr_NoHoldoff(buf);
|
||||||
|
@ -1678,7 +1678,7 @@ UnlockBuffers(void)
|
||||||
* signal.
|
* signal.
|
||||||
*/
|
*/
|
||||||
if ((buf->flags & BM_PIN_COUNT_WAITER) != 0 &&
|
if ((buf->flags & BM_PIN_COUNT_WAITER) != 0 &&
|
||||||
buf->wait_backend_id == MyBackendId)
|
buf->wait_backend_pid == MyProcPid)
|
||||||
buf->flags &= ~BM_PIN_COUNT_WAITER;
|
buf->flags &= ~BM_PIN_COUNT_WAITER;
|
||||||
|
|
||||||
UnlockBufHdr_NoHoldoff(buf);
|
UnlockBufHdr_NoHoldoff(buf);
|
||||||
|
@ -1820,7 +1820,7 @@ LockBufferForCleanup(Buffer buffer)
|
||||||
LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
|
LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
|
||||||
elog(ERROR, "multiple backends attempting to wait for pincount 1");
|
elog(ERROR, "multiple backends attempting to wait for pincount 1");
|
||||||
}
|
}
|
||||||
bufHdr->wait_backend_id = MyBackendId;
|
bufHdr->wait_backend_pid = MyProcPid;
|
||||||
bufHdr->flags |= BM_PIN_COUNT_WAITER;
|
bufHdr->flags |= BM_PIN_COUNT_WAITER;
|
||||||
PinCountWaitBuf = bufHdr;
|
PinCountWaitBuf = bufHdr;
|
||||||
UnlockBufHdr_NoHoldoff(bufHdr);
|
UnlockBufHdr_NoHoldoff(bufHdr);
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
#
|
#
|
||||||
# Makefile for storage/ipc
|
# Makefile for storage/ipc
|
||||||
#
|
#
|
||||||
# $PostgreSQL: pgsql/src/backend/storage/ipc/Makefile,v 1.18 2003/11/29 19:51:56 pgsql Exp $
|
# $PostgreSQL: pgsql/src/backend/storage/ipc/Makefile,v 1.19 2005/05/19 21:35:46 tgl Exp $
|
||||||
#
|
#
|
||||||
|
|
||||||
subdir = src/backend/storage/ipc
|
subdir = src/backend/storage/ipc
|
||||||
|
@ -15,7 +15,8 @@ override CFLAGS+= -fno-inline
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
OBJS = ipc.o ipci.o pmsignal.o shmem.o shmqueue.o sinval.o sinvaladt.o
|
OBJS = ipc.o ipci.o pmsignal.o procarray.o shmem.o shmqueue.o \
|
||||||
|
sinval.o sinvaladt.o
|
||||||
|
|
||||||
all: SUBSYS.o
|
all: SUBSYS.o
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/storage/ipc/ipci.c,v 1.75 2005/04/28 21:47:15 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/storage/ipc/ipci.c,v 1.76 2005/05/19 21:35:46 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -30,6 +30,7 @@
|
||||||
#include "storage/pg_shmem.h"
|
#include "storage/pg_shmem.h"
|
||||||
#include "storage/pmsignal.h"
|
#include "storage/pmsignal.h"
|
||||||
#include "storage/proc.h"
|
#include "storage/proc.h"
|
||||||
|
#include "storage/procarray.h"
|
||||||
#include "storage/sinval.h"
|
#include "storage/sinval.h"
|
||||||
#include "storage/spin.h"
|
#include "storage/spin.h"
|
||||||
|
|
||||||
|
@ -78,6 +79,7 @@ CreateSharedMemoryAndSemaphores(bool makePrivate,
|
||||||
size += SUBTRANSShmemSize();
|
size += SUBTRANSShmemSize();
|
||||||
size += MultiXactShmemSize();
|
size += MultiXactShmemSize();
|
||||||
size += LWLockShmemSize();
|
size += LWLockShmemSize();
|
||||||
|
size += ProcArrayShmemSize(maxBackends);
|
||||||
size += SInvalShmemSize(maxBackends);
|
size += SInvalShmemSize(maxBackends);
|
||||||
size += FreeSpaceShmemSize();
|
size += FreeSpaceShmemSize();
|
||||||
size += BgWriterShmemSize();
|
size += BgWriterShmemSize();
|
||||||
|
@ -155,6 +157,7 @@ CreateSharedMemoryAndSemaphores(bool makePrivate,
|
||||||
* Set up process table
|
* Set up process table
|
||||||
*/
|
*/
|
||||||
InitProcGlobal(maxBackends);
|
InitProcGlobal(maxBackends);
|
||||||
|
CreateSharedProcArray(maxBackends);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Set up shared-inval messaging
|
* Set up shared-inval messaging
|
||||||
|
|
|
@ -0,0 +1,787 @@
|
||||||
|
/*-------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* procarray.c
|
||||||
|
* POSTGRES process array code.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* This module maintains an unsorted array of the PGPROC structures for all
|
||||||
|
* active backends. Although there are several uses for this, the principal
|
||||||
|
* one is as a means of determining the set of currently running transactions.
|
||||||
|
*
|
||||||
|
* Because of various subtle race conditions it is critical that a backend
|
||||||
|
* hold the correct locks while setting or clearing its MyProc->xid field.
|
||||||
|
* See notes in GetSnapshotData.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
|
||||||
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* IDENTIFICATION
|
||||||
|
* $PostgreSQL: pgsql/src/backend/storage/ipc/procarray.c,v 1.1 2005/05/19 21:35:46 tgl Exp $
|
||||||
|
*
|
||||||
|
*-------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
#include "postgres.h"
|
||||||
|
|
||||||
|
#include "access/subtrans.h"
|
||||||
|
#include "miscadmin.h"
|
||||||
|
#include "storage/proc.h"
|
||||||
|
#include "storage/procarray.h"
|
||||||
|
#include "utils/tqual.h"
|
||||||
|
|
||||||
|
|
||||||
|
/* Our shared memory area */
|
||||||
|
typedef struct ProcArrayStruct
|
||||||
|
{
|
||||||
|
int numProcs; /* number of valid procs entries */
|
||||||
|
int maxProcs; /* allocated size of procs array */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We declare procs[] as 1 entry because C wants a fixed-size array,
|
||||||
|
* but actually it is maxProcs entries long.
|
||||||
|
*/
|
||||||
|
PGPROC *procs[1]; /* VARIABLE LENGTH ARRAY */
|
||||||
|
} ProcArrayStruct;
|
||||||
|
|
||||||
|
static ProcArrayStruct *procArray;
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef XIDCACHE_DEBUG
|
||||||
|
|
||||||
|
/* counters for XidCache measurement */
|
||||||
|
static long xc_by_recent_xmin = 0;
|
||||||
|
static long xc_by_main_xid = 0;
|
||||||
|
static long xc_by_child_xid = 0;
|
||||||
|
static long xc_slow_answer = 0;
|
||||||
|
|
||||||
|
#define xc_by_recent_xmin_inc() (xc_by_recent_xmin++)
|
||||||
|
#define xc_by_main_xid_inc() (xc_by_main_xid++)
|
||||||
|
#define xc_by_child_xid_inc() (xc_by_child_xid++)
|
||||||
|
#define xc_slow_answer_inc() (xc_slow_answer++)
|
||||||
|
|
||||||
|
static void DisplayXidCache(void);
|
||||||
|
|
||||||
|
#else /* !XIDCACHE_DEBUG */
|
||||||
|
|
||||||
|
#define xc_by_recent_xmin_inc() ((void) 0)
|
||||||
|
#define xc_by_main_xid_inc() ((void) 0)
|
||||||
|
#define xc_by_child_xid_inc() ((void) 0)
|
||||||
|
#define xc_slow_answer_inc() ((void) 0)
|
||||||
|
|
||||||
|
#endif /* XIDCACHE_DEBUG */
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Report shared-memory space needed by CreateSharedProcArray.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
ProcArrayShmemSize(int maxBackends)
|
||||||
|
{
|
||||||
|
/* sizeof(ProcArrayStruct) includes the first array element */
|
||||||
|
return MAXALIGN(sizeof(ProcArrayStruct) +
|
||||||
|
(maxBackends - 1) * sizeof(PGPROC *));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Initialize the shared PGPROC array during postmaster startup.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
CreateSharedProcArray(int maxBackends)
|
||||||
|
{
|
||||||
|
bool found;
|
||||||
|
|
||||||
|
/* Create or attach to the ProcArray shared structure */
|
||||||
|
procArray = (ProcArrayStruct *)
|
||||||
|
ShmemInitStruct("Proc Array", ProcArrayShmemSize(maxBackends),
|
||||||
|
&found);
|
||||||
|
|
||||||
|
if (!found)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* We're the first - initialize.
|
||||||
|
*/
|
||||||
|
procArray->numProcs = 0;
|
||||||
|
procArray->maxProcs = maxBackends;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add my own PGPROC (found in the global MyProc) to the shared array.
|
||||||
|
*
|
||||||
|
* This must be called during backend startup, after fully initializing
|
||||||
|
* the contents of MyProc.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
ProcArrayAddMyself(void)
|
||||||
|
{
|
||||||
|
ProcArrayStruct *arrayP = procArray;
|
||||||
|
|
||||||
|
LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE);
|
||||||
|
|
||||||
|
if (arrayP->numProcs >= arrayP->maxProcs)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Ooops, no room. (This really shouldn't happen, since there is
|
||||||
|
* a fixed supply of PGPROC structs too, and so we should have
|
||||||
|
* failed earlier.)
|
||||||
|
*/
|
||||||
|
LWLockRelease(ProcArrayLock);
|
||||||
|
ereport(FATAL,
|
||||||
|
(errcode(ERRCODE_TOO_MANY_CONNECTIONS),
|
||||||
|
errmsg("sorry, too many clients already")));
|
||||||
|
}
|
||||||
|
|
||||||
|
arrayP->procs[arrayP->numProcs] = MyProc;
|
||||||
|
arrayP->numProcs++;
|
||||||
|
|
||||||
|
LWLockRelease(ProcArrayLock);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Remove my own PGPROC (found in the global MyProc) from the shared array.
|
||||||
|
*
|
||||||
|
* This must be called during backend shutdown.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
ProcArrayRemoveMyself(void)
|
||||||
|
{
|
||||||
|
ProcArrayStruct *arrayP = procArray;
|
||||||
|
int index;
|
||||||
|
|
||||||
|
#ifdef XIDCACHE_DEBUG
|
||||||
|
DisplayXidCache();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE);
|
||||||
|
|
||||||
|
for (index = 0; index < arrayP->numProcs; index++)
|
||||||
|
{
|
||||||
|
if (arrayP->procs[index] == MyProc)
|
||||||
|
{
|
||||||
|
arrayP->procs[index] = arrayP->procs[arrayP->numProcs - 1];
|
||||||
|
arrayP->numProcs--;
|
||||||
|
LWLockRelease(ProcArrayLock);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Ooops */
|
||||||
|
LWLockRelease(ProcArrayLock);
|
||||||
|
|
||||||
|
elog(LOG, "failed to find my own proc %p in ProcArray", MyProc);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* TransactionIdIsInProgress -- is given transaction running in some backend
|
||||||
|
*
|
||||||
|
* There are three possibilities for finding a running transaction:
|
||||||
|
*
|
||||||
|
* 1. the given Xid is a main transaction Id. We will find this out cheaply
|
||||||
|
* by looking at the PGPROC struct for each backend.
|
||||||
|
*
|
||||||
|
* 2. the given Xid is one of the cached subxact Xids in the PGPROC array.
|
||||||
|
* We can find this out cheaply too.
|
||||||
|
*
|
||||||
|
* 3. Search the SubTrans tree to find the Xid's topmost parent, and then
|
||||||
|
* see if that is running according to PGPROC. This is the slowest, but
|
||||||
|
* sadly it has to be done always if the other two failed, unless we see
|
||||||
|
* that the cached subxact sets are complete (none have overflowed).
|
||||||
|
*
|
||||||
|
* ProcArrayLock has to be held while we do 1 and 2. If we save the top Xids
|
||||||
|
* while doing 1, we can release the ProcArrayLock while we do 3. This buys
|
||||||
|
* back some concurrency (we can't retrieve the main Xids from PGPROC again
|
||||||
|
* anyway; see GetNewTransactionId).
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
TransactionIdIsInProgress(TransactionId xid)
|
||||||
|
{
|
||||||
|
bool result = false;
|
||||||
|
ProcArrayStruct *arrayP = procArray;
|
||||||
|
int i,
|
||||||
|
j;
|
||||||
|
int nxids = 0;
|
||||||
|
TransactionId *xids;
|
||||||
|
TransactionId topxid;
|
||||||
|
bool locked;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Don't bother checking a transaction older than RecentXmin; it
|
||||||
|
* could not possibly still be running.
|
||||||
|
*/
|
||||||
|
if (TransactionIdPrecedes(xid, RecentXmin))
|
||||||
|
{
|
||||||
|
xc_by_recent_xmin_inc();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get workspace to remember main XIDs in */
|
||||||
|
xids = (TransactionId *) palloc(sizeof(TransactionId) * arrayP->maxProcs);
|
||||||
|
|
||||||
|
LWLockAcquire(ProcArrayLock, LW_SHARED);
|
||||||
|
locked = true;
|
||||||
|
|
||||||
|
for (i = 0; i < arrayP->numProcs; i++)
|
||||||
|
{
|
||||||
|
PGPROC *proc = arrayP->procs[i];
|
||||||
|
|
||||||
|
/* Fetch xid just once - see GetNewTransactionId */
|
||||||
|
TransactionId pxid = proc->xid;
|
||||||
|
|
||||||
|
if (!TransactionIdIsValid(pxid))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Step 1: check the main Xid
|
||||||
|
*/
|
||||||
|
if (TransactionIdEquals(pxid, xid))
|
||||||
|
{
|
||||||
|
xc_by_main_xid_inc();
|
||||||
|
result = true;
|
||||||
|
goto result_known;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We can ignore main Xids that are younger than the target
|
||||||
|
* Xid, since the target could not possibly be their child.
|
||||||
|
*/
|
||||||
|
if (TransactionIdPrecedes(xid, pxid))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Step 2: check the cached child-Xids arrays
|
||||||
|
*/
|
||||||
|
for (j = proc->subxids.nxids - 1; j >= 0; j--)
|
||||||
|
{
|
||||||
|
/* Fetch xid just once - see GetNewTransactionId */
|
||||||
|
TransactionId cxid = proc->subxids.xids[j];
|
||||||
|
|
||||||
|
if (TransactionIdEquals(cxid, xid))
|
||||||
|
{
|
||||||
|
xc_by_child_xid_inc();
|
||||||
|
result = true;
|
||||||
|
goto result_known;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Save the main Xid for step 3. We only need to remember
|
||||||
|
* main Xids that have uncached children. (Note: there is no
|
||||||
|
* race condition here because the overflowed flag cannot be
|
||||||
|
* cleared, only set, while we hold ProcArrayLock. So we can't
|
||||||
|
* miss an Xid that we need to worry about.)
|
||||||
|
*/
|
||||||
|
if (proc->subxids.overflowed)
|
||||||
|
xids[nxids++] = pxid;
|
||||||
|
}
|
||||||
|
|
||||||
|
LWLockRelease(ProcArrayLock);
|
||||||
|
locked = false;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If none of the relevant caches overflowed, we know the Xid is not
|
||||||
|
* running without looking at pg_subtrans.
|
||||||
|
*/
|
||||||
|
if (nxids == 0)
|
||||||
|
goto result_known;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Step 3: have to check pg_subtrans.
|
||||||
|
*
|
||||||
|
* At this point, we know it's either a subtransaction of one of the Xids
|
||||||
|
* in xids[], or it's not running. If it's an already-failed
|
||||||
|
* subtransaction, we want to say "not running" even though its parent
|
||||||
|
* may still be running. So first, check pg_clog to see if it's been
|
||||||
|
* aborted.
|
||||||
|
*/
|
||||||
|
xc_slow_answer_inc();
|
||||||
|
|
||||||
|
if (TransactionIdDidAbort(xid))
|
||||||
|
goto result_known;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* It isn't aborted, so check whether the transaction tree it belongs
|
||||||
|
* to is still running (or, more precisely, whether it was running
|
||||||
|
* when this routine started -- note that we already released
|
||||||
|
* ProcArrayLock).
|
||||||
|
*/
|
||||||
|
topxid = SubTransGetTopmostTransaction(xid);
|
||||||
|
Assert(TransactionIdIsValid(topxid));
|
||||||
|
if (!TransactionIdEquals(topxid, xid))
|
||||||
|
{
|
||||||
|
for (i = 0; i < nxids; i++)
|
||||||
|
{
|
||||||
|
if (TransactionIdEquals(xids[i], topxid))
|
||||||
|
{
|
||||||
|
result = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result_known:
|
||||||
|
if (locked)
|
||||||
|
LWLockRelease(ProcArrayLock);
|
||||||
|
|
||||||
|
pfree(xids);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* GetOldestXmin -- returns oldest transaction that was running
|
||||||
|
* when any current transaction was started.
|
||||||
|
*
|
||||||
|
* If allDbs is TRUE then all backends are considered; if allDbs is FALSE
|
||||||
|
* then only backends running in my own database are considered.
|
||||||
|
*
|
||||||
|
* This is used by VACUUM to decide which deleted tuples must be preserved
|
||||||
|
* in a table. allDbs = TRUE is needed for shared relations, but allDbs =
|
||||||
|
* FALSE is sufficient for non-shared relations, since only backends in my
|
||||||
|
* own database could ever see the tuples in them.
|
||||||
|
*
|
||||||
|
* This is also used to determine where to truncate pg_subtrans. allDbs
|
||||||
|
* must be TRUE for that case.
|
||||||
|
*
|
||||||
|
* Note: we include the currently running xids in the set of considered xids.
|
||||||
|
* This ensures that if a just-started xact has not yet set its snapshot,
|
||||||
|
* when it does set the snapshot it cannot set xmin less than what we compute.
|
||||||
|
*/
|
||||||
|
TransactionId
|
||||||
|
GetOldestXmin(bool allDbs)
|
||||||
|
{
|
||||||
|
ProcArrayStruct *arrayP = procArray;
|
||||||
|
TransactionId result;
|
||||||
|
int index;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Normally we start the min() calculation with our own XID. But if
|
||||||
|
* called by checkpointer, we will not be inside a transaction, so use
|
||||||
|
* next XID as starting point for min() calculation. (Note that if
|
||||||
|
* there are no xacts running at all, that will be the subtrans
|
||||||
|
* truncation point!)
|
||||||
|
*/
|
||||||
|
if (IsTransactionState())
|
||||||
|
result = GetTopTransactionId();
|
||||||
|
else
|
||||||
|
result = ReadNewTransactionId();
|
||||||
|
|
||||||
|
LWLockAcquire(ProcArrayLock, LW_SHARED);
|
||||||
|
|
||||||
|
for (index = 0; index < arrayP->numProcs; index++)
|
||||||
|
{
|
||||||
|
PGPROC *proc = arrayP->procs[index];
|
||||||
|
|
||||||
|
if (allDbs || proc->databaseId == MyDatabaseId)
|
||||||
|
{
|
||||||
|
/* Fetch xid just once - see GetNewTransactionId */
|
||||||
|
TransactionId xid = proc->xid;
|
||||||
|
|
||||||
|
if (TransactionIdIsNormal(xid))
|
||||||
|
{
|
||||||
|
if (TransactionIdPrecedes(xid, result))
|
||||||
|
result = xid;
|
||||||
|
xid = proc->xmin;
|
||||||
|
if (TransactionIdIsNormal(xid))
|
||||||
|
if (TransactionIdPrecedes(xid, result))
|
||||||
|
result = xid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LWLockRelease(ProcArrayLock);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*----------
|
||||||
|
* GetSnapshotData -- returns information about running transactions.
|
||||||
|
*
|
||||||
|
* The returned snapshot includes xmin (lowest still-running xact ID),
|
||||||
|
* xmax (next xact ID to be assigned), and a list of running xact IDs
|
||||||
|
* in the range xmin <= xid < xmax. It is used as follows:
|
||||||
|
* All xact IDs < xmin are considered finished.
|
||||||
|
* All xact IDs >= xmax are considered still running.
|
||||||
|
* For an xact ID xmin <= xid < xmax, consult list to see whether
|
||||||
|
* it is considered running or not.
|
||||||
|
* This ensures that the set of transactions seen as "running" by the
|
||||||
|
* current xact will not change after it takes the snapshot.
|
||||||
|
*
|
||||||
|
* Note that only top-level XIDs are included in the snapshot. We can
|
||||||
|
* still apply the xmin and xmax limits to subtransaction XIDs, but we
|
||||||
|
* need to work a bit harder to see if XIDs in [xmin..xmax) are running.
|
||||||
|
*
|
||||||
|
* We also update the following backend-global variables:
|
||||||
|
* TransactionXmin: the oldest xmin of any snapshot in use in the
|
||||||
|
* current transaction (this is the same as MyProc->xmin). This
|
||||||
|
* is just the xmin computed for the first, serializable snapshot.
|
||||||
|
* RecentXmin: the xmin computed for the most recent snapshot. XIDs
|
||||||
|
* older than this are known not running any more.
|
||||||
|
* RecentGlobalXmin: the global xmin (oldest TransactionXmin across all
|
||||||
|
* running transactions). This is the same computation done by
|
||||||
|
* GetOldestXmin(TRUE).
|
||||||
|
*----------
|
||||||
|
*/
|
||||||
|
Snapshot
|
||||||
|
GetSnapshotData(Snapshot snapshot, bool serializable)
|
||||||
|
{
|
||||||
|
ProcArrayStruct *arrayP = procArray;
|
||||||
|
TransactionId xmin;
|
||||||
|
TransactionId xmax;
|
||||||
|
TransactionId globalxmin;
|
||||||
|
int index;
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
|
Assert(snapshot != NULL);
|
||||||
|
|
||||||
|
/* Serializable snapshot must be computed before any other... */
|
||||||
|
Assert(serializable ?
|
||||||
|
!TransactionIdIsValid(MyProc->xmin) :
|
||||||
|
TransactionIdIsValid(MyProc->xmin));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Allocating space for MaxBackends xids is usually overkill;
|
||||||
|
* lastBackend would be sufficient. But it seems better to do the
|
||||||
|
* malloc while not holding the lock, so we can't look at lastBackend.
|
||||||
|
*
|
||||||
|
* This does open a possibility for avoiding repeated malloc/free: since
|
||||||
|
* MaxBackends does not change at runtime, we can simply reuse the
|
||||||
|
* previous xip array if any. (This relies on the fact that all
|
||||||
|
* callers pass static SnapshotData structs.)
|
||||||
|
*/
|
||||||
|
if (snapshot->xip == NULL)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* First call for this snapshot
|
||||||
|
*/
|
||||||
|
snapshot->xip = (TransactionId *)
|
||||||
|
malloc(MaxBackends * sizeof(TransactionId));
|
||||||
|
if (snapshot->xip == NULL)
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_OUT_OF_MEMORY),
|
||||||
|
errmsg("out of memory")));
|
||||||
|
}
|
||||||
|
|
||||||
|
globalxmin = xmin = GetTopTransactionId();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If we are going to set MyProc->xmin then we'd better get exclusive
|
||||||
|
* lock; if not, this is a read-only operation so it can be shared.
|
||||||
|
*/
|
||||||
|
LWLockAcquire(ProcArrayLock, serializable ? LW_EXCLUSIVE : LW_SHARED);
|
||||||
|
|
||||||
|
/*--------------------
|
||||||
|
* Unfortunately, we have to call ReadNewTransactionId() after acquiring
|
||||||
|
* ProcArrayLock above. It's not good because ReadNewTransactionId() does
|
||||||
|
* LWLockAcquire(XidGenLock), but *necessary*. We need to be sure that
|
||||||
|
* no transactions exit the set of currently-running transactions
|
||||||
|
* between the time we fetch xmax and the time we finish building our
|
||||||
|
* snapshot. Otherwise we could have a situation like this:
|
||||||
|
*
|
||||||
|
* 1. Tx Old is running (in Read Committed mode).
|
||||||
|
* 2. Tx S reads new transaction ID into xmax, then
|
||||||
|
* is swapped out before acquiring ProcArrayLock.
|
||||||
|
* 3. Tx New gets new transaction ID (>= S' xmax),
|
||||||
|
* makes changes and commits.
|
||||||
|
* 4. Tx Old changes some row R changed by Tx New and commits.
|
||||||
|
* 5. Tx S finishes getting its snapshot data. It sees Tx Old as
|
||||||
|
* done, but sees Tx New as still running (since New >= xmax).
|
||||||
|
*
|
||||||
|
* Now S will see R changed by both Tx Old and Tx New, *but* does not
|
||||||
|
* see other changes made by Tx New. If S is supposed to be in
|
||||||
|
* Serializable mode, this is wrong.
|
||||||
|
*
|
||||||
|
* By locking ProcArrayLock before we read xmax, we ensure that TX Old
|
||||||
|
* cannot exit the set of running transactions seen by Tx S. Therefore
|
||||||
|
* both Old and New will be seen as still running => no inconsistency.
|
||||||
|
*--------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
xmax = ReadNewTransactionId();
|
||||||
|
|
||||||
|
for (index = 0; index < arrayP->numProcs; index++)
|
||||||
|
{
|
||||||
|
PGPROC *proc = arrayP->procs[index];
|
||||||
|
|
||||||
|
/* Fetch xid just once - see GetNewTransactionId */
|
||||||
|
TransactionId xid = proc->xid;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Ignore my own proc (dealt with my xid above), procs not
|
||||||
|
* running a transaction, and xacts started since we read the
|
||||||
|
* next transaction ID. There's no need to store XIDs above
|
||||||
|
* what we got from ReadNewTransactionId, since we'll treat
|
||||||
|
* them as running anyway. We also assume that such xacts
|
||||||
|
* can't compute an xmin older than ours, so they needn't be
|
||||||
|
* considered in computing globalxmin.
|
||||||
|
*/
|
||||||
|
if (proc == MyProc ||
|
||||||
|
!TransactionIdIsNormal(xid) ||
|
||||||
|
TransactionIdFollowsOrEquals(xid, xmax))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (TransactionIdPrecedes(xid, xmin))
|
||||||
|
xmin = xid;
|
||||||
|
snapshot->xip[count] = xid;
|
||||||
|
count++;
|
||||||
|
|
||||||
|
/* Update globalxmin to be the smallest valid xmin */
|
||||||
|
xid = proc->xmin;
|
||||||
|
if (TransactionIdIsNormal(xid))
|
||||||
|
if (TransactionIdPrecedes(xid, globalxmin))
|
||||||
|
globalxmin = xid;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (serializable)
|
||||||
|
MyProc->xmin = TransactionXmin = xmin;
|
||||||
|
|
||||||
|
LWLockRelease(ProcArrayLock);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Update globalxmin to include actual process xids. This is a
|
||||||
|
* slightly different way of computing it than GetOldestXmin uses, but
|
||||||
|
* should give the same result.
|
||||||
|
*/
|
||||||
|
if (TransactionIdPrecedes(xmin, globalxmin))
|
||||||
|
globalxmin = xmin;
|
||||||
|
|
||||||
|
/* Update global variables too */
|
||||||
|
RecentGlobalXmin = globalxmin;
|
||||||
|
RecentXmin = xmin;
|
||||||
|
|
||||||
|
snapshot->xmin = xmin;
|
||||||
|
snapshot->xmax = xmax;
|
||||||
|
snapshot->xcnt = count;
|
||||||
|
|
||||||
|
snapshot->curcid = GetCurrentCommandId();
|
||||||
|
|
||||||
|
return snapshot;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* DatabaseHasActiveBackends -- are there any backends running in the given DB
|
||||||
|
*
|
||||||
|
* If 'ignoreMyself' is TRUE, ignore this particular backend while checking
|
||||||
|
* for backends in the target database.
|
||||||
|
*
|
||||||
|
* This function is used to interlock DROP DATABASE against there being
|
||||||
|
* any active backends in the target DB --- dropping the DB while active
|
||||||
|
* backends remain would be a Bad Thing. Note that we cannot detect here
|
||||||
|
* the possibility of a newly-started backend that is trying to connect
|
||||||
|
* to the doomed database, so additional interlocking is needed during
|
||||||
|
* backend startup.
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
DatabaseHasActiveBackends(Oid databaseId, bool ignoreMyself)
|
||||||
|
{
|
||||||
|
bool result = false;
|
||||||
|
ProcArrayStruct *arrayP = procArray;
|
||||||
|
int index;
|
||||||
|
|
||||||
|
LWLockAcquire(ProcArrayLock, LW_SHARED);
|
||||||
|
|
||||||
|
for (index = 0; index < arrayP->numProcs; index++)
|
||||||
|
{
|
||||||
|
PGPROC *proc = arrayP->procs[index];
|
||||||
|
|
||||||
|
if (proc->databaseId == databaseId)
|
||||||
|
{
|
||||||
|
if (ignoreMyself && proc == MyProc)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
result = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LWLockRelease(ProcArrayLock);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* BackendPidGetProc -- get a backend's PGPROC given its PID
|
||||||
|
*/
|
||||||
|
struct PGPROC *
|
||||||
|
BackendPidGetProc(int pid)
|
||||||
|
{
|
||||||
|
PGPROC *result = NULL;
|
||||||
|
ProcArrayStruct *arrayP = procArray;
|
||||||
|
int index;
|
||||||
|
|
||||||
|
LWLockAcquire(ProcArrayLock, LW_SHARED);
|
||||||
|
|
||||||
|
for (index = 0; index < arrayP->numProcs; index++)
|
||||||
|
{
|
||||||
|
PGPROC *proc = arrayP->procs[index];
|
||||||
|
|
||||||
|
if (proc->pid == pid)
|
||||||
|
{
|
||||||
|
result = proc;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LWLockRelease(ProcArrayLock);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* IsBackendPid -- is a given pid a running backend
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
IsBackendPid(int pid)
|
||||||
|
{
|
||||||
|
return (BackendPidGetProc(pid) != NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* CountActiveBackends --- count backends (other than myself) that are in
|
||||||
|
* active transactions. This is used as a heuristic to decide if
|
||||||
|
* a pre-XLOG-flush delay is worthwhile during commit.
|
||||||
|
*
|
||||||
|
* An active transaction is something that has written at least one XLOG
|
||||||
|
* record; read-only transactions don't count. Also, do not count backends
|
||||||
|
* that are blocked waiting for locks, since they are not going to get to
|
||||||
|
* run until someone else commits.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
CountActiveBackends(void)
|
||||||
|
{
|
||||||
|
ProcArrayStruct *arrayP = procArray;
|
||||||
|
int count = 0;
|
||||||
|
int index;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Note: for speed, we don't acquire ProcArrayLock. This is a little bit
|
||||||
|
* bogus, but since we are only testing xrecoff for zero or nonzero,
|
||||||
|
* it should be OK. The result is only used for heuristic purposes
|
||||||
|
* anyway...
|
||||||
|
*/
|
||||||
|
for (index = 0; index < arrayP->numProcs; index++)
|
||||||
|
{
|
||||||
|
PGPROC *proc = arrayP->procs[index];
|
||||||
|
|
||||||
|
if (proc == MyProc)
|
||||||
|
continue; /* do not count myself */
|
||||||
|
if (proc->logRec.xrecoff == 0)
|
||||||
|
continue; /* do not count if not in a transaction */
|
||||||
|
if (proc->waitLock != NULL)
|
||||||
|
continue; /* do not count if blocked on a lock */
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* CountEmptyBackendSlots - count empty slots in backend process table
|
||||||
|
*
|
||||||
|
* Acquiring the lock here is almost certainly overkill, but just in
|
||||||
|
* case fetching an int is not atomic on your machine ...
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
CountEmptyBackendSlots(void)
|
||||||
|
{
|
||||||
|
int count;
|
||||||
|
|
||||||
|
LWLockAcquire(ProcArrayLock, LW_SHARED);
|
||||||
|
|
||||||
|
count = procArray->maxProcs - procArray->numProcs;
|
||||||
|
|
||||||
|
LWLockRelease(ProcArrayLock);
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define XidCacheRemove(i) \
|
||||||
|
do { \
|
||||||
|
MyProc->subxids.xids[i] = MyProc->subxids.xids[MyProc->subxids.nxids - 1]; \
|
||||||
|
MyProc->subxids.nxids--; \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* XidCacheRemoveRunningXids
|
||||||
|
*
|
||||||
|
* Remove a bunch of TransactionIds from the list of known-running
|
||||||
|
* subtransactions for my backend. Both the specified xid and those in
|
||||||
|
* the xids[] array (of length nxids) are removed from the subxids cache.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
XidCacheRemoveRunningXids(TransactionId xid, int nxids, TransactionId *xids)
|
||||||
|
{
|
||||||
|
int i,
|
||||||
|
j;
|
||||||
|
|
||||||
|
Assert(!TransactionIdEquals(xid, InvalidTransactionId));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We must hold ProcArrayLock exclusively in order to remove transactions
|
||||||
|
* from the PGPROC array. (See notes in GetSnapshotData.) It's
|
||||||
|
* possible this could be relaxed since we know this routine is only
|
||||||
|
* used to abort subtransactions, but pending closer analysis we'd
|
||||||
|
* best be conservative.
|
||||||
|
*/
|
||||||
|
LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Under normal circumstances xid and xids[] will be in increasing
|
||||||
|
* order, as will be the entries in subxids. Scan backwards to avoid
|
||||||
|
* O(N^2) behavior when removing a lot of xids.
|
||||||
|
*/
|
||||||
|
for (i = nxids - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
TransactionId anxid = xids[i];
|
||||||
|
|
||||||
|
for (j = MyProc->subxids.nxids - 1; j >= 0; j--)
|
||||||
|
{
|
||||||
|
if (TransactionIdEquals(MyProc->subxids.xids[j], anxid))
|
||||||
|
{
|
||||||
|
XidCacheRemove(j);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* Ordinarily we should have found it, unless the cache has overflowed.
|
||||||
|
* However it's also possible for this routine to be invoked multiple
|
||||||
|
* times for the same subtransaction, in case of an error during
|
||||||
|
* AbortSubTransaction. So instead of Assert, emit a debug warning.
|
||||||
|
*/
|
||||||
|
if (j < 0 && !MyProc->subxids.overflowed)
|
||||||
|
elog(WARNING, "did not find subXID %u in MyProc", anxid);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (j = MyProc->subxids.nxids - 1; j >= 0; j--)
|
||||||
|
{
|
||||||
|
if (TransactionIdEquals(MyProc->subxids.xids[j], xid))
|
||||||
|
{
|
||||||
|
XidCacheRemove(j);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* Ordinarily we should have found it, unless the cache has overflowed */
|
||||||
|
if (j < 0 && !MyProc->subxids.overflowed)
|
||||||
|
elog(WARNING, "did not find subXID %u in MyProc", xid);
|
||||||
|
|
||||||
|
LWLockRelease(ProcArrayLock);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef XIDCACHE_DEBUG
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Print stats about effectiveness of XID cache
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
DisplayXidCache(void)
|
||||||
|
{
|
||||||
|
fprintf(stderr,
|
||||||
|
"XidCache: xmin: %ld, mainxid: %ld, childxid: %ld, slow: %ld\n",
|
||||||
|
xc_by_recent_xmin,
|
||||||
|
xc_by_main_xid,
|
||||||
|
xc_by_child_xid,
|
||||||
|
xc_slow_answer);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* XIDCACHE_DEBUG */
|
|
@ -8,7 +8,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/storage/ipc/sinval.c,v 1.75 2004/12/31 22:00:56 pgsql Exp $
|
* $PostgreSQL: pgsql/src/backend/storage/ipc/sinval.c,v 1.76 2005/05/19 21:35:46 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -16,41 +16,17 @@
|
||||||
|
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
|
|
||||||
#include "access/subtrans.h"
|
#include "access/xact.h"
|
||||||
#include "access/transam.h"
|
|
||||||
#include "commands/async.h"
|
#include "commands/async.h"
|
||||||
|
#include "miscadmin.h"
|
||||||
|
#include "storage/backendid.h"
|
||||||
#include "storage/ipc.h"
|
#include "storage/ipc.h"
|
||||||
#include "storage/proc.h"
|
#include "storage/proc.h"
|
||||||
#include "storage/sinval.h"
|
#include "storage/sinval.h"
|
||||||
#include "storage/sinvaladt.h"
|
#include "storage/sinvaladt.h"
|
||||||
#include "utils/inval.h"
|
#include "utils/inval.h"
|
||||||
#include "utils/tqual.h"
|
|
||||||
#include "miscadmin.h"
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef XIDCACHE_DEBUG
|
|
||||||
|
|
||||||
/* counters for XidCache measurement */
|
|
||||||
static long xc_by_recent_xmin = 0;
|
|
||||||
static long xc_by_main_xid = 0;
|
|
||||||
static long xc_by_child_xid = 0;
|
|
||||||
static long xc_slow_answer = 0;
|
|
||||||
|
|
||||||
#define xc_by_recent_xmin_inc() (xc_by_recent_xmin++)
|
|
||||||
#define xc_by_main_xid_inc() (xc_by_main_xid++)
|
|
||||||
#define xc_by_child_xid_inc() (xc_by_child_xid++)
|
|
||||||
#define xc_slow_answer_inc() (xc_slow_answer++)
|
|
||||||
|
|
||||||
static void DisplayXidCache(int code, Datum arg);
|
|
||||||
|
|
||||||
#else /* !XIDCACHE_DEBUG */
|
|
||||||
|
|
||||||
#define xc_by_recent_xmin_inc() ((void) 0)
|
|
||||||
#define xc_by_main_xid_inc() ((void) 0)
|
|
||||||
#define xc_by_child_xid_inc() ((void) 0)
|
|
||||||
#define xc_slow_answer_inc() ((void) 0)
|
|
||||||
#endif /* XIDCACHE_DEBUG */
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Because backends sitting idle will not be reading sinval events, we
|
* Because backends sitting idle will not be reading sinval events, we
|
||||||
* need a way to give an idle backend a swift kick in the rear and make
|
* need a way to give an idle backend a swift kick in the rear and make
|
||||||
|
@ -103,10 +79,6 @@ InitBackendSharedInvalidationState(void)
|
||||||
ereport(FATAL,
|
ereport(FATAL,
|
||||||
(errcode(ERRCODE_TOO_MANY_CONNECTIONS),
|
(errcode(ERRCODE_TOO_MANY_CONNECTIONS),
|
||||||
errmsg("sorry, too many clients already")));
|
errmsg("sorry, too many clients already")));
|
||||||
|
|
||||||
#ifdef XIDCACHE_DEBUG
|
|
||||||
on_proc_exit(DisplayXidCache, (Datum) 0);
|
|
||||||
#endif /* XIDCACHE_DEBUG */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -161,12 +133,6 @@ ReceiveSharedInvalidMessages(
|
||||||
* this is not exactly the normal (read-only) interpretation of a
|
* this is not exactly the normal (read-only) interpretation of a
|
||||||
* shared lock! Look closely at the interactions before allowing
|
* shared lock! Look closely at the interactions before allowing
|
||||||
* SInvalLock to be grabbed in shared mode for any other reason!
|
* SInvalLock to be grabbed in shared mode for any other reason!
|
||||||
*
|
|
||||||
* The routines later in this file that use shared mode are okay with
|
|
||||||
* this, because they aren't looking at the ProcState fields
|
|
||||||
* associated with SI message transfer; they only use the
|
|
||||||
* ProcState array as an easy way to find all the PGPROC
|
|
||||||
* structures.
|
|
||||||
*/
|
*/
|
||||||
LWLockAcquire(SInvalLock, LW_SHARED);
|
LWLockAcquire(SInvalLock, LW_SHARED);
|
||||||
getResult = SIGetDataEntry(shmInvalBuffer, MyBackendId, &data);
|
getResult = SIGetDataEntry(shmInvalBuffer, MyBackendId, &data);
|
||||||
|
@ -391,725 +357,3 @@ ProcessCatchupEvent(void)
|
||||||
if (notify_enabled)
|
if (notify_enabled)
|
||||||
EnableNotifyInterrupt();
|
EnableNotifyInterrupt();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/****************************************************************************/
|
|
||||||
/* Functions that need to scan the PGPROC structures of all running backends. */
|
|
||||||
/* It's a bit strange to keep these in sinval.c, since they don't have any */
|
|
||||||
/* direct relationship to shared-cache invalidation. But the procState */
|
|
||||||
/* array in the SI segment is the only place in the system where we have */
|
|
||||||
/* an array of per-backend data, so it is the most convenient place to keep */
|
|
||||||
/* pointers to the backends' PGPROC structures. We used to implement these */
|
|
||||||
/* functions with a slow, ugly search through the ShmemIndex hash table --- */
|
|
||||||
/* now they are simple loops over the SI ProcState array. */
|
|
||||||
/****************************************************************************/
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* DatabaseHasActiveBackends -- are there any backends running in the given DB
|
|
||||||
*
|
|
||||||
* If 'ignoreMyself' is TRUE, ignore this particular backend while checking
|
|
||||||
* for backends in the target database.
|
|
||||||
*
|
|
||||||
* This function is used to interlock DROP DATABASE against there being
|
|
||||||
* any active backends in the target DB --- dropping the DB while active
|
|
||||||
* backends remain would be a Bad Thing. Note that we cannot detect here
|
|
||||||
* the possibility of a newly-started backend that is trying to connect
|
|
||||||
* to the doomed database, so additional interlocking is needed during
|
|
||||||
* backend startup.
|
|
||||||
*/
|
|
||||||
bool
|
|
||||||
DatabaseHasActiveBackends(Oid databaseId, bool ignoreMyself)
|
|
||||||
{
|
|
||||||
bool result = false;
|
|
||||||
SISeg *segP = shmInvalBuffer;
|
|
||||||
ProcState *stateP = segP->procState;
|
|
||||||
int index;
|
|
||||||
|
|
||||||
LWLockAcquire(SInvalLock, LW_SHARED);
|
|
||||||
|
|
||||||
for (index = 0; index < segP->lastBackend; index++)
|
|
||||||
{
|
|
||||||
SHMEM_OFFSET pOffset = stateP[index].procStruct;
|
|
||||||
|
|
||||||
if (pOffset != INVALID_OFFSET)
|
|
||||||
{
|
|
||||||
PGPROC *proc = (PGPROC *) MAKE_PTR(pOffset);
|
|
||||||
|
|
||||||
if (proc->databaseId == databaseId)
|
|
||||||
{
|
|
||||||
if (ignoreMyself && proc == MyProc)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
result = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
LWLockRelease(SInvalLock);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* IsBackendPid -- is a given pid a running backend
|
|
||||||
*/
|
|
||||||
bool
|
|
||||||
IsBackendPid(int pid)
|
|
||||||
{
|
|
||||||
bool result = false;
|
|
||||||
SISeg *segP = shmInvalBuffer;
|
|
||||||
ProcState *stateP = segP->procState;
|
|
||||||
int index;
|
|
||||||
|
|
||||||
LWLockAcquire(SInvalLock, LW_SHARED);
|
|
||||||
|
|
||||||
for (index = 0; index < segP->lastBackend; index++)
|
|
||||||
{
|
|
||||||
SHMEM_OFFSET pOffset = stateP[index].procStruct;
|
|
||||||
|
|
||||||
if (pOffset != INVALID_OFFSET)
|
|
||||||
{
|
|
||||||
PGPROC *proc = (PGPROC *) MAKE_PTR(pOffset);
|
|
||||||
|
|
||||||
if (proc->pid == pid)
|
|
||||||
{
|
|
||||||
result = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
LWLockRelease(SInvalLock);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* TransactionIdIsInProgress -- is given transaction running in some backend
|
|
||||||
*
|
|
||||||
* There are three possibilities for finding a running transaction:
|
|
||||||
*
|
|
||||||
* 1. the given Xid is a main transaction Id. We will find this out cheaply
|
|
||||||
* by looking at the PGPROC struct for each backend.
|
|
||||||
*
|
|
||||||
* 2. the given Xid is one of the cached subxact Xids in the PGPROC array.
|
|
||||||
* We can find this out cheaply too.
|
|
||||||
*
|
|
||||||
* 3. Search the SubTrans tree to find the Xid's topmost parent, and then
|
|
||||||
* see if that is running according to PGPROC. This is the slowest, but
|
|
||||||
* sadly it has to be done always if the other two failed, unless we see
|
|
||||||
* that the cached subxact sets are complete (none have overflowed).
|
|
||||||
*
|
|
||||||
* SInvalLock has to be held while we do 1 and 2. If we save the top Xids
|
|
||||||
* while doing 1, we can release the SInvalLock while we do 3. This buys back
|
|
||||||
* some concurrency (we can't retrieve the main Xids from PGPROC again anyway;
|
|
||||||
* see GetNewTransactionId).
|
|
||||||
*/
|
|
||||||
bool
|
|
||||||
TransactionIdIsInProgress(TransactionId xid)
|
|
||||||
{
|
|
||||||
bool result = false;
|
|
||||||
SISeg *segP = shmInvalBuffer;
|
|
||||||
ProcState *stateP = segP->procState;
|
|
||||||
int i,
|
|
||||||
j;
|
|
||||||
int nxids = 0;
|
|
||||||
TransactionId *xids;
|
|
||||||
TransactionId topxid;
|
|
||||||
bool locked;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Don't bother checking a transaction older than RecentXmin; it
|
|
||||||
* could not possibly still be running.
|
|
||||||
*/
|
|
||||||
if (TransactionIdPrecedes(xid, RecentXmin))
|
|
||||||
{
|
|
||||||
xc_by_recent_xmin_inc();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Get workspace to remember main XIDs in */
|
|
||||||
xids = (TransactionId *) palloc(sizeof(TransactionId) * segP->maxBackends);
|
|
||||||
|
|
||||||
LWLockAcquire(SInvalLock, LW_SHARED);
|
|
||||||
locked = true;
|
|
||||||
|
|
||||||
for (i = 0; i < segP->lastBackend; i++)
|
|
||||||
{
|
|
||||||
SHMEM_OFFSET pOffset = stateP[i].procStruct;
|
|
||||||
|
|
||||||
if (pOffset != INVALID_OFFSET)
|
|
||||||
{
|
|
||||||
PGPROC *proc = (PGPROC *) MAKE_PTR(pOffset);
|
|
||||||
|
|
||||||
/* Fetch xid just once - see GetNewTransactionId */
|
|
||||||
TransactionId pxid = proc->xid;
|
|
||||||
|
|
||||||
if (!TransactionIdIsValid(pxid))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Step 1: check the main Xid
|
|
||||||
*/
|
|
||||||
if (TransactionIdEquals(pxid, xid))
|
|
||||||
{
|
|
||||||
xc_by_main_xid_inc();
|
|
||||||
result = true;
|
|
||||||
goto result_known;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* We can ignore main Xids that are younger than the target
|
|
||||||
* Xid, since the target could not possibly be their child.
|
|
||||||
*/
|
|
||||||
if (TransactionIdPrecedes(xid, pxid))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Step 2: check the cached child-Xids arrays
|
|
||||||
*/
|
|
||||||
for (j = proc->subxids.nxids - 1; j >= 0; j--)
|
|
||||||
{
|
|
||||||
/* Fetch xid just once - see GetNewTransactionId */
|
|
||||||
TransactionId cxid = proc->subxids.xids[j];
|
|
||||||
|
|
||||||
if (TransactionIdEquals(cxid, xid))
|
|
||||||
{
|
|
||||||
xc_by_child_xid_inc();
|
|
||||||
result = true;
|
|
||||||
goto result_known;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Save the main Xid for step 3. We only need to remember
|
|
||||||
* main Xids that have uncached children. (Note: there is no
|
|
||||||
* race condition here because the overflowed flag cannot be
|
|
||||||
* cleared, only set, while we hold SInvalLock. So we can't
|
|
||||||
* miss an Xid that we need to worry about.)
|
|
||||||
*/
|
|
||||||
if (proc->subxids.overflowed)
|
|
||||||
xids[nxids++] = pxid;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
LWLockRelease(SInvalLock);
|
|
||||||
locked = false;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* If none of the relevant caches overflowed, we know the Xid is not
|
|
||||||
* running without looking at pg_subtrans.
|
|
||||||
*/
|
|
||||||
if (nxids == 0)
|
|
||||||
goto result_known;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Step 3: have to check pg_subtrans.
|
|
||||||
*
|
|
||||||
* At this point, we know it's either a subtransaction of one of the Xids
|
|
||||||
* in xids[], or it's not running. If it's an already-failed
|
|
||||||
* subtransaction, we want to say "not running" even though its parent
|
|
||||||
* may still be running. So first, check pg_clog to see if it's been
|
|
||||||
* aborted.
|
|
||||||
*/
|
|
||||||
xc_slow_answer_inc();
|
|
||||||
|
|
||||||
if (TransactionIdDidAbort(xid))
|
|
||||||
goto result_known;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* It isn't aborted, so check whether the transaction tree it belongs
|
|
||||||
* to is still running (or, more precisely, whether it was running
|
|
||||||
* when this routine started -- note that we already released
|
|
||||||
* SInvalLock).
|
|
||||||
*/
|
|
||||||
topxid = SubTransGetTopmostTransaction(xid);
|
|
||||||
Assert(TransactionIdIsValid(topxid));
|
|
||||||
if (!TransactionIdEquals(topxid, xid))
|
|
||||||
{
|
|
||||||
for (i = 0; i < nxids; i++)
|
|
||||||
{
|
|
||||||
if (TransactionIdEquals(xids[i], topxid))
|
|
||||||
{
|
|
||||||
result = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
result_known:
|
|
||||||
if (locked)
|
|
||||||
LWLockRelease(SInvalLock);
|
|
||||||
|
|
||||||
pfree(xids);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* GetOldestXmin -- returns oldest transaction that was running
|
|
||||||
* when any current transaction was started.
|
|
||||||
*
|
|
||||||
* If allDbs is TRUE then all backends are considered; if allDbs is FALSE
|
|
||||||
* then only backends running in my own database are considered.
|
|
||||||
*
|
|
||||||
* This is used by VACUUM to decide which deleted tuples must be preserved
|
|
||||||
* in a table. allDbs = TRUE is needed for shared relations, but allDbs =
|
|
||||||
* FALSE is sufficient for non-shared relations, since only backends in my
|
|
||||||
* own database could ever see the tuples in them.
|
|
||||||
*
|
|
||||||
* This is also used to determine where to truncate pg_subtrans. allDbs
|
|
||||||
* must be TRUE for that case.
|
|
||||||
*
|
|
||||||
* Note: we include the currently running xids in the set of considered xids.
|
|
||||||
* This ensures that if a just-started xact has not yet set its snapshot,
|
|
||||||
* when it does set the snapshot it cannot set xmin less than what we compute.
|
|
||||||
*/
|
|
||||||
TransactionId
|
|
||||||
GetOldestXmin(bool allDbs)
|
|
||||||
{
|
|
||||||
SISeg *segP = shmInvalBuffer;
|
|
||||||
ProcState *stateP = segP->procState;
|
|
||||||
TransactionId result;
|
|
||||||
int index;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Normally we start the min() calculation with our own XID. But if
|
|
||||||
* called by checkpointer, we will not be inside a transaction, so use
|
|
||||||
* next XID as starting point for min() calculation. (Note that if
|
|
||||||
* there are no xacts running at all, that will be the subtrans
|
|
||||||
* truncation point!)
|
|
||||||
*/
|
|
||||||
if (IsTransactionState())
|
|
||||||
result = GetTopTransactionId();
|
|
||||||
else
|
|
||||||
result = ReadNewTransactionId();
|
|
||||||
|
|
||||||
LWLockAcquire(SInvalLock, LW_SHARED);
|
|
||||||
|
|
||||||
for (index = 0; index < segP->lastBackend; index++)
|
|
||||||
{
|
|
||||||
SHMEM_OFFSET pOffset = stateP[index].procStruct;
|
|
||||||
|
|
||||||
if (pOffset != INVALID_OFFSET)
|
|
||||||
{
|
|
||||||
PGPROC *proc = (PGPROC *) MAKE_PTR(pOffset);
|
|
||||||
|
|
||||||
if (allDbs || proc->databaseId == MyDatabaseId)
|
|
||||||
{
|
|
||||||
/* Fetch xid just once - see GetNewTransactionId */
|
|
||||||
TransactionId xid = proc->xid;
|
|
||||||
|
|
||||||
if (TransactionIdIsNormal(xid))
|
|
||||||
{
|
|
||||||
if (TransactionIdPrecedes(xid, result))
|
|
||||||
result = xid;
|
|
||||||
xid = proc->xmin;
|
|
||||||
if (TransactionIdIsNormal(xid))
|
|
||||||
if (TransactionIdPrecedes(xid, result))
|
|
||||||
result = xid;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
LWLockRelease(SInvalLock);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*----------
|
|
||||||
* GetSnapshotData -- returns information about running transactions.
|
|
||||||
*
|
|
||||||
* The returned snapshot includes xmin (lowest still-running xact ID),
|
|
||||||
* xmax (next xact ID to be assigned), and a list of running xact IDs
|
|
||||||
* in the range xmin <= xid < xmax. It is used as follows:
|
|
||||||
* All xact IDs < xmin are considered finished.
|
|
||||||
* All xact IDs >= xmax are considered still running.
|
|
||||||
* For an xact ID xmin <= xid < xmax, consult list to see whether
|
|
||||||
* it is considered running or not.
|
|
||||||
* This ensures that the set of transactions seen as "running" by the
|
|
||||||
* current xact will not change after it takes the snapshot.
|
|
||||||
*
|
|
||||||
* Note that only top-level XIDs are included in the snapshot. We can
|
|
||||||
* still apply the xmin and xmax limits to subtransaction XIDs, but we
|
|
||||||
* need to work a bit harder to see if XIDs in [xmin..xmax) are running.
|
|
||||||
*
|
|
||||||
* We also update the following backend-global variables:
|
|
||||||
* TransactionXmin: the oldest xmin of any snapshot in use in the
|
|
||||||
* current transaction (this is the same as MyProc->xmin). This
|
|
||||||
* is just the xmin computed for the first, serializable snapshot.
|
|
||||||
* RecentXmin: the xmin computed for the most recent snapshot. XIDs
|
|
||||||
* older than this are known not running any more.
|
|
||||||
* RecentGlobalXmin: the global xmin (oldest TransactionXmin across all
|
|
||||||
* running transactions). This is the same computation done by
|
|
||||||
* GetOldestXmin(TRUE).
|
|
||||||
*----------
|
|
||||||
*/
|
|
||||||
Snapshot
|
|
||||||
GetSnapshotData(Snapshot snapshot, bool serializable)
|
|
||||||
{
|
|
||||||
SISeg *segP = shmInvalBuffer;
|
|
||||||
ProcState *stateP = segP->procState;
|
|
||||||
TransactionId xmin;
|
|
||||||
TransactionId xmax;
|
|
||||||
TransactionId globalxmin;
|
|
||||||
int index;
|
|
||||||
int count = 0;
|
|
||||||
|
|
||||||
Assert(snapshot != NULL);
|
|
||||||
|
|
||||||
/* Serializable snapshot must be computed before any other... */
|
|
||||||
Assert(serializable ?
|
|
||||||
!TransactionIdIsValid(MyProc->xmin) :
|
|
||||||
TransactionIdIsValid(MyProc->xmin));
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Allocating space for MaxBackends xids is usually overkill;
|
|
||||||
* lastBackend would be sufficient. But it seems better to do the
|
|
||||||
* malloc while not holding the lock, so we can't look at lastBackend.
|
|
||||||
*
|
|
||||||
* This does open a possibility for avoiding repeated malloc/free: since
|
|
||||||
* MaxBackends does not change at runtime, we can simply reuse the
|
|
||||||
* previous xip array if any. (This relies on the fact that all
|
|
||||||
* callers pass static SnapshotData structs.)
|
|
||||||
*/
|
|
||||||
if (snapshot->xip == NULL)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* First call for this snapshot
|
|
||||||
*/
|
|
||||||
snapshot->xip = (TransactionId *)
|
|
||||||
malloc(MaxBackends * sizeof(TransactionId));
|
|
||||||
if (snapshot->xip == NULL)
|
|
||||||
ereport(ERROR,
|
|
||||||
(errcode(ERRCODE_OUT_OF_MEMORY),
|
|
||||||
errmsg("out of memory")));
|
|
||||||
}
|
|
||||||
|
|
||||||
globalxmin = xmin = GetTopTransactionId();
|
|
||||||
|
|
||||||
/*
|
|
||||||
* If we are going to set MyProc->xmin then we'd better get exclusive
|
|
||||||
* lock; if not, this is a read-only operation so it can be shared.
|
|
||||||
*/
|
|
||||||
LWLockAcquire(SInvalLock, serializable ? LW_EXCLUSIVE : LW_SHARED);
|
|
||||||
|
|
||||||
/*--------------------
|
|
||||||
* Unfortunately, we have to call ReadNewTransactionId() after acquiring
|
|
||||||
* SInvalLock above. It's not good because ReadNewTransactionId() does
|
|
||||||
* LWLockAcquire(XidGenLock), but *necessary*. We need to be sure that
|
|
||||||
* no transactions exit the set of currently-running transactions
|
|
||||||
* between the time we fetch xmax and the time we finish building our
|
|
||||||
* snapshot. Otherwise we could have a situation like this:
|
|
||||||
*
|
|
||||||
* 1. Tx Old is running (in Read Committed mode).
|
|
||||||
* 2. Tx S reads new transaction ID into xmax, then
|
|
||||||
* is swapped out before acquiring SInvalLock.
|
|
||||||
* 3. Tx New gets new transaction ID (>= S' xmax),
|
|
||||||
* makes changes and commits.
|
|
||||||
* 4. Tx Old changes some row R changed by Tx New and commits.
|
|
||||||
* 5. Tx S finishes getting its snapshot data. It sees Tx Old as
|
|
||||||
* done, but sees Tx New as still running (since New >= xmax).
|
|
||||||
*
|
|
||||||
* Now S will see R changed by both Tx Old and Tx New, *but* does not
|
|
||||||
* see other changes made by Tx New. If S is supposed to be in
|
|
||||||
* Serializable mode, this is wrong.
|
|
||||||
*
|
|
||||||
* By locking SInvalLock before we read xmax, we ensure that TX Old
|
|
||||||
* cannot exit the set of running transactions seen by Tx S. Therefore
|
|
||||||
* both Old and New will be seen as still running => no inconsistency.
|
|
||||||
*--------------------
|
|
||||||
*/
|
|
||||||
|
|
||||||
xmax = ReadNewTransactionId();
|
|
||||||
|
|
||||||
for (index = 0; index < segP->lastBackend; index++)
|
|
||||||
{
|
|
||||||
SHMEM_OFFSET pOffset = stateP[index].procStruct;
|
|
||||||
|
|
||||||
if (pOffset != INVALID_OFFSET)
|
|
||||||
{
|
|
||||||
PGPROC *proc = (PGPROC *) MAKE_PTR(pOffset);
|
|
||||||
|
|
||||||
/* Fetch xid just once - see GetNewTransactionId */
|
|
||||||
TransactionId xid = proc->xid;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Ignore my own proc (dealt with my xid above), procs not
|
|
||||||
* running a transaction, and xacts started since we read the
|
|
||||||
* next transaction ID. There's no need to store XIDs above
|
|
||||||
* what we got from ReadNewTransactionId, since we'll treat
|
|
||||||
* them as running anyway. We also assume that such xacts
|
|
||||||
* can't compute an xmin older than ours, so they needn't be
|
|
||||||
* considered in computing globalxmin.
|
|
||||||
*/
|
|
||||||
if (proc == MyProc ||
|
|
||||||
!TransactionIdIsNormal(xid) ||
|
|
||||||
TransactionIdFollowsOrEquals(xid, xmax))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (TransactionIdPrecedes(xid, xmin))
|
|
||||||
xmin = xid;
|
|
||||||
snapshot->xip[count] = xid;
|
|
||||||
count++;
|
|
||||||
|
|
||||||
/* Update globalxmin to be the smallest valid xmin */
|
|
||||||
xid = proc->xmin;
|
|
||||||
if (TransactionIdIsNormal(xid))
|
|
||||||
if (TransactionIdPrecedes(xid, globalxmin))
|
|
||||||
globalxmin = xid;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (serializable)
|
|
||||||
MyProc->xmin = TransactionXmin = xmin;
|
|
||||||
|
|
||||||
LWLockRelease(SInvalLock);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Update globalxmin to include actual process xids. This is a
|
|
||||||
* slightly different way of computing it than GetOldestXmin uses, but
|
|
||||||
* should give the same result.
|
|
||||||
*/
|
|
||||||
if (TransactionIdPrecedes(xmin, globalxmin))
|
|
||||||
globalxmin = xmin;
|
|
||||||
|
|
||||||
/* Update global variables too */
|
|
||||||
RecentGlobalXmin = globalxmin;
|
|
||||||
RecentXmin = xmin;
|
|
||||||
|
|
||||||
snapshot->xmin = xmin;
|
|
||||||
snapshot->xmax = xmax;
|
|
||||||
snapshot->xcnt = count;
|
|
||||||
|
|
||||||
snapshot->curcid = GetCurrentCommandId();
|
|
||||||
|
|
||||||
return snapshot;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* CountActiveBackends --- count backends (other than myself) that are in
|
|
||||||
* active transactions. This is used as a heuristic to decide if
|
|
||||||
* a pre-XLOG-flush delay is worthwhile during commit.
|
|
||||||
*
|
|
||||||
* An active transaction is something that has written at least one XLOG
|
|
||||||
* record; read-only transactions don't count. Also, do not count backends
|
|
||||||
* that are blocked waiting for locks, since they are not going to get to
|
|
||||||
* run until someone else commits.
|
|
||||||
*/
|
|
||||||
int
|
|
||||||
CountActiveBackends(void)
|
|
||||||
{
|
|
||||||
SISeg *segP = shmInvalBuffer;
|
|
||||||
ProcState *stateP = segP->procState;
|
|
||||||
int count = 0;
|
|
||||||
int index;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Note: for speed, we don't acquire SInvalLock. This is a little bit
|
|
||||||
* bogus, but since we are only testing xrecoff for zero or nonzero,
|
|
||||||
* it should be OK. The result is only used for heuristic purposes
|
|
||||||
* anyway...
|
|
||||||
*/
|
|
||||||
for (index = 0; index < segP->lastBackend; index++)
|
|
||||||
{
|
|
||||||
SHMEM_OFFSET pOffset = stateP[index].procStruct;
|
|
||||||
|
|
||||||
if (pOffset != INVALID_OFFSET)
|
|
||||||
{
|
|
||||||
PGPROC *proc = (PGPROC *) MAKE_PTR(pOffset);
|
|
||||||
|
|
||||||
if (proc == MyProc)
|
|
||||||
continue; /* do not count myself */
|
|
||||||
if (proc->logRec.xrecoff == 0)
|
|
||||||
continue; /* do not count if not in a transaction */
|
|
||||||
if (proc->waitLock != NULL)
|
|
||||||
continue; /* do not count if blocked on a lock */
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef NOT_USED
|
|
||||||
/*
|
|
||||||
* GetUndoRecPtr -- returns oldest PGPROC->logRec.
|
|
||||||
*/
|
|
||||||
XLogRecPtr
|
|
||||||
GetUndoRecPtr(void)
|
|
||||||
{
|
|
||||||
SISeg *segP = shmInvalBuffer;
|
|
||||||
ProcState *stateP = segP->procState;
|
|
||||||
XLogRecPtr urec = {0, 0};
|
|
||||||
XLogRecPtr tempr;
|
|
||||||
int index;
|
|
||||||
|
|
||||||
LWLockAcquire(SInvalLock, LW_SHARED);
|
|
||||||
|
|
||||||
for (index = 0; index < segP->lastBackend; index++)
|
|
||||||
{
|
|
||||||
SHMEM_OFFSET pOffset = stateP[index].procStruct;
|
|
||||||
|
|
||||||
if (pOffset != INVALID_OFFSET)
|
|
||||||
{
|
|
||||||
PGPROC *proc = (PGPROC *) MAKE_PTR(pOffset);
|
|
||||||
|
|
||||||
tempr = proc->logRec;
|
|
||||||
if (tempr.xrecoff == 0)
|
|
||||||
continue;
|
|
||||||
if (urec.xrecoff != 0 && XLByteLT(urec, tempr))
|
|
||||||
continue;
|
|
||||||
urec = tempr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
LWLockRelease(SInvalLock);
|
|
||||||
|
|
||||||
return (urec);
|
|
||||||
}
|
|
||||||
#endif /* NOT_USED */
|
|
||||||
|
|
||||||
/*
|
|
||||||
* BackendIdGetProc - given a BackendId, find its PGPROC structure
|
|
||||||
*
|
|
||||||
* This is a trivial lookup in the ProcState array. We assume that the caller
|
|
||||||
* knows that the backend isn't going to go away, so we do not bother with
|
|
||||||
* locking.
|
|
||||||
*/
|
|
||||||
struct PGPROC *
|
|
||||||
BackendIdGetProc(BackendId procId)
|
|
||||||
{
|
|
||||||
SISeg *segP = shmInvalBuffer;
|
|
||||||
|
|
||||||
if (procId > 0 && procId <= segP->lastBackend)
|
|
||||||
{
|
|
||||||
ProcState *stateP = &segP->procState[procId - 1];
|
|
||||||
SHMEM_OFFSET pOffset = stateP->procStruct;
|
|
||||||
|
|
||||||
if (pOffset != INVALID_OFFSET)
|
|
||||||
{
|
|
||||||
PGPROC *proc = (PGPROC *) MAKE_PTR(pOffset);
|
|
||||||
|
|
||||||
return proc;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* CountEmptyBackendSlots - count empty slots in backend process table
|
|
||||||
*
|
|
||||||
* We don't actually need to count, since sinvaladt.c maintains a
|
|
||||||
* freeBackends counter in the SI segment.
|
|
||||||
*
|
|
||||||
* Acquiring the lock here is almost certainly overkill, but just in
|
|
||||||
* case fetching an int is not atomic on your machine ...
|
|
||||||
*/
|
|
||||||
int
|
|
||||||
CountEmptyBackendSlots(void)
|
|
||||||
{
|
|
||||||
int count;
|
|
||||||
|
|
||||||
LWLockAcquire(SInvalLock, LW_SHARED);
|
|
||||||
|
|
||||||
count = shmInvalBuffer->freeBackends;
|
|
||||||
|
|
||||||
LWLockRelease(SInvalLock);
|
|
||||||
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define XidCacheRemove(i) \
|
|
||||||
do { \
|
|
||||||
MyProc->subxids.xids[i] = MyProc->subxids.xids[MyProc->subxids.nxids - 1]; \
|
|
||||||
MyProc->subxids.nxids--; \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
/*
|
|
||||||
* XidCacheRemoveRunningXids
|
|
||||||
*
|
|
||||||
* Remove a bunch of TransactionIds from the list of known-running
|
|
||||||
* subtransactions for my backend. Both the specified xid and those in
|
|
||||||
* the xids[] array (of length nxids) are removed from the subxids cache.
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
XidCacheRemoveRunningXids(TransactionId xid, int nxids, TransactionId *xids)
|
|
||||||
{
|
|
||||||
int i,
|
|
||||||
j;
|
|
||||||
|
|
||||||
Assert(!TransactionIdEquals(xid, InvalidTransactionId));
|
|
||||||
|
|
||||||
/*
|
|
||||||
* We must hold SInvalLock exclusively in order to remove transactions
|
|
||||||
* from the PGPROC array. (See notes in GetSnapshotData.) It's
|
|
||||||
* possible this could be relaxed since we know this routine is only
|
|
||||||
* used to abort subtransactions, but pending closer analysis we'd
|
|
||||||
* best be conservative.
|
|
||||||
*/
|
|
||||||
LWLockAcquire(SInvalLock, LW_EXCLUSIVE);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Under normal circumstances xid and xids[] will be in increasing
|
|
||||||
* order, as will be the entries in subxids. Scan backwards to avoid
|
|
||||||
* O(N^2) behavior when removing a lot of xids.
|
|
||||||
*/
|
|
||||||
for (i = nxids - 1; i >= 0; i--)
|
|
||||||
{
|
|
||||||
TransactionId anxid = xids[i];
|
|
||||||
|
|
||||||
for (j = MyProc->subxids.nxids - 1; j >= 0; j--)
|
|
||||||
{
|
|
||||||
if (TransactionIdEquals(MyProc->subxids.xids[j], anxid))
|
|
||||||
{
|
|
||||||
XidCacheRemove(j);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
* Ordinarily we should have found it, unless the cache has overflowed.
|
|
||||||
* However it's also possible for this routine to be invoked multiple
|
|
||||||
* times for the same subtransaction, in case of an error during
|
|
||||||
* AbortSubTransaction. So instead of Assert, emit a debug warning.
|
|
||||||
*/
|
|
||||||
if (j < 0 && !MyProc->subxids.overflowed)
|
|
||||||
elog(WARNING, "did not find subXID %u in MyProc", anxid);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (j = MyProc->subxids.nxids - 1; j >= 0; j--)
|
|
||||||
{
|
|
||||||
if (TransactionIdEquals(MyProc->subxids.xids[j], xid))
|
|
||||||
{
|
|
||||||
XidCacheRemove(j);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* Ordinarily we should have found it, unless the cache has overflowed */
|
|
||||||
if (j < 0 && !MyProc->subxids.overflowed)
|
|
||||||
elog(WARNING, "did not find subXID %u in MyProc", xid);
|
|
||||||
|
|
||||||
LWLockRelease(SInvalLock);
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef XIDCACHE_DEBUG
|
|
||||||
|
|
||||||
/*
|
|
||||||
* on_proc_exit hook to print stats about effectiveness of XID cache
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
DisplayXidCache(int code, Datum arg)
|
|
||||||
{
|
|
||||||
fprintf(stderr,
|
|
||||||
"XidCache: xmin: %ld, mainxid: %ld, childxid: %ld, slow: %ld\n",
|
|
||||||
xc_by_recent_xmin,
|
|
||||||
xc_by_main_xid,
|
|
||||||
xc_by_child_xid,
|
|
||||||
xc_slow_answer);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif /* XIDCACHE_DEBUG */
|
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/storage/ipc/sinvaladt.c,v 1.58 2004/12/31 22:00:56 pgsql Exp $
|
* $PostgreSQL: pgsql/src/backend/storage/ipc/sinvaladt.c,v 1.59 2005/05/19 21:35:46 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -17,10 +17,12 @@
|
||||||
#include "miscadmin.h"
|
#include "miscadmin.h"
|
||||||
#include "storage/backendid.h"
|
#include "storage/backendid.h"
|
||||||
#include "storage/ipc.h"
|
#include "storage/ipc.h"
|
||||||
|
#include "storage/lwlock.h"
|
||||||
#include "storage/pmsignal.h"
|
#include "storage/pmsignal.h"
|
||||||
#include "storage/proc.h"
|
#include "storage/shmem.h"
|
||||||
#include "storage/sinvaladt.h"
|
#include "storage/sinvaladt.h"
|
||||||
|
|
||||||
|
|
||||||
SISeg *shmInvalBuffer;
|
SISeg *shmInvalBuffer;
|
||||||
|
|
||||||
static void CleanupInvalidationState(int status, Datum arg);
|
static void CleanupInvalidationState(int status, Datum arg);
|
||||||
|
@ -72,7 +74,6 @@ SIBufferInit(int maxBackends)
|
||||||
{
|
{
|
||||||
segP->procState[i].nextMsgNum = -1; /* inactive */
|
segP->procState[i].nextMsgNum = -1; /* inactive */
|
||||||
segP->procState[i].resetState = false;
|
segP->procState[i].resetState = false;
|
||||||
segP->procState[i].procStruct = INVALID_OFFSET;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -133,7 +134,6 @@ SIBackendInit(SISeg *segP)
|
||||||
/* mark myself active, with all extant messages already read */
|
/* mark myself active, with all extant messages already read */
|
||||||
stateP->nextMsgNum = segP->maxMsgNum;
|
stateP->nextMsgNum = segP->maxMsgNum;
|
||||||
stateP->resetState = false;
|
stateP->resetState = false;
|
||||||
stateP->procStruct = MAKE_OFFSET(MyProc);
|
|
||||||
|
|
||||||
/* register exit routine to mark my entry inactive at exit */
|
/* register exit routine to mark my entry inactive at exit */
|
||||||
on_shmem_exit(CleanupInvalidationState, PointerGetDatum(segP));
|
on_shmem_exit(CleanupInvalidationState, PointerGetDatum(segP));
|
||||||
|
@ -163,7 +163,6 @@ CleanupInvalidationState(int status, Datum arg)
|
||||||
/* Mark myself inactive */
|
/* Mark myself inactive */
|
||||||
segP->procState[MyBackendId - 1].nextMsgNum = -1;
|
segP->procState[MyBackendId - 1].nextMsgNum = -1;
|
||||||
segP->procState[MyBackendId - 1].resetState = false;
|
segP->procState[MyBackendId - 1].resetState = false;
|
||||||
segP->procState[MyBackendId - 1].procStruct = INVALID_OFFSET;
|
|
||||||
|
|
||||||
/* Recompute index of last active backend */
|
/* Recompute index of last active backend */
|
||||||
for (i = segP->lastBackend; i > 0; i--)
|
for (i = segP->lastBackend; i > 0; i--)
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/storage/lmgr/lmgr.c,v 1.73 2005/04/30 19:03:33 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/storage/lmgr/lmgr.c,v 1.74 2005/05/19 21:35:46 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -21,7 +21,7 @@
|
||||||
#include "catalog/catalog.h"
|
#include "catalog/catalog.h"
|
||||||
#include "miscadmin.h"
|
#include "miscadmin.h"
|
||||||
#include "storage/lmgr.h"
|
#include "storage/lmgr.h"
|
||||||
#include "storage/sinval.h"
|
#include "storage/procarray.h"
|
||||||
#include "utils/inval.h"
|
#include "utils/inval.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/storage/lmgr/proc.c,v 1.157 2005/04/15 04:18:10 neilc Exp $
|
* $PostgreSQL: pgsql/src/backend/storage/lmgr/proc.c,v 1.158 2005/05/19 21:35:46 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -49,7 +49,7 @@
|
||||||
#include "storage/bufmgr.h"
|
#include "storage/bufmgr.h"
|
||||||
#include "storage/ipc.h"
|
#include "storage/ipc.h"
|
||||||
#include "storage/proc.h"
|
#include "storage/proc.h"
|
||||||
#include "storage/sinval.h"
|
#include "storage/procarray.h"
|
||||||
#include "storage/spin.h"
|
#include "storage/spin.h"
|
||||||
|
|
||||||
|
|
||||||
|
@ -116,8 +116,7 @@ ProcGlobalSemas(int maxBackends)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* InitProcGlobal -
|
* InitProcGlobal -
|
||||||
* initializes the global process table. We put it here so that
|
* Initialize the global process table during postmaster startup.
|
||||||
* the postmaster can do this initialization.
|
|
||||||
*
|
*
|
||||||
* We also create all the per-process semaphores we will need to support
|
* We also create all the per-process semaphores we will need to support
|
||||||
* the requested number of backends. We used to allocate semaphores
|
* the requested number of backends. We used to allocate semaphores
|
||||||
|
@ -263,6 +262,11 @@ InitProcess(void)
|
||||||
MyProc->waitProcLock = NULL;
|
MyProc->waitProcLock = NULL;
|
||||||
SHMQueueInit(&(MyProc->procLocks));
|
SHMQueueInit(&(MyProc->procLocks));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add our PGPROC to the PGPROC array in shared memory.
|
||||||
|
*/
|
||||||
|
ProcArrayAddMyself();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Arrange to clean up at backend exit.
|
* Arrange to clean up at backend exit.
|
||||||
*/
|
*/
|
||||||
|
@ -473,6 +477,9 @@ ProcKill(int code, Datum arg)
|
||||||
LockReleaseAll(USER_LOCKMETHOD, true);
|
LockReleaseAll(USER_LOCKMETHOD, true);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* Remove our PGPROC from the PGPROC array in shared memory */
|
||||||
|
ProcArrayRemoveMyself();
|
||||||
|
|
||||||
SpinLockAcquire(ProcStructLock);
|
SpinLockAcquire(ProcStructLock);
|
||||||
|
|
||||||
/* Return PGPROC structure (and semaphore) to freelist */
|
/* Return PGPROC structure (and semaphore) to freelist */
|
||||||
|
@ -978,12 +985,12 @@ ProcCancelWaitForSignal(void)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ProcSendSignal - send a signal to a backend identified by BackendId
|
* ProcSendSignal - send a signal to a backend identified by PID
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
ProcSendSignal(BackendId procId)
|
ProcSendSignal(int pid)
|
||||||
{
|
{
|
||||||
PGPROC *proc = BackendIdGetProc(procId);
|
PGPROC *proc = BackendPidGetProc(pid);
|
||||||
|
|
||||||
if (proc != NULL)
|
if (proc != NULL)
|
||||||
PGSemaphoreUnlock(&proc->sem);
|
PGSemaphoreUnlock(&proc->sem);
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/utils/adt/misc.c,v 1.42 2005/05/10 22:27:30 momjian Exp $
|
* $PostgreSQL: pgsql/src/backend/utils/adt/misc.c,v 1.43 2005/05/19 21:35:47 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -20,7 +20,7 @@
|
||||||
|
|
||||||
#include "commands/dbcommands.h"
|
#include "commands/dbcommands.h"
|
||||||
#include "miscadmin.h"
|
#include "miscadmin.h"
|
||||||
#include "storage/sinval.h"
|
#include "storage/procarray.h"
|
||||||
#include "storage/fd.h"
|
#include "storage/fd.h"
|
||||||
#include "utils/builtins.h"
|
#include "utils/builtins.h"
|
||||||
#include "funcapi.h"
|
#include "funcapi.h"
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/utils/init/postinit.c,v 1.146 2005/05/05 19:53:26 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/utils/init/postinit.c,v 1.147 2005/05/19 21:35:47 tgl Exp $
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
|
@ -30,9 +30,11 @@
|
||||||
#include "mb/pg_wchar.h"
|
#include "mb/pg_wchar.h"
|
||||||
#include "miscadmin.h"
|
#include "miscadmin.h"
|
||||||
#include "postmaster/postmaster.h"
|
#include "postmaster/postmaster.h"
|
||||||
|
#include "storage/backendid.h"
|
||||||
#include "storage/fd.h"
|
#include "storage/fd.h"
|
||||||
#include "storage/ipc.h"
|
#include "storage/ipc.h"
|
||||||
#include "storage/proc.h"
|
#include "storage/proc.h"
|
||||||
|
#include "storage/procarray.h"
|
||||||
#include "storage/sinval.h"
|
#include "storage/sinval.h"
|
||||||
#include "storage/smgr.h"
|
#include "storage/smgr.h"
|
||||||
#include "utils/flatfiles.h"
|
#include "utils/flatfiles.h"
|
||||||
|
|
|
@ -32,7 +32,7 @@
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/utils/time/tqual.c,v 1.88 2005/05/07 21:22:01 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/utils/time/tqual.c,v 1.89 2005/05/19 21:35:47 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -41,7 +41,7 @@
|
||||||
|
|
||||||
#include "access/multixact.h"
|
#include "access/multixact.h"
|
||||||
#include "access/subtrans.h"
|
#include "access/subtrans.h"
|
||||||
#include "storage/sinval.h"
|
#include "storage/procarray.h"
|
||||||
#include "utils/tqual.h"
|
#include "utils/tqual.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -8,14 +8,13 @@
|
||||||
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/include/storage/buf_internals.h,v 1.77 2005/03/04 20:21:07 tgl Exp $
|
* $PostgreSQL: pgsql/src/include/storage/buf_internals.h,v 1.78 2005/05/19 21:35:47 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
#ifndef BUFMGR_INTERNALS_H
|
#ifndef BUFMGR_INTERNALS_H
|
||||||
#define BUFMGR_INTERNALS_H
|
#define BUFMGR_INTERNALS_H
|
||||||
|
|
||||||
#include "storage/backendid.h"
|
|
||||||
#include "storage/buf.h"
|
#include "storage/buf.h"
|
||||||
#include "storage/lwlock.h"
|
#include "storage/lwlock.h"
|
||||||
#include "storage/shmem.h"
|
#include "storage/shmem.h"
|
||||||
|
@ -94,7 +93,7 @@ typedef struct buftag
|
||||||
* BufferDesc -- shared descriptor/state data for a single shared buffer.
|
* BufferDesc -- shared descriptor/state data for a single shared buffer.
|
||||||
*
|
*
|
||||||
* Note: buf_hdr_lock must be held to examine or change the tag, flags,
|
* Note: buf_hdr_lock must be held to examine or change the tag, flags,
|
||||||
* usage_count, refcount, or wait_backend_id fields. buf_id field never
|
* usage_count, refcount, or wait_backend_pid fields. buf_id field never
|
||||||
* changes after initialization, so does not need locking. freeNext is
|
* changes after initialization, so does not need locking. freeNext is
|
||||||
* protected by the BufFreelistLock not buf_hdr_lock. The LWLocks can take
|
* protected by the BufFreelistLock not buf_hdr_lock. The LWLocks can take
|
||||||
* care of themselves. The buf_hdr_lock is *not* used to control access to
|
* care of themselves. The buf_hdr_lock is *not* used to control access to
|
||||||
|
@ -108,8 +107,8 @@ typedef struct buftag
|
||||||
*
|
*
|
||||||
* We can't physically remove items from a disk page if another backend has
|
* We can't physically remove items from a disk page if another backend has
|
||||||
* the buffer pinned. Hence, a backend may need to wait for all other pins
|
* the buffer pinned. Hence, a backend may need to wait for all other pins
|
||||||
* to go away. This is signaled by storing its own backend ID into
|
* to go away. This is signaled by storing its own PID into
|
||||||
* wait_backend_id and setting flag bit BM_PIN_COUNT_WAITER. At present,
|
* wait_backend_pid and setting flag bit BM_PIN_COUNT_WAITER. At present,
|
||||||
* there can be only one such waiter per buffer.
|
* there can be only one such waiter per buffer.
|
||||||
*
|
*
|
||||||
* We use this same struct for local buffer headers, but the lock fields
|
* We use this same struct for local buffer headers, but the lock fields
|
||||||
|
@ -121,7 +120,7 @@ typedef struct sbufdesc
|
||||||
BufFlags flags; /* see bit definitions above */
|
BufFlags flags; /* see bit definitions above */
|
||||||
uint16 usage_count; /* usage counter for clock sweep code */
|
uint16 usage_count; /* usage counter for clock sweep code */
|
||||||
unsigned refcount; /* # of backends holding pins on buffer */
|
unsigned refcount; /* # of backends holding pins on buffer */
|
||||||
BackendId wait_backend_id; /* backend ID of pin-count waiter */
|
int wait_backend_pid; /* backend PID of pin-count waiter */
|
||||||
|
|
||||||
slock_t buf_hdr_lock; /* protects the above fields */
|
slock_t buf_hdr_lock; /* protects the above fields */
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/include/storage/lwlock.h,v 1.18 2005/04/28 21:47:18 tgl Exp $
|
* $PostgreSQL: pgsql/src/include/storage/lwlock.h,v 1.19 2005/05/19 21:35:47 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -30,6 +30,7 @@ typedef enum LWLockId
|
||||||
LockMgrLock,
|
LockMgrLock,
|
||||||
OidGenLock,
|
OidGenLock,
|
||||||
XidGenLock,
|
XidGenLock,
|
||||||
|
ProcArrayLock,
|
||||||
SInvalLock,
|
SInvalLock,
|
||||||
FreeSpaceLock,
|
FreeSpaceLock,
|
||||||
MMCacheLock,
|
MMCacheLock,
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/include/storage/proc.h,v 1.77 2004/12/31 22:03:42 pgsql Exp $
|
* $PostgreSQL: pgsql/src/include/storage/proc.h,v 1.78 2005/05/19 21:35:47 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -15,7 +15,6 @@
|
||||||
#define _PROC_H_
|
#define _PROC_H_
|
||||||
|
|
||||||
#include "access/xlog.h"
|
#include "access/xlog.h"
|
||||||
#include "storage/backendid.h"
|
|
||||||
#include "storage/lock.h"
|
#include "storage/lock.h"
|
||||||
#include "storage/pg_sema.h"
|
#include "storage/pg_sema.h"
|
||||||
|
|
||||||
|
@ -137,7 +136,7 @@ extern bool LockWaitCancel(void);
|
||||||
|
|
||||||
extern void ProcWaitForSignal(void);
|
extern void ProcWaitForSignal(void);
|
||||||
extern void ProcCancelWaitForSignal(void);
|
extern void ProcCancelWaitForSignal(void);
|
||||||
extern void ProcSendSignal(BackendId procId);
|
extern void ProcSendSignal(int pid);
|
||||||
|
|
||||||
extern bool enable_sig_alarm(int delayms, bool is_statement_timeout);
|
extern bool enable_sig_alarm(int delayms, bool is_statement_timeout);
|
||||||
extern bool disable_sig_alarm(bool is_statement_timeout);
|
extern bool disable_sig_alarm(bool is_statement_timeout);
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
/*-------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* procarray.h
|
||||||
|
* POSTGRES process array definitions.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
|
||||||
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
|
*
|
||||||
|
* $PostgreSQL: pgsql/src/include/storage/procarray.h,v 1.1 2005/05/19 21:35:47 tgl Exp $
|
||||||
|
*
|
||||||
|
*-------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
#ifndef PROCARRAY_H
|
||||||
|
#define PROCARRAY_H
|
||||||
|
|
||||||
|
extern int ProcArrayShmemSize(int maxBackends);
|
||||||
|
extern void CreateSharedProcArray(int maxBackends);
|
||||||
|
extern void ProcArrayAddMyself(void);
|
||||||
|
extern void ProcArrayRemoveMyself(void);
|
||||||
|
|
||||||
|
extern bool TransactionIdIsInProgress(TransactionId xid);
|
||||||
|
extern TransactionId GetOldestXmin(bool allDbs);
|
||||||
|
|
||||||
|
/* Use "struct PGPROC", not PGPROC, to avoid including proc.h here */
|
||||||
|
extern struct PGPROC *BackendPidGetProc(int pid);
|
||||||
|
extern bool IsBackendPid(int pid);
|
||||||
|
extern bool DatabaseHasActiveBackends(Oid databaseId, bool ignoreMyself);
|
||||||
|
|
||||||
|
extern int CountActiveBackends(void);
|
||||||
|
extern int CountEmptyBackendSlots(void);
|
||||||
|
|
||||||
|
extern void XidCacheRemoveRunningXids(TransactionId xid,
|
||||||
|
int nxids, TransactionId *xids);
|
||||||
|
|
||||||
|
#endif /* PROCARRAY_H */
|
|
@ -7,14 +7,13 @@
|
||||||
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/include/storage/sinval.h,v 1.40 2005/01/10 21:57:19 tgl Exp $
|
* $PostgreSQL: pgsql/src/include/storage/sinval.h,v 1.41 2005/05/19 21:35:47 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
#ifndef SINVAL_H
|
#ifndef SINVAL_H
|
||||||
#define SINVAL_H
|
#define SINVAL_H
|
||||||
|
|
||||||
#include "storage/backendid.h"
|
|
||||||
#include "storage/itemptr.h"
|
#include "storage/itemptr.h"
|
||||||
#include "storage/relfilenode.h"
|
#include "storage/relfilenode.h"
|
||||||
|
|
||||||
|
@ -87,24 +86,12 @@ typedef union
|
||||||
extern int SInvalShmemSize(int maxBackends);
|
extern int SInvalShmemSize(int maxBackends);
|
||||||
extern void CreateSharedInvalidationState(int maxBackends);
|
extern void CreateSharedInvalidationState(int maxBackends);
|
||||||
extern void InitBackendSharedInvalidationState(void);
|
extern void InitBackendSharedInvalidationState(void);
|
||||||
|
|
||||||
extern void SendSharedInvalidMessage(SharedInvalidationMessage *msg);
|
extern void SendSharedInvalidMessage(SharedInvalidationMessage *msg);
|
||||||
extern void ReceiveSharedInvalidMessages(
|
extern void ReceiveSharedInvalidMessages(
|
||||||
void (*invalFunction) (SharedInvalidationMessage *msg),
|
void (*invalFunction) (SharedInvalidationMessage *msg),
|
||||||
void (*resetFunction) (void));
|
void (*resetFunction) (void));
|
||||||
|
|
||||||
extern bool DatabaseHasActiveBackends(Oid databaseId, bool ignoreMyself);
|
|
||||||
extern bool TransactionIdIsInProgress(TransactionId xid);
|
|
||||||
extern bool IsBackendPid(int pid);
|
|
||||||
extern TransactionId GetOldestXmin(bool allDbs);
|
|
||||||
extern int CountActiveBackends(void);
|
|
||||||
extern int CountEmptyBackendSlots(void);
|
|
||||||
|
|
||||||
/* Use "struct PGPROC", not PGPROC, to avoid including proc.h here */
|
|
||||||
extern struct PGPROC *BackendIdGetProc(BackendId procId);
|
|
||||||
|
|
||||||
extern void XidCacheRemoveRunningXids(TransactionId xid,
|
|
||||||
int nxids, TransactionId *xids);
|
|
||||||
|
|
||||||
/* signal handler for catchup events (SIGUSR1) */
|
/* signal handler for catchup events (SIGUSR1) */
|
||||||
extern void CatchupInterruptHandler(SIGNAL_ARGS);
|
extern void CatchupInterruptHandler(SIGNAL_ARGS);
|
||||||
|
|
||||||
|
|
|
@ -7,16 +7,16 @@
|
||||||
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/include/storage/sinvaladt.h,v 1.37 2004/12/31 22:03:42 pgsql Exp $
|
* $PostgreSQL: pgsql/src/include/storage/sinvaladt.h,v 1.38 2005/05/19 21:35:47 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
#ifndef SINVALADT_H
|
#ifndef SINVALADT_H
|
||||||
#define SINVALADT_H
|
#define SINVALADT_H
|
||||||
|
|
||||||
#include "storage/shmem.h"
|
|
||||||
#include "storage/sinval.h"
|
#include "storage/sinval.h"
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The shared cache invalidation manager is responsible for transmitting
|
* The shared cache invalidation manager is responsible for transmitting
|
||||||
* invalidation messages between backends. Any message sent by any backend
|
* invalidation messages between backends. Any message sent by any backend
|
||||||
|
@ -71,7 +71,6 @@ typedef struct ProcState
|
||||||
/* nextMsgNum is -1 in an inactive ProcState array entry. */
|
/* nextMsgNum is -1 in an inactive ProcState array entry. */
|
||||||
int nextMsgNum; /* next message number to read, or -1 */
|
int nextMsgNum; /* next message number to read, or -1 */
|
||||||
bool resetState; /* true, if backend has to reset its state */
|
bool resetState; /* true, if backend has to reset its state */
|
||||||
SHMEM_OFFSET procStruct; /* location of backend's PGPROC struct */
|
|
||||||
} ProcState;
|
} ProcState;
|
||||||
|
|
||||||
/* Shared cache invalidation memory segment */
|
/* Shared cache invalidation memory segment */
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/include/utils/tqual.h,v 1.56 2005/03/20 23:40:34 neilc Exp $
|
* $PostgreSQL: pgsql/src/include/utils/tqual.h,v 1.57 2005/05/19 21:35:48 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -133,7 +133,7 @@ extern Snapshot CopySnapshot(Snapshot snapshot);
|
||||||
extern void FreeSnapshot(Snapshot snapshot);
|
extern void FreeSnapshot(Snapshot snapshot);
|
||||||
extern void FreeXactSnapshot(void);
|
extern void FreeXactSnapshot(void);
|
||||||
|
|
||||||
/* in sinval.c; declared here to avoid including tqual.h in sinval.h: */
|
/* in procarray.c; declared here to avoid including tqual.h in procarray.h: */
|
||||||
extern Snapshot GetSnapshotData(Snapshot snapshot, bool serializable);
|
extern Snapshot GetSnapshotData(Snapshot snapshot, bool serializable);
|
||||||
|
|
||||||
#endif /* TQUAL_H */
|
#endif /* TQUAL_H */
|
||||||
|
|
Loading…
Reference in New Issue