aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/src/sgml/ref/postmaster.sgml28
-rw-r--r--src/backend/access/transam/xlog.c10
-rw-r--r--src/backend/commands/async.c11
-rw-r--r--src/backend/postmaster/postmaster.c97
-rw-r--r--src/backend/storage/buffer/buf_init.c43
-rw-r--r--src/backend/storage/buffer/bufmgr.c104
-rw-r--r--src/backend/storage/buffer/s_lock.c6
-rw-r--r--src/backend/storage/buffer/xlog_bufmgr.c104
-rw-r--r--src/backend/storage/ipc/ipc.c830
-rw-r--r--src/backend/storage/ipc/ipci.c147
-rw-r--r--src/backend/storage/ipc/shmem.c266
-rw-r--r--src/backend/storage/ipc/sinval.c43
-rw-r--r--src/backend/storage/ipc/sinvaladt.c95
-rw-r--r--src/backend/storage/ipc/spin.c403
-rw-r--r--src/backend/storage/lmgr/lock.c9
-rw-r--r--src/backend/storage/lmgr/proc.c258
-rw-r--r--src/backend/utils/init/postinit.c32
-rw-r--r--src/include/storage/buf_internals.h9
-rw-r--r--src/include/storage/bufmgr.h4
-rw-r--r--src/include/storage/ipc.h188
-rw-r--r--src/include/storage/lmgr.h5
-rw-r--r--src/include/storage/proc.h74
-rw-r--r--src/include/storage/s_lock.h50
-rw-r--r--src/include/storage/shmem.h26
-rw-r--r--src/include/storage/sinval.h8
-rw-r--r--src/include/storage/sinvaladt.h8
-rw-r--r--src/include/storage/spin.h15
27 files changed, 1145 insertions, 1728 deletions
diff --git a/doc/src/sgml/ref/postmaster.sgml b/doc/src/sgml/ref/postmaster.sgml
index beb6c0ee93f..5bf5e4cb92b 100644
--- a/doc/src/sgml/ref/postmaster.sgml
+++ b/doc/src/sgml/ref/postmaster.sgml
@@ -1,5 +1,5 @@
<!--
-$Header: /cvsroot/pgsql/doc/src/sgml/ref/postmaster.sgml,v 1.16 2000/11/22 01:41:13 momjian Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/ref/postmaster.sgml,v 1.17 2000/11/28 23:27:54 tgl Exp $
Postgres documentation
-->
@@ -400,32 +400,6 @@ $ ps -e | grep postmast
</para>
</listitem>
</varlistentry>
-
- <varlistentry>
- <term><computeroutput>
-IpcMemoryAttach: shmat() failed: Permission denied
- </computeroutput></term>
- <listitem>
- <para>
- A likely explanation is that another user attempted to start a
- <application>postmaster</application>
- process on the same port which acquired shared resources and then
- died. Since Postgres shared memory keys are based on the port number
- assigned to the
- <application>postmaster</application>,
- such conflicts are likely if there is more than one installation on
- a single host. If there are no other
- <application>postmaster</application>
- processes currently running (see above), run
- <application>ipcclean</application>
- and try again. If other <application>postmaster</application>
- images
- are running, you will have to find the owners of those processes to
- coordinate the assignment of port numbers and/or removal of unused
- shared memory segments.
- </para>
- </listitem>
- </varlistentry>
</variablelist>
</para>
</refsect2>
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index b31a6182ee2..c70dbc7b4fb 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Header: /cvsroot/pgsql/src/backend/access/transam/xlog.c,v 1.35 2000/11/27 05:36:12 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/access/transam/xlog.c,v 1.36 2000/11/28 23:27:54 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -85,12 +85,6 @@ typedef struct XLogCtlWrite
} XLogCtlWrite;
-#ifndef HAS_TEST_AND_SET
-#define TAS(lck) 0
-#define S_UNLOCK(lck)
-#define S_INIT_LOCK(lck)
-#endif
-
typedef struct XLogCtlData
{
XLogCtlInsert Insert;
@@ -102,12 +96,10 @@ typedef struct XLogCtlData
uint32 XLogCacheByte;
uint32 XLogCacheBlck;
StartUpID ThisStartUpID;
-#ifdef HAS_TEST_AND_SET
slock_t insert_lck;
slock_t info_lck;
slock_t lgwr_lck;
slock_t chkp_lck; /* checkpoint lock */
-#endif
} XLogCtlData;
static XLogCtlData *XLogCtl = NULL;
diff --git a/src/backend/commands/async.c b/src/backend/commands/async.c
index e364d53b9df..6316262d042 100644
--- a/src/backend/commands/async.c
+++ b/src/backend/commands/async.c
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/commands/async.c,v 1.72 2000/11/21 21:15:59 petere Exp $
+ * $Header: /cvsroot/pgsql/src/backend/commands/async.c,v 1.73 2000/11/28 23:27:54 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -119,8 +119,8 @@ static Dllist *pendingNotifies = NULL;
static volatile int notifyInterruptEnabled = 0;
static volatile int notifyInterruptOccurred = 0;
-/* True if we've registered an on_shmem_exit cleanup (or at least tried to). */
-static int unlistenExitRegistered = 0;
+/* True if we've registered an on_shmem_exit cleanup */
+static bool unlistenExitRegistered = false;
static void Async_UnlistenAll(void);
@@ -253,9 +253,8 @@ Async_Listen(char *relname, int pid)
*/
if (!unlistenExitRegistered)
{
- if (on_shmem_exit(Async_UnlistenOnExit, 0) < 0)
- elog(NOTICE, "Async_Listen: out of shmem_exit slots");
- unlistenExitRegistered = 1;
+ on_shmem_exit(Async_UnlistenOnExit, 0);
+ unlistenExitRegistered = true;
}
}
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index 3b2eb95d4fc..9fa4ebb3689 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -11,7 +11,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/postmaster/postmaster.c,v 1.193 2000/11/27 04:03:20 inoue Exp $
+ * $Header: /cvsroot/pgsql/src/backend/postmaster/postmaster.c,v 1.194 2000/11/28 23:27:55 tgl Exp $
*
* NOTES
*
@@ -118,26 +118,6 @@ char * UnixSocketDir;
char * Virtual_host;
/*
- * This is a sequence number that indicates how many times we've had to
- * throw away the shared memory and start over because we doubted its
- * integrity. It starts off at zero and is incremented every time we
- * start over. We use this to ensure that we use a new IPC shared memory
- * key for the new shared memory segment in case the old segment isn't
- * entirely gone yet.
- *
- * The sequence actually cycles back to 0 after 9, so pathologically there
- * could be an IPC failure if 10 sets of backends are all stuck and won't
- * release IPC resources.
- */
-static short shmem_seq = 0;
-
-/*
- * This is the base IPC shared memory key. Other keys are generated by
- * adding to this.
- */
-static IpcMemoryKey ipc_key;
-
-/*
* MaxBackends is the actual limit on the number of backends we will
* start. The default is established by configure, but it can be
* readjusted from 1..MAXBACKENDS with the postmaster -N switch. Note
@@ -1292,39 +1272,6 @@ ConnFree(Port *conn)
free(conn);
}
-/*
- * get_host_port -- return a pseudo port number (16 bits)
- * derived from the primary IP address of Virtual_host.
- */
-static unsigned short
-get_host_port(void)
-{
- static unsigned short hostPort = 0;
-
- if (hostPort == 0)
- {
- SockAddr saddr;
- struct hostent *hp;
-
- hp = gethostbyname(Virtual_host);
- if ((hp == NULL) || (hp->h_addrtype != AF_INET))
- {
- char msg[1024];
- snprintf(msg, sizeof(msg),
- "FATAL: get_host_port: gethostbyname(%s) failed\n",
- Virtual_host);
- fputs(msg, stderr);
- pqdebug("%s", msg);
- exit(1);
- }
- memmove((char *) &(saddr.in.sin_addr),
- (char *) hp->h_addr,
- hp->h_length);
- hostPort = ntohl(saddr.in.sin_addr.s_addr) & 0xFFFF;
- }
-
- return hostPort;
-}
/*
* reset_shared -- reset shared memory and semaphores
@@ -1333,40 +1280,16 @@ static void
reset_shared(unsigned short port)
{
/*
- * A typical ipc_key is 5432001, which is port 5432, sequence
- * number 0, and 01 as the index in IPCKeyGetBufferMemoryKey().
- * The 32-bit INT_MAX is 2147483 6 47.
- *
- * The default algorithm for calculating the IPC keys assumes that all
- * instances of postmaster on a given host are listening on different
- * ports. In order to work (prevent shared memory collisions) if you
- * run multiple PostgreSQL instances on the same port and different IP
- * addresses on a host, we change the algorithm if you give postmaster
- * the -h option, or set PGHOST, to a value other than the internal
- * default.
- *
- * If Virtual_host is set, then we generate the IPC keys using the
- * last two octets of the IP address instead of the port number.
- * This algorithm assumes that no one will run multiple PostgreSQL
- * instances on one host using two IP addresses that have the same two
- * last octets in different class C networks. If anyone does, it
- * would be rare.
- *
- * So, if you use -h or PGHOST, don't try to run two instances of
- * PostgreSQL on the same IP address but different ports. If you
- * don't use them, then you must use different ports (via -p or
- * PGPORT). And, of course, don't try to use both approaches on one
- * host.
+ * Reset assignment of shared mem and semaphore IPC keys.
+ * Doing this means that in normal cases we'll assign the same keys
+ * on each "cycle of life", and thereby avoid leaving dead IPC objects
+ * floating around if the postmaster crashes and is restarted.
*/
-
- if (Virtual_host[0] != '\0')
- port = get_host_port();
-
- ipc_key = port * 1000 + shmem_seq * 100;
- CreateSharedMemoryAndSemaphores(ipc_key, MaxBackends);
- shmem_seq += 1;
- if (shmem_seq >= 10)
- shmem_seq -= 10;
+ IpcInitKeyAssignment(port);
+ /*
+ * Create or re-create shared memory and semaphores.
+ */
+ CreateSharedMemoryAndSemaphores(false, MaxBackends);
}
diff --git a/src/backend/storage/buffer/buf_init.c b/src/backend/storage/buffer/buf_init.c
index 3c40009422d..0fda21972f6 100644
--- a/src/backend/storage/buffer/buf_init.c
+++ b/src/backend/storage/buffer/buf_init.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/storage/buffer/buf_init.c,v 1.37 2000/10/23 04:10:06 vadim Exp $
+ * $Header: /cvsroot/pgsql/src/backend/storage/buffer/buf_init.c,v 1.38 2000/11/28 23:27:55 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -56,13 +56,6 @@ int Num_Descriptors;
BufferDesc *BufferDescriptors;
BufferBlock BufferBlocks;
-#ifndef HAS_TEST_AND_SET
-long *NWaitIOBackendP;
-
-#endif
-
-extern IpcSemaphoreId WaitIOSemId;
-
long *PrivateRefCount; /* also used in freelist.c */
bits8 *BufferLocks; /* flag bits showing locks I have set */
BufferTag *BufferTagLastDirtied; /* tag buffer had when last
@@ -139,7 +132,7 @@ long int LocalBufferFlushCount;
* amount of available memory.
*/
void
-InitBufferPool(IPCKey key)
+InitBufferPool(void)
{
bool foundBufs,
foundDescs;
@@ -170,18 +163,6 @@ InitBufferPool(IPCKey key)
ShmemInitStruct("Buffer Blocks",
NBuffers * BLCKSZ, &foundBufs);
-#ifndef HAS_TEST_AND_SET
- {
- bool foundNWaitIO;
-
- NWaitIOBackendP = (long *) ShmemInitStruct("#Backends Waiting IO",
- sizeof(long),
- &foundNWaitIO);
- if (!foundNWaitIO)
- *NWaitIOBackendP = 0;
- }
-#endif
-
if (foundDescs || foundBufs)
{
@@ -214,10 +195,8 @@ InitBufferPool(IPCKey key)
buf->flags = (BM_DELETED | BM_FREE | BM_VALID);
buf->refcount = 0;
buf->buf_id = i;
-#ifdef HAS_TEST_AND_SET
S_INIT_LOCK(&(buf->io_in_progress_lock));
S_INIT_LOCK(&(buf->cntx_lock));
-#endif
}
/* close the circular queue */
@@ -231,22 +210,6 @@ InitBufferPool(IPCKey key)
SpinRelease(BufMgrLock);
-#ifndef HAS_TEST_AND_SET
- {
- extern IpcSemaphoreId WaitIOSemId;
- extern IpcSemaphoreId WaitCLSemId;
-
- WaitIOSemId = IpcSemaphoreCreate(IPCKeyGetWaitIOSemaphoreKey(key),
- 1, IPCProtection, 0, 1);
- if (WaitIOSemId < 0)
- elog(FATAL, "InitBufferPool: IpcSemaphoreCreate(WaitIOSemId) failed");
- WaitCLSemId = IpcSemaphoreCreate(IPCKeyGetWaitCLSemaphoreKey(key),
- 1, IPCProtection,
- IpcSemaphoreDefaultStartValue, 1);
- if (WaitCLSemId < 0)
- elog(FATAL, "InitBufferPool: IpcSemaphoreCreate(WaitCLSemId) failed");
- }
-#endif
PrivateRefCount = (long *) calloc(NBuffers, sizeof(long));
BufferLocks = (bits8 *) calloc(NBuffers, sizeof(bits8));
BufferTagLastDirtied = (BufferTag *) calloc(NBuffers, sizeof(BufferTag));
@@ -262,7 +225,7 @@ InitBufferPool(IPCKey key)
* ----------------------------------------------------
*/
int
-BufferShmemSize()
+BufferShmemSize(void)
{
int size = 0;
diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c
index 907e8194743..8ed03138fac 100644
--- a/src/backend/storage/buffer/bufmgr.c
+++ b/src/backend/storage/buffer/bufmgr.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/storage/buffer/bufmgr.c,v 1.94 2000/11/20 16:47:31 petere Exp $
+ * $Header: /cvsroot/pgsql/src/backend/storage/buffer/bufmgr.c,v 1.95 2000/11/28 23:27:55 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -93,12 +93,6 @@ extern void AbortBufferIO(void);
*/
#define BUFFER_IS_BROKEN(buf) ((buf->flags & BM_IO_ERROR) && !(buf->flags & BM_DIRTY))
-#ifndef HAS_TEST_AND_SET
-static void SignalIO(BufferDesc *buf);
-extern long *NWaitIOBackendP; /* defined in buf_init.c */
-
-#endif /* HAS_TEST_AND_SET */
-
static Buffer ReadBufferWithBufferLock(Relation relation, BlockNumber blockNum,
bool bufferLockHeld);
static BufferDesc *BufferAlloc(Relation reln, BlockNumber blockNum,
@@ -1187,27 +1181,7 @@ BufferSync()
*
* Should be entered with buffer manager spinlock held; releases it before
* waiting and re-acquires it afterwards.
- *
- * OLD NOTES:
- * Because IO_IN_PROGRESS conflicts are
- * expected to be rare, there is only one BufferIO
- * lock in the entire system. All processes block
- * on this semaphore when they try to use a buffer
- * that someone else is faulting in. Whenever a
- * process finishes an IO and someone is waiting for
- * the buffer, BufferIO is signaled (SignalIO). All
- * waiting processes then wake up and check to see
- * if their buffer is now ready. This implementation
- * is simple, but efficient enough if WaitIO is
- * rarely called by multiple processes simultaneously.
- *
- * NEW NOTES:
- * The above is true only on machines without test-and-set
- * semaphores (which we hope are few, these days). On better
- * hardware, each buffer has a spinlock that we can wait on.
*/
-#ifdef HAS_TEST_AND_SET
-
static void
WaitIO(BufferDesc *buf, SPINLOCK spinlock)
{
@@ -1224,43 +1198,6 @@ WaitIO(BufferDesc *buf, SPINLOCK spinlock)
}
}
-#else /* !HAS_TEST_AND_SET */
-
-IpcSemaphoreId WaitIOSemId;
-IpcSemaphoreId WaitCLSemId;
-
-static void
-WaitIO(BufferDesc *buf, SPINLOCK spinlock)
-{
- bool inProgress;
-
- for (;;)
- {
-
- /* wait until someone releases IO lock */
- (*NWaitIOBackendP)++;
- SpinRelease(spinlock);
- IpcSemaphoreLock(WaitIOSemId, 0, 1);
- SpinAcquire(spinlock);
- inProgress = (buf->flags & BM_IO_IN_PROGRESS);
- if (!inProgress)
- break;
- }
-}
-
-/*
- * SignalIO
- */
-static void
-SignalIO(BufferDesc *buf)
-{
- /* somebody better be waiting. */
- Assert(buf->refcount > 1);
- IpcSemaphoreUnlock(WaitIOSemId, 0, *NWaitIOBackendP);
- *NWaitIOBackendP = 0;
-}
-
-#endif /* HAS_TEST_AND_SET */
long NDirectFileRead; /* some I/O's are direct file access.
* bypass bufmgr */
@@ -2297,11 +2234,7 @@ UnlockBuffers()
Assert(BufferIsValid(i + 1));
buf = &(BufferDescriptors[i]);
-#ifdef HAS_TEST_AND_SET
S_LOCK(&(buf->cntx_lock));
-#else
- IpcSemaphoreLock(WaitCLSemId, 0, IpcExclusiveLock);
-#endif
if (BufferLocks[i] & BL_R_LOCK)
{
@@ -2324,11 +2257,9 @@ UnlockBuffers()
Assert(buf->w_lock);
buf->w_lock = false;
}
-#ifdef HAS_TEST_AND_SET
+
S_UNLOCK(&(buf->cntx_lock));
-#else
- IpcSemaphoreUnlock(WaitCLSemId, 0, IpcExclusiveLock);
-#endif
+
BufferLocks[i] = 0;
}
}
@@ -2346,11 +2277,7 @@ LockBuffer(Buffer buffer, int mode)
buf = &(BufferDescriptors[buffer - 1]);
buflock = &(BufferLocks[buffer - 1]);
-#ifdef HAS_TEST_AND_SET
S_LOCK(&(buf->cntx_lock));
-#else
- IpcSemaphoreLock(WaitCLSemId, 0, IpcExclusiveLock);
-#endif
if (mode == BUFFER_LOCK_UNLOCK)
{
@@ -2380,15 +2307,9 @@ LockBuffer(Buffer buffer, int mode)
Assert(!(*buflock & (BL_R_LOCK | BL_W_LOCK | BL_RI_LOCK)));
while (buf->ri_lock || buf->w_lock)
{
-#ifdef HAS_TEST_AND_SET
S_UNLOCK(&(buf->cntx_lock));
s_lock_sleep(i++);
S_LOCK(&(buf->cntx_lock));
-#else
- IpcSemaphoreUnlock(WaitCLSemId, 0, IpcExclusiveLock);
- s_lock_sleep(i++);
- IpcSemaphoreLock(WaitCLSemId, 0, IpcExclusiveLock);
-#endif
}
(buf->r_locks)++;
*buflock |= BL_R_LOCK;
@@ -2412,15 +2333,9 @@ LockBuffer(Buffer buffer, int mode)
*buflock |= BL_RI_LOCK;
buf->ri_lock = true;
}
-#ifdef HAS_TEST_AND_SET
S_UNLOCK(&(buf->cntx_lock));
s_lock_sleep(i++);
S_LOCK(&(buf->cntx_lock));
-#else
- IpcSemaphoreUnlock(WaitCLSemId, 0, IpcExclusiveLock);
- s_lock_sleep(i++);
- IpcSemaphoreLock(WaitCLSemId, 0, IpcExclusiveLock);
-#endif
}
buf->w_lock = true;
*buflock |= BL_W_LOCK;
@@ -2438,12 +2353,7 @@ LockBuffer(Buffer buffer, int mode)
else
elog(ERROR, "LockBuffer: unknown lock mode %d", mode);
-#ifdef HAS_TEST_AND_SET
S_UNLOCK(&(buf->cntx_lock));
-#else
- IpcSemaphoreUnlock(WaitCLSemId, 0, IpcExclusiveLock);
-#endif
-
}
/*
@@ -2471,7 +2381,6 @@ StartBufferIO(BufferDesc *buf, bool forInput)
Assert(!InProgressBuf);
Assert(!(buf->flags & BM_IO_IN_PROGRESS));
buf->flags |= BM_IO_IN_PROGRESS;
-#ifdef HAS_TEST_AND_SET
/*
* There used to be
@@ -2485,7 +2394,7 @@ StartBufferIO(BufferDesc *buf, bool forInput)
* happen -- tgl
*/
S_LOCK(&(buf->io_in_progress_lock));
-#endif /* HAS_TEST_AND_SET */
+
InProgressBuf = buf;
IsForInput = forInput;
}
@@ -2502,12 +2411,7 @@ static void
TerminateBufferIO(BufferDesc *buf)
{
Assert(buf == InProgressBuf);
-#ifdef HAS_TEST_AND_SET
S_UNLOCK(&(buf->io_in_progress_lock));
-#else
- if (buf->refcount > 1)
- SignalIO(buf);
-#endif /* HAS_TEST_AND_SET */
InProgressBuf = (BufferDesc *) 0;
}
diff --git a/src/backend/storage/buffer/s_lock.c b/src/backend/storage/buffer/s_lock.c
index 883c150b923..72b167977d5 100644
--- a/src/backend/storage/buffer/s_lock.c
+++ b/src/backend/storage/buffer/s_lock.c
@@ -1,22 +1,22 @@
/*-------------------------------------------------------------------------
*
* s_lock.c
- * buffer manager interface routines
+ * Spinlock support routines
*
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/storage/buffer/Attic/s_lock.c,v 1.25 2000/11/16 05:51:01 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/storage/buffer/Attic/s_lock.c,v 1.26 2000/11/28 23:27:55 tgl Exp $
*
*-------------------------------------------------------------------------
*/
+#include "postgres.h"
#include <sys/time.h>
#include <unistd.h>
-#include "postgres.h"
#include "storage/s_lock.h"
diff --git a/src/backend/storage/buffer/xlog_bufmgr.c b/src/backend/storage/buffer/xlog_bufmgr.c
index ff6bff29a1d..9672510547a 100644
--- a/src/backend/storage/buffer/xlog_bufmgr.c
+++ b/src/backend/storage/buffer/xlog_bufmgr.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/storage/buffer/Attic/xlog_bufmgr.c,v 1.4 2000/11/22 02:19:14 inoue Exp $
+ * $Header: /cvsroot/pgsql/src/backend/storage/buffer/Attic/xlog_bufmgr.c,v 1.5 2000/11/28 23:27:55 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -88,12 +88,6 @@ extern void AbortBufferIO(void);
*/
#define BUFFER_IS_BROKEN(buf) ((buf->flags & BM_IO_ERROR) && !(buf->flags & BM_DIRTY))
-#ifndef HAS_TEST_AND_SET
-static void SignalIO(BufferDesc *buf);
-extern long *NWaitIOBackendP; /* defined in buf_init.c */
-
-#endif /* HAS_TEST_AND_SET */
-
static Buffer ReadBufferWithBufferLock(Relation relation, BlockNumber blockNum,
bool bufferLockHeld);
static BufferDesc *BufferAlloc(Relation reln, BlockNumber blockNum,
@@ -853,27 +847,7 @@ BufferSync()
*
* Should be entered with buffer manager spinlock held; releases it before
* waiting and re-acquires it afterwards.
- *
- * OLD NOTES:
- * Because IO_IN_PROGRESS conflicts are
- * expected to be rare, there is only one BufferIO
- * lock in the entire system. All processes block
- * on this semaphore when they try to use a buffer
- * that someone else is faulting in. Whenever a
- * process finishes an IO and someone is waiting for
- * the buffer, BufferIO is signaled (SignalIO). All
- * waiting processes then wake up and check to see
- * if their buffer is now ready. This implementation
- * is simple, but efficient enough if WaitIO is
- * rarely called by multiple processes simultaneously.
- *
- * NEW NOTES:
- * The above is true only on machines without test-and-set
- * semaphores (which we hope are few, these days). On better
- * hardware, each buffer has a spinlock that we can wait on.
*/
-#ifdef HAS_TEST_AND_SET
-
static void
WaitIO(BufferDesc *buf, SPINLOCK spinlock)
{
@@ -890,43 +864,6 @@ WaitIO(BufferDesc *buf, SPINLOCK spinlock)
}
}
-#else /* !HAS_TEST_AND_SET */
-
-IpcSemaphoreId WaitIOSemId;
-IpcSemaphoreId WaitCLSemId;
-
-static void
-WaitIO(BufferDesc *buf, SPINLOCK spinlock)
-{
- bool inProgress;
-
- for (;;)
- {
-
- /* wait until someone releases IO lock */
- (*NWaitIOBackendP)++;
- SpinRelease(spinlock);
- IpcSemaphoreLock(WaitIOSemId, 0, 1);
- SpinAcquire(spinlock);
- inProgress = (buf->flags & BM_IO_IN_PROGRESS);
- if (!inProgress)
- break;
- }
-}
-
-/*
- * SignalIO
- */
-static void
-SignalIO(BufferDesc *buf)
-{
- /* somebody better be waiting. */
- Assert(buf->refcount > 1);
- IpcSemaphoreUnlock(WaitIOSemId, 0, *NWaitIOBackendP);
- *NWaitIOBackendP = 0;
-}
-
-#endif /* HAS_TEST_AND_SET */
long NDirectFileRead; /* some I/O's are direct file access.
* bypass bufmgr */
@@ -1965,11 +1902,7 @@ UnlockBuffers()
Assert(BufferIsValid(i + 1));
buf = &(BufferDescriptors[i]);
-#ifdef HAS_TEST_AND_SET
S_LOCK(&(buf->cntx_lock));
-#else
- IpcSemaphoreLock(WaitCLSemId, 0, IpcExclusiveLock);
-#endif
if (BufferLocks[i] & BL_R_LOCK)
{
@@ -1992,11 +1925,9 @@ UnlockBuffers()
Assert(buf->w_lock);
buf->w_lock = false;
}
-#ifdef HAS_TEST_AND_SET
+
S_UNLOCK(&(buf->cntx_lock));
-#else
- IpcSemaphoreUnlock(WaitCLSemId, 0, IpcExclusiveLock);
-#endif
+
BufferLocks[i] = 0;
}
}
@@ -2014,11 +1945,7 @@ LockBuffer(Buffer buffer, int mode)
buf = &(BufferDescriptors[buffer - 1]);
buflock = &(BufferLocks[buffer - 1]);
-#ifdef HAS_TEST_AND_SET
S_LOCK(&(buf->cntx_lock));
-#else
- IpcSemaphoreLock(WaitCLSemId, 0, IpcExclusiveLock);
-#endif
if (mode == BUFFER_LOCK_UNLOCK)
{
@@ -2048,15 +1975,9 @@ LockBuffer(Buffer buffer, int mode)
Assert(!(*buflock & (BL_R_LOCK | BL_W_LOCK | BL_RI_LOCK)));
while (buf->ri_lock || buf->w_lock)
{
-#ifdef HAS_TEST_AND_SET
S_UNLOCK(&(buf->cntx_lock));
s_lock_sleep(i++);
S_LOCK(&(buf->cntx_lock));
-#else
- IpcSemaphoreUnlock(WaitCLSemId, 0, IpcExclusiveLock);
- s_lock_sleep(i++);
- IpcSemaphoreLock(WaitCLSemId, 0, IpcExclusiveLock);
-#endif
}
(buf->r_locks)++;
*buflock |= BL_R_LOCK;
@@ -2080,15 +2001,9 @@ LockBuffer(Buffer buffer, int mode)
*buflock |= BL_RI_LOCK;
buf->ri_lock = true;
}
-#ifdef HAS_TEST_AND_SET
S_UNLOCK(&(buf->cntx_lock));
s_lock_sleep(i++);
S_LOCK(&(buf->cntx_lock));
-#else
- IpcSemaphoreUnlock(WaitCLSemId, 0, IpcExclusiveLock);
- s_lock_sleep(i++);
- IpcSemaphoreLock(WaitCLSemId, 0, IpcExclusiveLock);
-#endif
}
buf->w_lock = true;
*buflock |= BL_W_LOCK;
@@ -2109,12 +2024,7 @@ LockBuffer(Buffer buffer, int mode)
else
elog(ERROR, "LockBuffer: unknown lock mode %d", mode);
-#ifdef HAS_TEST_AND_SET
S_UNLOCK(&(buf->cntx_lock));
-#else
- IpcSemaphoreUnlock(WaitCLSemId, 0, IpcExclusiveLock);
-#endif
-
}
/*
@@ -2142,7 +2052,6 @@ StartBufferIO(BufferDesc *buf, bool forInput)
Assert(!InProgressBuf);
Assert(!(buf->flags & BM_IO_IN_PROGRESS));
buf->flags |= BM_IO_IN_PROGRESS;
-#ifdef HAS_TEST_AND_SET
/*
* There used to be
@@ -2156,7 +2065,7 @@ StartBufferIO(BufferDesc *buf, bool forInput)
* happen -- tgl
*/
S_LOCK(&(buf->io_in_progress_lock));
-#endif /* HAS_TEST_AND_SET */
+
InProgressBuf = buf;
IsForInput = forInput;
}
@@ -2173,12 +2082,7 @@ static void
TerminateBufferIO(BufferDesc *buf)
{
Assert(buf == InProgressBuf);
-#ifdef HAS_TEST_AND_SET
S_UNLOCK(&(buf->io_in_progress_lock));
-#else
- if (buf->refcount > 1)
- SignalIO(buf);
-#endif /* HAS_TEST_AND_SET */
InProgressBuf = (BufferDesc *) 0;
}
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 98d90bc62e0..920f7f9bfeb 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/storage/ipc/ipc.c,v 1.53 2000/11/21 21:16:01 petere Exp $
+ * $Header: /cvsroot/pgsql/src/backend/storage/ipc/ipc.c,v 1.54 2000/11/28 23:27:56 tgl Exp $
*
* NOTES
*
@@ -30,6 +30,7 @@
#include <sys/types.h>
#include <sys/file.h>
#include <errno.h>
+#include <unistd.h>
#include "storage/ipc.h"
#include "storage/s_lock.h"
@@ -51,6 +52,7 @@
#include <sys/ipc.h>
#endif
+
/*
* 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
@@ -58,12 +60,31 @@
*/
bool proc_exit_inprogress = false;
-static int UsePrivateMemory = 0;
+static IpcSemaphoreId InternalIpcSemaphoreCreate(IpcSemaphoreKey semKey,
+ int numSems, int permission,
+ int semStartValue, bool removeOnExit);
+static void CallbackSemaphoreKill(int status, Datum semId);
+static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, uint32 size,
+ int permission);
+static void IpcMemoryDetach(int status, Datum shmaddr);
+static void IpcMemoryDelete(int status, Datum shmId);
+static void *PrivateMemoryCreate(uint32 size);
+static void PrivateMemoryDelete(int status, Datum memaddr);
-static void IpcMemoryDetach(int status, char *shmaddr);
/* ----------------------------------------------------------------
* 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.
+ *
+ * XXX these functions probably ought to live in some other module.
* ----------------------------------------------------------------
*/
@@ -73,43 +94,12 @@ static struct ONEXIT
{
void (*function) ();
Datum arg;
-} on_proc_exit_list[MAX_ON_EXITS], on_shmem_exit_list[MAX_ON_EXITS];
+} on_proc_exit_list[MAX_ON_EXITS],
+ on_shmem_exit_list[MAX_ON_EXITS];
static int on_proc_exit_index,
on_shmem_exit_index;
-typedef struct _PrivateMemStruct
-{
- int id;
- char *memptr;
-} PrivateMem;
-
-static PrivateMem IpcPrivateMem[16];
-
-
-static int
-PrivateMemoryCreate(IpcMemoryKey memKey,
- uint32 size)
-{
- static int memid = 0;
-
- UsePrivateMemory = 1;
-
- IpcPrivateMem[memid].id = memid;
- IpcPrivateMem[memid].memptr = malloc(size);
- if (IpcPrivateMem[memid].memptr == NULL)
- elog(ERROR, "PrivateMemoryCreate: not enough memory to malloc");
- MemSet(IpcPrivateMem[memid].memptr, 0, size); /* XXX PURIFY */
-
- return memid++;
-}
-
-static char *
-PrivateMemoryAttach(IpcMemoryId memid)
-{
- return IpcPrivateMem[memid].memptr;
-}
-
/* ----------------------------------------------------------------
* proc_exit
@@ -156,9 +146,9 @@ proc_exit(int code)
}
/* ------------------
- * Run all of the on_shmem_exit routines but don't exit in the end.
+ * 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
+ * semaphores after a backend dies horribly.
* ------------------
*/
void
@@ -188,18 +178,16 @@ shmem_exit(int code)
* functions invoked by proc_exit(). -cim 2/6/90
* ----------------------------------------------------------------
*/
-int
+void
on_proc_exit(void (*function) (), Datum arg)
{
if (on_proc_exit_index >= MAX_ON_EXITS)
- return -1;
+ 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;
-
- return 0;
}
/* ----------------------------------------------------------------
@@ -209,24 +197,25 @@ on_proc_exit(void (*function) (), Datum arg)
* functions invoked by shmem_exit(). -cim 2/6/90
* ----------------------------------------------------------------
*/
-int
+void
on_shmem_exit(void (*function) (), Datum arg)
{
if (on_shmem_exit_index >= MAX_ON_EXITS)
- return -1;
+ 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;
-
- return 0;
}
/* ----------------------------------------------------------------
* on_exit_reset
*
- * this function clears all proc_exit() registered functions.
+ * 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
@@ -236,190 +225,135 @@ on_exit_reset(void)
on_proc_exit_index = 0;
}
-/****************************************************************************/
-/* IPCPrivateSemaphoreKill(status, semId) */
-/* */
-/****************************************************************************/
-static void
-IPCPrivateSemaphoreKill(int status, int semId)
-{
- union semun semun;
- semun.val = 0; /* unused */
-
- if (semctl(semId, 0, IPC_RMID, semun) == -1)
- elog(NOTICE, "IPCPrivateSemaphoreKill: semctl(%d, 0, IPC_RMID, ...) failed: %s",
- semId, strerror(errno));
-}
-
-
-/****************************************************************************/
-/* IPCPrivateMemoryKill(status, shmId) */
-/* */
-/****************************************************************************/
-static void
-IPCPrivateMemoryKill(int status, int shmId)
-{
- if (UsePrivateMemory)
- {
- /* free ( IpcPrivateMem[shmId].memptr ); */
- }
- else
- {
- if (shmctl(shmId, IPC_RMID, (struct shmid_ds *) NULL) < 0)
- {
- elog(NOTICE, "IPCPrivateMemoryKill: shmctl(%d, %d, 0) failed: %m",
- shmId, IPC_RMID);
- }
- }
-}
-/*
- * Note:
- * XXX This should be split into two different calls. One should
- * XXX be used to create a semaphore set. The other to "attach" a
- * XXX existing set. It should be an error for the semaphore set
- * XXX to to already exist or for it not to, respectively.
+/* ----------------------------------------------------------------
+ * Semaphore support
*
- * Currently, the semaphore sets are "attached" and an error
- * is detected only when a later shared memory attach fails.
+ * These routines represent a fairly thin layer on top of SysV semaphore
+ * functionality.
+ * ----------------------------------------------------------------
*/
-IpcSemaphoreId
-IpcSemaphoreCreate(IpcSemaphoreKey semKey,
- int semNum,
- int permission,
- int semStartValue,
- int removeOnExit)
+/* ----------------------------------------------------------------
+ * InternalIpcSemaphoreCreate(semKey, numSems, permission,
+ * semStartValue, removeOnExit)
+ *
+ * Attempt to create a new semaphore set with the specified key.
+ * Will fail (return -1) if such a set already exists.
+ * On success, a callback is optionally registered with on_shmem_exit
+ * to delete the semaphore set when on_shmem_exit is called.
+ *
+ * If we fail with a failure code other than collision-with-existing-set,
+ * print out an error and abort. Other types of errors are not recoverable.
+ * ----------------------------------------------------------------
+ */
+static IpcSemaphoreId
+InternalIpcSemaphoreCreate(IpcSemaphoreKey semKey,
+ int numSems, int permission,
+ int semStartValue, bool removeOnExit)
{
int semId;
int i;
- int errStatus;
u_short array[IPC_NMAXSEM];
union semun semun;
- /* check arguments */
- if (semNum > IPC_NMAXSEM || semNum <= 0)
- return (-1);
+ Assert(numSems > 0 && numSems <= IPC_NMAXSEM);
- semId = semget(semKey, 0, 0);
+ semId = semget(semKey, numSems, IPC_CREAT | IPC_EXCL | permission);
- if (semId == -1)
+ if (semId < 0)
{
-#ifdef DEBUG_IPC
- fprintf(stderr, "calling semget(%d, %d, 0%o)\n",
- semKey, semNum, (unsigned)(IPC_CREAT|permission));
-#endif
+ /*
+ * Fail quietly if error indicates a collision with existing set.
+ * One would expect EEXIST, given that we said IPC_EXCL, but perhaps
+ * we could get a permission violation instead?
+ */
+ if (errno == EEXIST || errno == EACCES)
+ return -1;
+ /*
+ * Else complain and abort
+ */
+ fprintf(stderr, "IpcSemaphoreCreate: semget(key=%d, num=%d, 0%o) failed: %s\n",
+ (int) semKey, numSems, (IPC_CREAT|IPC_EXCL|permission),
+ strerror(errno));
- semId = semget(semKey, semNum, IPC_CREAT | permission);
+ if (errno == ENOSPC)
+ fprintf(stderr,
+ "\nThis error does *not* mean that you have run out of disk space.\n\n"
+ "It occurs either because system limit for the maximum number of\n"
+ "semaphore sets (SEMMNI), or the system wide maximum number of\n"
+ "semaphores (SEMMNS), would be exceeded. You need to raise the\n"
+ "respective kernel parameter. Look into the PostgreSQL documentation\n"
+ "for details.\n\n");
- if (semId < 0)
- {
- fprintf(stderr, "IpcSemaphoreCreate: semget(key=%d, num=%d, 0%o) failed: %s\n",
- semKey, semNum, (unsigned)(permission|IPC_CREAT),
- strerror(errno));
-
- if (errno == ENOSPC)
- fprintf(stderr,
- "\nThis error does *not* mean that you have run out of disk space.\n\n"
- "It occurs either because system limit for the maximum number of\n"
- "semaphore sets (SEMMNI), or the system wide maximum number of\n"
- "semaphores (SEMMNS), would be exceeded. You need to raise the\n"
- "respective kernel parameter. Look into the PostgreSQL documentation\n"
- "for details.\n\n");
-
- return (-1);
- }
- for (i = 0; i < semNum; i++)
- array[i] = semStartValue;
- semun.array = array;
- errStatus = semctl(semId, 0, SETALL, semun);
- if (errStatus == -1)
- {
- fprintf(stderr, "IpcSemaphoreCreate: semctl(id=%d, 0, SETALL, ...) failed: %s\n",
- semId, strerror(errno));
+ proc_exit(1);
+ }
- if (errno == ERANGE)
- fprintf(stderr,
- "You possibly need to raise your kernel's SEMVMX value to be at least\n"
- "%d. Look into the PostgreSQL documentation for details.\n",
- semStartValue);
+ /* Initialize new semas to specified start value */
+ for (i = 0; i < numSems; i++)
+ array[i] = semStartValue;
+ semun.array = array;
+ if (semctl(semId, 0, SETALL, semun) < 0)
+ {
+ fprintf(stderr, "IpcSemaphoreCreate: semctl(id=%d, 0, SETALL, ...) failed: %s\n",
+ semId, strerror(errno));
- semctl(semId, 0, IPC_RMID, semun);
- return (-1);
- }
+ if (errno == ERANGE)
+ fprintf(stderr,
+ "You possibly need to raise your kernel's SEMVMX value to be at least\n"
+ "%d. Look into the PostgreSQL documentation for details.\n",
+ semStartValue);
- if (removeOnExit)
- on_shmem_exit(IPCPrivateSemaphoreKill, (Datum) semId);
+ IpcSemaphoreKill(semId);
+ proc_exit(1);
}
-
-#ifdef DEBUG_IPC
- fprintf(stderr, "IpcSemaphoreCreate returns %d\n", semId);
- fflush(stdout);
- fflush(stderr);
-#endif
+ /* Register on-exit routine to delete the new set */
+ if (removeOnExit)
+ on_shmem_exit(CallbackSemaphoreKill, Int32GetDatum(semId));
return semId;
}
-
/****************************************************************************/
-/* IpcSemaphoreSet() - sets the initial value of the semaphore */
+/* IpcSemaphoreKill(semId) - removes a semaphore set */
/* */
-/* note: the xxx_return variables are only used for debugging. */
/****************************************************************************/
-#ifdef NOT_USED
-static int IpcSemaphoreSet_return;
-
void
-IpcSemaphoreSet(int semId, int semno, int value)
+IpcSemaphoreKill(IpcSemaphoreId semId)
{
- int errStatus;
union semun semun;
- semun.val = value;
- errStatus = semctl(semId, semno, SETVAL, semun);
- IpcSemaphoreSet_return = errStatus;
+ semun.val = 0; /* unused, but keep compiler quiet */
- if (errStatus == -1)
- fprintf(stderr, "IpcSemaphoreSet: semctl(id=%d) failed: %s\n",
+ if (semctl(semId, 0, IPC_RMID, semun) < 0)
+ fprintf(stderr, "IpcSemaphoreKill: semctl(%d, 0, IPC_RMID, ...) failed: %s\n",
semId, strerror(errno));
+ /* We used to report a failure via elog(NOTICE), but that's pretty
+ * pointless considering any client has long since disconnected ...
+ */
}
-#endif /* NOT_USED */
-
/****************************************************************************/
-/* IpcSemaphoreKill(key) - removes a semaphore */
-/* */
+/* CallbackSemaphoreKill(status, semId) */
+/* (called as an on_shmem_exit callback, hence funny argument list) */
/****************************************************************************/
-void
-IpcSemaphoreKill(IpcSemaphoreKey key)
+static void
+CallbackSemaphoreKill(int status, Datum semId)
{
- int semId;
- union semun semun;
- semun.val = 0; /* unused */
-
- /* kill semaphore if existent */
-
- semId = semget(key, 0, 0);
- if (semId != -1)
- semctl(semId, 0, IPC_RMID, semun);
+ IpcSemaphoreKill(DatumGetInt32(semId));
}
/****************************************************************************/
-/* IpcSemaphoreLock(semId, sem, lock) - locks a semaphore */
-/* */
-/* note: the xxx_return variables are only used for debugging. */
+/* IpcSemaphoreLock(semId, sem) - locks a semaphore */
/****************************************************************************/
-static int IpcSemaphoreLock_return;
-
void
-IpcSemaphoreLock(IpcSemaphoreId semId, int sem, int lock)
+IpcSemaphoreLock(IpcSemaphoreId semId, int sem)
{
int errStatus;
struct sembuf sops;
- sops.sem_op = lock;
+ sops.sem_op = -1; /* decrement */
sops.sem_flg = 0;
sops.sem_num = sem;
@@ -427,11 +361,6 @@ IpcSemaphoreLock(IpcSemaphoreId semId, int sem, int lock)
* Note: if errStatus is -1 and errno == EINTR then it means we
* returned from the operation prematurely because we were
* sent a signal. So we try and lock the semaphore again.
- * I am not certain this is correct, but the semantics aren't
- * clear it fixes problems with parallel abort synchronization,
- * namely that after processing an abort signal, the semaphore
- * call returns with -1 (and errno == EINTR) before it should.
- * -cim 3/28/90
* ----------------
*/
do
@@ -439,8 +368,6 @@ IpcSemaphoreLock(IpcSemaphoreId semId, int sem, int lock)
errStatus = semop(semId, &sops, 1);
} while (errStatus == -1 && errno == EINTR);
- IpcSemaphoreLock_return = errStatus;
-
if (errStatus == -1)
{
fprintf(stderr, "IpcSemaphoreLock: semop(id=%d) failed: %s\n",
@@ -450,19 +377,15 @@ IpcSemaphoreLock(IpcSemaphoreId semId, int sem, int lock)
}
/****************************************************************************/
-/* IpcSemaphoreUnlock(semId, sem, lock) - unlocks a semaphore */
-/* */
-/* note: the xxx_return variables are only used for debugging. */
+/* IpcSemaphoreUnlock(semId, sem) - unlocks a semaphore */
/****************************************************************************/
-static int IpcSemaphoreUnlock_return;
-
void
-IpcSemaphoreUnlock(IpcSemaphoreId semId, int sem, int lock)
+IpcSemaphoreUnlock(IpcSemaphoreId semId, int sem)
{
int errStatus;
struct sembuf sops;
- sops.sem_op = -lock;
+ sops.sem_op = 1; /* increment */
sops.sem_flg = 0;
sops.sem_num = sem;
@@ -470,12 +393,8 @@ IpcSemaphoreUnlock(IpcSemaphoreId semId, int sem, int lock)
/* ----------------
* Note: if errStatus is -1 and errno == EINTR then it means we
* returned from the operation prematurely because we were
- * sent a signal. So we try and lock the semaphore again.
- * I am not certain this is correct, but the semantics aren't
- * clear it fixes problems with parallel abort synchronization,
- * namely that after processing an abort signal, the semaphore
- * call returns with -1 (and errno == EINTR) before it should.
- * -cim 3/28/90
+ * sent a signal. So we try and unlock the semaphore again.
+ * Not clear this can really happen, but might as well cope.
* ----------------
*/
do
@@ -483,8 +402,6 @@ IpcSemaphoreUnlock(IpcSemaphoreId semId, int sem, int lock)
errStatus = semop(semId, &sops, 1);
} while (errStatus == -1 && errno == EINTR);
- IpcSemaphoreUnlock_return = errStatus;
-
if (errStatus == -1)
{
fprintf(stderr, "IpcSemaphoreUnlock: semop(id=%d) failed: %s\n",
@@ -493,53 +410,115 @@ IpcSemaphoreUnlock(IpcSemaphoreId semId, int sem, int lock)
}
}
+/****************************************************************************/
+/* IpcSemaphoreTryLock(semId, sem) - conditionally locks a semaphore */
+/* Lock the semaphore if it's free, but don't block. */
+/****************************************************************************/
+bool
+IpcSemaphoreTryLock(IpcSemaphoreId semId, int sem)
+{
+ int errStatus;
+ struct sembuf sops;
+
+ sops.sem_op = -1; /* decrement */
+ sops.sem_flg = IPC_NOWAIT; /* but don't block */
+ sops.sem_num = sem;
+
+ /* ----------------
+ * Note: if errStatus is -1 and errno == EINTR then it means we
+ * returned from the operation prematurely because we were
+ * sent a signal. So we try and lock the semaphore again.
+ * ----------------
+ */
+ do
+ {
+ errStatus = semop(semId, &sops, 1);
+ } while (errStatus == -1 && errno == EINTR);
+
+ if (errStatus == -1)
+ {
+ /* Expect EAGAIN or EWOULDBLOCK (platform-dependent) */
+#ifdef EAGAIN
+ if (errno == EAGAIN)
+ return false; /* failed to lock it */
+#endif
+#if defined(EWOULDBLOCK) && (!defined(EAGAIN) || (EWOULDBLOCK != EAGAIN))
+ if (errno == EWOULDBLOCK)
+ return false; /* failed to lock it */
+#endif
+ /* Otherwise we got trouble */
+ fprintf(stderr, "IpcSemaphoreTryLock: semop(id=%d) failed: %s\n",
+ semId, strerror(errno));
+ proc_exit(255);
+ }
+
+ return true;
+}
+
+/* Get the current value (semval) of the semaphore */
int
-IpcSemaphoreGetCount(IpcSemaphoreId semId, int sem)
+IpcSemaphoreGetValue(IpcSemaphoreId semId, int sem)
{
- int semncnt;
union semun dummy; /* for Solaris */
dummy.val = 0; /* unused */
- semncnt = semctl(semId, sem, GETNCNT, dummy);
- return semncnt;
+ return semctl(semId, sem, GETVAL, dummy);
}
-int
-IpcSemaphoreGetValue(IpcSemaphoreId semId, int sem)
+/* Get the PID of the last process to do semop() on the semaphore */
+static pid_t
+IpcSemaphoreGetLastPID(IpcSemaphoreId semId, int sem)
{
- int semval;
union semun dummy; /* for Solaris */
dummy.val = 0; /* unused */
- semval = semctl(semId, sem, GETVAL, dummy);
- return semval;
+ return semctl(semId, sem, GETPID, dummy);
}
-/****************************************************************************/
-/* IpcMemoryCreate(memKey) */
-/* */
-/* - returns the memory identifier, if creation succeeds */
-/* returns IpcMemCreationFailed, if failure */
-/****************************************************************************/
-IpcMemoryId
-IpcMemoryCreate(IpcMemoryKey memKey, uint32 size, int permission)
+/* ----------------------------------------------------------------
+ * Shared memory support
+ *
+ * These routines represent a fairly thin layer on top of SysV shared
+ * memory functionality.
+ * ----------------------------------------------------------------
+ */
+
+/* ----------------------------------------------------------------
+ * InternalIpcMemoryCreate(memKey, size, permission)
+ *
+ * Attempt to create a new shared memory segment with the specified key.
+ * Will fail (return NULL) if such a segment already exists. If successful,
+ * attach the segment to the current process and return its attached address.
+ * On success, callbacks are registered with on_shmem_exit to detach and
+ * delete the segment when on_shmem_exit is called.
+ *
+ * If we fail with a failure code other than collision-with-existing-segment,
+ * print out an error and abort. Other types of errors are not recoverable.
+ * ----------------------------------------------------------------
+ */
+static void *
+InternalIpcMemoryCreate(IpcMemoryKey memKey, uint32 size, int permission)
{
IpcMemoryId shmid;
+ void *memAddress;
- if (memKey == PrivateIPCKey)
- {
- /* private */
- shmid = PrivateMemoryCreate(memKey, size);
- }
- else
-
- shmid = shmget(memKey, size, IPC_CREAT | permission);
+ shmid = shmget(memKey, size, IPC_CREAT | IPC_EXCL | permission);
if (shmid < 0)
{
- fprintf(stderr, "IpcMemoryCreate: shmget(key=%d, size=%d, 0%o) failed: %s\n",
- (int)memKey, size, (unsigned)(IPC_CREAT|permission),
+ /*
+ * Fail quietly if error indicates a collision with existing segment.
+ * One would expect EEXIST, given that we said IPC_EXCL, but perhaps
+ * we could get a permission violation instead?
+ */
+ if (errno == EEXIST || errno == EACCES)
+ return NULL;
+ /*
+ * Else complain and abort
+ */
+ fprintf(stderr, "IpcMemoryCreate: shmget(key=%d, size=%u, 0%o) failed: %s\n",
+ (int) memKey, size, (IPC_CREAT | IPC_EXCL | permission),
strerror(errno));
if (errno == EINVAL)
@@ -547,7 +526,7 @@ IpcMemoryCreate(IpcMemoryKey memKey, uint32 size, int permission)
"\nThis error can be caused by one of three things:\n\n"
"1. The maximum size for shared memory segments on your system was\n"
" exceeded. You need to raise the SHMMAX parameter in your kernel\n"
- " to be at least %d bytes.\n\n"
+ " to be at least %u bytes.\n\n"
"2. The requested shared memory segment was too small for your system.\n"
" You need to lower the SHMMIN parameter in your kernel.\n\n"
"3. The requested shared memory segment already exists but is of the\n"
@@ -567,179 +546,302 @@ IpcMemoryCreate(IpcMemoryKey memKey, uint32 size, int permission)
"reached. The PostgreSQL Administrator's Guide contains more\n"
"information about shared memory configuration.\n\n");
- return IpcMemCreationFailed;
+ proc_exit(1);
}
+ /* Register on-exit routine to delete the new segment */
+ on_shmem_exit(IpcMemoryDelete, Int32GetDatum(shmid));
- /* if (memKey == PrivateIPCKey) */
- on_shmem_exit(IPCPrivateMemoryKill, (Datum) shmid);
+ /* OK, should be able to attach to the segment */
+ memAddress = shmat(shmid, 0, 0);
- return shmid;
-}
-
-/****************************************************************************/
-/* IpcMemoryIdGet(memKey, size) returns the shared memory Id */
-/* or IpcMemIdGetFailed */
-/****************************************************************************/
-IpcMemoryId
-IpcMemoryIdGet(IpcMemoryKey memKey, uint32 size)
-{
- IpcMemoryId shmid;
-
- shmid = shmget(memKey, size, 0);
-
- if (shmid < 0)
+ if (memAddress == (void *) -1)
{
- fprintf(stderr, "IpcMemoryIdGet: shmget(key=%d, size=%d, 0) failed: %s\n",
- memKey, size, strerror(errno));
- return IpcMemIdGetFailed;
+ fprintf(stderr, "IpcMemoryCreate: shmat(id=%d) failed: %s\n",
+ shmid, strerror(errno));
+ proc_exit(1);
}
- return shmid;
+ /* Register on-exit routine to detach new segment before deleting */
+ on_shmem_exit(IpcMemoryDetach, PointerGetDatum(memAddress));
+
+ return memAddress;
}
/****************************************************************************/
/* IpcMemoryDetach(status, shmaddr) removes a shared memory segment */
-/* from a backend address space */
-/* (only called by backends running under the postmaster) */
+/* from process' address spaceq */
+/* (called as an on_shmem_exit callback, hence funny argument list) */
/****************************************************************************/
static void
-IpcMemoryDetach(int status, char *shmaddr)
+IpcMemoryDetach(int status, Datum shmaddr)
{
- if (shmdt(shmaddr) < 0)
- elog(NOTICE, "IpcMemoryDetach: shmdt(0x%p) failed: %m", shmaddr);
+ if (shmdt(DatumGetPointer(shmaddr)) < 0)
+ fprintf(stderr, "IpcMemoryDetach: shmdt(%p) failed: %s\n",
+ DatumGetPointer(shmaddr), strerror(errno));
+ /* We used to report a failure via elog(NOTICE), but that's pretty
+ * pointless considering any client has long since disconnected ...
+ */
}
/****************************************************************************/
-/* IpcMemoryAttach(memId) returns the adress of shared memory */
-/* or IpcMemAttachFailed */
-/* */
-/* CALL IT: addr = (struct <MemoryStructure> *) IpcMemoryAttach(memId); */
-/* */
+/* IpcMemoryDelete(status, shmId) deletes a shared memory segment */
+/* (called as an on_shmem_exit callback, hence funny argument list) */
/****************************************************************************/
-char *
-IpcMemoryAttach(IpcMemoryId memId)
+static void
+IpcMemoryDelete(int status, Datum shmId)
{
- char *memAddress;
+ if (shmctl(DatumGetInt32(shmId), IPC_RMID, (struct shmid_ds *) NULL) < 0)
+ fprintf(stderr, "IpcMemoryDelete: shmctl(%d, %d, 0) failed: %s\n",
+ DatumGetInt32(shmId), IPC_RMID, strerror(errno));
+ /* We used to report a failure via elog(NOTICE), but that's pretty
+ * pointless considering any client has long since disconnected ...
+ */
+}
- if (UsePrivateMemory)
- memAddress = (char *) PrivateMemoryAttach(memId);
- else
- memAddress = (char *) shmat(memId, 0, 0);
+/* ----------------------------------------------------------------
+ * private memory support
+ *
+ * Rather than allocating shmem segments with IPC_PRIVATE key, we
+ * just malloc() the requested amount of space. This code emulates
+ * the needed shmem functions.
+ * ----------------------------------------------------------------
+ */
- /* if ( *memAddress == -1) { XXX ??? */
- if (memAddress == (char *) -1)
+static void *
+PrivateMemoryCreate(uint32 size)
+{
+ void *memAddress;
+
+ memAddress = malloc(size);
+ if (!memAddress)
{
- fprintf(stderr, "IpcMemoryAttach: shmat(id=%d) failed: %s\n",
- memId, strerror(errno));
- return IpcMemAttachFailed;
+ fprintf(stderr, "PrivateMemoryCreate: malloc(%u) failed\n", size);
+ proc_exit(1);
}
+ MemSet(memAddress, 0, size); /* keep Purify quiet */
- if (!UsePrivateMemory)
- on_shmem_exit(IpcMemoryDetach, PointerGetDatum(memAddress));
+ /* Register on-exit routine to release storage */
+ on_shmem_exit(PrivateMemoryDelete, PointerGetDatum(memAddress));
- return (char *) memAddress;
+ return memAddress;
}
-
-/****************************************************************************/
-/* IpcMemoryKill(memKey) removes a shared memory segment */
-/* (only called by the postmaster and standalone backends) */
-/****************************************************************************/
-void
-IpcMemoryKill(IpcMemoryKey memKey)
+static void
+PrivateMemoryDelete(int status, Datum memaddr)
{
- IpcMemoryId shmid;
-
- if (!UsePrivateMemory && (shmid = shmget(memKey, 0, 0)) >= 0)
- {
- if (shmctl(shmid, IPC_RMID, (struct shmid_ds *) NULL) < 0)
- {
- elog(NOTICE, "IpcMemoryKill: shmctl(%d, %d, 0) failed: %m",
- shmid, IPC_RMID);
- }
- }
+ free(DatumGetPointer(memaddr));
}
-#ifdef HAS_TEST_AND_SET
+
/* ------------------
- * use hardware locks to replace semaphores for sequent machines
- * to avoid costs of swapping processes and to provide unlimited
- * supply of locks.
+ * Routines to assign keys for new IPC objects
+ *
+ * The idea here is to detect and re-use keys that may have been assigned
+ * by a crashed postmaster or backend.
* ------------------
*/
-/* used in spin.c */
-SLock *SLockArray = NULL;
+static IpcMemoryKey NextShmemSegID = 0;
+static IpcSemaphoreKey NextSemaID = 0;
-static SLock **FreeSLockPP;
-static int *UnusedSLockIP;
-static slock_t *SLockMemoryLock;
-static IpcMemoryId SLockMemoryId = -1;
+/*
+ * (Re) initialize key assignment at startup of postmaster or standalone
+ * backend, also at postmaster reset.
+ */
+void
+IpcInitKeyAssignment(int port)
+{
+ NextShmemSegID = port * 1000;
+ NextSemaID = port * 1000;
+}
-struct ipcdummy
-{ /* to get alignment/size right */
- SLock *free;
- int unused;
- slock_t memlock;
- SLock slocks[MAX_SPINS + 1];
-};
+/*
+ * Create a shared memory segment of the given size and initialize its
+ * standard header. Dead Postgres segments are recycled if found,
+ * but we do not fail upon collision with non-Postgres shmem segments.
+ */
+PGShmemHeader *
+IpcMemoryCreate(uint32 size, bool private, int permission)
+{
+ void *memAddress;
+ PGShmemHeader *hdr;
-#define SLOCKMEMORYSIZE sizeof(struct ipcdummy)
+ /* Room for a header? */
+ Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
-void
-CreateAndInitSLockMemory(IPCKey key)
-{
- int id;
- SLock *slckP;
-
- SLockMemoryId = IpcMemoryCreate(key,
- SLOCKMEMORYSIZE,
- 0700);
- AttachSLockMemory(key);
- *FreeSLockPP = NULL;
- *UnusedSLockIP = (int) FIRSTFREELOCKID;
- for (id = 0; id < (int) FIRSTFREELOCKID; id++)
+ /* Loop till we find a free IPC key */
+ for (NextShmemSegID++ ; ; NextShmemSegID++)
{
- slckP = &(SLockArray[id]);
- S_INIT_LOCK(&(slckP->locklock));
- slckP->flag = NOLOCK;
- slckP->nshlocks = 0;
- S_INIT_LOCK(&(slckP->shlock));
- S_INIT_LOCK(&(slckP->exlock));
- S_INIT_LOCK(&(slckP->comlock));
- slckP->next = NULL;
+ IpcMemoryId shmid;
+
+ /* Special case if creating a private segment --- just malloc() it */
+ if (private)
+ {
+ memAddress = PrivateMemoryCreate(size);
+ break;
+ }
+
+ /* Try to create new segment */
+ memAddress = InternalIpcMemoryCreate(NextShmemSegID, size, permission);
+ if (memAddress)
+ break; /* successful create and attach */
+
+ /* See if it looks to be leftover from a dead Postgres process */
+ shmid = shmget(NextShmemSegID, sizeof(PGShmemHeader), 0);
+ if (shmid < 0)
+ continue; /* failed: must be some other app's */
+ memAddress = shmat(shmid, 0, 0);
+ if (memAddress == (void *) -1)
+ continue; /* failed: must be some other app's */
+ hdr = (PGShmemHeader *) memAddress;
+ if (hdr->magic != PGShmemMagic)
+ {
+ shmdt(memAddress);
+ continue; /* segment belongs to a non-Postgres app */
+ }
+ /*
+ * If the creator PID is my own PID or does not belong to any
+ * extant process, it's safe to zap it.
+ */
+ if (hdr->creatorPID != getpid())
+ {
+ if (kill(hdr->creatorPID, 0) == 0 ||
+ errno != ESRCH)
+ {
+ shmdt(memAddress);
+ continue; /* segment belongs to a live process */
+ }
+ }
+ /*
+ * The segment appears to be from a dead Postgres process, or
+ * from a previous cycle of life in this same process. Zap it,
+ * if possible. This probably shouldn't fail, but if it does,
+ * assume the segment belongs to someone else after all,
+ * and continue quietly.
+ */
+ shmdt(memAddress);
+ if (shmctl(shmid, IPC_RMID, (struct shmid_ds *) NULL) < 0)
+ continue;
+ /*
+ * Now try again to create the segment.
+ */
+ memAddress = InternalIpcMemoryCreate(NextShmemSegID, size, permission);
+ if (memAddress)
+ break; /* successful create and attach */
+ /*
+ * Can only get here if some other process managed to create the
+ * same shmem key before we did. Let him have that one,
+ * loop around to try next key.
+ */
}
- return;
-}
+ /*
+ * OK, we created a new segment. Mark it as created by this process.
+ * The order of assignments here is critical so that another Postgres
+ * process can't see the header as valid but belonging to an invalid
+ * PID!
+ */
+ hdr = (PGShmemHeader *) memAddress;
+ hdr->creatorPID = getpid();
+ hdr->magic = PGShmemMagic;
+ /*
+ * Initialize space allocation status for segment.
+ */
+ hdr->totalsize = size;
+ hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
-void
-AttachSLockMemory(IPCKey key)
-{
- struct ipcdummy *slockM;
-
- if (SLockMemoryId == -1)
- SLockMemoryId = IpcMemoryIdGet(key, SLOCKMEMORYSIZE);
- if (SLockMemoryId == -1)
- elog(FATAL, "SLockMemory not in shared memory");
- slockM = (struct ipcdummy *) IpcMemoryAttach(SLockMemoryId);
- if (slockM == IpcMemAttachFailed)
- elog(FATAL, "AttachSLockMemory: could not attach segment");
- FreeSLockPP = (SLock **) &(slockM->free);
- UnusedSLockIP = (int *) &(slockM->unused);
- SLockMemoryLock = (slock_t *) &(slockM->memlock);
- S_INIT_LOCK(SLockMemoryLock);
- SLockArray = (SLock *) &(slockM->slocks[0]);
- return;
+ return hdr;
}
-#ifdef NOT_USED
-bool
-LockIsFree(int lockid)
+/*
+ * Create a semaphore set with the given number of useful semaphores
+ * (an additional sema is actually allocated to serve as identifier).
+ * Dead Postgres sema sets are recycled if found, but we do not fail
+ * upon collision with non-Postgres sema sets.
+ */
+IpcSemaphoreId
+IpcSemaphoreCreate(int numSems, int permission,
+ int semStartValue, bool removeOnExit)
{
- return SLockArray[lockid].flag == NOLOCK;
-}
+ IpcSemaphoreId semId;
+ union semun semun;
-#endif
+ /* Loop till we find a free IPC key */
+ for (NextSemaID++ ; ; NextSemaID++)
+ {
+ pid_t creatorPID;
+
+ /* Try to create new semaphore set */
+ semId = InternalIpcSemaphoreCreate(NextSemaID, numSems+1,
+ permission, semStartValue,
+ removeOnExit);
+ if (semId >= 0)
+ break; /* successful create */
-#endif /* HAS_TEST_AND_SET */
+ /* See if it looks to be leftover from a dead Postgres process */
+ semId = semget(NextSemaID, numSems+1, 0);
+ if (semId < 0)
+ continue; /* failed: must be some other app's */
+ if (IpcSemaphoreGetValue(semId, numSems) != PGSemaMagic)
+ continue; /* sema belongs to a non-Postgres app */
+ /*
+ * If the creator PID is my own PID or does not belong to any
+ * extant process, it's safe to zap it.
+ */
+ creatorPID = IpcSemaphoreGetLastPID(semId, numSems);
+ if (creatorPID <= 0)
+ continue; /* oops, GETPID failed */
+ if (creatorPID != getpid())
+ {
+ if (kill(creatorPID, 0) == 0 ||
+ errno != ESRCH)
+ continue; /* sema belongs to a live process */
+ }
+ /*
+ * The sema set appears to be from a dead Postgres process, or
+ * from a previous cycle of life in this same process. Zap it,
+ * if possible. This probably shouldn't fail, but if it does,
+ * assume the sema set belongs to someone else after all,
+ * and continue quietly.
+ */
+ semun.val = 0; /* unused, but keep compiler quiet */
+ if (semctl(semId, 0, IPC_RMID, semun) < 0)
+ continue;
+ /*
+ * Now try again to create the sema set.
+ */
+ semId = InternalIpcSemaphoreCreate(NextSemaID, numSems+1,
+ permission, semStartValue,
+ removeOnExit);
+ if (semId >= 0)
+ break; /* successful create */
+ /*
+ * Can only get here if some other process managed to create the
+ * same sema key before we did. Let him have that one,
+ * loop around to try next key.
+ */
+ }
+ /*
+ * OK, we created a new sema set. Mark it as created by this process.
+ * We do this by setting the spare semaphore to PGSemaMagic-1 and then
+ * incrementing it with semop(). That leaves it with value PGSemaMagic
+ * and sempid referencing this process.
+ */
+ semun.val = PGSemaMagic-1;
+ if (semctl(semId, numSems, SETVAL, semun) < 0)
+ {
+ fprintf(stderr, "IpcSemaphoreCreate: semctl(id=%d, %d, SETVAL, %d) failed: %s\n",
+ semId, numSems, PGSemaMagic-1, strerror(errno));
+
+ if (errno == ERANGE)
+ fprintf(stderr,
+ "You possibly need to raise your kernel's SEMVMX value to be at least\n"
+ "%d. Look into the PostgreSQL documentation for details.\n",
+ PGSemaMagic);
+
+ proc_exit(1);
+ }
+ IpcSemaphoreUnlock(semId, numSems);
+
+ return semId;
+}
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 5c7e88af73e..7a5813df57d 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -8,148 +8,91 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/storage/ipc/ipci.c,v 1.34 2000/11/21 21:16:01 petere Exp $
+ * $Header: /cvsroot/pgsql/src/backend/storage/ipc/ipci.c,v 1.35 2000/11/28 23:27:56 tgl Exp $
*
*-------------------------------------------------------------------------
*/
-#include <sys/types.h>
-
#include "postgres.h"
+#include <sys/types.h>
+
#include "miscadmin.h"
#include "access/xlog.h"
#include "storage/bufmgr.h"
+#include "storage/proc.h"
#include "storage/sinval.h"
+#include "storage/spin.h"
-/*
- * SystemPortAddressCreateMemoryKey
- * Returns a memory key given a port address.
- */
-IPCKey
-SystemPortAddressCreateIPCKey(SystemPortAddress address)
-{
- Assert(address < 32768); /* XXX */
-
- return SystemPortAddressGetIPCKey(address);
-}
/*
* 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 "private" is true then we only need private memory, not shared
+ * memory. This is true for a standalone backend, false for a postmaster.
*/
-/**************************************************
-
- CreateSharedMemoryAndSemaphores
- is called exactly *ONCE* by the postmaster.
- It is *NEVER* called by the postgres backend,
- except in the case of a standalone backend.
-
- 0) destroy any existing semaphores for both buffer
- and lock managers.
- 1) create the appropriate *SHARED* memory segments
- for the two resource managers.
- 2) create shared semaphores as needed.
-
- **************************************************/
-
void
-CreateSharedMemoryAndSemaphores(IPCKey key, int maxBackends)
+CreateSharedMemoryAndSemaphores(bool private, int maxBackends)
{
int size;
-
-#ifdef HAS_TEST_AND_SET
-
- /*
- * Create shared memory for slocks
- */
- CreateAndInitSLockMemory(IPCKeyGetSLockSharedMemoryKey(key));
-#endif
-
- /*
- * Kill and create the buffer manager buffer pool (and semaphore)
- */
- CreateSpinlocks(IPCKeyGetSpinLockSemaphoreKey(key));
+ PGShmemHeader *seghdr;
/*
- * Size of the primary shared-memory block is estimated via
+ * 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() + LockShmemSize(maxBackends) + XLOGShmemSize();
+ size = BufferShmemSize() + LockShmemSize(maxBackends) +
+ XLOGShmemSize() + SLockShmemSize() + SInvalShmemSize(maxBackends);
#ifdef STABLE_MEMORY_STORAGE
size += MMShmemSize();
#endif
size += 100000;
- /* might as well round it off to a multiple of a K or so... */
- size += 1024 - (size % 1024);
+ /* might as well round it off to a multiple of a typical page size */
+ size += 8192 - (size % 8192);
if (DebugLvl > 1)
- {
- fprintf(stderr, "binding ShmemCreate(key=%x, size=%d)\n",
- IPCKeyGetBufferMemoryKey(key), size);
- }
- ShmemCreate(IPCKeyGetBufferMemoryKey(key), size);
- ShmemIndexReset();
- InitShmem(key, size);
- XLOGShmemInit();
- InitBufferPool(key);
+ fprintf(stderr, "invoking IpcMemoryCreate(size=%d)\n", size);
- /* ----------------
- * do the lock table stuff
- * ----------------
+ /*
+ * Create the shmem segment
*/
- InitLocks();
- if (InitLockTable() == INVALID_TABLEID)
- elog(FATAL, "Couldn't create the lock table");
+ seghdr = IpcMemoryCreate(size, private, IPCProtection);
- /* ----------------
- * do process table stuff
- * ----------------
+ /*
+ * First initialize spinlocks --- needed by InitShmemAllocation()
*/
- InitProcGlobal(key, maxBackends);
-
- CreateSharedInvalidationState(key, maxBackends);
-}
-
+ CreateSpinlocks(seghdr);
-/*
- * AttachSharedMemoryAndSemaphores
- * Attachs existant shared memory and semaphores.
- */
-void
-AttachSharedMemoryAndSemaphores(IPCKey key)
-{
- /* ----------------
- * create rather than attach if using private key
- * ----------------
+ /*
+ * Set up shmem.c hashtable
*/
- if (key == PrivateIPCKey)
- {
- CreateSharedMemoryAndSemaphores(key, 16);
- return;
- }
+ InitShmemAllocation(seghdr);
-#ifdef HAS_TEST_AND_SET
- /* ----------------
- * attach the slock shared memory
- * ----------------
- */
- AttachSLockMemory(IPCKeyGetSLockSharedMemoryKey(key));
-#endif
- /* ----------------
- * attach the buffer manager buffer pool (and semaphore)
- * ----------------
+ /*
+ * Set up xlog and buffers
*/
- InitShmem(key, 0);
- InitBufferPool(key);
+ XLOGShmemInit();
+ InitBufferPool();
- /* ----------------
- * initialize lock table stuff
- * ----------------
+ /*
+ * Set up lock manager
*/
InitLocks();
if (InitLockTable() == INVALID_TABLEID)
- elog(FATAL, "Couldn't attach to the lock table");
+ elog(FATAL, "Couldn't create the lock table");
+
+ /*
+ * Set up process table
+ */
+ InitProcGlobal(maxBackends);
- AttachSharedInvalidationState(key);
+ /*
+ * Set up shared-inval messaging
+ */
+ CreateSharedInvalidationState(maxBackends);
}
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index c5048a389b1..15922947087 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -8,14 +8,14 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/storage/ipc/shmem.c,v 1.54 2000/11/21 21:16:01 petere Exp $
+ * $Header: /cvsroot/pgsql/src/backend/storage/ipc/shmem.c,v 1.55 2000/11/28 23:27:56 tgl Exp $
*
*-------------------------------------------------------------------------
*/
/*
* POSTGRES processes share one or more regions of shared memory.
* The shared memory is created by a postmaster and is inherited
- * by each backends via fork(). The routines in this file are used for
+ * by each backend via fork(). The routines in this file are used for
* allocating and binding to shared memory data structures.
*
* NOTES:
@@ -56,153 +56,57 @@
*
* See InitSem() in sem.c for an example of how to use the
* shmem index.
- *
*/
#include "postgres.h"
+
#include "access/transam.h"
#include "utils/tqual.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) */
+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 */
SPINLOCK ShmemLock; /* lock for shared memory allocation */
SPINLOCK ShmemIndexLock; /* lock for shmem index access */
-static unsigned long *ShmemFreeStart = NULL; /* pointer to the OFFSET
- * of first free shared
- * memory */
-static unsigned long *ShmemIndexOffset = NULL; /* start of the shmem
- * index table (for
- * bootstrap) */
-static int ShmemBootstrap = FALSE; /* flag becomes true when shared
- * mem is created by POSTMASTER */
-
-static HTAB *ShmemIndex = NULL;
-
-/* ---------------------
- * ShmemIndexReset() - Resets the shmem index to NULL....
- * useful when the postmaster destroys existing shared memory
- * and creates all new segments after a backend crash.
- * ----------------------
- */
-void
-ShmemIndexReset(void)
-{
- ShmemIndex = (HTAB *) NULL;
-}
+static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
-/*
- * 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;
+static bool ShmemBootstrap = false; /* bootstrapping shmem index? */
-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.
- *
+ * InitShmemAllocation() --- set up shared-memory allocation and index table.
*/
-int
-InitShmem(unsigned int key, unsigned int size)
+void
+InitShmemAllocation(PGShmemHeader *seghdr)
{
- Pointer sharedRegion;
- unsigned long currFreeSpace;
-
HASHCTL info;
int hash_flags;
ShmemIndexEnt *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;
-
- /* First long in shared memory is the available-space pointer */
- ShmemFreeStart = (unsigned long *) ShmemBase;
- /* next is a shmem pointer to the shmem index */
- ShmemIndexOffset = ShmemFreeStart + 1;
- /* next is ShmemVariableCache */
- ShmemVariableCache = (VariableCache) (ShmemIndexOffset + 1);
-
- /* here is where to start dynamic allocation */
- currFreeSpace = MAXALIGN(sizeof(*ShmemFreeStart) +
- sizeof(*ShmemIndexOffset) +
- sizeof(*ShmemVariableCache));
- /*
- * bootstrap initialize spin locks so we can start to use the
- * allocator and shmem index.
- */
- InitSpinLocks();
+ /* Set up basic pointers to shared memory */
+ ShmemSegHdr = seghdr;
+ ShmemBase = (SHMEM_OFFSET) seghdr;
+ ShmemEnd = ShmemBase + seghdr->totalsize;
/*
- * We have just allocated additional space for two spinlocks. Now
- * setup the global free space count
+ * 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.
*/
- if (ShmemBootstrap)
- {
- *ShmemFreeStart = currFreeSpace;
- memset(ShmemVariableCache, 0, sizeof(*ShmemVariableCache));
- }
-
- /* if ShmemFreeStart is NULL, then the allocator won't work */
- Assert(*ShmemFreeStart);
+ ShmemIndex = (HTAB *) NULL;
+ ShmemBootstrap = true;
- /* create OR attach to the shared memory shmem index */
+ /* create the shared memory shmem index */
info.keysize = SHMEM_INDEX_KEYSIZE;
info.datasize = SHMEM_INDEX_DATASIZE;
hash_flags = HASH_ELEM;
@@ -211,60 +115,43 @@ InitShmem(unsigned int key, unsigned int size)
ShmemIndex = ShmemInitHash("ShmemIndex",
SHMEM_INDEX_SIZE, SHMEM_INDEX_SIZE,
&info, hash_flags);
-
if (!ShmemIndex)
- {
- elog(FATAL, "InitShmem: couldn't initialize Shmem Index");
- return FALSE;
- }
+ elog(FATAL, "InitShmemAllocation: couldn't initialize Shmem Index");
/*
- * Now, check the shmem index for an entry to the shmem index. If
- * there is an entry there, someone else created the table. Otherwise,
- * we did and we have to initialize it.
+ * 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, (char *) &item, HASH_ENTER, &found);
-
-
if (!result)
- {
- elog(FATAL, "InitShmem: corrupted shmem index");
- return FALSE;
- }
-
- if (!found)
- {
+ elog(FATAL, "InitShmemAllocation: corrupted shmem index");
- /*
- * bootstrapping shmem: we have to initialize the shmem index now.
- */
+ Assert(ShmemBootstrap && !found);
- Assert(ShmemBootstrap);
- result->location = MAKE_OFFSET(ShmemIndex->hctl);
- *ShmemIndexOffset = result->location;
- result->size = SHMEM_INDEX_SIZE;
+ result->location = MAKE_OFFSET(ShmemIndex->hctl);
+ result->size = SHMEM_INDEX_SIZE;
- ShmemBootstrap = FALSE;
+ ShmemBootstrap = false;
- }
- else
- Assert(!ShmemBootstrap);
- /* now release the lock acquired in ShmemHashInit */
+ /* now release the lock acquired in ShmemInitStruct */
SpinRelease(ShmemIndexLock);
- Assert(result->location == MAKE_OFFSET(ShmemIndex->hctl));
-
- return TRUE;
+ /*
+ * Initialize ShmemVariableCache for transaction manager.
+ */
+ ShmemVariableCache = (VariableCache)
+ ShmemAlloc(sizeof(*ShmemVariableCache));
+ memset(ShmemVariableCache, 0, sizeof(*ShmemVariableCache));
}
/*
- * ShmemAlloc -- allocate max-aligned byte string from shared memory
+ * ShmemAlloc -- allocate max-aligned chunk from shared memory
+ *
+ * Assumes ShmemLock and ShmemSegHdr are initialized.
*
- * 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 compatible with malloc().
@@ -272,7 +159,7 @@ InitShmem(unsigned int key, unsigned int size)
void *
ShmemAlloc(Size size)
{
- unsigned long tmpFree;
+ uint32 newFree;
void *newSpace;
/*
@@ -280,15 +167,15 @@ ShmemAlloc(Size size)
*/
size = MAXALIGN(size);
- Assert(*ShmemFreeStart);
+ Assert(ShmemSegHdr);
SpinAcquire(ShmemLock);
- tmpFree = *ShmemFreeStart + size;
- if (tmpFree <= ShmemSize)
+ newFree = ShmemSegHdr->freeoffset + size;
+ if (newFree <= ShmemSegHdr->totalsize)
{
- newSpace = (void *) MAKE_PTR(*ShmemFreeStart);
- *ShmemFreeStart += size;
+ newSpace = (void *) MAKE_PTR(ShmemSegHdr->freeoffset);
+ ShmemSegHdr->freeoffset = newFree;
}
else
newSpace = NULL;
@@ -306,7 +193,7 @@ ShmemAlloc(Size size)
*
* Returns TRUE if the pointer is valid.
*/
-int
+bool
ShmemIsValid(unsigned long addr)
{
return (addr < ShmemEnd) && (addr >= ShmemBase);
@@ -394,16 +281,15 @@ ShmemPIDLookup(int pid, SHMEM_OFFSET *locationPtr)
sprintf(item.key, "PID %d", pid);
SpinAcquire(ShmemIndexLock);
+
result = (ShmemIndexEnt *)
hash_search(ShmemIndex, (char *) &item, HASH_ENTER, &found);
if (!result)
{
-
SpinRelease(ShmemIndexLock);
elog(ERROR, "ShmemInitPID: ShmemIndex corrupted");
return FALSE;
-
}
if (found)
@@ -438,19 +324,19 @@ ShmemPIDDestroy(int pid)
sprintf(item.key, "PID %d", pid);
SpinAcquire(ShmemIndexLock);
+
result = (ShmemIndexEnt *)
hash_search(ShmemIndex, (char *) &item, HASH_REMOVE, &found);
if (found)
location = result->location;
+
SpinRelease(ShmemIndexLock);
if (!result)
{
-
elog(ERROR, "ShmemPIDDestroy: PID table corrupted");
return INVALID_OFFSET;
-
}
if (found)
@@ -487,53 +373,31 @@ ShmemInitStruct(char *name, Size size, bool *foundPtr)
if (!ShmemIndex)
{
-#ifdef USE_ASSERT_CHECKING
- char *strname = "ShmemIndex";
-
-#endif
-
/*
- * If the shmem index doesn't exist, we fake it.
+ * If the shmem index doesn't exist, we are bootstrapping: we must
+ * be trying to init the shmem index itself.
*
- * If we are creating the first shmem index, then let shmemalloc()
- * allocate the space for a new HTAB. Otherwise, find the old one
- * and return that. Notice that the ShmemIndexLock is held until
- * the shmem index has been completely initialized.
+ * Notice that the ShmemIndexLock is held until the shmem index has
+ * been completely initialized.
*/
- Assert(strcmp(name, strname) == 0);
- if (ShmemBootstrap)
- {
- /* in POSTMASTER/Single process */
-
- *foundPtr = FALSE;
- return ShmemAlloc(size);
- }
- else
- {
- Assert(*ShmemIndexOffset);
-
- *foundPtr = TRUE;
- return (void *) MAKE_PTR(*ShmemIndexOffset);
- }
-
-
- }
- else
- {
- /* look it up in the shmem index */
- result = (ShmemIndexEnt *)
- hash_search(ShmemIndex, (char *) &item, HASH_ENTER, foundPtr);
+ Assert(strcmp(name, "ShmemIndex") == 0);
+ Assert(ShmemBootstrap);
+ *foundPtr = FALSE;
+ return ShmemAlloc(size);
}
+ /* look it up in the shmem index */
+ result = (ShmemIndexEnt *)
+ hash_search(ShmemIndex, (char *) &item, HASH_ENTER, foundPtr);
+
if (!result)
{
SpinRelease(ShmemIndexLock);
-
elog(ERROR, "ShmemInitStruct: Shmem Index corrupted");
return NULL;
-
}
- else if (*foundPtr)
+
+ if (*foundPtr)
{
/*
diff --git a/src/backend/storage/ipc/sinval.c b/src/backend/storage/ipc/sinval.c
index fb2e4804dd3..c87fcd36602 100644
--- a/src/backend/storage/ipc/sinval.c
+++ b/src/backend/storage/ipc/sinval.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/storage/ipc/sinval.c,v 1.23 2000/11/12 20:51:51 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/storage/ipc/sinval.c,v 1.24 2000/11/28 23:27:56 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -27,52 +27,23 @@
SPINLOCK SInvalLock = (SPINLOCK) NULL;
/****************************************************************************/
-/* CreateSharedInvalidationState() Create a buffer segment */
+/* CreateSharedInvalidationState() Initialize SI buffer */
/* */
/* should be called only by the POSTMASTER */
/****************************************************************************/
void
-CreateSharedInvalidationState(IPCKey key, int maxBackends)
+CreateSharedInvalidationState(int maxBackends)
{
- int status;
-
- /* SInvalLock gets set in spin.c, during spinlock init */
- status = SISegmentInit(true, IPCKeyGetSIBufferMemoryBlock(key),
- maxBackends);
-
- if (status == -1)
- elog(FATAL, "CreateSharedInvalidationState: failed segment init");
-}
-
-/****************************************************************************/
-/* AttachSharedInvalidationState(key) Attach to existing buffer segment */
-/* */
-/* should be called by each backend during startup */
-/****************************************************************************/
-void
-AttachSharedInvalidationState(IPCKey key)
-{
- int status;
-
- if (key == PrivateIPCKey)
- {
- CreateSharedInvalidationState(key, 16);
- return;
- }
- /* SInvalLock gets set in spin.c, during spinlock init */
- status = SISegmentInit(false, IPCKeyGetSIBufferMemoryBlock(key), 0);
-
- if (status == -1)
- elog(FATAL, "AttachSharedInvalidationState: failed segment init");
+ /* SInvalLock must be initialized already, during spinlock init */
+ SIBufferInit(maxBackends);
}
/*
- * InitSharedInvalidationState
+ * InitBackendSharedInvalidationState
* Initialize new backend's state info in buffer segment.
- * Must be called after AttachSharedInvalidationState().
*/
void
-InitSharedInvalidationState(void)
+InitBackendSharedInvalidationState(void)
{
SpinAcquire(SInvalLock);
if (!SIBackendInit(shmInvalBuffer))
diff --git a/src/backend/storage/ipc/sinvaladt.c b/src/backend/storage/ipc/sinvaladt.c
index f4b29983433..c7612759793 100644
--- a/src/backend/storage/ipc/sinvaladt.c
+++ b/src/backend/storage/ipc/sinvaladt.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/storage/ipc/sinvaladt.c,v 1.35 2000/11/12 20:51:51 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/storage/ipc/sinvaladt.c,v 1.36 2000/11/28 23:27:56 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -25,95 +25,38 @@
SISeg *shmInvalBuffer;
-static void SISegmentAttach(IpcMemoryId shmid);
-static void SISegInit(SISeg *segP, int maxBackends);
static void CleanupInvalidationState(int status, Datum arg);
static void SISetProcStateInvalid(SISeg *segP);
-/*
- * SISegmentInit
- * Create a new SI memory segment, or attach to an existing one
- *
- * This is called with createNewSegment = true by the postmaster (or by
- * a standalone backend), and subsequently with createNewSegment = false
- * by backends started by the postmaster.
- *
- * Note: maxBackends param is only valid when createNewSegment is true
- */
-int
-SISegmentInit(bool createNewSegment, IPCKey key, int maxBackends)
-{
- int segSize;
- IpcMemoryId shmId;
-
- if (createNewSegment)
- {
- /* Kill existing segment, if any */
- IpcMemoryKill(key);
-
- /*
- * Figure space needed. Note sizeof(SISeg) includes the first
- * ProcState entry.
- */
- segSize = sizeof(SISeg) + sizeof(ProcState) * (maxBackends - 1);
-
- /* Get a shared segment */
- shmId = IpcMemoryCreate(key, segSize, IPCProtection);
- if (shmId < 0)
- {
- perror("SISegmentInit: segment create failed");
- return -1; /* an error */
- }
-
- /* Attach to the shared cache invalidation segment */
- /* sets the global variable shmInvalBuffer */
- SISegmentAttach(shmId);
-
- /* Init shared memory contents */
- SISegInit(shmInvalBuffer, maxBackends);
- }
- else
- {
- /* find existing segment */
- shmId = IpcMemoryIdGet(key, 0);
- if (shmId < 0)
- {
- perror("SISegmentInit: segment get failed");
- return -1; /* an error */
- }
-
- /* Attach to the shared cache invalidation segment */
- /* sets the global variable shmInvalBuffer */
- SISegmentAttach(shmId);
- }
- return 1;
-}
/*
- * SISegmentAttach
- * Attach to specified shared memory segment
+ * SInvalShmemSize --- return shared-memory space needed
*/
-static void
-SISegmentAttach(IpcMemoryId shmid)
+int
+SInvalShmemSize(int maxBackends)
{
- shmInvalBuffer = (SISeg *) IpcMemoryAttach(shmid);
-
- if (shmInvalBuffer == IpcMemAttachFailed)
- {
- /* XXX use validity function */
- elog(FATAL, "SISegmentAttach: Could not attach segment: %m");
- }
+ /*
+ * Figure space needed. Note sizeof(SISeg) includes the first
+ * ProcState entry.
+ */
+ return sizeof(SISeg) + sizeof(ProcState) * (maxBackends - 1);
}
/*
- * SISegInit
- * Initialize contents of a new shared memory sinval segment
+ * SIBufferInit
+ * Create and initialize a new SI message buffer
*/
-static void
-SISegInit(SISeg *segP, int maxBackends)
+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;
diff --git a/src/backend/storage/ipc/spin.c b/src/backend/storage/ipc/spin.c
index 674ee06a9a3..a93ae69e032 100644
--- a/src/backend/storage/ipc/spin.c
+++ b/src/backend/storage/ipc/spin.c
@@ -3,31 +3,24 @@
* spin.c
* routines for managing spin locks
*
+ * POSTGRES has two kinds of locks: semaphores (which put the
+ * process to sleep) and spinlocks (which are supposed to be
+ * short term locks). Spinlocks are implemented via test-and-set (TAS)
+ * instructions if possible, else via semaphores. The semaphore method
+ * is too slow to be useful :-(
+ *
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/storage/ipc/Attic/spin.c,v 1.25 2000/05/31 00:28:29 petere Exp $
+ * $Header: /cvsroot/pgsql/src/backend/storage/ipc/Attic/spin.c,v 1.26 2000/11/28 23:27:56 tgl Exp $
*
*-------------------------------------------------------------------------
*/
-/*
- * POSTGRES has two kinds of locks: semaphores (which put the
- * process to sleep) and spinlocks (which are supposed to be
- * short term locks). Currently both are implemented as SysV
- * semaphores, but presumably this can change if we move to
- * a machine with a test-and-set (TAS) instruction. Its probably
- * a good idea to think about (and allocate) short term and long
- * term semaphores separately anyway.
- *
- * NOTE: These routines are not supposed to be widely used in Postgres.
- * They are preserved solely for the purpose of porting Mark Sullivan's
- * buffer manager to Postgres.
- */
-#include <errno.h>
#include "postgres.h"
+#include <errno.h>
#ifndef HAS_TEST_AND_SET
#include <sys/sem.h>
#endif
@@ -35,39 +28,33 @@
#include "storage/proc.h"
#include "storage/s_lock.h"
-
-/* globals used in this file */
-IpcSemaphoreId SpinLockId;
-
-#ifdef HAS_TEST_AND_SET
-/* real spin lock implementations */
-
-void
-CreateSpinlocks(IPCKey key)
-{
- /* the spin lock shared memory must have been created by now */
- return;
-}
-
-void
-InitSpinLocks(void)
-{
- extern SPINLOCK ShmemLock;
- extern SPINLOCK ShmemIndexLock;
- extern SPINLOCK BufMgrLock;
- extern SPINLOCK LockMgrLock;
- extern SPINLOCK ProcStructLock;
- extern SPINLOCK SInvalLock;
- extern SPINLOCK OidGenLockId;
- extern SPINLOCK XidGenLockId;
- extern SPINLOCK ControlFileLockId;
+/* Probably should move these to an appropriate header file */
+extern SPINLOCK ShmemLock;
+extern SPINLOCK ShmemIndexLock;
+extern SPINLOCK BufMgrLock;
+extern SPINLOCK LockMgrLock;
+extern SPINLOCK ProcStructLock;
+extern SPINLOCK SInvalLock;
+extern SPINLOCK OidGenLockId;
+extern SPINLOCK XidGenLockId;
+extern SPINLOCK ControlFileLockId;
#ifdef STABLE_MEMORY_STORAGE
- extern SPINLOCK MMCacheLock;
+extern SPINLOCK MMCacheLock;
#endif
- /* These six spinlocks have fixed location is shmem */
+
+/*
+ * Initialize identifiers for permanent spinlocks during startup
+ *
+ * The same identifiers are used for both TAS and semaphore implementations,
+ * although in one case they are indexes into a shmem array and in the other
+ * they are semaphore numbers.
+ */
+static void
+InitSpinLockIDs(void)
+{
ShmemLock = (SPINLOCK) SHMEMLOCKID;
ShmemIndexLock = (SPINLOCK) SHMEMINDEXLOCKID;
BufMgrLock = (SPINLOCK) BUFMGRLOCKID;
@@ -81,11 +68,18 @@ InitSpinLocks(void)
#ifdef STABLE_MEMORY_STORAGE
MMCacheLock = (SPINLOCK) MMCACHELOCKID;
#endif
-
- return;
}
+#ifdef HAS_TEST_AND_SET
+
+/* real spin lock implementation */
+
+typedef struct slock
+{
+ slock_t shlock;
+} SLock;
+
#ifdef LOCK_DEBUG
bool Trace_spinlocks = false;
@@ -93,193 +87,268 @@ inline static void
PRINT_SLDEBUG(const char * where, SPINLOCK lockid, const SLock * lock)
{
if (Trace_spinlocks)
- elog(DEBUG,
- "%s: id=%d (locklock=%d, flag=%d, nshlocks=%d, shlock=%d, exlock=%d)",
- where, lockid,
- lock->locklock, lock->flag, lock->nshlocks, lock->shlock, lock->exlock);
+ elog(DEBUG, "%s: id=%d", where, lockid);
}
#else /* not LOCK_DEBUG */
#define PRINT_SLDEBUG(a,b,c)
#endif /* not LOCK_DEBUG */
-/* from ipc.c */
-extern SLock *SLockArray;
+static SLock *SLockArray = NULL;
+
+#define SLOCKMEMORYSIZE ((int) MAX_SPINS * sizeof(SLock))
+
+/*
+ * SLockShmemSize --- return shared-memory space needed
+ */
+int
+SLockShmemSize(void)
+{
+ return MAXALIGN(SLOCKMEMORYSIZE);
+}
+
+/*
+ * CreateSpinlocks --- create and initialize spinlocks during startup
+ */
+void
+CreateSpinlocks(PGShmemHeader *seghdr)
+{
+ int id;
+
+ /*
+ * We must allocate the space "by hand" because shmem.c isn't up yet
+ */
+ SLockArray = (SLock *) (((char *) seghdr) + seghdr->freeoffset);
+ seghdr->freeoffset += MAXALIGN(SLOCKMEMORYSIZE);
+ Assert(seghdr->freeoffset <= seghdr->totalsize);
+
+ /*
+ * Initialize all spinlocks to "unlocked" state
+ */
+ for (id = 0; id < (int) MAX_SPINS; id++)
+ {
+ SLock *slckP = &(SLockArray[id]);
+
+ S_INIT_LOCK(&(slckP->shlock));
+ }
+
+ /*
+ * Assign indexes for fixed spinlocks
+ */
+ InitSpinLockIDs();
+}
void
SpinAcquire(SPINLOCK lockid)
{
- SLock *slckP;
+ SLock *slckP = &(SLockArray[lockid]);
- /* This used to be in ipc.c, but move here to reduce function calls */
- slckP = &(SLockArray[lockid]);
PRINT_SLDEBUG("SpinAcquire", lockid, slckP);
-ex_try_again:
- S_LOCK(&(slckP->locklock));
- switch (slckP->flag)
- {
- case NOLOCK:
- slckP->flag = EXCLUSIVELOCK;
- S_LOCK(&(slckP->exlock));
- S_LOCK(&(slckP->shlock));
- S_UNLOCK(&(slckP->locklock));
- PRINT_SLDEBUG("OUT", lockid, slckP);
- break;
- case SHAREDLOCK:
- case EXCLUSIVELOCK:
- S_UNLOCK(&(slckP->locklock));
- S_LOCK(&(slckP->exlock));
- S_UNLOCK(&(slckP->exlock));
- goto ex_try_again;
- }
+ S_LOCK(&(slckP->shlock));
PROC_INCR_SLOCK(lockid);
- PRINT_SLDEBUG("SpinAcquire/success", lockid, slckP);
+ PRINT_SLDEBUG("SpinAcquire/done", lockid, slckP);
}
void
SpinRelease(SPINLOCK lockid)
{
- SLock *slckP;
-
- /* This used to be in ipc.c, but move here to reduce function calls */
- slckP = &(SLockArray[lockid]);
+ SLock *slckP = &(SLockArray[lockid]);
/*
* Check that we are actually holding the lock we are releasing. This
* can be done only after MyProc has been initialized.
*/
Assert(!MyProc || MyProc->sLocks[lockid] > 0);
- Assert(slckP->flag != NOLOCK);
-
PROC_DECR_SLOCK(lockid);
PRINT_SLDEBUG("SpinRelease", lockid, slckP);
- S_LOCK(&(slckP->locklock));
- /* -------------
- * give favor to read processes
- * -------------
+ S_UNLOCK(&(slckP->shlock));
+ PRINT_SLDEBUG("SpinRelease/done", lockid, slckP);
+}
+
+#else /* !HAS_TEST_AND_SET */
+
+/*
+ * No TAS, so spinlocks are implemented using SysV semaphores.
+ *
+ * We support two slightly different APIs here: SpinAcquire/SpinRelease
+ * work with SPINLOCK integer indexes for the permanent spinlocks, which
+ * are all assumed to live in the first spinlock semaphore set. There
+ * is also an emulation of the s_lock.h TAS-spinlock macros; for that case,
+ * typedef slock_t stores the semId and sem number of the sema to use.
+ * The semas needed are created by CreateSpinlocks and doled out by
+ * s_init_lock_sema.
+ *
+ * Since many systems have a rather small SEMMSL limit on semas per set,
+ * we allocate the semaphores required in sets of SPINLOCKS_PER_SET semas.
+ * This value is deliberately made equal to PROC_NSEMS_PER_SET so that all
+ * sema sets allocated by Postgres will be the same size; that eases the
+ * semaphore-recycling logic in IpcSemaphoreCreate().
+ *
+ * Note that the SpinLockIds array is not in shared memory; it is filled
+ * by the postmaster and then inherited through fork() by backends. This
+ * is OK because its contents do not change after system startup.
+ */
+
+#define SPINLOCKS_PER_SET PROC_NSEMS_PER_SET
+
+static IpcSemaphoreId *SpinLockIds = NULL;
+
+static int numSpinSets = 0; /* number of sema sets used */
+static int numSpinLocks = 0; /* total number of semas allocated */
+static int nextSpinLock = 0; /* next free spinlock index */
+
+static void SpinFreeAllSemaphores(void);
+
+/*
+ * SLockShmemSize --- return shared-memory space needed
+ */
+int
+SLockShmemSize(void)
+{
+ return 0;
+}
+
+/*
+ * CreateSpinlocks --- create and initialize spinlocks during startup
+ */
+void
+CreateSpinlocks(PGShmemHeader *seghdr)
+{
+ int i;
+
+ if (SpinLockIds == NULL)
+ {
+ /*
+ * Compute number of spinlocks needed. If this logic gets any more
+ * complicated, it should be distributed into the affected modules,
+ * similar to the way shmem space estimation is handled.
+ *
+ * For now, though, we just need the fixed spinlocks (MAX_SPINS),
+ * two spinlocks per shared disk buffer, and four spinlocks for XLOG.
+ */
+ numSpinLocks = (int) MAX_SPINS + 2 * NBuffers + 4;
+
+ /* might as well round up to a multiple of SPINLOCKS_PER_SET */
+ numSpinSets = (numSpinLocks - 1) / SPINLOCKS_PER_SET + 1;
+ numSpinLocks = numSpinSets * SPINLOCKS_PER_SET;
+
+ SpinLockIds = (IpcSemaphoreId *)
+ malloc(numSpinSets * sizeof(IpcSemaphoreId));
+ Assert(SpinLockIds != NULL);
+ }
+
+ for (i = 0; i < numSpinSets; i++)
+ SpinLockIds[i] = -1;
+
+ /*
+ * Arrange to delete semas on exit --- set this up now so that we
+ * will clean up if allocation fails. We use our own freeproc,
+ * rather than IpcSemaphoreCreate's removeOnExit option, because
+ * we don't want to fill up the on_shmem_exit list with a separate
+ * entry for each semaphore set.
*/
- slckP->flag = NOLOCK;
- if (slckP->nshlocks > 0)
+ on_shmem_exit(SpinFreeAllSemaphores, 0);
+
+ /* Create sema sets and set all semas to count 1 */
+ for (i = 0; i < numSpinSets; i++)
{
- while (slckP->nshlocks > 0)
- {
- S_UNLOCK(&(slckP->shlock));
- S_LOCK(&(slckP->comlock));
- }
- S_UNLOCK(&(slckP->shlock));
+ SpinLockIds[i] = IpcSemaphoreCreate(SPINLOCKS_PER_SET,
+ IPCProtection,
+ 1,
+ false);
}
- else
- S_UNLOCK(&(slckP->shlock));
- S_UNLOCK(&(slckP->exlock));
- S_UNLOCK(&(slckP->locklock));
- PRINT_SLDEBUG("SpinRelease/released", lockid, slckP);
+
+ /*
+ * Assign indexes for fixed spinlocks
+ */
+ Assert(MAX_SPINS <= SPINLOCKS_PER_SET);
+ InitSpinLockIDs();
+
+ /* Init counter for allocating dynamic spinlocks */
+ nextSpinLock = MAX_SPINS;
}
-#else /* !HAS_TEST_AND_SET */
-/* Spinlocks are implemented using SysV semaphores */
+/*
+ * SpinFreeAllSemaphores -
+ * called at shmem_exit time, ie when exiting the postmaster or
+ * destroying shared state for a failed set of backends.
+ * Free up all the semaphores allocated for spinlocks.
+ */
+static void
+SpinFreeAllSemaphores(void)
+{
+ int i;
-static bool AttachSpinLocks(IPCKey key);
-static bool SpinIsLocked(SPINLOCK lock);
+ for (i = 0; i < numSpinSets; i++)
+ {
+ if (SpinLockIds[i] >= 0)
+ IpcSemaphoreKill(SpinLockIds[i]);
+ }
+}
/*
- * SpinAcquire -- try to grab a spinlock
+ * SpinAcquire -- grab a fixed spinlock
*
* FAILS if the semaphore is corrupted.
*/
void
SpinAcquire(SPINLOCK lock)
{
- IpcSemaphoreLock(SpinLockId, lock, IpcExclusiveLock);
+ IpcSemaphoreLock(SpinLockIds[0], lock);
PROC_INCR_SLOCK(lock);
}
/*
- * SpinRelease -- release a spin lock
+ * SpinRelease -- release a fixed spin lock
*
* FAILS if the semaphore is corrupted
*/
void
SpinRelease(SPINLOCK lock)
{
- Assert(SpinIsLocked(lock));
- PROC_DECR_SLOCK(lock);
- IpcSemaphoreUnlock(SpinLockId, lock, IpcExclusiveLock);
-}
-
-static bool
-SpinIsLocked(SPINLOCK lock)
-{
+#ifdef USE_ASSERT_CHECKING
+ /* Check it's locked */
int semval;
- semval = IpcSemaphoreGetValue(SpinLockId, lock);
- return semval < IpcSemaphoreDefaultStartValue;
+ semval = IpcSemaphoreGetValue(SpinLockIds[0], lock);
+ Assert(semval < 1);
+#endif
+ PROC_DECR_SLOCK(lock);
+ IpcSemaphoreUnlock(SpinLockIds[0], lock);
}
/*
- * CreateSpinlocks -- Create a sysV semaphore array for
- * the spinlocks
- *
+ * s_lock.h hardware-spinlock emulation
*/
+
void
-CreateSpinlocks(IPCKey key)
+s_init_lock_sema(volatile slock_t *lock)
{
-
- SpinLockId = IpcSemaphoreCreate(key, MAX_SPINS, IPCProtection,
- IpcSemaphoreDefaultStartValue, 1);
-
- if (SpinLockId <= 0)
- elog(STOP, "CreateSpinlocks: cannot create spin locks");
-
- return;
+ if (nextSpinLock >= numSpinLocks)
+ elog(FATAL, "s_init_lock_sema: not enough semaphores");
+ lock->semId = SpinLockIds[nextSpinLock / SPINLOCKS_PER_SET];
+ lock->sem = nextSpinLock % SPINLOCKS_PER_SET;
+ nextSpinLock++;
}
-/*
- * InitSpinLocks -- Spinlock bootstrapping
- *
- * We need several spinlocks for bootstrapping:
- * ShmemIndexLock (for the shmem index table) and
- * ShmemLock (for the shmem allocator), BufMgrLock (for buffer
- * pool exclusive access), LockMgrLock (for the lock table), and
- * ProcStructLock (a spin lock for the shared process structure).
- * If there's a Sony WORM drive attached, we also have a spinlock
- * (SJCacheLock) for it. Same story for the main memory storage mgr.
- *
- */
void
-InitSpinLocks(void)
+s_unlock_sema(volatile slock_t *lock)
{
- extern SPINLOCK ShmemLock;
- extern SPINLOCK ShmemIndexLock;
- extern SPINLOCK BufMgrLock;
- extern SPINLOCK LockMgrLock;
- extern SPINLOCK ProcStructLock;
- extern SPINLOCK SInvalLock;
- extern SPINLOCK OidGenLockId;
- extern SPINLOCK XidGenLockId;
- extern SPINLOCK ControlFileLockId;
-
-#ifdef STABLE_MEMORY_STORAGE
- extern SPINLOCK MMCacheLock;
-
-#endif
-
- /* These five (or six) spinlocks have fixed location is shmem */
- ShmemLock = (SPINLOCK) SHMEMLOCKID;
- ShmemIndexLock = (SPINLOCK) SHMEMINDEXLOCKID;
- BufMgrLock = (SPINLOCK) BUFMGRLOCKID;
- LockMgrLock = (SPINLOCK) LOCKMGRLOCKID;
- ProcStructLock = (SPINLOCK) PROCSTRUCTLOCKID;
- SInvalLock = (SPINLOCK) SINVALLOCKID;
- OidGenLockId = (SPINLOCK) OIDGENLOCKID;
- XidGenLockId = (SPINLOCK) XIDGENLOCKID;
- ControlFileLockId = (SPINLOCK) CNTLFILELOCKID;
+ IpcSemaphoreUnlock(lock->semId, lock->sem);
+}
-#ifdef STABLE_MEMORY_STORAGE
- MMCacheLock = (SPINLOCK) MMCACHELOCKID;
-#endif
+bool
+s_lock_free_sema(volatile slock_t *lock)
+{
+ return IpcSemaphoreGetValue(lock->semId, lock->sem) > 0;
+}
- return;
+int
+tas_sema(volatile slock_t *lock)
+{
+ /* Note that TAS macros return 0 if *success* */
+ return ! IpcSemaphoreTryLock(lock->semId, lock->sem);
}
#endif /* !HAS_TEST_AND_SET */
diff --git a/src/backend/storage/lmgr/lock.c b/src/backend/storage/lmgr/lock.c
index 14325e53183..cf99be3b115 100644
--- a/src/backend/storage/lmgr/lock.c
+++ b/src/backend/storage/lmgr/lock.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/storage/lmgr/lock.c,v 1.72 2000/11/08 22:10:00 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/storage/lmgr/lock.c,v 1.73 2000/11/28 23:27:56 tgl Exp $
*
* NOTES
* Outside modules can create a lock table and acquire/release
@@ -56,6 +56,7 @@ static char *lock_types[] =
"AccessExclusiveLock"
};
+static char *DeadLockMessage = "Deadlock detected.\n\tSee the lock(l) manual page for a possible cause.";
#ifdef LOCK_DEBUG
@@ -943,8 +944,7 @@ WaitOnLock(LOCKMETHOD lockmethod, LOCK *lock, LOCKMODE lockmode)
lock) != NO_ERROR)
{
/* -------------------
- * This could have happend as a result of a deadlock,
- * see HandleDeadLock().
+ * We failed as a result of a deadlock, see HandleDeadLock().
* Decrement the lock nHolding and holders fields as
* we are no longer waiting on this lock.
* -------------------
@@ -957,8 +957,7 @@ WaitOnLock(LOCKMETHOD lockmethod, LOCK *lock, LOCKMODE lockmode)
if (lock->activeHolders[lockmode] == lock->holders[lockmode])
lock->waitMask &= BITS_OFF[lockmode];
SpinRelease(lockMethodTable->ctl->masterLock);
- elog(ERROR, "WaitOnLock: error on wakeup - Aborting this transaction");
-
+ elog(ERROR, DeadLockMessage);
/* not reached */
}
diff --git a/src/backend/storage/lmgr/proc.c b/src/backend/storage/lmgr/proc.c
index 2da1495cd3b..0193a7ad2e7 100644
--- a/src/backend/storage/lmgr/proc.c
+++ b/src/backend/storage/lmgr/proc.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/storage/lmgr/proc.c,v 1.83 2000/10/07 14:39:13 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/storage/lmgr/proc.c,v 1.84 2000/11/28 23:27:56 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -47,7 +47,7 @@
* This is so that we can support more backends. (system-wide semaphore
* sets run out pretty fast.) -ay 4/95
*
- * $Header: /cvsroot/pgsql/src/backend/storage/lmgr/proc.c,v 1.83 2000/10/07 14:39:13 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/storage/lmgr/proc.c,v 1.84 2000/11/28 23:27:56 tgl Exp $
*/
#include "postgres.h"
@@ -91,10 +91,8 @@ static PROC_HDR *ProcGlobal = NULL;
PROC *MyProc = NULL;
static void ProcKill(int exitStatus, Datum pid);
-static void ProcGetNewSemKeyAndNum(IPCKey *key, int *semNum);
-static void ProcFreeSem(IpcSemaphoreKey semKey, int semNum);
-
-static char *DeadLockMessage = "Deadlock detected -- See the lock(l) manual page for a possible cause.";
+static void ProcGetNewSemIdAndNum(IpcSemaphoreId *semId, int *semNum);
+static void ProcFreeSem(IpcSemaphoreId semId, int semNum);
/*
* InitProcGlobal -
@@ -116,7 +114,7 @@ static char *DeadLockMessage = "Deadlock detected -- See the lock(l) manual page
* rather than later.
*/
void
-InitProcGlobal(IPCKey key, int maxBackends)
+InitProcGlobal(int maxBackends)
{
bool found = false;
@@ -135,39 +133,35 @@ InitProcGlobal(IPCKey key, int maxBackends)
int i;
ProcGlobal->freeProcs = INVALID_OFFSET;
- ProcGlobal->currKey = IPCGetProcessSemaphoreInitKey(key);
- for (i = 0; i < MAX_PROC_SEMS / PROC_NSEMS_PER_SET; i++)
+ for (i = 0; i < PROC_SEM_MAP_ENTRIES; i++)
+ {
+ ProcGlobal->procSemIds[i] = -1;
ProcGlobal->freeSemMap[i] = 0;
+ }
/*
* Arrange to delete semas on exit --- set this up now so that we
- * will clean up if pre-allocation fails...
+ * will clean up if pre-allocation fails. We use our own freeproc,
+ * rather than IpcSemaphoreCreate's removeOnExit option, because
+ * we don't want to fill up the on_shmem_exit list with a separate
+ * entry for each semaphore set.
*/
on_shmem_exit(ProcFreeAllSemaphores, 0);
/*
- * Pre-create the semaphores for the first maxBackends processes,
- * unless we are running as a standalone backend.
+ * Pre-create the semaphores for the first maxBackends processes.
*/
- if (key != PrivateIPCKey)
+ Assert(maxBackends > 0 && maxBackends <= MAXBACKENDS);
+
+ for (i = 0; i < ((maxBackends-1)/PROC_NSEMS_PER_SET+1); i++)
{
- for (i = 0;
- i < (maxBackends + PROC_NSEMS_PER_SET - 1) / PROC_NSEMS_PER_SET;
- i++)
- {
- IPCKey semKey = ProcGlobal->currKey + i;
- int semId;
-
- semId = IpcSemaphoreCreate(semKey,
- PROC_NSEMS_PER_SET,
- IPCProtection,
- IpcSemaphoreDefaultStartValue,
- 0);
- if (semId < 0)
- elog(FATAL, "InitProcGlobal: IpcSemaphoreCreate failed");
- /* mark this sema set allocated */
- ProcGlobal->freeSemMap[i] = (1 << PROC_NSEMS_PER_SET);
- }
+ IpcSemaphoreId semId;
+
+ semId = IpcSemaphoreCreate(PROC_NSEMS_PER_SET,
+ IPCProtection,
+ 1,
+ false);
+ ProcGlobal->procSemIds[i] = semId;
}
}
}
@@ -178,7 +172,7 @@ InitProcGlobal(IPCKey key, int maxBackends)
* ------------------------
*/
void
-InitProcess(IPCKey key)
+InitProcess(void)
{
bool found = false;
unsigned long location,
@@ -186,7 +180,7 @@ InitProcess(IPCKey key)
SpinAcquire(ProcStructLock);
- /* attach to the free list */
+ /* attach to the ProcGlobal structure */
ProcGlobal = (PROC_HDR *)
ShmemInitStruct("Proc Header", sizeof(PROC_HDR), &found);
if (!found)
@@ -199,10 +193,9 @@ InitProcess(IPCKey key)
{
SpinRelease(ProcStructLock);
elog(ERROR, "ProcInit: you already exist");
- return;
}
- /* try to get a proc from the free list first */
+ /* try to get a proc struct from the free list first */
myOffset = ProcGlobal->freeProcs;
@@ -243,36 +236,22 @@ InitProcess(IPCKey key)
if (IsUnderPostmaster)
{
- IPCKey semKey;
- int semNum;
- int semId;
- union semun semun;
-
- ProcGetNewSemKeyAndNum(&semKey, &semNum);
+ IpcSemaphoreId semId;
+ int semNum;
+ union semun semun;
- /*
- * Note: because of the pre-allocation done in InitProcGlobal,
- * this call should always attach to an existing semaphore. It
- * will (try to) create a new group of semaphores only if the
- * postmaster tries to start more backends than it said it would.
- */
- semId = IpcSemaphoreCreate(semKey,
- PROC_NSEMS_PER_SET,
- IPCProtection,
- IpcSemaphoreDefaultStartValue,
- 0);
+ ProcGetNewSemIdAndNum(&semId, &semNum);
/*
* we might be reusing a semaphore that belongs to a dead backend.
* So be careful and reinitialize its value here.
*/
- semun.val = IpcSemaphoreDefaultStartValue;
+ semun.val = 1;
semctl(semId, semNum, SETVAL, semun);
- IpcSemaphoreLock(semId, semNum, IpcExclusiveLock);
+ IpcSemaphoreLock(semId, semNum);
MyProc->sem.semId = semId;
MyProc->sem.semNum = semNum;
- MyProc->sem.semKey = semKey;
}
else
MyProc->sem.semId = -1;
@@ -304,7 +283,7 @@ InitProcess(IPCKey key)
*/
location = MAKE_OFFSET(MyProc);
if ((!ShmemPIDLookup(MyProcPid, &location)) || (location != MAKE_OFFSET(MyProc)))
- elog(STOP, "InitProc: ShmemPID table broken");
+ elog(STOP, "InitProcess: ShmemPID table broken");
MyProc->errType = NO_ERROR;
SHMQueueElemInit(&(MyProc->links));
@@ -363,10 +342,7 @@ ProcReleaseLocks()
/*
* ProcRemove -
* used by the postmaster to clean up the global tables. This also frees
- * up the semaphore used for the lmgr of the process. (We have to do
- * this is the postmaster instead of doing a IpcSemaphoreKill on exiting
- * the process because the semaphore set is shared among backends and
- * we don't want to remove other's semaphores on exit.)
+ * up the semaphore used for the lmgr of the process.
*/
bool
ProcRemove(int pid)
@@ -383,7 +359,7 @@ ProcRemove(int pid)
SpinAcquire(ProcStructLock);
- ProcFreeSem(proc->sem.semKey, proc->sem.semNum);
+ ProcFreeSem(proc->sem.semId, proc->sem.semNum);
proc->links.next = ProcGlobal->freeProcs;
ProcGlobal->freeProcs = MAKE_OFFSET(proc);
@@ -490,6 +466,7 @@ ProcQueueInit(PROC_QUEUE *queue)
*
*/
static bool lockWaiting = false;
+
void
SetWaitingForLock(bool waiting)
{
@@ -514,12 +491,12 @@ SetWaitingForLock(bool waiting)
}
}
}
+
void
LockWaitCancel(void)
{
-/* BeOS doesn't have setitimer, but has set_alarm */
#ifndef __BEOS__
-struct itimerval timeval,
+ struct itimerval timeval,
dummy;
if (!lockWaiting)
@@ -529,6 +506,7 @@ struct itimerval timeval,
MemSet(&timeval, 0, sizeof(struct itimerval));
setitimer(ITIMER_REAL, &timeval, &dummy);
#else
+ /* BeOS doesn't have setitimer, but has set_alarm */
if (!lockWaiting)
return;
lockWaiting = false;
@@ -547,6 +525,8 @@ struct itimerval timeval,
* semaphore is cleared by default, so the first time we try
* to acquire it, we sleep.
*
+ * Result is NO_ERROR if we acquired the lock, STATUS_ERROR if not (deadlock).
+ *
* ASSUME: that no one will fiddle with the queue until after
* we release the spin lock.
*
@@ -566,7 +546,6 @@ ProcSleep(PROC_QUEUE *waitQueue,/* lock->waitProcs */
int aheadHolders[MAX_LOCKMODES];
bool selfConflict = (lockctl->conflictTab[token] & myMask),
prevSame = false;
- bool deadlock_checked = false;
#ifndef __BEOS__
struct itimerval timeval,
dummy;
@@ -595,8 +574,8 @@ ProcSleep(PROC_QUEUE *waitQueue,/* lock->waitProcs */
/* is he waiting for me ? */
if (lockctl->conflictTab[proc->token] & MyProc->holdLock)
{
+ /* Yes, report deadlock failure */
MyProc->errType = STATUS_ERROR;
- elog(NOTICE, DeadLockMessage);
goto rt;
}
/* being waiting for him - go past */
@@ -642,10 +621,16 @@ ins:;
lock->waitMask |= myMask;
SpinRelease(spinlock);
+ MyProc->errType = NO_ERROR; /* initialize result for success */
+
/* --------------
- * We set this so we can wake up periodically and check for a deadlock.
- * If a deadlock is detected, the handler releases the processes
- * semaphore and aborts the current transaction.
+ * Set timer so we can wake up after awhile and check for a deadlock.
+ * If a deadlock is detected, the handler releases the process's
+ * semaphore and sets MyProc->errType = STATUS_ERROR, allowing us to
+ * know that we must report failure rather than success.
+ *
+ * By delaying the check until we've waited for a bit, we can avoid
+ * running the rather expensive deadlock-check code in most cases.
*
* Need to zero out struct to set the interval and the micro seconds fields
* to 0.
@@ -655,49 +640,42 @@ ins:;
MemSet(&timeval, 0, sizeof(struct itimerval));
timeval.it_value.tv_sec = DeadlockTimeout / 1000;
timeval.it_value.tv_usec = (DeadlockTimeout % 1000) * 1000;
+ if (setitimer(ITIMER_REAL, &timeval, &dummy))
+ elog(FATAL, "ProcSleep: Unable to set timer for process wakeup");
#else
- /* usecs */
- time_interval = DeadlockTimeout * 1000000;
+ time_interval = DeadlockTimeout * 1000000; /* usecs */
+ if (set_alarm(time_interval, B_ONE_SHOT_RELATIVE_ALARM) < 0)
+ elog(FATAL, "ProcSleep: Unable to set timer for process wakeup");
#endif
SetWaitingForLock(true);
- do
- {
- MyProc->errType = NO_ERROR; /* reset flag after deadlock check */
- if (!deadlock_checked)
-#ifndef __BEOS__
- if (setitimer(ITIMER_REAL, &timeval, &dummy))
-#else
- if (set_alarm(time_interval, B_ONE_SHOT_RELATIVE_ALARM) < 0)
-#endif
- elog(FATAL, "ProcSleep: Unable to set timer for process wakeup");
- deadlock_checked = true;
-
- /* --------------
- * if someone wakes us between SpinRelease and IpcSemaphoreLock,
- * IpcSemaphoreLock will not block. The wakeup is "saved" by
- * the semaphore implementation.
- * --------------
- */
- IpcSemaphoreLock(MyProc->sem.semId, MyProc->sem.semNum,
- IpcExclusiveLock);
- } while (MyProc->errType == STATUS_NOT_FOUND); /* sleep after deadlock
- * check */
+ /* --------------
+ * If someone wakes us between SpinRelease and IpcSemaphoreLock,
+ * IpcSemaphoreLock will not block. The wakeup is "saved" by
+ * the semaphore implementation. Note also that if HandleDeadLock
+ * is invoked but does not detect a deadlock, IpcSemaphoreLock()
+ * will continue to wait. There used to be a loop here, but it
+ * was useless code...
+ * --------------
+ */
+ IpcSemaphoreLock(MyProc->sem.semId, MyProc->sem.semNum);
+
lockWaiting = false;
/* ---------------
- * We were awoken before a timeout - now disable the timer
+ * Disable the timer, if it's still running
* ---------------
*/
#ifndef __BEOS__
timeval.it_value.tv_sec = 0;
timeval.it_value.tv_usec = 0;
if (setitimer(ITIMER_REAL, &timeval, &dummy))
+ elog(FATAL, "ProcSleep: Unable to disable timer for process wakeup");
#else
if (set_alarm(B_INFINITE_TIMEOUT, B_PERIODIC_ALARM) < 0)
+ elog(FATAL, "ProcSleep: Unable to disable timer for process wakeup");
#endif
- elog(FATAL, "ProcSleep: Unable to diable timer for process wakeup");
/* ----------------
* We were assumed to be in a critical section when we went
@@ -742,7 +720,7 @@ ProcWakeup(PROC *proc, int errType)
proc->errType = errType;
- IpcSemaphoreUnlock(proc->sem.semId, proc->sem.semNum, IpcExclusiveLock);
+ IpcSemaphoreUnlock(proc->sem.semId, proc->sem.semNum);
return retProc;
}
@@ -855,27 +833,11 @@ HandleDeadLock(SIGNAL_ARGS)
* Before we are awoken the process releasing the lock grants it to
* us so we know that we don't have to wait anymore.
*
- * Damn these names are LONG! -mer
+ * We check by looking to see if we've been unlinked from the wait queue.
+ * This is quicker than checking our semaphore's state, since no kernel
+ * call is needed, and it is safe because we hold the locktable lock.
* ---------------------
*/
- if (IpcSemaphoreGetCount(MyProc->sem.semId, MyProc->sem.semNum) ==
- IpcSemaphoreDefaultStartValue)
- {
- UnlockLockTable();
- return;
- }
-
- /*
- * you would think this would be unnecessary, but...
- *
- * this also means we've been removed already. in some ports (e.g.,
- * sparc and aix) the semop(2) implementation is such that we can
- * actually end up in this handler after someone has removed us from
- * the queue and bopped the semaphore *but the test above fails to
- * detect the semaphore update* (presumably something weird having to
- * do with the order in which the semaphore wakeup signal and SIGALRM
- * get handled).
- */
if (MyProc->links.prev == INVALID_OFFSET ||
MyProc->links.next == INVALID_OFFSET)
{
@@ -888,19 +850,18 @@ HandleDeadLock(SIGNAL_ARGS)
DumpAllLocks();
#endif
- MyProc->errType = STATUS_NOT_FOUND;
if (!DeadLockCheck(MyProc, MyProc->waitLock))
{
+ /* No deadlock, so keep waiting */
UnlockLockTable();
return;
}
- mywaitlock = MyProc->waitLock;
-
/* ------------------------
* Get this process off the lock's wait queue
* ------------------------
*/
+ mywaitlock = MyProc->waitLock;
Assert(mywaitlock->waitProcs.size > 0);
lockWaiting = false;
--mywaitlock->waitProcs.size;
@@ -908,12 +869,10 @@ HandleDeadLock(SIGNAL_ARGS)
SHMQueueElemInit(&(MyProc->links));
/* ------------------
- * Unlock my semaphore so that the count is right for next time.
- * I was awoken by a signal, not by someone unlocking my semaphore.
+ * Unlock my semaphore so that the interrupted ProcSleep() call can finish.
* ------------------
*/
- IpcSemaphoreUnlock(MyProc->sem.semId, MyProc->sem.semNum,
- IpcExclusiveLock);
+ IpcSemaphoreUnlock(MyProc->sem.semId, MyProc->sem.semNum);
/* -------------
* Set MyProc->errType to STATUS_ERROR so that we abort after
@@ -928,9 +887,6 @@ HandleDeadLock(SIGNAL_ARGS)
* conditions. i don't claim to understand this...
*/
UnlockLockTable();
-
- elog(NOTICE, DeadLockMessage);
- return;
}
void
@@ -959,31 +915,32 @@ ProcReleaseSpins(PROC *proc)
*****************************************************************************/
/*
- * ProcGetNewSemKeyAndNum -
+ * ProcGetNewSemIdAndNum -
* scan the free semaphore bitmap and allocate a single semaphore from
- * a semaphore set. (If the semaphore set doesn't exist yet,
- * IpcSemaphoreCreate will create it. Otherwise, we use the existing
- * semaphore set.)
+ * a semaphore set.
*/
static void
-ProcGetNewSemKeyAndNum(IPCKey *key, int *semNum)
+ProcGetNewSemIdAndNum(IpcSemaphoreId *semId, int *semNum)
{
int i;
+ IpcSemaphoreId *procSemIds = ProcGlobal->procSemIds;
int32 *freeSemMap = ProcGlobal->freeSemMap;
- int32 fullmask = (1 << (PROC_NSEMS_PER_SET + 1)) - 1;
+ int32 fullmask = (1 << PROC_NSEMS_PER_SET) - 1;
/*
* we hold ProcStructLock when entering this routine. We scan through
* the bitmap to look for a free semaphore.
*/
- for (i = 0; i < MAX_PROC_SEMS / PROC_NSEMS_PER_SET; i++)
+ for (i = 0; i < PROC_SEM_MAP_ENTRIES; i++)
{
int mask = 1;
int j;
if (freeSemMap[i] == fullmask)
continue; /* this set is fully allocated */
+ if (procSemIds[i] < 0)
+ continue; /* this set hasn't been initialized */
for (j = 0; j < PROC_NSEMS_PER_SET; j++)
{
@@ -991,12 +948,11 @@ ProcGetNewSemKeyAndNum(IPCKey *key, int *semNum)
{
/*
- * a free semaphore found. Mark it as allocated. Also set
- * the bit indicating whole set is allocated.
+ * a free semaphore found. Mark it as allocated.
*/
- freeSemMap[i] |= mask + (1 << PROC_NSEMS_PER_SET);
+ freeSemMap[i] |= mask;
- *key = ProcGlobal->currKey + i;
+ *semId = procSemIds[i];
*semNum = j;
return;
}
@@ -1005,7 +961,7 @@ ProcGetNewSemKeyAndNum(IPCKey *key, int *semNum)
}
/* if we reach here, all the semaphores are in use. */
- elog(ERROR, "InitProc: cannot allocate a free semaphore");
+ elog(ERROR, "ProcGetNewSemIdAndNum: cannot allocate a free semaphore");
}
/*
@@ -1013,23 +969,22 @@ ProcGetNewSemKeyAndNum(IPCKey *key, int *semNum)
* free up our semaphore in the semaphore set.
*/
static void
-ProcFreeSem(IpcSemaphoreKey semKey, int semNum)
+ProcFreeSem(IpcSemaphoreId semId, int semNum)
{
- int mask;
+ int32 mask;
int i;
- int32 *freeSemMap = ProcGlobal->freeSemMap;
- i = semKey - ProcGlobal->currKey;
mask = ~(1 << semNum);
- freeSemMap[i] &= mask;
- /*
- * Formerly we'd release a semaphore set if it was now completely
- * unused, but now we keep the semaphores to ensure we won't run out
- * when starting new backends --- cf. InitProcGlobal. Note that the
- * PROC_NSEMS_PER_SET+1'st bit of the freeSemMap entry remains set to
- * indicate it is still allocated; ProcFreeAllSemaphores() needs that.
- */
+ for (i = 0; i < PROC_SEM_MAP_ENTRIES; i++)
+ {
+ if (ProcGlobal->procSemIds[i] == semId)
+ {
+ ProcGlobal->freeSemMap[i] &= mask;
+ return;
+ }
+ }
+ fprintf(stderr, "ProcFreeSem: no ProcGlobal entry for semId %d\n", semId);
}
/*
@@ -1039,14 +994,13 @@ ProcFreeSem(IpcSemaphoreKey semKey, int semNum)
* Free up all the semaphores allocated to the lmgrs of the backends.
*/
static void
-ProcFreeAllSemaphores()
+ProcFreeAllSemaphores(void)
{
int i;
- int32 *freeSemMap = ProcGlobal->freeSemMap;
- for (i = 0; i < MAX_PROC_SEMS / PROC_NSEMS_PER_SET; i++)
+ for (i = 0; i < PROC_SEM_MAP_ENTRIES; i++)
{
- if (freeSemMap[i] != 0)
- IpcSemaphoreKill(ProcGlobal->currKey + i);
+ if (ProcGlobal->procSemIds[i] >= 0)
+ IpcSemaphoreKill(ProcGlobal->procSemIds[i]);
}
}
diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c
index bee4f7e9219..0454ffc8486 100644
--- a/src/backend/utils/init/postinit.c
+++ b/src/backend/utils/init/postinit.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/utils/init/postinit.c,v 1.72 2000/11/16 22:30:39 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/utils/init/postinit.c,v 1.73 2000/11/28 23:27:57 tgl Exp $
*
*
*-------------------------------------------------------------------------
@@ -45,7 +45,6 @@
static void ReverifyMyDatabase(const char *name);
static void InitCommunication(void);
-static IPCKey PostgresIpcKey;
/*** InitPostgres support ***/
@@ -141,7 +140,7 @@ ReverifyMyDatabase(const char *name)
* --------------------------------
*/
static void
-InitCommunication()
+InitCommunication(void)
{
/* ----------------
* initialize shared memory and semaphores appropriately.
@@ -151,26 +150,11 @@ InitCommunication()
{
/* ----------------
* we're running a postgres backend by itself with
- * no front end or postmaster.
+ * no front end or postmaster. Create private "shmem"
+ * and semaphores. Setting MaxBackends = 16 is arbitrary.
* ----------------
*/
- char *ipc_key; /* value of environment variable */
- IPCKey key;
-
- ipc_key = getenv("IPC_KEY");
-
- if (!PointerIsValid(ipc_key))
- {
- /* Normal standalone backend */
- key = PrivateIPCKey;
- }
- else
- {
- /* Allow standalone's IPC key to be set */
- key = atoi(ipc_key);
- }
- PostgresIpcKey = key;
- AttachSharedMemoryAndSemaphores(key);
+ CreateSharedMemoryAndSemaphores(true, 16);
}
}
@@ -295,7 +279,7 @@ InitPostgres(const char *dbname, const char *username)
/*
* Set up my per-backend PROC struct in shared memory.
*/
- InitProcess(PostgresIpcKey);
+ InitProcess();
/*
* Initialize my entry in the shared-invalidation manager's array of
@@ -307,7 +291,7 @@ InitPostgres(const char *dbname, const char *username)
*/
MyBackendId = InvalidBackendId;
- InitSharedInvalidationState();
+ InitBackendSharedInvalidationState();
if (MyBackendId > MAXBACKENDS || MyBackendId <= 0)
elog(FATAL, "cinit2: bad backend id %d", MyBackendId);
@@ -365,11 +349,11 @@ BaseInit(void)
*/
InitCommunication();
DebugFileOpen();
+
smgrinit();
EnablePortalManager(); /* memory for portal/transaction stuff */
/* initialize the local buffer manager */
InitLocalBuffer();
-
}
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index fc15e59859b..ae417118878 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: buf_internals.h,v 1.43 2000/11/08 22:10:02 tgl Exp $
+ * $Id: buf_internals.h,v 1.44 2000/11/28 23:27:57 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -16,6 +16,7 @@
#include "storage/buf.h"
#include "storage/lmgr.h"
+#include "storage/s_lock.h"
/* Buf Mgr constants */
/* in bufmgr.c */
@@ -100,11 +101,9 @@ typedef struct sbufdesc
BufFlags flags; /* see bit definitions above */
unsigned refcount; /* # of times buffer is pinned */
-#ifdef HAS_TEST_AND_SET
- /* can afford a dedicated lock if test-and-set locks are available */
- slock_t io_in_progress_lock;
+ slock_t io_in_progress_lock; /* to block for I/O to complete */
slock_t cntx_lock; /* to lock access to page context */
-#endif /* HAS_TEST_AND_SET */
+
unsigned r_locks; /* # of shared locks */
bool ri_lock; /* read-intent lock */
bool w_lock; /* context exclusively locked */
diff --git a/src/include/storage/bufmgr.h b/src/include/storage/bufmgr.h
index 22c0ccde7d5..275146eea80 100644
--- a/src/include/storage/bufmgr.h
+++ b/src/include/storage/bufmgr.h
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: bufmgr.h,v 1.43 2000/11/08 22:10:02 tgl Exp $
+ * $Id: bufmgr.h,v 1.44 2000/11/28 23:27:57 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -154,7 +154,7 @@ extern Buffer ReleaseAndReadBuffer(Buffer buffer, Relation relation,
BlockNumber blockNum);
extern int FlushBuffer(Buffer buffer, bool sync, bool release);
-extern void InitBufferPool(IPCKey key);
+extern void InitBufferPool(void);
extern void PrintBufferUsage(FILE *statfp);
extern void ResetBufferUsage(void);
extern void ResetBufferPool(bool isCommit);
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index eea082a574e..b633297d5f6 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -7,14 +7,10 @@
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: ipc.h,v 1.42 2000/10/07 14:39:17 momjian Exp $
- *
- * NOTES
- * This file is very architecture-specific. This stuff should actually
- * be factored into the port/ directories.
+ * $Id: ipc.h,v 1.43 2000/11/28 23:27:57 tgl Exp $
*
* Some files that would normally need to include only sys/ipc.h must
- * instead included this file because on Ultrix, sys/ipc.h is not designed
+ * instead include this file because on Ultrix, sys/ipc.h is not designed
* to be included multiple times. This file (by virtue of the ifndef IPC_H)
* is.
*-------------------------------------------------------------------------
@@ -26,11 +22,9 @@
#include <sys/types.h>
#ifdef HAVE_SYS_IPC_H
-#include <sys/ipc.h> /* For IPC_PRIVATE */
+#include <sys/ipc.h>
#endif /* HAVE_SYS_IPC_H */
-#include "config.h"
-
#ifndef HAVE_UNION_SEMUN
union semun
{
@@ -38,79 +32,41 @@ union semun
struct semid_ds *buf;
unsigned short *array;
};
-
#endif
-typedef uint16 SystemPortAddress;
-
-/* semaphore definitions */
+/* generic IPC definitions */
#define IPCProtection (0600) /* access/modify by user only */
-#define IPC_NMAXSEM 25 /* maximum number of semaphores */
-#define IpcSemaphoreDefaultStartValue 255
-#define IpcSharedLock (-1)
-#define IpcExclusiveLock (-255)
-
-#define IpcUnknownStatus (-1)
-#define IpcInvalidArgument (-2)
-#define IpcSemIdExist (-3)
-#define IpcSemIdNotExist (-4)
-
-typedef uint32 IpcSemaphoreKey; /* semaphore key */
-typedef int IpcSemaphoreId;
-
-/* shared memory definitions */
-
-#define IpcMemCreationFailed (-1)
-#define IpcMemIdGetFailed (-2)
-#define IpcMemAttachFailed 0
-
-typedef uint32 IPCKey;
-
-#define PrivateIPCKey IPC_PRIVATE
-#define DefaultIPCKey 17317
+/* semaphore definitions */
-typedef uint32 IpcMemoryKey; /* shared memory key */
-typedef int IpcMemoryId;
+typedef uint32 IpcSemaphoreKey; /* semaphore key passed to semget(2) */
+typedef int IpcSemaphoreId; /* semaphore ID returned by semget(2) */
+#define IPC_NMAXSEM 32 /* maximum number of semaphores per semID */
-/* ipc.c */
-extern bool proc_exit_inprogress;
+#define PGSemaMagic 537 /* must be less than SEMVMX */
-extern void proc_exit(int code);
-extern void shmem_exit(int code);
-extern int on_shmem_exit(void (*function) (), Datum arg);
-extern int on_proc_exit(void (*function) (), Datum arg);
-extern void on_exit_reset(void);
+/* shared memory definitions */
-extern IpcSemaphoreId IpcSemaphoreCreate(IpcSemaphoreKey semKey,
- int semNum, int permission, int semStartValue,
- int removeOnExit);
-extern void IpcSemaphoreKill(IpcSemaphoreKey key);
-extern void IpcSemaphoreLock(IpcSemaphoreId semId, int sem, int lock);
-extern void IpcSemaphoreUnlock(IpcSemaphoreId semId, int sem, int lock);
-extern int IpcSemaphoreGetCount(IpcSemaphoreId semId, int sem);
-extern int IpcSemaphoreGetValue(IpcSemaphoreId semId, int sem);
-extern IpcMemoryId IpcMemoryCreate(IpcMemoryKey memKey, uint32 size,
- int permission);
-extern IpcMemoryId IpcMemoryIdGet(IpcMemoryKey memKey, uint32 size);
-extern char *IpcMemoryAttach(IpcMemoryId memId);
-extern void IpcMemoryKill(IpcMemoryKey memKey);
-extern void CreateAndInitSLockMemory(IPCKey key);
-extern void AttachSLockMemory(IPCKey key);
+typedef uint32 IpcMemoryKey; /* shared memory key passed to shmget(2) */
+typedef int IpcMemoryId; /* shared memory ID returned by shmget(2) */
+typedef struct /* standard header for all Postgres shmem */
+{
+ int32 magic; /* magic # to identify Postgres segments */
+#define PGShmemMagic 679834892
+ pid_t creatorPID; /* PID of creating process */
+ uint32 totalsize; /* total size of segment */
+ uint32 freeoffset; /* offset to first free space */
+} PGShmemHeader;
-#ifdef HAS_TEST_AND_SET
-#define NOLOCK 0
-#define SHAREDLOCK 1
-#define EXCLUSIVELOCK 2
+/* spinlock definitions */
typedef enum _LockId_
{
BUFMGRLOCKID,
- LOCKLOCKID,
OIDGENLOCKID,
XIDGENLOCKID,
CNTLFILELOCKID,
@@ -118,100 +74,40 @@ typedef enum _LockId_
SHMEMINDEXLOCKID,
LOCKMGRLOCKID,
SINVALLOCKID,
-
-#ifdef STABLE_MEMORY_STORAGE
- MMCACHELOCKID,
-#endif
-
PROCSTRUCTLOCKID,
- FIRSTFREELOCKID
-} _LockId_;
-
-#define MAX_SPINS FIRSTFREELOCKID
-
-typedef struct slock
-{
- slock_t locklock;
- unsigned char flag;
- short nshlocks;
- slock_t shlock;
- slock_t exlock;
- slock_t comlock;
- struct slock *next;
-} SLock;
-
-#else /* HAS_TEST_AND_SET */
-
-typedef enum _LockId_
-{
- SHMEMLOCKID,
- SHMEMINDEXLOCKID,
- BUFMGRLOCKID,
- LOCKMGRLOCKID,
- SINVALLOCKID,
#ifdef STABLE_MEMORY_STORAGE
MMCACHELOCKID,
#endif
- PROCSTRUCTLOCKID,
- OIDGENLOCKID,
- XIDGENLOCKID,
- CNTLFILELOCKID,
- FIRSTFREELOCKID
+ MAX_SPINS /* must be last item! */
} _LockId_;
-#define MAX_SPINS FIRSTFREELOCKID
-#endif /* HAS_TEST_AND_SET */
+/* ipc.c */
+extern bool proc_exit_inprogress;
-/*
- * the following are originally in ipci.h but the prototypes have circular
- * dependencies and most files include both ipci.h and ipc.h anyway, hence
- * combined.
- *
- */
+extern void proc_exit(int code);
+extern void shmem_exit(int code);
+extern void on_proc_exit(void (*function) (), Datum arg);
+extern void on_shmem_exit(void (*function) (), Datum arg);
+extern void on_exit_reset(void);
-/*
- * Note:
- * These must not hash to DefaultIPCKey or PrivateIPCKey.
- */
-#define SystemPortAddressGetIPCKey(address) \
- (28597 * (address) + 17491)
+extern void IpcInitKeyAssignment(int port);
-/*
- * these keys are originally numbered from 1 to 12 consecutively but not
- * all are used. The unused ones are removed. - ay 4/95.
- */
-#define IPCKeyGetBufferMemoryKey(key) \
- ((key == PrivateIPCKey) ? key : 1 + (key))
-
-#define IPCKeyGetSIBufferMemoryBlock(key) \
- ((key == PrivateIPCKey) ? key : 7 + (key))
-
-#define IPCKeyGetSLockSharedMemoryKey(key) \
- ((key == PrivateIPCKey) ? key : 10 + (key))
-
-#define IPCKeyGetSpinLockSemaphoreKey(key) \
- ((key == PrivateIPCKey) ? key : 11 + (key))
-#define IPCKeyGetWaitIOSemaphoreKey(key) \
- ((key == PrivateIPCKey) ? key : 12 + (key))
-#define IPCKeyGetWaitCLSemaphoreKey(key) \
- ((key == PrivateIPCKey) ? key : 13 + (key))
-
-/* --------------------------
- * NOTE: This macro must always give the highest numbered key as every backend
- * process forked off by the postmaster will be trying to acquire a semaphore
- * with a unique key value starting at key+14 and incrementing up. Each
- * backend uses the current key value then increments it by one.
- * --------------------------
- */
-#define IPCGetProcessSemaphoreInitKey(key) \
- ((key == PrivateIPCKey) ? key : 14 + (key))
+extern IpcSemaphoreId IpcSemaphoreCreate(int numSems, int permission,
+ int semStartValue,
+ bool removeOnExit);
+extern void IpcSemaphoreKill(IpcSemaphoreId semId);
+extern void IpcSemaphoreLock(IpcSemaphoreId semId, int sem);
+extern void IpcSemaphoreUnlock(IpcSemaphoreId semId, int sem);
+extern bool IpcSemaphoreTryLock(IpcSemaphoreId semId, int sem);
+extern int IpcSemaphoreGetValue(IpcSemaphoreId semId, int sem);
+
+extern PGShmemHeader *IpcMemoryCreate(uint32 size, bool private,
+ int permission);
/* ipci.c */
-extern IPCKey SystemPortAddressCreateIPCKey(SystemPortAddress address);
-extern void CreateSharedMemoryAndSemaphores(IPCKey key, int maxBackends);
-extern void AttachSharedMemoryAndSemaphores(IPCKey key);
+extern void CreateSharedMemoryAndSemaphores(bool private, int maxBackends);
#endif /* IPC_H */
diff --git a/src/include/storage/lmgr.h b/src/include/storage/lmgr.h
index 71a59cb9cc7..f859dc0762f 100644
--- a/src/include/storage/lmgr.h
+++ b/src/include/storage/lmgr.h
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: lmgr.h,v 1.25 2000/06/08 22:37:54 momjian Exp $
+ * $Id: lmgr.h,v 1.26 2000/11/28 23:27:57 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -47,7 +47,4 @@ extern void UnlockPage(Relation relation, BlockNumber blkno, LOCKMODE lockmode);
extern void XactLockTableInsert(TransactionId xid);
extern void XactLockTableWait(TransactionId xid);
-/* proc.c */
-extern void InitProcGlobal(IPCKey key, int maxBackends);
-
#endif /* LMGR_H */
diff --git a/src/include/storage/proc.h b/src/include/storage/proc.h
index edc6359fc43..41305d80831 100644
--- a/src/include/storage/proc.h
+++ b/src/include/storage/proc.h
@@ -1,13 +1,13 @@
/*-------------------------------------------------------------------------
*
* proc.h
- *
+ * per-process shared memory data structures
*
*
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: proc.h,v 1.31 2000/05/31 00:28:38 petere Exp $
+ * $Id: proc.h,v 1.32 2000/11/28 23:27:57 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -23,9 +23,8 @@ extern int DeadlockTimeout;
typedef struct
{
int sleeplock;
- int semNum;
IpcSemaphoreId semId;
- IpcSemaphoreKey semKey;
+ int semNum;
} SEMA;
/*
@@ -33,7 +32,6 @@ typedef struct
*/
typedef struct proc
{
-
/* proc->links MUST BE THE FIRST ELEMENT OF STRUCT (see ProcWakeup()) */
SHM_QUEUE links; /* proc can be waiting for one event(lock) */
@@ -63,34 +61,6 @@ typedef struct proc
* transaction */
} PROC;
-
-/*
- * PROC_NSEMS_PER_SET is the number of semaphores in each sys-V semaphore set
- * we allocate. It must be *less than* 32 (or however many bits in an int
- * on your machine), or our free-semaphores bitmap won't work. You also must
- * not set it higher than your kernel's SEMMSL (max semaphores per set)
- * parameter, which is often around 25.
- *
- * MAX_PROC_SEMS is the maximum number of per-process semaphores (those used
- * by the lock mgr) we can keep track of. It must be a multiple of
- * PROC_NSEMS_PER_SET.
- */
-#define PROC_NSEMS_PER_SET 16
-#define MAX_PROC_SEMS (((MAXBACKENDS-1)/PROC_NSEMS_PER_SET+1)*PROC_NSEMS_PER_SET)
-
-typedef struct procglobal
-{
- SHMEM_OFFSET freeProcs;
- IPCKey currKey;
- int32 freeSemMap[MAX_PROC_SEMS / PROC_NSEMS_PER_SET];
-
- /*
- * In each freeSemMap entry, the PROC_NSEMS_PER_SET least-significant
- * bits flag whether individual semaphores are in use, and the next
- * higher bit is set to show that the entire set is allocated.
- */
-} PROC_HDR;
-
extern PROC *MyProc;
#define PROC_INCR_SLOCK(lock) \
@@ -115,16 +85,46 @@ do { \
extern SPINLOCK ProcStructLock;
+
+/*
+ * There is one ProcGlobal struct for the whole installation.
+ *
+ * PROC_NSEMS_PER_SET is the number of semaphores in each sys-V semaphore set
+ * we allocate. It must be no more than 32 (or however many bits in an int
+ * on your machine), or our free-semaphores bitmap won't work. It also must
+ * be *less than* your kernel's SEMMSL (max semaphores per set) parameter,
+ * which is often around 25. (Less than, because we allocate one extra sema
+ * in each set for identification purposes.)
+ *
+ * PROC_SEM_MAP_ENTRIES is the number of semaphore sets we need to allocate
+ * to keep track of up to MAXBACKENDS backends.
+ */
+#define PROC_NSEMS_PER_SET 16
+#define PROC_SEM_MAP_ENTRIES ((MAXBACKENDS-1)/PROC_NSEMS_PER_SET+1)
+
+typedef struct procglobal
+{
+ /* Head of list of free PROC structures */
+ SHMEM_OFFSET freeProcs;
+
+ /* Info about semaphore sets used for per-process semaphores */
+ IpcSemaphoreId procSemIds[PROC_SEM_MAP_ENTRIES];
+ int32 freeSemMap[PROC_SEM_MAP_ENTRIES];
+
+ /*
+ * In each freeSemMap entry, bit i is set if the i'th semaphore of the
+ * set is allocated to a process. (i counts from 0 at the LSB)
+ */
+} PROC_HDR;
+
/*
* Function Prototypes
*/
-extern void InitProcess(IPCKey key);
+extern void InitProcGlobal(int maxBackends);
+extern void InitProcess(void);
extern void ProcReleaseLocks(void);
extern bool ProcRemove(int pid);
-/* extern bool ProcKill(int exitStatus, int pid); */
-/* make static in storage/lmgr/proc.c -- jolly */
-
extern void ProcQueueInit(PROC_QUEUE *queue);
extern int ProcSleep(PROC_QUEUE *queue, LOCKMETHODCTL *lockctl, int token,
LOCK *lock);
diff --git a/src/include/storage/s_lock.h b/src/include/storage/s_lock.h
index 9f0fd7c68e3..d50e3564bb7 100644
--- a/src/include/storage/s_lock.h
+++ b/src/include/storage/s_lock.h
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/include/storage/s_lock.h,v 1.73 2000/10/22 22:15:03 petere Exp $
+ * $Header: /cvsroot/pgsql/src/include/storage/s_lock.h,v 1.74 2000/11/28 23:27:57 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -26,7 +26,7 @@
* void S_LOCK_FREE(slock_t *lock)
* Tests if the lock is free. Returns non-zero if free, 0 if locked.
*
- * The S_LOCK() macro implements a primitive but still useful random
+ * The S_LOCK() macro implements a primitive but still useful random
* backoff to avoid hordes of busywaiting lockers chewing CPU.
*
* Effectively:
@@ -64,7 +64,7 @@
* manual for POWER in any case.
*
*/
-#if !defined(S_LOCK_H)
+#ifndef S_LOCK_H
#define S_LOCK_H
#include "storage/ipc.h"
@@ -403,8 +403,8 @@ extern void s_lock(volatile slock_t *lock, const char *file, const int line);
#define S_LOCK(lock) \
do { \
- if (TAS((volatile slock_t *) lock)) \
- s_lock((volatile slock_t *) lock, __FILE__, __LINE__); \
+ if (TAS((volatile slock_t *) (lock))) \
+ s_lock((volatile slock_t *) (lock), __FILE__, __LINE__); \
} while (0)
#endif /* S_LOCK */
@@ -421,12 +421,46 @@ extern void s_lock(volatile slock_t *lock, const char *file, const int line);
#endif /* S_INIT_LOCK */
#if !defined(TAS)
-int tas(volatile slock_t *lock); /* port/.../tas.s, or
+extern int tas(volatile slock_t *lock); /* port/.../tas.s, or
* s_lock.c */
-#define TAS(lock) tas((volatile slock_t *) lock)
+#define TAS(lock) tas((volatile slock_t *) (lock))
#endif /* TAS */
+
+#else /* !HAS_TEST_AND_SET */
+
+/*
+ * Fake spinlock implementation using SysV semaphores --- slow and prone
+ * to fall foul of kernel limits on number of semaphores, so don't use this
+ * unless you must!
+ */
+
+typedef struct
+{
+ /* reference to semaphore used to implement this spinlock */
+ IpcSemaphoreId semId;
+ int sem;
+} slock_t;
+
+extern bool s_lock_free_sema(volatile slock_t *lock);
+extern void s_unlock_sema(volatile slock_t *lock);
+extern void s_init_lock_sema(volatile slock_t *lock);
+extern int tas_sema(volatile slock_t *lock);
+
+extern void s_lock(volatile slock_t *lock, const char *file, const int line);
+
+#define S_LOCK(lock) \
+ do { \
+ if (TAS((volatile slock_t *) (lock))) \
+ s_lock((volatile slock_t *) (lock), __FILE__, __LINE__); \
+ } while (0)
+
+#define S_LOCK_FREE(lock) s_lock_free_sema(lock)
+#define S_UNLOCK(lock) s_unlock_sema(lock)
+#define S_INIT_LOCK(lock) s_init_lock_sema(lock)
+#define TAS(lock) tas_sema(lock)
+
#endif /* HAS_TEST_AND_SET */
-#endif /* S_LOCK_H */
+#endif /* S_LOCK_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 35545f95191..8b2cc4487f0 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: shmem.h,v 1.23 2000/06/28 03:33:27 tgl Exp $
+ * $Id: shmem.h,v 1.24 2000/11/28 23:27:57 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -18,17 +18,23 @@
#include "utils/hsearch.h"
-/* The shared memory region can start at a different address
+/*
+ * The shared memory region can start at a different address
* in every process. Shared memory "pointers" are actually
* offsets relative to the start of the shared memory region(s).
+ *
+ * In current usage, this is not actually a problem, but we keep
+ * the code that used to handle it...
*/
typedef unsigned long SHMEM_OFFSET;
#define INVALID_OFFSET (-1)
#define BAD_LOCATION (-1)
-/* start of the lowest shared memory region. For now, assume that
- * there is only one shared memory region
+/*
+ * Start of the primary shared memory region, in this process' address space.
+ * The macros in this header file can only cope with offsets into this
+ * shared memory region!
*/
extern SHMEM_OFFSET ShmemBase;
@@ -39,14 +45,14 @@ extern SHMEM_OFFSET ShmemBase;
/* coerce a pointer into a shmem offset */
#define MAKE_OFFSET(xx_ptr)\
- (SHMEM_OFFSET) (((unsigned long)(xx_ptr))-ShmemBase)
+ ((SHMEM_OFFSET) (((unsigned long)(xx_ptr))-ShmemBase))
#define SHM_PTR_VALID(xx_ptr)\
- (((unsigned long)xx_ptr) > ShmemBase)
+ (((unsigned long)(xx_ptr)) > ShmemBase)
/* cannot have an offset to ShmemFreeStart (offset 0) */
#define SHM_OFFSET_VALID(xx_offs)\
- ((xx_offs != 0) && (xx_offs != INVALID_OFFSET))
+ (((xx_offs) != 0) && ((xx_offs) != INVALID_OFFSET))
extern SPINLOCK ShmemLock;
@@ -60,11 +66,9 @@ typedef struct SHM_QUEUE
} SHM_QUEUE;
/* shmem.c */
-extern void ShmemIndexReset(void);
-extern void ShmemCreate(unsigned int key, unsigned int size);
-extern int InitShmem(unsigned int key, unsigned int size);
+extern void InitShmemAllocation(PGShmemHeader *seghdr);
extern void *ShmemAlloc(Size size);
-extern int ShmemIsValid(unsigned long addr);
+extern bool ShmemIsValid(unsigned long addr);
extern HTAB *ShmemInitHash(char *name, long init_size, long max_size,
HASHCTL *infoP, int hash_flags);
extern bool ShmemPIDLookup(int pid, SHMEM_OFFSET *locationPtr);
diff --git a/src/include/storage/sinval.h b/src/include/storage/sinval.h
index 4c80f760faa..1cadb54fd24 100644
--- a/src/include/storage/sinval.h
+++ b/src/include/storage/sinval.h
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: sinval.h,v 1.15 2000/11/12 20:51:52 tgl Exp $
+ * $Id: sinval.h,v 1.16 2000/11/28 23:27:57 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -19,9 +19,9 @@
extern SPINLOCK SInvalLock;
-extern void CreateSharedInvalidationState(IPCKey key, int maxBackends);
-extern void AttachSharedInvalidationState(IPCKey key);
-extern void InitSharedInvalidationState(void);
+extern int SInvalShmemSize(int maxBackends);
+extern void CreateSharedInvalidationState(int maxBackends);
+extern void InitBackendSharedInvalidationState(void);
extern void RegisterSharedInvalid(int cacheId, Index hashIndex,
ItemPointer pointer);
extern void InvalidateSharedInvalid(void (*invalFunction) (),
diff --git a/src/include/storage/sinvaladt.h b/src/include/storage/sinvaladt.h
index b9704d34e4d..c31caf53bf5 100644
--- a/src/include/storage/sinvaladt.h
+++ b/src/include/storage/sinvaladt.h
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: sinvaladt.h,v 1.23 2000/11/12 20:51:52 tgl Exp $
+ * $Id: sinvaladt.h,v 1.24 2000/11/28 23:27:57 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -107,15 +107,13 @@ typedef struct SISeg
} SISeg;
-extern SISeg *shmInvalBuffer; /* pointer to the shared buffer segment,
- * set by SISegmentAttach() */
+extern SISeg *shmInvalBuffer; /* pointer to the shared inval buffer */
/*
* prototypes for functions in sinvaladt.c
*/
-extern int SISegmentInit(bool createNewSegment, IPCKey key,
- int maxBackends);
+extern void SIBufferInit(int maxBackends);
extern int SIBackendInit(SISeg *segP);
extern bool SIInsertDataEntry(SISeg *segP, SharedInvalidData *data);
diff --git a/src/include/storage/spin.h b/src/include/storage/spin.h
index 656e1097a23..53efabcadb1 100644
--- a/src/include/storage/spin.h
+++ b/src/include/storage/spin.h
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: spin.h,v 1.12 2000/05/31 00:28:38 petere Exp $
+ * $Id: spin.h,v 1.13 2000/11/28 23:27:57 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -19,11 +19,10 @@
/*
* two implementations of spin locks
*
- * sequent, sparc, sun3: real spin locks. uses a TAS instruction; see
- * src/storage/ipc/s_lock.c for details.
- *
- * default: fake spin locks using semaphores. see spin.c
+ * Where TAS instruction is available: real spin locks.
+ * See src/storage/ipc/s_lock.c for details.
*
+ * Otherwise: fake spin locks using semaphores. see spin.c
*/
typedef int SPINLOCK;
@@ -32,8 +31,10 @@ typedef int SPINLOCK;
extern bool Trace_spinlocks;
#endif
-extern void CreateSpinlocks(IPCKey key);
-extern void InitSpinLocks(void);
+
+extern int SLockShmemSize(void);
+extern void CreateSpinlocks(PGShmemHeader *seghdr);
+
extern void SpinAcquire(SPINLOCK lockid);
extern void SpinRelease(SPINLOCK lockid);