1 /*-------------------------------------------------------------------------
4 * POSTGRES inter-process communication definitions.
6 * Copyright (c) 1994, Regents of the University of California
10 * $Header: /cvsroot/pgsql/src/backend/storage/ipc/ipc.c,v 1.42 1999/11/06 19:46:57 momjian Exp $
14 * Currently, semaphores are used (my understanding anyway) in two
16 * 1. as mutexes on machines that don't have test-and-set (eg.
18 * 2. for putting processes to sleep when waiting on a lock
19 * and waking them up when the lock is free.
20 * The number of semaphores in (1) is fixed and those are shared
21 * among all backends. In (2), there is 1 semaphore per process and those
22 * are not shared with anyone else.
25 *-------------------------------------------------------------------------
27 #include <sys/types.h>
33 #include "storage/ipc.h"
34 #include "storage/s_lock.h"
35 /* In Ultrix, sem.h and shm.h must be included AFTER ipc.h */
38 #include "utils/memutils.h"
39 #include "libpq/libpq.h"
40 #include "utils/trace.h"
42 #if defined(solaris_sparc)
46 static int UsePrivateMemory = 0;
48 static void IpcMemoryDetach(int status, char *shmaddr);
49 static void IpcConfigTip(void);
51 /* ----------------------------------------------------------------
52 * exit() handling stuff
53 * ----------------------------------------------------------------
56 #define MAX_ON_EXITS 20
62 } on_proc_exit_list[MAX_ON_EXITS], on_shmem_exit_list[MAX_ON_EXITS];
64 static int on_proc_exit_index,
67 typedef struct _PrivateMemStruct
73 PrivateMem IpcPrivateMem[16];
76 PrivateMemoryCreate(IpcMemoryKey memKey,
83 IpcPrivateMem[memid].id = memid;
84 IpcPrivateMem[memid].memptr = malloc(size);
85 if (IpcPrivateMem[memid].memptr == NULL)
86 elog(ERROR, "PrivateMemoryCreate: not enough memory to malloc");
87 MemSet(IpcPrivateMem[memid].memptr, 0, size); /* XXX PURIFY */
93 PrivateMemoryAttach(IpcMemoryId memid)
95 return IpcPrivateMem[memid].memptr;
99 /* ----------------------------------------------------------------
102 * this function calls all the callbacks registered
103 * for it (to free resources) and then calls exit.
104 * This should be the only function to call exit().
106 * ----------------------------------------------------------------
108 static int proc_exit_inprogress = 0;
115 TPRINTF(TRACE_VERBOSE, "proc_exit(%d) [#%d]", code, proc_exit_inprogress);
118 * If proc_exit is called too many times something bad is happening, so
119 * exit immediately. This is crafted in two if's for a reason.
122 if (++proc_exit_inprogress == 9)
123 elog(ERROR, "infinite recursion in proc_exit");
124 if (proc_exit_inprogress >= 9)
128 * if proc_exit_inprocess > 1, then it means that we
129 * are being invoked from within an on_exit() handler
130 * and so we return immediately to avoid recursion.
133 if (proc_exit_inprogress > 1)
136 /* do our shared memory exits first */
140 * call all the callbacks registered before calling exit().
143 for (i = on_proc_exit_index - 1; i >= 0; --i)
144 (*on_proc_exit_list[i].function) (code, on_proc_exit_list[i].arg);
147 TPRINTF(TRACE_VERBOSE, "exit(%d)", code);
151 /* ------------------
152 * Run all of the on_shmem_exit routines but don't exit in the end.
153 * This is used by the postmaster to re-initialize shared memory and
154 * semaphores after a backend dies horribly
157 static int shmem_exit_inprogress = 0;
164 TPRINTF(TRACE_VERBOSE, "shmem_exit(%d) [#%d]",
165 code, shmem_exit_inprogress);
168 * If shmem_exit is called too many times something bad is happenig,
169 * so exit immediately.
171 if (shmem_exit_inprogress > 9)
173 elog(ERROR, "infinite recursion in shmem_exit");
178 * if shmem_exit_inprocess is true, then it means that we
179 * are being invoked from within an on_exit() handler
180 * and so we return immediately to avoid recursion.
183 if (shmem_exit_inprogress++)
187 * call all the callbacks registered before calling exit().
190 for (i = on_shmem_exit_index - 1; i >= 0; --i)
191 (*on_shmem_exit_list[i].function) (code, on_shmem_exit_list[i].arg);
193 on_shmem_exit_index = 0;
194 shmem_exit_inprogress = 0;
197 /* ----------------------------------------------------------------
200 * this function adds a callback function to the list of
201 * functions invoked by proc_exit(). -cim 2/6/90
202 * ----------------------------------------------------------------
205 on_proc_exit(void (*function) (), caddr_t arg)
207 if (on_proc_exit_index >= MAX_ON_EXITS)
210 on_proc_exit_list[on_proc_exit_index].function = function;
211 on_proc_exit_list[on_proc_exit_index].arg = arg;
213 ++on_proc_exit_index;
218 /* ----------------------------------------------------------------
221 * this function adds a callback function to the list of
222 * functions invoked by shmem_exit(). -cim 2/6/90
223 * ----------------------------------------------------------------
226 on_shmem_exit(void (*function) (), caddr_t arg)
228 if (on_shmem_exit_index >= MAX_ON_EXITS)
231 on_shmem_exit_list[on_shmem_exit_index].function = function;
232 on_shmem_exit_list[on_shmem_exit_index].arg = arg;
234 ++on_shmem_exit_index;
239 /* ----------------------------------------------------------------
242 * this function clears all proc_exit() registered functions.
243 * ----------------------------------------------------------------
248 on_shmem_exit_index = 0;
249 on_proc_exit_index = 0;
252 /****************************************************************************/
253 /* IPCPrivateSemaphoreKill(status, semId) */
255 /****************************************************************************/
257 IPCPrivateSemaphoreKill(int status,
258 int semId) /* caddr_t */
262 semctl(semId, 0, IPC_RMID, semun);
266 /****************************************************************************/
267 /* IPCPrivateMemoryKill(status, shmId) */
269 /****************************************************************************/
271 IPCPrivateMemoryKill(int status,
272 int shmId) /* caddr_t */
274 if (UsePrivateMemory)
276 /* free ( IpcPrivateMem[shmId].memptr ); */
280 if (shmctl(shmId, IPC_RMID, (struct shmid_ds *) NULL) < 0)
282 elog(NOTICE, "IPCPrivateMemoryKill: shmctl(%d, %d, 0) failed: %m",
290 * XXX This should be split into two different calls. One should
291 * XXX be used to create a semaphore set. The other to "attach" a
292 * XXX existing set. It should be an error for the semaphore set
293 * XXX to to already exist or for it not to, respectively.
295 * Currently, the semaphore sets are "attached" and an error
296 * is detected only when a later shared memory attach fails.
300 IpcSemaphoreCreate(IpcSemaphoreKey semKey,
309 u_short array[IPC_NMAXSEM];
312 /* check arguments */
313 if (semNum > IPC_NMAXSEM || semNum <= 0)
316 semId = semget(semKey, 0, 0);
321 EPRINTF("calling semget with %d, %d , %d\n",
324 IPC_CREAT | permission);
326 semId = semget(semKey, semNum, IPC_CREAT | permission);
330 EPRINTF("IpcSemaphoreCreate: semget failed (%s) "
331 "key=%d, num=%d, permission=%o",
332 strerror(errno), semKey, semNum, permission);
336 for (i = 0; i < semNum; i++)
337 array[i] = semStartValue;
339 errStatus = semctl(semId, 0, SETALL, semun);
342 EPRINTF("IpcSemaphoreCreate: semctl failed (%s) id=%d",
343 strerror(errno), semId);
344 semctl(semId, 0, IPC_RMID, semun);
350 on_shmem_exit(IPCPrivateSemaphoreKill, (caddr_t) semId);
354 EPRINTF("\nIpcSemaphoreCreate, returns %d\n", semId);
362 /****************************************************************************/
363 /* IpcSemaphoreSet() - sets the initial value of the semaphore */
365 /* note: the xxx_return variables are only used for debugging. */
366 /****************************************************************************/
368 static int IpcSemaphoreSet_return;
371 IpcSemaphoreSet(int semId, int semno, int value)
377 errStatus = semctl(semId, semno, SETVAL, semun);
378 IpcSemaphoreSet_return = errStatus;
382 EPRINTF("IpcSemaphoreSet: semctl failed (%s) id=%d",
383 strerror(errno), semId);
389 /****************************************************************************/
390 /* IpcSemaphoreKill(key) - removes a semaphore */
392 /****************************************************************************/
394 IpcSemaphoreKill(IpcSemaphoreKey key)
399 /* kill semaphore if existent */
401 semId = semget(key, 0, 0);
403 semctl(semId, 0, IPC_RMID, semun);
406 /****************************************************************************/
407 /* IpcSemaphoreLock(semId, sem, lock) - locks a semaphore */
409 /* note: the xxx_return variables are only used for debugging. */
410 /****************************************************************************/
411 static int IpcSemaphoreLock_return;
414 IpcSemaphoreLock(IpcSemaphoreId semId, int sem, int lock)
425 * Note: if errStatus is -1 and errno == EINTR then it means we
426 * returned from the operation prematurely because we were
427 * sent a signal. So we try and lock the semaphore again.
428 * I am not certain this is correct, but the semantics aren't
429 * clear it fixes problems with parallel abort synchronization,
430 * namely that after processing an abort signal, the semaphore
431 * call returns with -1 (and errno == EINTR) before it should.
437 errStatus = semop(semId, &sops, 1);
438 } while (errStatus == -1 && errno == EINTR);
440 IpcSemaphoreLock_return = errStatus;
444 EPRINTF("IpcSemaphoreLock: semop failed (%s) id=%d",
445 strerror(errno), semId);
450 /****************************************************************************/
451 /* IpcSemaphoreUnlock(semId, sem, lock) - unlocks a semaphore */
453 /* note: the xxx_return variables are only used for debugging. */
454 /****************************************************************************/
455 static int IpcSemaphoreUnlock_return;
458 IpcSemaphoreUnlock(IpcSemaphoreId semId, int sem, int lock)
470 * Note: if errStatus is -1 and errno == EINTR then it means we
471 * returned from the operation prematurely because we were
472 * sent a signal. So we try and lock the semaphore again.
473 * I am not certain this is correct, but the semantics aren't
474 * clear it fixes problems with parallel abort synchronization,
475 * namely that after processing an abort signal, the semaphore
476 * call returns with -1 (and errno == EINTR) before it should.
482 errStatus = semop(semId, &sops, 1);
483 } while (errStatus == -1 && errno == EINTR);
485 IpcSemaphoreUnlock_return = errStatus;
489 EPRINTF("IpcSemaphoreUnlock: semop failed (%s) id=%d",
490 strerror(errno), semId);
496 IpcSemaphoreGetCount(IpcSemaphoreId semId, int sem)
499 union semun dummy; /* for Solaris */
501 semncnt = semctl(semId, sem, GETNCNT, dummy);
506 IpcSemaphoreGetValue(IpcSemaphoreId semId, int sem)
509 union semun dummy; /* for Solaris */
511 semval = semctl(semId, sem, GETVAL, dummy);
515 /****************************************************************************/
516 /* IpcMemoryCreate(memKey) */
518 /* - returns the memory identifier, if creation succeeds */
519 /* returns IpcMemCreationFailed, if failure */
520 /****************************************************************************/
523 IpcMemoryCreate(IpcMemoryKey memKey, uint32 size, int permission)
527 if (memKey == PrivateIPCKey)
530 shmid = PrivateMemoryCreate(memKey, size);
533 shmid = shmget(memKey, size, IPC_CREAT | permission);
537 EPRINTF("IpcMemoryCreate: shmget failed (%s) "
538 "key=%d, size=%d, permission=%o",
539 strerror(errno), memKey, size, permission);
541 return IpcMemCreationFailed;
544 /* if (memKey == PrivateIPCKey) */
545 on_shmem_exit(IPCPrivateMemoryKill, (caddr_t) shmid);
550 /****************************************************************************/
551 /* IpcMemoryIdGet(memKey, size) returns the shared memory Id */
552 /* or IpcMemIdGetFailed */
553 /****************************************************************************/
555 IpcMemoryIdGet(IpcMemoryKey memKey, uint32 size)
559 shmid = shmget(memKey, size, 0);
563 EPRINTF("IpcMemoryIdGet: shmget failed (%s) "
564 "key=%d, size=%d, permission=%o",
565 strerror(errno), memKey, size, 0);
566 return IpcMemIdGetFailed;
572 /****************************************************************************/
573 /* IpcMemoryDetach(status, shmaddr) removes a shared memory segment */
574 /* from a backend address space */
575 /* (only called by backends running under the postmaster) */
576 /****************************************************************************/
578 IpcMemoryDetach(int status, char *shmaddr)
580 if (shmdt(shmaddr) < 0)
581 elog(NOTICE, "IpcMemoryDetach: shmdt(0x%x): %m", shmaddr);
584 /****************************************************************************/
585 /* IpcMemoryAttach(memId) returns the adress of shared memory */
586 /* or IpcMemAttachFailed */
588 /* CALL IT: addr = (struct <MemoryStructure> *) IpcMemoryAttach(memId); */
590 /****************************************************************************/
592 IpcMemoryAttach(IpcMemoryId memId)
596 if (UsePrivateMemory)
597 memAddress = (char *) PrivateMemoryAttach(memId);
599 memAddress = (char *) shmat(memId, 0, 0);
601 /* if ( *memAddress == -1) { XXX ??? */
602 if (memAddress == (char *) -1)
604 EPRINTF("IpcMemoryAttach: shmat failed (%s) id=%d",
605 strerror(errno), memId);
606 return IpcMemAttachFailed;
609 if (!UsePrivateMemory)
610 on_shmem_exit(IpcMemoryDetach, (caddr_t) memAddress);
612 return (char *) memAddress;
616 /****************************************************************************/
617 /* IpcMemoryKill(memKey) removes a shared memory segment */
618 /* (only called by the postmaster and standalone backends) */
619 /****************************************************************************/
621 IpcMemoryKill(IpcMemoryKey memKey)
625 if (!UsePrivateMemory && (shmid = shmget(memKey, 0, 0)) >= 0)
627 if (shmctl(shmid, IPC_RMID, (struct shmid_ds *) NULL) < 0)
629 elog(NOTICE, "IpcMemoryKill: shmctl(%d, %d, 0) failed: %m",
635 #ifdef HAS_TEST_AND_SET
636 /* ------------------
637 * use hardware locks to replace semaphores for sequent machines
638 * to avoid costs of swapping processes and to provide unlimited
644 SLock *SLockArray = NULL;
646 static SLock **FreeSLockPP;
647 static int *UnusedSLockIP;
648 static slock_t *SLockMemoryLock;
649 static IpcMemoryId SLockMemoryId = -1;
652 { /* to get alignment/size right */
656 SLock slocks[MAX_SPINS + 1];
659 #define SLOCKMEMORYSIZE sizeof(struct ipcdummy)
662 CreateAndInitSLockMemory(IPCKey key)
667 SLockMemoryId = IpcMemoryCreate(key,
670 AttachSLockMemory(key);
672 *UnusedSLockIP = (int) FIRSTFREELOCKID;
673 for (id = 0; id < (int) FIRSTFREELOCKID; id++)
675 slckP = &(SLockArray[id]);
676 S_INIT_LOCK(&(slckP->locklock));
677 slckP->flag = NOLOCK;
679 S_INIT_LOCK(&(slckP->shlock));
680 S_INIT_LOCK(&(slckP->exlock));
681 S_INIT_LOCK(&(slckP->comlock));
688 AttachSLockMemory(IPCKey key)
690 struct ipcdummy *slockM;
692 if (SLockMemoryId == -1)
693 SLockMemoryId = IpcMemoryIdGet(key, SLOCKMEMORYSIZE);
694 if (SLockMemoryId == -1)
695 elog(FATAL, "SLockMemory not in shared memory");
696 slockM = (struct ipcdummy *) IpcMemoryAttach(SLockMemoryId);
697 if (slockM == IpcMemAttachFailed)
698 elog(FATAL, "AttachSLockMemory: could not attach segment");
699 FreeSLockPP = (SLock **) &(slockM->free);
700 UnusedSLockIP = (int *) &(slockM->unused);
701 SLockMemoryLock = (slock_t *) &(slockM->memlock);
702 S_INIT_LOCK(SLockMemoryLock);
703 SLockArray = (SLock *) &(slockM->slocks[0]);
709 LockIsFree(int lockid)
711 return SLockArray[lockid].flag == NOLOCK;
716 #endif /* HAS_TEST_AND_SET */
721 fprintf(stderr, "This type of error is usually caused by an improper\n");
722 fprintf(stderr, "shared memory or System V IPC semaphore configuration.\n");
723 fprintf(stderr, "For more information, see the FAQ and platform-specific\n");
724 fprintf(stderr, "FAQ's in the source directory pgsql/doc or on our\n");
725 fprintf(stderr, "web site at http://www.postgresql.org.\n");