mirror of
https://github.com/postgres/postgres.git
synced 2025-11-12 05:01:15 +03:00
Postgres95 1.01 Distribution - Virgin Sources
This commit is contained in:
561
src/backend/storage/ipc/shmem.c
Normal file
561
src/backend/storage/ipc/shmem.c
Normal file
@@ -0,0 +1,561 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* shmem.c--
|
||||
* create shared memory and initialize shared memory data structures.
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/storage/ipc/shmem.c,v 1.1.1.1 1996/07/09 06:21:54 scrappy Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
/*
|
||||
* POSTGRES processes share one or more regions of shared memory.
|
||||
* The shared memory is created by a postmaster and is "attached to"
|
||||
* by each of the backends. 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 "Binding Table".
|
||||
* 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 binding table 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 binding table,
|
||||
* it is initialized. If it is not, it is uninitialized. Second,
|
||||
* the binding table 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
|
||||
* binding table.
|
||||
*
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "postgres.h"
|
||||
#include "storage/ipc.h"
|
||||
#include "storage/shmem.h"
|
||||
#include "storage/spin.h"
|
||||
#include "utils/hsearch.h"
|
||||
#include "utils/elog.h"
|
||||
|
||||
/* shared memory global variables */
|
||||
|
||||
unsigned long ShmemBase = 0; /* start and end address of
|
||||
* shared memory
|
||||
*/
|
||||
static unsigned long ShmemEnd = 0;
|
||||
static unsigned long ShmemSize = 0; /* current size (and default) */
|
||||
|
||||
SPINLOCK ShmemLock; /* lock for shared memory allocation */
|
||||
|
||||
SPINLOCK BindingLock; /* lock for binding table access */
|
||||
|
||||
static unsigned long *ShmemFreeStart = NULL; /* pointer to the OFFSET of
|
||||
* first free shared memory
|
||||
*/
|
||||
static unsigned long *ShmemBindingTabOffset = NULL; /* start of the binding
|
||||
* table (for bootstrap)
|
||||
*/
|
||||
static int ShmemBootstrap = FALSE; /* flag becomes true when shared mem
|
||||
* is created by POSTMASTER
|
||||
*/
|
||||
|
||||
static HTAB *BindingTable = NULL;
|
||||
|
||||
/* ---------------------
|
||||
* ShmemBindingTabReset() - Resets the binding table to NULL....
|
||||
* useful when the postmaster destroys existing shared memory
|
||||
* and creates all new segments after a backend crash.
|
||||
* ----------------------
|
||||
*/
|
||||
void
|
||||
ShmemBindingTabReset()
|
||||
{
|
||||
BindingTable = (HTAB *)NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* CreateSharedRegion() --
|
||||
*
|
||||
* This routine is called once by the postmaster to
|
||||
* initialize the shared buffer pool. Assume there is
|
||||
* only one postmaster so no synchronization is necessary
|
||||
* until after this routine completes successfully.
|
||||
*
|
||||
* key is a unique identifier for the shmem region.
|
||||
* size is the size of the region.
|
||||
*/
|
||||
static IpcMemoryId ShmemId;
|
||||
|
||||
void
|
||||
ShmemCreate(unsigned int key, unsigned int size)
|
||||
{
|
||||
if (size)
|
||||
ShmemSize = size;
|
||||
/* create shared mem region */
|
||||
if ((ShmemId=IpcMemoryCreate(key,ShmemSize,IPCProtection))
|
||||
==IpcMemCreationFailed) {
|
||||
elog(FATAL,"ShmemCreate: cannot create region");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* ShmemBootstrap is true if shared memory has been
|
||||
* created, but not yet initialized. Only the
|
||||
* postmaster/creator-of-all-things should have
|
||||
* this flag set.
|
||||
*/
|
||||
ShmemBootstrap = TRUE;
|
||||
}
|
||||
|
||||
/*
|
||||
* InitShmem() -- map region into process address space
|
||||
* and initialize shared data structures.
|
||||
*
|
||||
*/
|
||||
int
|
||||
InitShmem(unsigned int key, unsigned int size)
|
||||
{
|
||||
Pointer sharedRegion;
|
||||
unsigned long currFreeSpace;
|
||||
|
||||
HASHCTL info;
|
||||
int hash_flags;
|
||||
BindingEnt * result,item;
|
||||
bool found;
|
||||
IpcMemoryId shmid;
|
||||
|
||||
/* if zero key, use default memory size */
|
||||
if (size)
|
||||
ShmemSize = size;
|
||||
|
||||
/* default key is 0 */
|
||||
|
||||
/* attach to shared memory region (SysV or BSD OS specific) */
|
||||
if (ShmemBootstrap && key == PrivateIPCKey)
|
||||
/* if we are running backend alone */
|
||||
shmid = ShmemId;
|
||||
else
|
||||
shmid = IpcMemoryIdGet(IPCKeyGetBufferMemoryKey(key), ShmemSize);
|
||||
sharedRegion = IpcMemoryAttach(shmid);
|
||||
if (sharedRegion == NULL) {
|
||||
elog(FATAL,"AttachSharedRegion: couldn't attach to shmem\n");
|
||||
return(FALSE);
|
||||
}
|
||||
|
||||
/* get pointers to the dimensions of shared memory */
|
||||
ShmemBase = (unsigned long) sharedRegion;
|
||||
ShmemEnd = (unsigned long) sharedRegion + ShmemSize;
|
||||
currFreeSpace = 0;
|
||||
|
||||
/* First long in shared memory is the count of available space */
|
||||
ShmemFreeStart = (unsigned long *) ShmemBase;
|
||||
/* next is a shmem pointer to the binding table */
|
||||
ShmemBindingTabOffset = ShmemFreeStart + 1;
|
||||
|
||||
currFreeSpace +=
|
||||
sizeof(ShmemFreeStart) + sizeof(ShmemBindingTabOffset);
|
||||
|
||||
/* bootstrap initialize spin locks so we can start to use the
|
||||
* allocator and binding table.
|
||||
*/
|
||||
if (! InitSpinLocks(ShmemBootstrap, IPCKeyGetSpinLockSemaphoreKey(key))) {
|
||||
return(FALSE);
|
||||
}
|
||||
|
||||
/* We have just allocated additional space for two spinlocks.
|
||||
* Now setup the global free space count
|
||||
*/
|
||||
if (ShmemBootstrap) {
|
||||
*ShmemFreeStart = currFreeSpace;
|
||||
}
|
||||
|
||||
/* if ShmemFreeStart is NULL, then the allocator won't work */
|
||||
Assert(*ShmemFreeStart);
|
||||
|
||||
/* create OR attach to the shared memory binding table */
|
||||
info.keysize = BTABLE_KEYSIZE;
|
||||
info.datasize = BTABLE_DATASIZE;
|
||||
hash_flags = (HASH_ELEM);
|
||||
|
||||
/* This will acquire the binding table lock, but not release it. */
|
||||
BindingTable = ShmemInitHash("BindingTable",
|
||||
BTABLE_SIZE,BTABLE_SIZE,
|
||||
&info,hash_flags);
|
||||
|
||||
if (! BindingTable) {
|
||||
elog(FATAL,"InitShmem: couldn't initialize Binding Table");
|
||||
return(FALSE);
|
||||
}
|
||||
|
||||
/* Now, check the binding table for an entry to the binding
|
||||
* table. If there is an entry there, someone else created
|
||||
* the table. Otherwise, we did and we have to initialize it.
|
||||
*/
|
||||
memset(item.key, 0, BTABLE_KEYSIZE);
|
||||
strncpy(item.key,"BindingTable",BTABLE_KEYSIZE);
|
||||
|
||||
result = (BindingEnt *)
|
||||
hash_search(BindingTable,(char *) &item,HASH_ENTER, &found);
|
||||
|
||||
|
||||
if (! result ) {
|
||||
elog(FATAL,"InitShmem: corrupted binding table");
|
||||
return(FALSE);
|
||||
}
|
||||
|
||||
if (! found) {
|
||||
/* bootstrapping shmem: we have to initialize the
|
||||
* binding table now.
|
||||
*/
|
||||
|
||||
Assert(ShmemBootstrap);
|
||||
result->location = MAKE_OFFSET(BindingTable->hctl);
|
||||
*ShmemBindingTabOffset = result->location;
|
||||
result->size = BTABLE_SIZE;
|
||||
|
||||
ShmemBootstrap = FALSE;
|
||||
|
||||
} else {
|
||||
Assert(! ShmemBootstrap);
|
||||
}
|
||||
/* now release the lock acquired in ShmemHashInit */
|
||||
SpinRelease (BindingLock);
|
||||
|
||||
Assert (result->location == MAKE_OFFSET(BindingTable->hctl));
|
||||
|
||||
return(TRUE);
|
||||
}
|
||||
|
||||
/*
|
||||
* ShmemAlloc -- allocate word-aligned byte string from
|
||||
* shared memory
|
||||
*
|
||||
* Assumes ShmemLock and ShmemFreeStart 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 compatable with malloc().
|
||||
*/
|
||||
long *
|
||||
ShmemAlloc(unsigned long size)
|
||||
{
|
||||
unsigned long tmpFree;
|
||||
long *newSpace;
|
||||
|
||||
/*
|
||||
* ensure space is word aligned.
|
||||
*
|
||||
* Word-alignment is not good enough. We have to be more
|
||||
* conservative: doubles need 8-byte alignment. (We probably only need
|
||||
* this on RISC platforms but this is not a big waste of space.)
|
||||
* - ay 12/94
|
||||
*/
|
||||
if (size % sizeof(double))
|
||||
size += sizeof(double) - (size % sizeof(double));
|
||||
|
||||
Assert(*ShmemFreeStart);
|
||||
|
||||
SpinAcquire(ShmemLock);
|
||||
|
||||
tmpFree = *ShmemFreeStart + size;
|
||||
if (tmpFree <= ShmemSize) {
|
||||
newSpace = (long *)MAKE_PTR(*ShmemFreeStart);
|
||||
*ShmemFreeStart += size;
|
||||
} else {
|
||||
newSpace = NULL;
|
||||
}
|
||||
|
||||
SpinRelease(ShmemLock);
|
||||
|
||||
if (! newSpace) {
|
||||
elog(NOTICE,"ShmemAlloc: out of memory ");
|
||||
}
|
||||
return(newSpace);
|
||||
}
|
||||
|
||||
/*
|
||||
* ShmemIsValid -- test if an offset refers to valid shared memory
|
||||
*
|
||||
* Returns TRUE if the pointer is valid.
|
||||
*/
|
||||
int
|
||||
ShmemIsValid(unsigned long addr)
|
||||
{
|
||||
return ((addr<ShmemEnd) && (addr>=ShmemBase));
|
||||
}
|
||||
|
||||
/*
|
||||
* 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. Use SpinAlloc() to create a spinlock
|
||||
* for the structure before creating the structure itself.
|
||||
*/
|
||||
HTAB *
|
||||
ShmemInitHash(char *name, /* table string name for binding */
|
||||
long init_size, /* initial 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;
|
||||
long * location;
|
||||
|
||||
/* shared memory hash tables have a fixed max size so that the
|
||||
* control structures don't try to grow. The segbase is for
|
||||
* calculating pointer values. The shared memory allocator
|
||||
* must be specified.
|
||||
*/
|
||||
infoP->segbase = (long *) ShmemBase;
|
||||
infoP->alloc = ShmemAlloc;
|
||||
infoP->max_size = max_size;
|
||||
hash_flags |= HASH_SHARED_MEM;
|
||||
|
||||
/* look it up in the binding table */
|
||||
location =
|
||||
ShmemInitStruct(name,my_log2(max_size) + sizeof(HHDR),&found);
|
||||
|
||||
/* binding table is corrupted. Let someone else give the
|
||||
* error message since they have more information
|
||||
*/
|
||||
if (location == NULL) {
|
||||
return(0);
|
||||
}
|
||||
|
||||
/* it already exists, attach to it rather than allocate and
|
||||
* initialize new space
|
||||
*/
|
||||
if (found) {
|
||||
hash_flags |= HASH_ATTACH;
|
||||
}
|
||||
|
||||
/* these structures were allocated or bound in ShmemInitStruct */
|
||||
/* control information and parameters */
|
||||
infoP->hctl = (long *) location;
|
||||
/* directory for hash lookup */
|
||||
infoP->dir = (long *) (location + sizeof(HHDR));
|
||||
|
||||
return(hash_create(init_size, infoP, hash_flags));;
|
||||
}
|
||||
|
||||
/*
|
||||
* ShmemPIDLookup -- lookup process data structure using process id
|
||||
*
|
||||
* Returns: TRUE if no error. locationPtr is initialized if PID is
|
||||
* found in the binding table.
|
||||
*
|
||||
* NOTES:
|
||||
* only information about success or failure is the value of
|
||||
* locationPtr.
|
||||
*/
|
||||
bool
|
||||
ShmemPIDLookup(int pid, SHMEM_OFFSET* locationPtr)
|
||||
{
|
||||
BindingEnt * result,item;
|
||||
bool found;
|
||||
|
||||
Assert (BindingTable);
|
||||
memset(item.key, 0, BTABLE_KEYSIZE);
|
||||
sprintf(item.key,"PID %d",pid);
|
||||
|
||||
SpinAcquire(BindingLock);
|
||||
result = (BindingEnt *)
|
||||
hash_search(BindingTable,(char *) &item, HASH_ENTER, &found);
|
||||
|
||||
if (! result) {
|
||||
|
||||
SpinRelease(BindingLock);
|
||||
elog(WARN,"ShmemInitPID: BindingTable corrupted");
|
||||
return(FALSE);
|
||||
|
||||
}
|
||||
|
||||
if (found) {
|
||||
*locationPtr = result->location;
|
||||
} else {
|
||||
result->location = *locationPtr;
|
||||
}
|
||||
|
||||
SpinRelease(BindingLock);
|
||||
return (TRUE);
|
||||
}
|
||||
|
||||
/*
|
||||
* ShmemPIDDestroy -- destroy binding table entry for process
|
||||
* using process id
|
||||
*
|
||||
* Returns: offset of the process struct in shared memory or
|
||||
* INVALID_OFFSET if not found.
|
||||
*
|
||||
* Side Effect: removes the entry from the binding table
|
||||
*/
|
||||
SHMEM_OFFSET
|
||||
ShmemPIDDestroy(int pid)
|
||||
{
|
||||
BindingEnt * result,item;
|
||||
bool found;
|
||||
SHMEM_OFFSET location;
|
||||
|
||||
Assert(BindingTable);
|
||||
|
||||
memset(item.key, 0, BTABLE_KEYSIZE);
|
||||
sprintf(item.key,"PID %d",pid);
|
||||
|
||||
SpinAcquire(BindingLock);
|
||||
result = (BindingEnt *)
|
||||
hash_search(BindingTable,(char *) &item, HASH_REMOVE, &found);
|
||||
|
||||
if (found)
|
||||
location = result->location;
|
||||
SpinRelease(BindingLock);
|
||||
|
||||
if (! result) {
|
||||
|
||||
elog(WARN,"ShmemPIDDestroy: PID table corrupted");
|
||||
return(INVALID_OFFSET);
|
||||
|
||||
}
|
||||
|
||||
if (found)
|
||||
return (location);
|
||||
else {
|
||||
return(INVALID_OFFSET);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 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 binding table (hence, already
|
||||
* initialized).
|
||||
*/
|
||||
long *
|
||||
ShmemInitStruct(char *name, unsigned long size, bool *foundPtr)
|
||||
{
|
||||
BindingEnt * result,item;
|
||||
long * structPtr;
|
||||
|
||||
strncpy(item.key,name,BTABLE_KEYSIZE);
|
||||
item.location = BAD_LOCATION;
|
||||
|
||||
SpinAcquire(BindingLock);
|
||||
|
||||
if (! BindingTable) {
|
||||
/* Assert() is a macro now. substitutes inside quotes. */
|
||||
char *strname = "BindingTable";
|
||||
|
||||
/* If the binding table doesnt exist, we fake it.
|
||||
*
|
||||
* If we are creating the first binding table, then let
|
||||
* shmemalloc() allocate the space for a new HTAB. Otherwise,
|
||||
* find the old one and return that. Notice that the
|
||||
* BindingLock is held until the binding table has been completely
|
||||
* initialized.
|
||||
*/
|
||||
Assert (! strcmp(name,strname)) ;
|
||||
if (ShmemBootstrap) {
|
||||
/* in POSTMASTER/Single process */
|
||||
|
||||
*foundPtr = FALSE;
|
||||
return((long *)ShmemAlloc(size));
|
||||
|
||||
} else {
|
||||
Assert (ShmemBindingTabOffset);
|
||||
|
||||
*foundPtr = TRUE;
|
||||
return((long *)MAKE_PTR(*ShmemBindingTabOffset));
|
||||
}
|
||||
|
||||
|
||||
} else {
|
||||
/* look it up in the bindint table */
|
||||
result = (BindingEnt *)
|
||||
hash_search(BindingTable,(char *) &item,HASH_ENTER, foundPtr);
|
||||
}
|
||||
|
||||
if (! result) {
|
||||
|
||||
SpinRelease(BindingLock);
|
||||
|
||||
elog(WARN,"ShmemInitStruct: Binding Table corrupted");
|
||||
return(NULL);
|
||||
|
||||
} else if (*foundPtr) {
|
||||
/*
|
||||
* Structure is in the binding table 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) {
|
||||
SpinRelease(BindingLock);
|
||||
|
||||
elog(NOTICE,"ShmemInitStruct: BindingTable entry size is wrong");
|
||||
/* let caller print its message too */
|
||||
return(NULL);
|
||||
}
|
||||
structPtr = (long *)MAKE_PTR(result->location);
|
||||
} else {
|
||||
|
||||
/* It isn't in the table yet. allocate and initialize it */
|
||||
structPtr = ShmemAlloc((long)size);
|
||||
if (! structPtr) {
|
||||
/* out of memory */
|
||||
Assert (BindingTable);
|
||||
(void) hash_search(BindingTable,(char *) &item,HASH_REMOVE, foundPtr);
|
||||
SpinRelease(BindingLock);
|
||||
*foundPtr = FALSE;
|
||||
|
||||
elog(NOTICE,"ShmemInitStruct: cannot allocate '%s'",
|
||||
name);
|
||||
return(NULL);
|
||||
}
|
||||
result->size = size;
|
||||
result->location = MAKE_OFFSET(structPtr);
|
||||
}
|
||||
Assert (ShmemIsValid((unsigned long)structPtr));
|
||||
|
||||
SpinRelease(BindingLock);
|
||||
return(structPtr);
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user