diff --git a/doc/src/sgml/ref/postmaster.sgml b/doc/src/sgml/ref/postmaster.sgml index beb6c0ee93f661301e00cc4a85c36e5ec8148298..5bf5e4cb92b205ce12839b9f6b70c7124843427d 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 b31a6182ee2f0c2cf0e89dfc0ac8ee5829fc3d10..c70dbc7b4fbe8edef792a7ab01f1a542c52a10bf 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 e364d53b9df0e14a9893a046e10864b8a41811b8..6316262d042ff9e654763d07caf648a8bc2840fe 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 3b2eb95d4fc2bd881d6d6533cad1474213cae96d..9fa4ebb368901cf848727c4c3b74616e3f60a482 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 * @@ -117,26 +117,6 @@ int PostPortNumber; 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 @@ -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 3c40009422da9bed0d11b6a2125c430b2917ead7..0fda21972f60947c8290c084c2bcd8c21bb16c27 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 907e819474378f478981458e94b01a78d4f313b8..8ed03138fac13be36884ebf5653fa78c9da046d8 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 883c150b92384ed83b2e9cbe1f009abba5aadba0..72b167977d52a387775f4bfa4d0759eaa6a2ba6b 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 ff6bff29a1d82ead0266a4760dcc8e92f4f96bb5..9672510547a8a5917e3ea140e5eae6af74a8a327 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 98d90bc62e02eeadbefc0ad6d7ce077efddf541b..920f7f9bfeb7d7115bd11eee83a928dcbdbc20fb 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 5c7e88af73e42d1069b27a163e469a9c62e6c5d7..7a5813df57d0952ee7c2eb964a278cce6ed2ce06 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 c5048a389b1ade3bb88cc2ad0d7e9908349e428a..1592294708752bf92e8822eea4b4bda217d9de89 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 fb2e4804dd3c23b8f4e865b791947cb11bcb4aab..c87fcd3660292bfb876fdbda63f37cdfcfc0ab85 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 f4b2998343314e6eeb30188171d28074e3077b03..c7612759793fcd6831bcad9440b98d761853426d 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 674ee06a9a34f3959c4123b6adcafa171d26100b..a93ae69e0327f71c7920c8b16d64347f5d0c49f9 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 14325e53183d7f91bd7d04c6c9be7d2f1d233680..cf99be3b11500ede5e97e354b75552712acc5de5 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 2da1495cd3b5416a164a5dd6dda444af67d62567..0193a7ad2e73746f200a1aa8afa8a713b86897b5 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 bee4f7e9219063ac0870f3617f4f45ceb0a5be76..0454ffc8486a4316c17c4bf99cd66a96a6b03bbf 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 fc15e59859b8e9386212c06edb14c432fc5ceb3a..ae41711887848dcddc68d07e588aee8b726e722b 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 22c0ccde7d5c607f25af84dbc0a91498caef9616..275146eea80f6b9e377f80fee094bf3be9934e2e 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 eea082a574eb0f1c12a1af875862f3ad73693792..b633297d5f675b25ea65e530b554168d51b4d60b 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 71a59cb9cc72df16ad5042b60b5b3f9e79c0cc0d..f859dc0762f222adb032d052752f6f5be3d50834 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 edc6359fc43e4e2d8d044e1ca9a6875a988eb17f..41305d80831ddd01b6bf09e31305c65c0e761b15 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 9f0fd7c68e3f703bac3db352e4bc9d8e5f6fd3ba..d50e3564bb7eb0cc9007a13f5b250199883d1eb2 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 35545f95191fab380f68e6d6e857507df8d2a8ee..8b2cc4487f0aa400e79275a4ec36190e288535ed 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 4c80f760faa508899d6865a99f7fccdc0e92c257..1cadb54fd246f4201d96c1f83e7197674ee15eae 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 b9704d34e4d9753056cbcbf360d43a7de124cb46..c31caf53bf5e7e641233a9afcd851f41fe77e97f 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 656e1097a239a5fa5e63acafff0c677af0c64dce..53efabcadb1edab4da5505b17f69fc8f496d9294 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);