From ee3b71f6bce929b07636f76d1654832cb6b5a489 Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Thu, 19 May 2005 21:35:48 +0000 Subject: [PATCH] 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. --- src/backend/access/heap/heapam.c | 4 +- src/backend/access/transam/README | 4 +- src/backend/access/transam/multixact.c | 8 +- src/backend/access/transam/subtrans.c | 3 +- src/backend/access/transam/varsup.c | 7 +- src/backend/access/transam/xact.c | 28 +- src/backend/access/transam/xlog.c | 12 +- src/backend/catalog/index.c | 4 +- src/backend/commands/dbcommands.c | 4 +- src/backend/commands/vacuum.c | 4 +- src/backend/commands/vacuumlazy.c | 3 +- src/backend/storage/buffer/buf_init.c | 4 +- src/backend/storage/buffer/bufmgr.c | 10 +- src/backend/storage/ipc/Makefile | 5 +- src/backend/storage/ipc/ipci.c | 5 +- src/backend/storage/ipc/procarray.c | 787 +++++++++++++++++++++++++ src/backend/storage/ipc/sinval.c | 764 +----------------------- src/backend/storage/ipc/sinvaladt.c | 9 +- src/backend/storage/lmgr/lmgr.c | 4 +- src/backend/storage/lmgr/proc.c | 21 +- src/backend/utils/adt/misc.c | 4 +- src/backend/utils/init/postinit.c | 4 +- src/backend/utils/time/tqual.c | 4 +- src/include/storage/buf_internals.h | 11 +- src/include/storage/lwlock.h | 3 +- src/include/storage/proc.h | 5 +- src/include/storage/procarray.h | 36 ++ src/include/storage/sinval.h | 17 +- src/include/storage/sinvaladt.h | 5 +- src/include/utils/tqual.h | 4 +- 30 files changed, 923 insertions(+), 860 deletions(-) create mode 100644 src/backend/storage/ipc/procarray.c create mode 100644 src/include/storage/procarray.h diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c index 60f744001f..bffd56dd54 100644 --- a/src/backend/access/heap/heapam.c +++ b/src/backend/access/heap/heapam.c @@ -8,7 +8,7 @@ * * * 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 @@ -47,7 +47,7 @@ #include "catalog/catalog.h" #include "catalog/namespace.h" #include "miscadmin.h" -#include "storage/sinval.h" +#include "storage/procarray.h" #include "utils/inval.h" #include "utils/relcache.h" #include "pgstat.h" diff --git a/src/backend/access/transam/README b/src/backend/access/transam/README index a11f5707c7..177ba26cf3 100644 --- a/src/backend/access/transam/README +++ b/src/backend/access/transam/README @@ -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 ---------------------- @@ -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 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 -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 implements the LRU policy for in-memory buffer pages. The high-level routines diff --git a/src/backend/access/transam/multixact.c b/src/backend/access/transam/multixact.c index 44c9541209..85acfe2cc0 100644 --- a/src/backend/access/transam/multixact.c +++ b/src/backend/access/transam/multixact.c @@ -31,7 +31,7 @@ * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * 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 "storage/backendid.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, - * walking the PGPROC array only once for the whole array. But in most + * This could be made faster by having another entry point in procarray.c, + * walking the PGPROC array only once for all the members. But in most * cases nmembers should be small enough that it doesn't much matter. */ for (i = 0; i < nmembers; i++) diff --git a/src/backend/access/transam/subtrans.c b/src/backend/access/transam/subtrans.c index b92e38fc38..0b77436388 100644 --- a/src/backend/access/transam/subtrans.c +++ b/src/backend/access/transam/subtrans.c @@ -22,7 +22,7 @@ * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * 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/subtrans.h" -#include "storage/sinval.h" #include "utils/tqual.h" diff --git a/src/backend/access/transam/varsup.c b/src/backend/access/transam/varsup.c index cb532d8df3..5c51e69253 100644 --- a/src/backend/access/transam/varsup.c +++ b/src/backend/access/transam/varsup.c @@ -6,7 +6,7 @@ * Copyright (c) 2000-2005, PostgreSQL Global Development Group * * 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 * 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 * 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 @@ -120,8 +120,7 @@ GetNewTransactionId(bool isSubXact) * * 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 - * and related fields. (SInvalLock would then mean primarily that - * PGPROCs couldn't be added/removed while holding the lock.) + * and related fields. * * If there's no room to fit a subtransaction XID into PGPROC, set the * cache-overflowed flag instead. This forces readers to look in diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c index a318db6134..81fba82b48 100644 --- a/src/backend/access/transam/xact.c +++ b/src/backend/access/transam/xact.c @@ -10,7 +10,7 @@ * * * 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 "storage/fd.h" #include "storage/proc.h" -#include "storage/sinval.h" +#include "storage/procarray.h" #include "storage/smgr.h" #include "utils/flatfiles.h" #include "utils/guc.h" @@ -1503,16 +1503,18 @@ CommitTransaction(void) * this must be done _before_ releasing locks we hold and _after_ * RecordTransactionCommit. * - * LWLockAcquire(SInvalLock) is required: UPDATE with xid 0 is blocked by - * xid 1' UPDATE, xid 1 is doing commit while xid 2 gets snapshot - if - * xid 2' GetSnapshotData sees xid 1 as running then it must see xid 0 - * as running as well or it will see two tuple versions - one deleted - * by xid 1 and one inserted by xid 0. See notes in GetSnapshotData. + * LWLockAcquire(ProcArrayLock) is required; consider this example: + * UPDATE with xid 0 is blocked by xid 1's UPDATE. + * xid 1 is doing commit while xid 2 gets snapshot. + * If xid 2's GetSnapshotData sees xid 1 as running then it must see + * 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) { - /* Lock SInvalLock because that's what GetSnapshotData uses. */ - LWLockAcquire(SInvalLock, LW_EXCLUSIVE); + /* Lock ProcArrayLock because that's what GetSnapshotData uses. */ + LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE); MyProc->xid = InvalidTransactionId; MyProc->xmin = InvalidTransactionId; @@ -1520,7 +1522,7 @@ CommitTransaction(void) MyProc->subxids.nxids = 0; MyProc->subxids.overflowed = false; - LWLockRelease(SInvalLock); + LWLockRelease(ProcArrayLock); } /* @@ -1688,8 +1690,8 @@ AbortTransaction(void) */ if (MyProc != NULL) { - /* Lock SInvalLock because that's what GetSnapshotData uses. */ - LWLockAcquire(SInvalLock, LW_EXCLUSIVE); + /* Lock ProcArrayLock because that's what GetSnapshotData uses. */ + LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE); MyProc->xid = InvalidTransactionId; MyProc->xmin = InvalidTransactionId; @@ -1697,7 +1699,7 @@ AbortTransaction(void) MyProc->subxids.nxids = 0; MyProc->subxids.overflowed = false; - LWLockRelease(SInvalLock); + LWLockRelease(ProcArrayLock); } /* diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index fdee576911..bff12a1437 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * 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/pmsignal.h" #include "storage/proc.h" -#include "storage/sinval.h" +#include "storage/procarray.h" #include "storage/spin.h" #include "utils/builtins.h" #include "utils/guc.h" @@ -776,7 +776,7 @@ begin:; 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 * WALInsertLock, instead. A better solution would be a per-PROC * 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). * * XXX temporarily ifdef'd out to avoid three-way deadlock condition: - * GetUndoRecPtr needs to grab SInvalLock to ensure that it is looking - * at a stable set of proc records, but grabbing SInvalLock while + * GetUndoRecPtr needs to grab ProcArrayLock to ensure that it is looking + * at a stable set of proc records, but grabbing ProcArrayLock while * holding WALInsertLock is no good. GetNewTransactionId may cause a * 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 * pgsql-hackers discussion of 17-Dec-01. * diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9ad6b343a3..ee4f8c6800 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -8,7 +8,7 @@ * * * 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 @@ -38,7 +38,7 @@ #include "miscadmin.h" #include "optimizer/clauses.h" #include "parser/parse_expr.h" -#include "storage/sinval.h" +#include "storage/procarray.h" #include "storage/smgr.h" #include "utils/builtins.h" #include "utils/fmgroids.h" diff --git a/src/backend/commands/dbcommands.c b/src/backend/commands/dbcommands.c index ec04619175..22a9afd076 100644 --- a/src/backend/commands/dbcommands.c +++ b/src/backend/commands/dbcommands.c @@ -15,7 +15,7 @@ * * * 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 "storage/fd.h" #include "storage/freespace.h" -#include "storage/sinval.h" +#include "storage/procarray.h" #include "utils/acl.h" #include "utils/array.h" #include "utils/builtins.h" diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index f9dbd3f461..7ccbb70cbb 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -13,7 +13,7 @@ * * * 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 "miscadmin.h" #include "storage/freespace.h" -#include "storage/sinval.h" +#include "storage/procarray.h" #include "storage/smgr.h" #include "tcop/pquery.h" #include "utils/acl.h" diff --git a/src/backend/commands/vacuumlazy.c b/src/backend/commands/vacuumlazy.c index d2b6da9ef8..52207000ee 100644 --- a/src/backend/commands/vacuumlazy.c +++ b/src/backend/commands/vacuumlazy.c @@ -31,7 +31,7 @@ * * * 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 "miscadmin.h" #include "storage/freespace.h" -#include "storage/sinval.h" #include "storage/smgr.h" #include "utils/lsyscache.h" diff --git a/src/backend/storage/buffer/buf_init.c b/src/backend/storage/buffer/buf_init.c index 1b8f042079..b2537cbc07 100644 --- a/src/backend/storage/buffer/buf_init.c +++ b/src/backend/storage/buffer/buf_init.c @@ -8,7 +8,7 @@ * * * 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->usage_count = 0; buf->refcount = 0; - buf->wait_backend_id = 0; + buf->wait_backend_pid = 0; SpinLockInit(&buf->buf_hdr_lock); diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index e3a60612e3..1fe681e1ab 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -8,7 +8,7 @@ * * * 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) { /* 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; UnlockBufHdr_NoHoldoff(buf); - ProcSendSignal(wait_backend_id); + ProcSendSignal(wait_backend_pid); } else UnlockBufHdr_NoHoldoff(buf); @@ -1678,7 +1678,7 @@ UnlockBuffers(void) * signal. */ if ((buf->flags & BM_PIN_COUNT_WAITER) != 0 && - buf->wait_backend_id == MyBackendId) + buf->wait_backend_pid == MyProcPid) buf->flags &= ~BM_PIN_COUNT_WAITER; UnlockBufHdr_NoHoldoff(buf); @@ -1820,7 +1820,7 @@ LockBufferForCleanup(Buffer buffer) LockBuffer(buffer, BUFFER_LOCK_UNLOCK); 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; PinCountWaitBuf = bufHdr; UnlockBufHdr_NoHoldoff(bufHdr); diff --git a/src/backend/storage/ipc/Makefile b/src/backend/storage/ipc/Makefile index 1e85fb62f2..d7ff6f8d30 100644 --- a/src/backend/storage/ipc/Makefile +++ b/src/backend/storage/ipc/Makefile @@ -1,7 +1,7 @@ # # 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 @@ -15,7 +15,8 @@ override CFLAGS+= -fno-inline 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 diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index 975d5f131d..22333a1f55 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -8,7 +8,7 @@ * * * 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/pmsignal.h" #include "storage/proc.h" +#include "storage/procarray.h" #include "storage/sinval.h" #include "storage/spin.h" @@ -78,6 +79,7 @@ CreateSharedMemoryAndSemaphores(bool makePrivate, size += SUBTRANSShmemSize(); size += MultiXactShmemSize(); size += LWLockShmemSize(); + size += ProcArrayShmemSize(maxBackends); size += SInvalShmemSize(maxBackends); size += FreeSpaceShmemSize(); size += BgWriterShmemSize(); @@ -155,6 +157,7 @@ CreateSharedMemoryAndSemaphores(bool makePrivate, * Set up process table */ InitProcGlobal(maxBackends); + CreateSharedProcArray(maxBackends); /* * Set up shared-inval messaging diff --git a/src/backend/storage/ipc/procarray.c b/src/backend/storage/ipc/procarray.c new file mode 100644 index 0000000000..85ae3c762b --- /dev/null +++ b/src/backend/storage/ipc/procarray.c @@ -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 */ diff --git a/src/backend/storage/ipc/sinval.c b/src/backend/storage/ipc/sinval.c index 27716516b7..e771eea196 100644 --- a/src/backend/storage/ipc/sinval.c +++ b/src/backend/storage/ipc/sinval.c @@ -8,7 +8,7 @@ * * * 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 -#include "access/subtrans.h" -#include "access/transam.h" +#include "access/xact.h" #include "commands/async.h" +#include "miscadmin.h" +#include "storage/backendid.h" #include "storage/ipc.h" #include "storage/proc.h" #include "storage/sinval.h" #include "storage/sinvaladt.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 * need a way to give an idle backend a swift kick in the rear and make @@ -103,10 +79,6 @@ InitBackendSharedInvalidationState(void) ereport(FATAL, (errcode(ERRCODE_TOO_MANY_CONNECTIONS), 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 * shared lock! Look closely at the interactions before allowing * 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); getResult = SIGetDataEntry(shmInvalBuffer, MyBackendId, &data); @@ -391,725 +357,3 @@ ProcessCatchupEvent(void) if (notify_enabled) 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 */ diff --git a/src/backend/storage/ipc/sinvaladt.c b/src/backend/storage/ipc/sinvaladt.c index 98b1e8fd6a..64a9672b30 100644 --- a/src/backend/storage/ipc/sinvaladt.c +++ b/src/backend/storage/ipc/sinvaladt.c @@ -8,7 +8,7 @@ * * * 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 "storage/backendid.h" #include "storage/ipc.h" +#include "storage/lwlock.h" #include "storage/pmsignal.h" -#include "storage/proc.h" +#include "storage/shmem.h" #include "storage/sinvaladt.h" + SISeg *shmInvalBuffer; static void CleanupInvalidationState(int status, Datum arg); @@ -72,7 +74,6 @@ SIBufferInit(int maxBackends) { segP->procState[i].nextMsgNum = -1; /* inactive */ 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 */ stateP->nextMsgNum = segP->maxMsgNum; stateP->resetState = false; - stateP->procStruct = MAKE_OFFSET(MyProc); /* register exit routine to mark my entry inactive at exit */ on_shmem_exit(CleanupInvalidationState, PointerGetDatum(segP)); @@ -163,7 +163,6 @@ CleanupInvalidationState(int status, Datum arg) /* Mark myself inactive */ segP->procState[MyBackendId - 1].nextMsgNum = -1; segP->procState[MyBackendId - 1].resetState = false; - segP->procState[MyBackendId - 1].procStruct = INVALID_OFFSET; /* Recompute index of last active backend */ for (i = segP->lastBackend; i > 0; i--) diff --git a/src/backend/storage/lmgr/lmgr.c b/src/backend/storage/lmgr/lmgr.c index f0e2dadfa8..716ccf2903 100644 --- a/src/backend/storage/lmgr/lmgr.c +++ b/src/backend/storage/lmgr/lmgr.c @@ -8,7 +8,7 @@ * * * 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 "miscadmin.h" #include "storage/lmgr.h" -#include "storage/sinval.h" +#include "storage/procarray.h" #include "utils/inval.h" diff --git a/src/backend/storage/lmgr/proc.c b/src/backend/storage/lmgr/proc.c index c3ba654421..597e97a5b0 100644 --- a/src/backend/storage/lmgr/proc.c +++ b/src/backend/storage/lmgr/proc.c @@ -8,7 +8,7 @@ * * * 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/ipc.h" #include "storage/proc.h" -#include "storage/sinval.h" +#include "storage/procarray.h" #include "storage/spin.h" @@ -116,8 +116,7 @@ ProcGlobalSemas(int maxBackends) /* * InitProcGlobal - - * initializes the global process table. We put it here so that - * the postmaster can do this initialization. + * Initialize the global process table during postmaster startup. * * We also create all the per-process semaphores we will need to support * the requested number of backends. We used to allocate semaphores @@ -263,6 +262,11 @@ InitProcess(void) MyProc->waitProcLock = NULL; SHMQueueInit(&(MyProc->procLocks)); + /* + * Add our PGPROC to the PGPROC array in shared memory. + */ + ProcArrayAddMyself(); + /* * Arrange to clean up at backend exit. */ @@ -473,6 +477,9 @@ ProcKill(int code, Datum arg) LockReleaseAll(USER_LOCKMETHOD, true); #endif + /* Remove our PGPROC from the PGPROC array in shared memory */ + ProcArrayRemoveMyself(); + SpinLockAcquire(ProcStructLock); /* 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 -ProcSendSignal(BackendId procId) +ProcSendSignal(int pid) { - PGPROC *proc = BackendIdGetProc(procId); + PGPROC *proc = BackendPidGetProc(pid); if (proc != NULL) PGSemaphoreUnlock(&proc->sem); diff --git a/src/backend/utils/adt/misc.c b/src/backend/utils/adt/misc.c index 2b1c5e7406..886ff01122 100644 --- a/src/backend/utils/adt/misc.c +++ b/src/backend/utils/adt/misc.c @@ -8,7 +8,7 @@ * * * 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 "miscadmin.h" -#include "storage/sinval.h" +#include "storage/procarray.h" #include "storage/fd.h" #include "utils/builtins.h" #include "funcapi.h" diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c index fef1565a04..7130365d4f 100644 --- a/src/backend/utils/init/postinit.c +++ b/src/backend/utils/init/postinit.c @@ -8,7 +8,7 @@ * * * 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 "miscadmin.h" #include "postmaster/postmaster.h" +#include "storage/backendid.h" #include "storage/fd.h" #include "storage/ipc.h" #include "storage/proc.h" +#include "storage/procarray.h" #include "storage/sinval.h" #include "storage/smgr.h" #include "utils/flatfiles.h" diff --git a/src/backend/utils/time/tqual.c b/src/backend/utils/time/tqual.c index c66a3d0e1f..9463324964 100644 --- a/src/backend/utils/time/tqual.c +++ b/src/backend/utils/time/tqual.c @@ -32,7 +32,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * 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/subtrans.h" -#include "storage/sinval.h" +#include "storage/procarray.h" #include "utils/tqual.h" /* diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h index 17687510cd..2bd210dfb5 100644 --- a/src/include/storage/buf_internals.h +++ b/src/include/storage/buf_internals.h @@ -8,14 +8,13 @@ * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * 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 #define BUFMGR_INTERNALS_H -#include "storage/backendid.h" #include "storage/buf.h" #include "storage/lwlock.h" #include "storage/shmem.h" @@ -94,7 +93,7 @@ typedef struct buftag * BufferDesc -- shared descriptor/state data for a single shared buffer. * * 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 * 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 @@ -108,8 +107,8 @@ typedef struct buftag * * 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 - * to go away. This is signaled by storing its own backend ID into - * wait_backend_id and setting flag bit BM_PIN_COUNT_WAITER. At present, + * to go away. This is signaled by storing its own PID into + * wait_backend_pid and setting flag bit BM_PIN_COUNT_WAITER. At present, * there can be only one such waiter per buffer. * * 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 */ uint16 usage_count; /* usage counter for clock sweep code */ 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 */ diff --git a/src/include/storage/lwlock.h b/src/include/storage/lwlock.h index 18215f4838..eb45f3e39a 100644 --- a/src/include/storage/lwlock.h +++ b/src/include/storage/lwlock.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * 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, OidGenLock, XidGenLock, + ProcArrayLock, SInvalLock, FreeSpaceLock, MMCacheLock, diff --git a/src/include/storage/proc.h b/src/include/storage/proc.h index 8d514fce94..f771d71933 100644 --- a/src/include/storage/proc.h +++ b/src/include/storage/proc.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * 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_ #include "access/xlog.h" -#include "storage/backendid.h" #include "storage/lock.h" #include "storage/pg_sema.h" @@ -137,7 +136,7 @@ extern bool LockWaitCancel(void); extern void ProcWaitForSignal(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 disable_sig_alarm(bool is_statement_timeout); diff --git a/src/include/storage/procarray.h b/src/include/storage/procarray.h new file mode 100644 index 0000000000..437ac306e0 --- /dev/null +++ b/src/include/storage/procarray.h @@ -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 */ diff --git a/src/include/storage/sinval.h b/src/include/storage/sinval.h index 092b722048..0f4c0f68f3 100644 --- a/src/include/storage/sinval.h +++ b/src/include/storage/sinval.h @@ -7,14 +7,13 @@ * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * 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 #define SINVAL_H -#include "storage/backendid.h" #include "storage/itemptr.h" #include "storage/relfilenode.h" @@ -87,24 +86,12 @@ typedef union extern int SInvalShmemSize(int maxBackends); extern void CreateSharedInvalidationState(int maxBackends); extern void InitBackendSharedInvalidationState(void); + extern void SendSharedInvalidMessage(SharedInvalidationMessage *msg); extern void ReceiveSharedInvalidMessages( void (*invalFunction) (SharedInvalidationMessage *msg), 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) */ extern void CatchupInterruptHandler(SIGNAL_ARGS); diff --git a/src/include/storage/sinvaladt.h b/src/include/storage/sinvaladt.h index 7fce6070da..0be6a38650 100644 --- a/src/include/storage/sinvaladt.h +++ b/src/include/storage/sinvaladt.h @@ -7,16 +7,16 @@ * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * 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 #define SINVALADT_H -#include "storage/shmem.h" #include "storage/sinval.h" + /* * The shared cache invalidation manager is responsible for transmitting * 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. */ int nextMsgNum; /* next message number to read, or -1 */ bool resetState; /* true, if backend has to reset its state */ - SHMEM_OFFSET procStruct; /* location of backend's PGPROC struct */ } ProcState; /* Shared cache invalidation memory segment */ diff --git a/src/include/utils/tqual.h b/src/include/utils/tqual.h index e553cfedf8..f12ae2233f 100644 --- a/src/include/utils/tqual.h +++ b/src/include/utils/tqual.h @@ -8,7 +8,7 @@ * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * 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 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); #endif /* TQUAL_H */