diff options
Diffstat (limited to 'src/backend/storage/ipc')
-rw-r--r-- | src/backend/storage/ipc/Makefile | 33 | ||||
-rw-r--r-- | src/backend/storage/ipc/README | 31 | ||||
-rw-r--r-- | src/backend/storage/ipc/ipc.c | 193 | ||||
-rw-r--r-- | src/backend/storage/ipc/ipci.c | 137 | ||||
-rw-r--r-- | src/backend/storage/ipc/pmsignal.c | 83 | ||||
-rw-r--r-- | src/backend/storage/ipc/shmem.c | 379 | ||||
-rw-r--r-- | src/backend/storage/ipc/shmqueue.c | 257 | ||||
-rw-r--r-- | src/backend/storage/ipc/sinval.c | 540 | ||||
-rw-r--r-- | src/backend/storage/ipc/sinvaladt.c | 349 |
9 files changed, 0 insertions, 2002 deletions
diff --git a/src/backend/storage/ipc/Makefile b/src/backend/storage/ipc/Makefile deleted file mode 100644 index 960097b1d18..00000000000 --- a/src/backend/storage/ipc/Makefile +++ /dev/null @@ -1,33 +0,0 @@ -# -# Makefile for storage/ipc -# -# $Header: /cvsroot/pgsql/src/backend/storage/ipc/Makefile,v 1.17 2001/11/04 19:55:31 tgl Exp $ -# - -subdir = src/backend/storage/ipc -top_builddir = ../../../.. -include $(top_builddir)/src/Makefile.global - -# seems to be required 1999/07/22 bjm -ifeq "$(findstring alpha,$(host_cpu))" "alpha" -ifeq "$(GCC)" "yes" -override CFLAGS+= -fno-inline -endif -endif - -OBJS = ipc.o ipci.o pmsignal.o shmem.o shmqueue.o sinval.o sinvaladt.o - -all: SUBSYS.o - -SUBSYS.o: $(OBJS) - $(LD) $(LDREL) $(LDOUT) SUBSYS.o $(OBJS) - -depend dep: - $(CC) -MM $(CFLAGS) *.c >depend - -clean: - rm -f SUBSYS.o $(OBJS) - -ifeq (depend,$(wildcard depend)) -include depend -endif diff --git a/src/backend/storage/ipc/README b/src/backend/storage/ipc/README deleted file mode 100644 index 02d66045f82..00000000000 --- a/src/backend/storage/ipc/README +++ /dev/null @@ -1,31 +0,0 @@ -$Header: /cvsroot/pgsql/src/backend/storage/ipc/README,v 1.1.1.1 1996/07/09 06:21:54 scrappy Exp $ -Mon Jul 18 11:09:22 PDT 1988 W.KLAS - -Cache invalidation synchronization routines: -=========================================== - -The cache synchronization is done using a message queue. Every -backend can register a message which then has to be read by -all backends. A message read by all backends is removed from the -queue automatically. If a message has been lost because the buffer -was full, all backends that haven't read this message will be -noticed that they have to reset their cache state. This is done -at the time when they try to read the message queue. - -The message queue is implemented as a shared buffer segment. Actually, -the queue is a circle to allow fast inserting, reading (invalidate data) and -maintaining the buffer. - -Access to this shared message buffer is synchronized by the lock manager. -The lock manager treats the buffer as a regular relation and sets -relation level locks (with mode = LockWait) to block backends while -another backend is writing or reading the buffer. The identifiers used -for this special 'relation' are database id = 0 and relation id = 0. - -The current implementation prints regular (e)log information -when a message has been removed from the buffer because the buffer -is full, and a backend has to reset its cache state. The elog level -is NOTICE. This can be used to improve teh behavior of backends -when invalidating or reseting their cache state. - - diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c deleted file mode 100644 index e71bb0c17af..00000000000 --- a/src/backend/storage/ipc/ipc.c +++ /dev/null @@ -1,193 +0,0 @@ -/*------------------------------------------------------------------------- - * - * ipc.c - * POSTGRES inter-process communication definitions. - * - * This file is misnamed, as it no longer has much of anything directly - * to do with IPC. The functionality here is concerned with managing - * exit-time cleanup for either a postmaster or a backend. - * - * - * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group - * Portions Copyright (c) 1994, Regents of the University of California - * - * - * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/storage/ipc/ipc.c,v 1.80 2002/06/20 20:29:34 momjian Exp $ - * - *------------------------------------------------------------------------- - */ -#include "postgres.h" - -#include <errno.h> -#include <signal.h> -#include <unistd.h> - -#include "miscadmin.h" -#include "storage/ipc.h" - - -/* - * This flag is set during proc_exit() to change elog()'s behavior, - * so that an elog() from an on_proc_exit routine cannot get us out - * of the exit procedure. We do NOT want to go back to the idle loop... - */ -bool proc_exit_inprogress = false; - - -/* ---------------------------------------------------------------- - * exit() handling stuff - * - * These functions are in generally the same spirit as atexit(2), - * but provide some additional features we need --- in particular, - * we want to register callbacks to invoke when we are disconnecting - * from a broken shared-memory context but not exiting the postmaster. - * - * Callback functions can take zero, one, or two args: the first passed - * arg is the integer exitcode, the second is the Datum supplied when - * the callback was registered. - * ---------------------------------------------------------------- - */ - -#define MAX_ON_EXITS 20 - -static struct ONEXIT -{ - void (*function) (); - Datum arg; -} on_proc_exit_list[MAX_ON_EXITS], on_shmem_exit_list[MAX_ON_EXITS]; - -static int on_proc_exit_index, - on_shmem_exit_index; - - -/* ---------------------------------------------------------------- - * proc_exit - * - * this function calls all the callbacks registered - * for it (to free resources) and then calls exit. - * This should be the only function to call exit(). - * -cim 2/6/90 - * ---------------------------------------------------------------- - */ -void -proc_exit(int code) -{ - /* - * Once we set this flag, we are committed to exit. Any elog() will - * NOT send control back to the main loop, but right back here. - */ - proc_exit_inprogress = true; - - /* - * Forget any pending cancel or die requests; we're doing our best to - * close up shop already. Note that the signal handlers will not set - * these flags again, now that proc_exit_inprogress is set. - */ - InterruptPending = false; - ProcDiePending = false; - QueryCancelPending = false; - /* And let's just make *sure* we're not interrupted ... */ - ImmediateInterruptOK = false; - InterruptHoldoffCount = 1; - CritSectionCount = 0; - - elog(DEBUG2, "proc_exit(%d)", code); - - /* do our shared memory exits first */ - shmem_exit(code); - - /* - * call all the callbacks registered before calling exit(). - * - * Note that since we decrement on_proc_exit_index each time, if a - * callback calls elog(ERROR) or elog(FATAL) then it won't be invoked - * again when control comes back here (nor will the - * previously-completed callbacks). So, an infinite loop should not - * be possible. - */ - while (--on_proc_exit_index >= 0) - (*on_proc_exit_list[on_proc_exit_index].function) (code, - on_proc_exit_list[on_proc_exit_index].arg); - - elog(DEBUG2, "exit(%d)", code); - exit(code); -} - -/* ------------------ - * Run all of the on_shmem_exit routines --- but don't actually exit. - * This is used by the postmaster to re-initialize shared memory and - * semaphores after a backend dies horribly. - * ------------------ - */ -void -shmem_exit(int code) -{ - elog(DEBUG2, "shmem_exit(%d)", code); - - /* - * call all the registered callbacks. - * - * As with proc_exit(), we remove each callback from the list before - * calling it, to avoid infinite loop in case of error. - */ - while (--on_shmem_exit_index >= 0) - (*on_shmem_exit_list[on_shmem_exit_index].function) (code, - on_shmem_exit_list[on_shmem_exit_index].arg); - - on_shmem_exit_index = 0; -} - -/* ---------------------------------------------------------------- - * on_proc_exit - * - * this function adds a callback function to the list of - * functions invoked by proc_exit(). -cim 2/6/90 - * ---------------------------------------------------------------- - */ -void - on_proc_exit(void (*function) (), Datum arg) -{ - if (on_proc_exit_index >= MAX_ON_EXITS) - elog(FATAL, "Out of on_proc_exit slots"); - - on_proc_exit_list[on_proc_exit_index].function = function; - on_proc_exit_list[on_proc_exit_index].arg = arg; - - ++on_proc_exit_index; -} - -/* ---------------------------------------------------------------- - * on_shmem_exit - * - * this function adds a callback function to the list of - * functions invoked by shmem_exit(). -cim 2/6/90 - * ---------------------------------------------------------------- - */ -void - on_shmem_exit(void (*function) (), Datum arg) -{ - if (on_shmem_exit_index >= MAX_ON_EXITS) - elog(FATAL, "Out of on_shmem_exit slots"); - - on_shmem_exit_list[on_shmem_exit_index].function = function; - on_shmem_exit_list[on_shmem_exit_index].arg = arg; - - ++on_shmem_exit_index; -} - -/* ---------------------------------------------------------------- - * on_exit_reset - * - * this function clears all on_proc_exit() and on_shmem_exit() - * registered functions. This is used just after forking a backend, - * so that the backend doesn't believe it should call the postmaster's - * on-exit routines when it exits... - * ---------------------------------------------------------------- - */ -void -on_exit_reset(void) -{ - on_shmem_exit_index = 0; - on_proc_exit_index = 0; -} diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c deleted file mode 100644 index 04e4f5bc38e..00000000000 --- a/src/backend/storage/ipc/ipci.c +++ /dev/null @@ -1,137 +0,0 @@ -/*------------------------------------------------------------------------- - * - * ipci.c - * POSTGRES inter-process communication initialization code. - * - * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group - * Portions Copyright (c) 1994, Regents of the University of California - * - * - * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/storage/ipc/ipci.c,v 1.48 2002/06/20 20:29:35 momjian Exp $ - * - *------------------------------------------------------------------------- - */ -#include "postgres.h" - -#include <sys/types.h> - -#include "miscadmin.h" -#include "access/clog.h" -#include "access/xlog.h" -#include "storage/bufmgr.h" -#include "storage/freespace.h" -#include "storage/ipc.h" -#include "storage/lmgr.h" -#include "storage/lwlock.h" -#include "storage/pg_sema.h" -#include "storage/pg_shmem.h" -#include "storage/pmsignal.h" -#include "storage/proc.h" -#include "storage/sinval.h" -#include "storage/spin.h" - - -/* - * CreateSharedMemoryAndSemaphores - * Creates and initializes shared memory and semaphores. - * - * This is called by the postmaster or by a standalone backend. - * It is NEVER called by a backend forked from the postmaster; - * for such a backend, the shared memory is already ready-to-go. - * - * If "makePrivate" is true then we only need private memory, not shared - * memory. This is true for a standalone backend, false for a postmaster. - */ -void -CreateSharedMemoryAndSemaphores(bool makePrivate, - int maxBackends, - int port) -{ - int size; - int numSemas; - PGShmemHeader *seghdr; - - /* - * Size of the Postgres shared-memory block is estimated via - * moderately-accurate estimates for the big hogs, plus 100K for the - * stuff that's too small to bother with estimating. - */ - size = BufferShmemSize(); - size += LockShmemSize(maxBackends); - size += XLOGShmemSize(); - size += CLOGShmemSize(); - size += LWLockShmemSize(); - size += SInvalShmemSize(maxBackends); - size += FreeSpaceShmemSize(); -#ifdef STABLE_MEMORY_STORAGE - size += MMShmemSize(); -#endif - size += 100000; - /* might as well round it off to a multiple of a typical page size */ - size += 8192 - (size % 8192); - - elog(DEBUG2, "invoking IpcMemoryCreate(size=%d)", size); - - /* - * Create the shmem segment - */ - seghdr = PGSharedMemoryCreate(size, makePrivate, port); - - /* - * Create semaphores - */ - numSemas = ProcGlobalSemas(maxBackends); - numSemas += SpinlockSemas(); - PGReserveSemaphores(numSemas, port); - - /* - * Set up shared memory allocation mechanism - */ - InitShmemAllocation(seghdr); - - /* - * Now initialize LWLocks, which do shared memory allocation and are - * needed for InitShmemIndex. - */ - CreateLWLocks(); - - /* - * Set up shmem.c index hashtable - */ - InitShmemIndex(); - - /* - * Set up xlog, clog, and buffers - */ - XLOGShmemInit(); - CLOGShmemInit(); - InitBufferPool(); - - /* - * Set up lock manager - */ - InitLocks(); - if (InitLockTable(maxBackends) == INVALID_TABLEID) - elog(FATAL, "Couldn't create the lock table"); - - /* - * Set up process table - */ - InitProcGlobal(maxBackends); - - /* - * Set up shared-inval messaging - */ - CreateSharedInvalidationState(maxBackends); - - /* - * Set up free-space map - */ - InitFreeSpaceMap(); - - /* - * Set up child-to-postmaster signaling mechanism - */ - PMSignalInit(); -} diff --git a/src/backend/storage/ipc/pmsignal.c b/src/backend/storage/ipc/pmsignal.c deleted file mode 100644 index cf351d1b0d0..00000000000 --- a/src/backend/storage/ipc/pmsignal.c +++ /dev/null @@ -1,83 +0,0 @@ -/*------------------------------------------------------------------------- - * - * pmsignal.c - * routines for signaling the postmaster from its child processes - * - * - * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group - * Portions Copyright (c) 1994, Regents of the University of California - * - * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/storage/ipc/pmsignal.c,v 1.4 2002/06/20 20:29:35 momjian Exp $ - * - *------------------------------------------------------------------------- - */ -#include "postgres.h" - -#include <signal.h> -#include <unistd.h> - -#include "miscadmin.h" -#include "storage/pmsignal.h" -#include "storage/shmem.h" - - -/* - * The postmaster is signaled by its children by sending SIGUSR1. The - * specific reason is communicated via flags in shared memory. We keep - * a boolean flag for each possible "reason", so that different reasons - * can be signaled by different backends at the same time. (However, - * if the same reason is signaled more than once simultaneously, the - * postmaster will observe it only once.) - * - * The flags are actually declared as "volatile sig_atomic_t" for maximum - * portability. This should ensure that loads and stores of the flag - * values are atomic, allowing us to dispense with any explicit locking. - */ - -static volatile sig_atomic_t *PMSignalFlags; - - -/* - * PMSignalInit - initialize during shared-memory creation - */ -void -PMSignalInit(void) -{ - PMSignalFlags = (sig_atomic_t *) - ShmemAlloc(NUM_PMSIGNALS * sizeof(sig_atomic_t)); - - MemSet(PMSignalFlags, 0, NUM_PMSIGNALS * sizeof(sig_atomic_t)); -} - -/* - * SendPostmasterSignal - signal the postmaster from a child process - */ -void -SendPostmasterSignal(PMSignalReason reason) -{ - /* If called in a standalone backend, do nothing */ - if (!IsUnderPostmaster) - return; - /* Atomically set the proper flag */ - PMSignalFlags[reason] = true; - /* Send signal to postmaster (assume it is our direct parent) */ - kill(getppid(), SIGUSR1); -} - -/* - * CheckPostmasterSignal - check to see if a particular reason has been - * signaled, and clear the signal flag. Should be called by postmaster - * after receiving SIGUSR1. - */ -bool -CheckPostmasterSignal(PMSignalReason reason) -{ - /* Careful here --- don't clear flag if we haven't seen it set */ - if (PMSignalFlags[reason]) - { - PMSignalFlags[reason] = false; - return true; - } - return false; -} diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c deleted file mode 100644 index 70dfb833752..00000000000 --- a/src/backend/storage/ipc/shmem.c +++ /dev/null @@ -1,379 +0,0 @@ -/*------------------------------------------------------------------------- - * - * shmem.c - * create shared memory and initialize shared memory data structures. - * - * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group - * Portions Copyright (c) 1994, Regents of the University of California - * - * - * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/storage/ipc/shmem.c,v 1.66 2002/06/20 20:29:35 momjian Exp $ - * - *------------------------------------------------------------------------- - */ -/* - * POSTGRES processes share one or more regions of shared memory. - * The shared memory is created by a postmaster and is inherited - * by each backend via fork(). The routines in this file are used for - * allocating and binding to shared memory data structures. - * - * NOTES: - * (a) There are three kinds of shared memory data structures - * available to POSTGRES: fixed-size structures, queues and hash - * tables. Fixed-size structures contain things like global variables - * for a module and should never be allocated after the process - * initialization phase. Hash tables have a fixed maximum size, but - * their actual size can vary dynamically. When entries are added - * to the table, more space is allocated. Queues link data structures - * that have been allocated either as fixed size structures or as hash - * buckets. Each shared data structure has a string name to identify - * it (assigned in the module that declares it). - * - * (b) During initialization, each module looks for its - * shared data structures in a hash table called the "Shmem Index". - * If the data structure is not present, the caller can allocate - * a new one and initialize it. If the data structure is present, - * the caller "attaches" to the structure by initializing a pointer - * in the local address space. - * The shmem index has two purposes: first, it gives us - * a simple model of how the world looks when a backend process - * initializes. If something is present in the shmem index, - * it is initialized. If it is not, it is uninitialized. Second, - * the shmem index allows us to allocate shared memory on demand - * instead of trying to preallocate structures and hard-wire the - * sizes and locations in header files. If you are using a lot - * of shared memory in a lot of different places (and changing - * things during development), this is important. - * - * (c) memory allocation model: shared memory can never be - * freed, once allocated. Each hash table has its own free list, - * so hash buckets can be reused when an item is deleted. However, - * if one hash table grows very large and then shrinks, its space - * cannot be redistributed to other tables. We could build a simple - * hash bucket garbage collector if need be. Right now, it seems - * unnecessary. - * - * See InitSem() in sem.c for an example of how to use the - * shmem index. - */ - -#include "postgres.h" - -#include "access/transam.h" -#include "storage/pg_shmem.h" -#include "storage/spin.h" -#include "utils/tqual.h" - - -/* shared memory global variables */ - -static PGShmemHeader *ShmemSegHdr; /* shared mem segment header */ - -SHMEM_OFFSET ShmemBase; /* start address of shared memory */ - -static SHMEM_OFFSET ShmemEnd; /* end+1 address of shared memory */ - -static slock_t *ShmemLock; /* spinlock for shared memory allocation */ - -static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */ - -static bool ShmemBootstrap = false; /* bootstrapping shmem index? */ - - -/* - * InitShmemAllocation() --- set up shared-memory allocation. - * - * Note: the argument should be declared "PGShmemHeader *seghdr", - * but we use void to avoid having to include ipc.h in shmem.h. - */ -void -InitShmemAllocation(void *seghdr) -{ - PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr; - - /* Set up basic pointers to shared memory */ - ShmemSegHdr = shmhdr; - ShmemBase = (SHMEM_OFFSET) shmhdr; - ShmemEnd = ShmemBase + shmhdr->totalsize; - - /* - * Initialize the spinlock used by ShmemAlloc. We have to do the - * space allocation the hard way, since ShmemAlloc can't be called - * yet. - */ - ShmemLock = (slock_t *) (((char *) shmhdr) + shmhdr->freeoffset); - shmhdr->freeoffset += MAXALIGN(sizeof(slock_t)); - Assert(shmhdr->freeoffset <= shmhdr->totalsize); - - SpinLockInit(ShmemLock); - - /* ShmemIndex can't be set up yet (need LWLocks first) */ - ShmemIndex = (HTAB *) NULL; - - /* - * Initialize ShmemVariableCache for transaction manager. - */ - ShmemVariableCache = (VariableCache) - ShmemAlloc(sizeof(*ShmemVariableCache)); - memset(ShmemVariableCache, 0, sizeof(*ShmemVariableCache)); -} - -/* - * ShmemAlloc -- allocate max-aligned chunk from shared memory - * - * Assumes ShmemLock and ShmemSegHdr are initialized. - * - * Returns: real pointer to memory or NULL if we are out - * of space. Has to return a real pointer in order - * to be compatible with malloc(). - */ -void * -ShmemAlloc(Size size) -{ - uint32 newFree; - void *newSpace; - /* use volatile pointer to prevent code rearrangement */ - volatile PGShmemHeader *shmemseghdr = ShmemSegHdr; - - /* - * ensure all space is adequately aligned. - */ - size = MAXALIGN(size); - - Assert(shmemseghdr != NULL); - - SpinLockAcquire(ShmemLock); - - newFree = shmemseghdr->freeoffset + size; - if (newFree <= shmemseghdr->totalsize) - { - newSpace = (void *) MAKE_PTR(shmemseghdr->freeoffset); - shmemseghdr->freeoffset = newFree; - } - else - newSpace = NULL; - - SpinLockRelease(ShmemLock); - - if (!newSpace) - elog(WARNING, "ShmemAlloc: out of memory"); - - return newSpace; -} - -/* - * ShmemIsValid -- test if an offset refers to valid shared memory - * - * Returns TRUE if the pointer is valid. - */ -bool -ShmemIsValid(unsigned long addr) -{ - return (addr < ShmemEnd) && (addr >= ShmemBase); -} - -/* - * InitShmemIndex() --- set up shmem index table. - */ -void -InitShmemIndex(void) -{ - HASHCTL info; - int hash_flags; - ShmemIndexEnt *result, - item; - bool found; - - /* - * Since ShmemInitHash calls ShmemInitStruct, which expects the - * ShmemIndex hashtable to exist already, we have a bit of a - * circularity problem in initializing the ShmemIndex itself. We set - * ShmemBootstrap to tell ShmemInitStruct to fake it. - */ - ShmemBootstrap = true; - - /* create the shared memory shmem index */ - info.keysize = SHMEM_INDEX_KEYSIZE; - info.entrysize = sizeof(ShmemIndexEnt); - hash_flags = HASH_ELEM; - - /* This will acquire the shmem index lock, but not release it. */ - ShmemIndex = ShmemInitHash("ShmemIndex", - SHMEM_INDEX_SIZE, SHMEM_INDEX_SIZE, - &info, hash_flags); - if (!ShmemIndex) - elog(FATAL, "InitShmemIndex: couldn't initialize Shmem Index"); - - /* - * Now, create an entry in the hashtable for the index itself. - */ - MemSet(item.key, 0, SHMEM_INDEX_KEYSIZE); - strncpy(item.key, "ShmemIndex", SHMEM_INDEX_KEYSIZE); - - result = (ShmemIndexEnt *) - hash_search(ShmemIndex, (void *) &item, HASH_ENTER, &found); - if (!result) - elog(FATAL, "InitShmemIndex: Shmem Index out of memory"); - - Assert(ShmemBootstrap && !found); - - result->location = MAKE_OFFSET(ShmemIndex->hctl); - result->size = SHMEM_INDEX_SIZE; - - ShmemBootstrap = false; - - /* now release the lock acquired in ShmemInitStruct */ - LWLockRelease(ShmemIndexLock); -} - -/* - * ShmemInitHash -- Create/Attach to and initialize - * shared memory hash table. - * - * Notes: - * - * assume caller is doing some kind of synchronization - * so that two people dont try to create/initialize the - * table at once. - */ -HTAB * -ShmemInitHash(const char *name, /* table string name for shmem index */ - long init_size, /* initial table size */ - long max_size, /* max size of the table */ - HASHCTL *infoP, /* info about key and bucket size */ - int hash_flags) /* info about infoP */ -{ - bool found; - void *location; - - /* - * Hash tables allocated in shared memory have a fixed directory; it - * can't grow or other backends wouldn't be able to find it. So, make - * sure we make it big enough to start with. - * - * The shared memory allocator must be specified too. - */ - infoP->dsize = infoP->max_dsize = hash_select_dirsize(max_size); - infoP->alloc = ShmemAlloc; - hash_flags |= HASH_SHARED_MEM | HASH_DIRSIZE; - - /* look it up in the shmem index */ - location = ShmemInitStruct(name, - sizeof(HASHHDR) + infoP->dsize * sizeof(HASHSEGMENT), - &found); - - /* - * shmem index is corrupted. Let someone else give the error - * message since they have more information - */ - if (location == NULL) - return NULL; - - /* - * if it already exists, attach to it rather than allocate and - * initialize new space - */ - if (found) - hash_flags |= HASH_ATTACH; - - /* Now provide the header and directory pointers */ - infoP->hctl = (HASHHDR *) location; - infoP->dir = (HASHSEGMENT *) (((char *) location) + sizeof(HASHHDR)); - - return hash_create(name, init_size, infoP, hash_flags); -} - -/* - * ShmemInitStruct -- Create/attach to a structure in shared - * memory. - * - * This is called during initialization to find or allocate - * a data structure in shared memory. If no other processes - * have created the structure, this routine allocates space - * for it. If it exists already, a pointer to the existing - * table is returned. - * - * Returns: real pointer to the object. FoundPtr is TRUE if - * the object is already in the shmem index (hence, already - * initialized). - */ -void * -ShmemInitStruct(const char *name, Size size, bool *foundPtr) -{ - ShmemIndexEnt *result, - item; - void *structPtr; - - strncpy(item.key, name, SHMEM_INDEX_KEYSIZE); - item.location = BAD_LOCATION; - - LWLockAcquire(ShmemIndexLock, LW_EXCLUSIVE); - - if (!ShmemIndex) - { - /* - * If the shmem index doesn't exist, we are bootstrapping: we must - * be trying to init the shmem index itself. - * - * Notice that the ShmemIndexLock is held until the shmem index has - * been completely initialized. - */ - Assert(strcmp(name, "ShmemIndex") == 0); - Assert(ShmemBootstrap); - *foundPtr = FALSE; - return ShmemAlloc(size); - } - - /* look it up in the shmem index */ - result = (ShmemIndexEnt *) - hash_search(ShmemIndex, (void *) &item, HASH_ENTER, foundPtr); - - if (!result) - { - LWLockRelease(ShmemIndexLock); - elog(ERROR, "ShmemInitStruct: Shmem Index out of memory"); - return NULL; - } - - if (*foundPtr) - { - /* - * Structure is in the shmem index so someone else has allocated - * it already. The size better be the same as the size we are - * trying to initialize to or there is a name conflict (or worse). - */ - if (result->size != size) - { - LWLockRelease(ShmemIndexLock); - - elog(WARNING, "ShmemInitStruct: ShmemIndex entry size is wrong"); - /* let caller print its message too */ - return NULL; - } - structPtr = (void *) MAKE_PTR(result->location); - } - else - { - /* It isn't in the table yet. allocate and initialize it */ - structPtr = ShmemAlloc(size); - if (!structPtr) - { - /* out of memory */ - Assert(ShmemIndex); - hash_search(ShmemIndex, (void *) &item, HASH_REMOVE, NULL); - LWLockRelease(ShmemIndexLock); - - elog(WARNING, "ShmemInitStruct: cannot allocate '%s'", - name); - *foundPtr = FALSE; - return NULL; - } - result->size = size; - result->location = MAKE_OFFSET(structPtr); - } - Assert(ShmemIsValid((unsigned long) structPtr)); - - LWLockRelease(ShmemIndexLock); - return structPtr; -} diff --git a/src/backend/storage/ipc/shmqueue.c b/src/backend/storage/ipc/shmqueue.c deleted file mode 100644 index bbd98038ef1..00000000000 --- a/src/backend/storage/ipc/shmqueue.c +++ /dev/null @@ -1,257 +0,0 @@ -/*------------------------------------------------------------------------- - * - * shmqueue.c - * shared memory linked lists - * - * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group - * Portions Copyright (c) 1994, Regents of the University of California - * - * - * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/storage/ipc/shmqueue.c,v 1.21 2002/06/20 20:29:35 momjian Exp $ - * - * NOTES - * - * Package for managing doubly-linked lists in shared memory. - * The only tricky thing is that SHM_QUEUE will usually be a field - * in a larger record. SHMQueueNext has to return a pointer - * to the record itself instead of a pointer to the SHMQueue field - * of the record. It takes an extra parameter and does some extra - * pointer arithmetic to do this correctly. - * - * NOTE: These are set up so they can be turned into macros some day. - * - *------------------------------------------------------------------------- - */ -#include "postgres.h" - -#include "storage/shmem.h" - -/*#define SHMQUEUE_DEBUG*/ -#ifdef SHMQUEUE_DEBUG - -#define SHMQUEUE_DEBUG_ELOG WARNING - -static void dumpQ(SHM_QUEUE *q, char *s); -#endif - - -/* - * ShmemQueueInit -- make the head of a new queue point - * to itself - */ -void -SHMQueueInit(SHM_QUEUE *queue) -{ - Assert(SHM_PTR_VALID(queue)); - (queue)->prev = (queue)->next = MAKE_OFFSET(queue); -} - -/* - * SHMQueueIsDetached -- TRUE if element is not currently - * in a queue. - */ -#ifdef NOT_USED -bool -SHMQueueIsDetached(SHM_QUEUE *queue) -{ - Assert(SHM_PTR_VALID(queue)); - return (queue)->prev == INVALID_OFFSET; -} -#endif - -/* - * SHMQueueElemInit -- clear an element's links - */ -void -SHMQueueElemInit(SHM_QUEUE *queue) -{ - Assert(SHM_PTR_VALID(queue)); - (queue)->prev = (queue)->next = INVALID_OFFSET; -} - -/* - * SHMQueueDelete -- remove an element from the queue and - * close the links - */ -void -SHMQueueDelete(SHM_QUEUE *queue) -{ - SHM_QUEUE *nextElem = (SHM_QUEUE *) MAKE_PTR((queue)->next); - SHM_QUEUE *prevElem = (SHM_QUEUE *) MAKE_PTR((queue)->prev); - - Assert(SHM_PTR_VALID(queue)); - Assert(SHM_PTR_VALID(nextElem)); - Assert(SHM_PTR_VALID(prevElem)); - -#ifdef SHMQUEUE_DEBUG - dumpQ(queue, "in SHMQueueDelete: begin"); -#endif - - prevElem->next = (queue)->next; - nextElem->prev = (queue)->prev; - - (queue)->prev = (queue)->next = INVALID_OFFSET; -} - -/* - * SHMQueueInsertBefore -- put elem in queue before the given queue - * element. Inserting "before" the queue head puts the elem - * at the tail of the queue. - */ -void -SHMQueueInsertBefore(SHM_QUEUE *queue, SHM_QUEUE *elem) -{ - SHM_QUEUE *prevPtr = (SHM_QUEUE *) MAKE_PTR((queue)->prev); - SHMEM_OFFSET elemOffset = MAKE_OFFSET(elem); - - Assert(SHM_PTR_VALID(queue)); - Assert(SHM_PTR_VALID(elem)); - -#ifdef SHMQUEUE_DEBUG - dumpQ(queue, "in SHMQueueInsertBefore: begin"); -#endif - - (elem)->next = prevPtr->next; - (elem)->prev = queue->prev; - (queue)->prev = elemOffset; - prevPtr->next = elemOffset; - -#ifdef SHMQUEUE_DEBUG - dumpQ(queue, "in SHMQueueInsertBefore: end"); -#endif -} - -/* - * SHMQueueInsertAfter -- put elem in queue after the given queue - * element. Inserting "after" the queue head puts the elem - * at the head of the queue. - */ -#ifdef NOT_USED -void -SHMQueueInsertAfter(SHM_QUEUE *queue, SHM_QUEUE *elem) -{ - SHM_QUEUE *nextPtr = (SHM_QUEUE *) MAKE_PTR((queue)->next); - SHMEM_OFFSET elemOffset = MAKE_OFFSET(elem); - - Assert(SHM_PTR_VALID(queue)); - Assert(SHM_PTR_VALID(elem)); - -#ifdef SHMQUEUE_DEBUG - dumpQ(queue, "in SHMQueueInsertAfter: begin"); -#endif - - (elem)->prev = nextPtr->prev; - (elem)->next = queue->next; - (queue)->next = elemOffset; - nextPtr->prev = elemOffset; - -#ifdef SHMQUEUE_DEBUG - dumpQ(queue, "in SHMQueueInsertAfter: end"); -#endif -} -#endif /* NOT_USED */ - -/*-------------------- - * SHMQueueNext -- Get the next element from a queue - * - * To start the iteration, pass the queue head as both queue and curElem. - * Returns NULL if no more elements. - * - * Next element is at curElem->next. If SHMQueue is part of - * a larger structure, we want to return a pointer to the - * whole structure rather than a pointer to its SHMQueue field. - * I.E. struct { - * int stuff; - * SHMQueue elem; - * } ELEMType; - * When this element is in a queue, (prevElem->next) is struct.elem. - * We subtract linkOffset to get the correct start address of the structure. - * - * calls to SHMQueueNext should take these parameters: - * - * &(queueHead), &(queueHead), offsetof(ELEMType, elem) - * or - * &(queueHead), &(curElem->elem), offsetof(ELEMType, elem) - *-------------------- - */ -Pointer -SHMQueueNext(SHM_QUEUE *queue, SHM_QUEUE *curElem, Size linkOffset) -{ - SHM_QUEUE *elemPtr = (SHM_QUEUE *) MAKE_PTR((curElem)->next); - - Assert(SHM_PTR_VALID(curElem)); - - if (elemPtr == queue) /* back to the queue head? */ - return NULL; - - return (Pointer) (((char *) elemPtr) - linkOffset); -} - -/* - * SHMQueueEmpty -- TRUE if queue head is only element, FALSE otherwise - */ -bool -SHMQueueEmpty(SHM_QUEUE *queue) -{ - Assert(SHM_PTR_VALID(queue)); - - if (queue->prev == MAKE_OFFSET(queue)) - { - Assert(queue->next = MAKE_OFFSET(queue)); - return TRUE; - } - return FALSE; -} - -#ifdef SHMQUEUE_DEBUG - -static void -dumpQ(SHM_QUEUE *q, char *s) -{ - char elem[NAMEDATALEN]; - char buf[1024]; - SHM_QUEUE *start = q; - int count = 0; - - sprintf(buf, "q prevs: %lx", MAKE_OFFSET(q)); - q = (SHM_QUEUE *) MAKE_PTR(q->prev); - while (q != start) - { - sprintf(elem, "--->%lx", MAKE_OFFSET(q)); - strcat(buf, elem); - q = (SHM_QUEUE *) MAKE_PTR(q->prev); - if (q->prev == MAKE_OFFSET(q)) - break; - if (count++ > 40) - { - strcat(buf, "BAD PREV QUEUE!!"); - break; - } - } - sprintf(elem, "--->%lx", MAKE_OFFSET(q)); - strcat(buf, elem); - elog(SHMQUEUE_DEBUG_ELOG, "%s: %s", s, buf); - - sprintf(buf, "q nexts: %lx", MAKE_OFFSET(q)); - count = 0; - q = (SHM_QUEUE *) MAKE_PTR(q->next); - while (q != start) - { - sprintf(elem, "--->%lx", MAKE_OFFSET(q)); - strcat(buf, elem); - q = (SHM_QUEUE *) MAKE_PTR(q->next); - if (q->next == MAKE_OFFSET(q)) - break; - if (count++ > 10) - { - strcat(buf, "BAD NEXT QUEUE!!"); - break; - } - } - sprintf(elem, "--->%lx", MAKE_OFFSET(q)); - strcat(buf, elem); - elog(SHMQUEUE_DEBUG_ELOG, "%s: %s", s, buf); -} - -#endif /* SHMQUEUE_DEBUG */ diff --git a/src/backend/storage/ipc/sinval.c b/src/backend/storage/ipc/sinval.c deleted file mode 100644 index 940ab32b331..00000000000 --- a/src/backend/storage/ipc/sinval.c +++ /dev/null @@ -1,540 +0,0 @@ -/*------------------------------------------------------------------------- - * - * sinval.c - * POSTGRES shared cache invalidation communication code. - * - * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group - * Portions Copyright (c) 1994, Regents of the University of California - * - * - * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/storage/ipc/sinval.c,v 1.49 2002/06/20 20:29:35 momjian Exp $ - * - *------------------------------------------------------------------------- - */ -#include "postgres.h" - -#include <sys/types.h> - -#include "storage/proc.h" -#include "storage/sinval.h" -#include "storage/sinvaladt.h" -#include "utils/tqual.h" -#include "miscadmin.h" - - -/****************************************************************************/ -/* CreateSharedInvalidationState() Initialize SI buffer */ -/* */ -/* should be called only by the POSTMASTER */ -/****************************************************************************/ -void -CreateSharedInvalidationState(int maxBackends) -{ - /* SInvalLock must be initialized already, during LWLock init */ - SIBufferInit(maxBackends); -} - -/* - * InitBackendSharedInvalidationState - * Initialize new backend's state info in buffer segment. - */ -void -InitBackendSharedInvalidationState(void) -{ - int flag; - - LWLockAcquire(SInvalLock, LW_EXCLUSIVE); - flag = SIBackendInit(shmInvalBuffer); - LWLockRelease(SInvalLock); - if (flag < 0) /* unexpected problem */ - elog(FATAL, "Backend cache invalidation initialization failed"); - if (flag == 0) /* expected problem: MaxBackends exceeded */ - elog(FATAL, "Sorry, too many clients already"); -} - -/* - * SendSharedInvalidMessage - * Add a shared-cache-invalidation message to the global SI message queue. - */ -void -SendSharedInvalidMessage(SharedInvalidationMessage *msg) -{ - bool insertOK; - - LWLockAcquire(SInvalLock, LW_EXCLUSIVE); - insertOK = SIInsertDataEntry(shmInvalBuffer, msg); - LWLockRelease(SInvalLock); - if (!insertOK) - elog(DEBUG3, "SendSharedInvalidMessage: SI buffer overflow"); -} - -/* - * ReceiveSharedInvalidMessages - * Process shared-cache-invalidation messages waiting for this backend - */ -void -ReceiveSharedInvalidMessages( - void (*invalFunction) (SharedInvalidationMessage *msg), - void (*resetFunction) (void)) -{ - SharedInvalidationMessage data; - int getResult; - bool gotMessage = false; - - for (;;) - { - /* - * We can run SIGetDataEntry in parallel with other backends - * running SIGetDataEntry for themselves, since each instance will - * modify only fields of its own backend's ProcState, and no - * instance will look at fields of other backends' ProcStates. We - * express this by grabbing SInvalLock in shared mode. Note that - * 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); - LWLockRelease(SInvalLock); - - if (getResult == 0) - break; /* nothing more to do */ - if (getResult < 0) - { - /* got a reset message */ - elog(DEBUG3, "ReceiveSharedInvalidMessages: cache state reset"); - resetFunction(); - } - else - { - /* got a normal data message */ - invalFunction(&data); - } - gotMessage = true; - } - - /* If we got any messages, try to release dead messages */ - if (gotMessage) - { - LWLockAcquire(SInvalLock, LW_EXCLUSIVE); - SIDelExpiredDataEntries(shmInvalBuffer); - LWLockRelease(SInvalLock); - } -} - - -/****************************************************************************/ -/* 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; -} - -/* - * TransactionIdIsInProgress -- is given transaction running by some backend - */ -bool -TransactionIdIsInProgress(TransactionId xid) -{ - 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); - - /* Fetch xid just once - see GetNewTransactionId */ - TransactionId pxid = proc->xid; - - if (TransactionIdEquals(pxid, xid)) - { - result = true; - break; - } - } - } - - LWLockRelease(SInvalLock); - - 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. - * - * 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; - - result = GetCurrentTransactionId(); - - 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. - * - * Also, we compute the current global xmin (oldest xmin across all running - * transactions) and save it in RecentGlobalXmin. This is the same - * computation done by GetOldestXmin(TRUE). - *---------- - */ -Snapshot -GetSnapshotData(bool serializable) -{ - Snapshot snapshot = (Snapshot) malloc(sizeof(SnapshotData)); - SISeg *segP = shmInvalBuffer; - ProcState *stateP = segP->procState; - TransactionId xmin; - TransactionId xmax; - TransactionId globalxmin; - int index; - int count = 0; - - if (snapshot == NULL) - elog(ERROR, "Memory exhausted in GetSnapshotData"); - - /* - * 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. - */ - snapshot->xip = (TransactionId *) - malloc(MaxBackends * sizeof(TransactionId)); - if (snapshot->xip == NULL) - elog(ERROR, "Memory exhausted in GetSnapshotData"); - - globalxmin = xmin = GetCurrentTransactionId(); - - /* - * 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 = xmin; - - LWLockRelease(SInvalLock); - - /* Serializable snapshot must be computed before any other... */ - Assert(TransactionIdIsValid(MyProc->xmin)); - - /* - * 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; - - RecentGlobalXmin = globalxmin; - - 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; -} - -/* - * 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); -} - -/* - * 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; -} diff --git a/src/backend/storage/ipc/sinvaladt.c b/src/backend/storage/ipc/sinvaladt.c deleted file mode 100644 index c88055c07b5..00000000000 --- a/src/backend/storage/ipc/sinvaladt.c +++ /dev/null @@ -1,349 +0,0 @@ -/*------------------------------------------------------------------------- - * - * sinvaladt.c - * POSTGRES shared cache invalidation segment definitions. - * - * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group - * Portions Copyright (c) 1994, Regents of the University of California - * - * - * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/storage/ipc/sinvaladt.c,v 1.47 2002/06/20 20:29:35 momjian Exp $ - * - *------------------------------------------------------------------------- - */ -#include "postgres.h" - -#include "miscadmin.h" -#include "storage/backendid.h" -#include "storage/ipc.h" -#include "storage/pmsignal.h" -#include "storage/proc.h" -#include "storage/sinvaladt.h" - -SISeg *shmInvalBuffer; - -static void CleanupInvalidationState(int status, Datum arg); -static void SISetProcStateInvalid(SISeg *segP); - - -/* - * SInvalShmemSize --- return shared-memory space needed - */ -int -SInvalShmemSize(int maxBackends) -{ - /* - * Figure space needed. Note sizeof(SISeg) includes the first - * ProcState entry. - */ - return sizeof(SISeg) + sizeof(ProcState) * (maxBackends - 1); -} - -/* - * SIBufferInit - * Create and initialize a new SI message buffer - */ -void -SIBufferInit(int maxBackends) -{ - int segSize; - SISeg *segP; - int i; - - /* Allocate space in shared memory */ - segSize = SInvalShmemSize(maxBackends); - shmInvalBuffer = segP = (SISeg *) ShmemAlloc(segSize); - - /* Clear message counters, save size of procState array */ - segP->minMsgNum = 0; - segP->maxMsgNum = 0; - segP->lastBackend = 0; - segP->maxBackends = maxBackends; - - /* The buffer[] array is initially all unused, so we need not fill it */ - - /* Mark all backends inactive */ - for (i = 0; i < maxBackends; i++) - { - segP->procState[i].nextMsgNum = -1; /* inactive */ - segP->procState[i].resetState = false; - segP->procState[i].procStruct = INVALID_OFFSET; - } -} - -/* - * SIBackendInit - * Initialize a new backend to operate on the sinval buffer - * - * Returns: - * >0 A-OK - * 0 Failed to find a free procState slot (ie, MaxBackends exceeded) - * <0 Some other failure (not currently used) - * - * NB: this routine, and all following ones, must be executed with the - * SInvalLock lock held, since there may be multiple backends trying - * to access the buffer. - */ -int -SIBackendInit(SISeg *segP) -{ - int index; - ProcState *stateP = NULL; - - /* Look for a free entry in the procState array */ - for (index = 0; index < segP->lastBackend; index++) - { - if (segP->procState[index].nextMsgNum < 0) /* inactive slot? */ - { - stateP = &segP->procState[index]; - break; - } - } - - if (stateP == NULL) - { - if (segP->lastBackend < segP->maxBackends) - { - stateP = &segP->procState[segP->lastBackend]; - Assert(stateP->nextMsgNum < 0); - segP->lastBackend++; - } - else - { - /* out of procState slots */ - MyBackendId = InvalidBackendId; - return 0; - } - } - - MyBackendId = (stateP - &segP->procState[0]) + 1; - -#ifdef INVALIDDEBUG - elog(DEBUG1, "SIBackendInit: backend id %d", MyBackendId); -#endif /* INVALIDDEBUG */ - - /* 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)); - - return 1; -} - -/* - * CleanupInvalidationState - * Mark the current backend as no longer active. - * - * This function is called via on_shmem_exit() during backend shutdown, - * so the caller has NOT acquired the lock for us. - * - * arg is really of type "SISeg*". - */ -static void -CleanupInvalidationState(int status, Datum arg) -{ - SISeg *segP = (SISeg *) DatumGetPointer(arg); - int i; - - Assert(PointerIsValid(segP)); - - LWLockAcquire(SInvalLock, LW_EXCLUSIVE); - - /* 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--) - { - if (segP->procState[i - 1].nextMsgNum >= 0) - break; - } - segP->lastBackend = i; - - LWLockRelease(SInvalLock); -} - -/* - * SIInsertDataEntry - * Add a new invalidation message to the buffer. - * - * If we are unable to insert the message because the buffer is full, - * then clear the buffer and assert the "reset" flag to each backend. - * This will cause all the backends to discard *all* invalidatable state. - * - * Returns true for normal successful insertion, false if had to reset. - */ -bool -SIInsertDataEntry(SISeg *segP, SharedInvalidationMessage *data) -{ - int numMsgs = segP->maxMsgNum - segP->minMsgNum; - - /* Is the buffer full? */ - if (numMsgs >= MAXNUMMESSAGES) - { - /* - * Don't panic just yet: slowest backend might have consumed some - * messages but not yet have done SIDelExpiredDataEntries() to - * advance minMsgNum. So, make sure minMsgNum is up-to-date. - */ - SIDelExpiredDataEntries(segP); - numMsgs = segP->maxMsgNum - segP->minMsgNum; - if (numMsgs >= MAXNUMMESSAGES) - { - /* Yup, it's definitely full, no choice but to reset */ - SISetProcStateInvalid(segP); - return false; - } - } - - /* - * Try to prevent table overflow. When the table is 70% full send a - * WAKEN_CHILDREN request to the postmaster. The postmaster will send - * a SIGUSR2 signal (ordinarily a NOTIFY signal) to all the backends. - * This will force idle backends to execute a transaction to look - * through pg_listener for NOTIFY messages, and as a byproduct of the - * transaction start they will read SI entries. - * - * This should never happen if all the backends are actively executing - * queries, but if a backend is sitting idle then it won't be starting - * transactions and so won't be reading SI entries. - * - * dz - 27 Jan 1998 - */ - if (numMsgs == (MAXNUMMESSAGES * 70 / 100) && - IsUnderPostmaster) - { - elog(DEBUG3, "SIInsertDataEntry: table is 70%% full, signaling postmaster"); - SendPostmasterSignal(PMSIGNAL_WAKEN_CHILDREN); - } - - /* - * Insert new message into proper slot of circular buffer - */ - segP->buffer[segP->maxMsgNum % MAXNUMMESSAGES] = *data; - segP->maxMsgNum++; - - return true; -} - -/* - * SISetProcStateInvalid - * Flush pending messages from buffer, assert reset flag for each backend - * - * This is used only to recover from SI buffer overflow. - */ -static void -SISetProcStateInvalid(SISeg *segP) -{ - int i; - - segP->minMsgNum = 0; - segP->maxMsgNum = 0; - - for (i = 0; i < segP->lastBackend; i++) - { - if (segP->procState[i].nextMsgNum >= 0) /* active backend? */ - { - segP->procState[i].resetState = true; - segP->procState[i].nextMsgNum = 0; - } - } -} - -/* - * SIGetDataEntry - * get next SI message for specified backend, if there is one - * - * Possible return values: - * 0: no SI message available - * 1: next SI message has been extracted into *data - * (there may be more messages available after this one!) - * -1: SI reset message extracted - * - * NB: this can run in parallel with other instances of SIGetDataEntry - * executing on behalf of other backends. See comments in sinval.c in - * ReceiveSharedInvalidMessages(). - */ -int -SIGetDataEntry(SISeg *segP, int backendId, - SharedInvalidationMessage *data) -{ - ProcState *stateP = &segP->procState[backendId - 1]; - - if (stateP->resetState) - { - /* - * Force reset. We can say we have dealt with any messages added - * since the reset, as well... - */ - stateP->resetState = false; - stateP->nextMsgNum = segP->maxMsgNum; - return -1; - } - - if (stateP->nextMsgNum >= segP->maxMsgNum) - return 0; /* nothing to read */ - - /* - * Retrieve message and advance my counter. - */ - *data = segP->buffer[stateP->nextMsgNum % MAXNUMMESSAGES]; - stateP->nextMsgNum++; - - /* - * There may be other backends that haven't read the message, so we - * cannot delete it here. SIDelExpiredDataEntries() should be called - * to remove dead messages. - */ - return 1; /* got a message */ -} - -/* - * SIDelExpiredDataEntries - * Remove messages that have been consumed by all active backends - */ -void -SIDelExpiredDataEntries(SISeg *segP) -{ - int min, - i, - h; - - min = segP->maxMsgNum; - if (min == segP->minMsgNum) - return; /* fast path if no messages exist */ - - /* Recompute minMsgNum = minimum of all backends' nextMsgNum */ - - for (i = 0; i < segP->lastBackend; i++) - { - h = segP->procState[i].nextMsgNum; - if (h >= 0) - { /* backend active */ - if (h < min) - min = h; - } - } - segP->minMsgNum = min; - - /* - * When minMsgNum gets really large, decrement all message counters so - * as to forestall overflow of the counters. - */ - if (min >= MSGNUMWRAPAROUND) - { - segP->minMsgNum -= MSGNUMWRAPAROUND; - segP->maxMsgNum -= MSGNUMWRAPAROUND; - for (i = 0; i < segP->lastBackend; i++) - { - if (segP->procState[i].nextMsgNum >= 0) - segP->procState[i].nextMsgNum -= MSGNUMWRAPAROUND; - } - } -} |