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.33 1998/09/01 04:31:47 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>
35 #include "storage/ipc.h"
36 #include "storage/s_lock.h"
37 /* In Ultrix, sem.h and shm.h must be included AFTER ipc.h */
40 #include "utils/memutils.h"
41 #include "libpq/libpq.h"
42 #include "utils/trace.h"
44 #if defined(solaris_sparc)
49 static int UsePrivateMemory = 0;
51 static void IpcMemoryDetach(int status, char *shmaddr);
53 /* ----------------------------------------------------------------
54 * exit() handling stuff
55 * ----------------------------------------------------------------
58 #define MAX_ON_EXITS 20
64 } on_proc_exit_list[MAX_ON_EXITS], on_shmem_exit_list[MAX_ON_EXITS];
66 static int on_proc_exit_index,
68 static void IpcConfigTip(void);
70 typedef struct _PrivateMemStruct
76 PrivateMem IpcPrivateMem[16];
79 PrivateMemoryCreate(IpcMemoryKey memKey,
86 IpcPrivateMem[memid].id = memid;
87 IpcPrivateMem[memid].memptr = malloc(size);
88 if (IpcPrivateMem[memid].memptr == NULL)
89 elog(ERROR, "PrivateMemoryCreate: not enough memory to malloc");
90 MemSet(IpcPrivateMem[memid].memptr, 0, size); /* XXX PURIFY */
96 PrivateMemoryAttach(IpcMemoryId memid)
98 return IpcPrivateMem[memid].memptr;
102 /* ----------------------------------------------------------------
105 * this function calls all the callbacks registered
106 * for it (to free resources) and then calls exit.
107 * This should be the only function to call exit().
109 * ----------------------------------------------------------------
111 static int proc_exit_inprogress = 0;
118 TPRINTF(TRACE_VERBOSE, "proc_exit(%d) [#%d]", code, proc_exit_inprogress);
121 * If proc_exit is called too many times something bad is happenig, so
124 if (proc_exit_inprogress > 9)
126 elog(ERROR, "infinite recursion in proc_exit");
131 * if proc_exit_inprocess is true, then it means that we
132 * are being invoked from within an on_exit() handler
133 * and so we return immediately to avoid recursion.
136 if (proc_exit_inprogress++)
139 /* do our shared memory exits first */
143 * call all the callbacks registered before calling exit().
146 for (i = on_proc_exit_index - 1; i >= 0; --i)
147 (*on_proc_exit_list[i].function) (code, on_proc_exit_list[i].arg);
150 TPRINTF(TRACE_VERBOSE, "exit(%d)", code);
154 /* ------------------
155 * Run all of the on_shmem_exit routines but don't exit in the end.
156 * This is used by the postmaster to re-initialize shared memory and
157 * semaphores after a backend dies horribly
160 static int shmem_exit_inprogress = 0;
167 TPRINTF(TRACE_VERBOSE, "shmem_exit(%d) [#%d]",
168 code, shmem_exit_inprogress);
171 * If shmem_exit is called too many times something bad is happenig,
172 * so exit immediately.
174 if (shmem_exit_inprogress > 9)
176 elog(ERROR, "infinite recursion in shmem_exit");
181 * if shmem_exit_inprocess is true, then it means that we
182 * are being invoked from within an on_exit() handler
183 * and so we return immediately to avoid recursion.
186 if (shmem_exit_inprogress++)
190 * call all the callbacks registered before calling exit().
193 for (i = on_shmem_exit_index - 1; i >= 0; --i)
194 (*on_shmem_exit_list[i].function) (code, on_shmem_exit_list[i].arg);
196 on_shmem_exit_index = 0;
197 shmem_exit_inprogress = 0;
200 /* ----------------------------------------------------------------
203 * this function adds a callback function to the list of
204 * functions invoked by proc_exit(). -cim 2/6/90
205 * ----------------------------------------------------------------
208 on_proc_exit(void (*function) (), caddr_t arg)
210 if (on_proc_exit_index >= MAX_ON_EXITS)
213 on_proc_exit_list[on_proc_exit_index].function = function;
214 on_proc_exit_list[on_proc_exit_index].arg = arg;
216 ++on_proc_exit_index;
221 /* ----------------------------------------------------------------
224 * this function adds a callback function to the list of
225 * functions invoked by shmem_exit(). -cim 2/6/90
226 * ----------------------------------------------------------------
229 on_shmem_exit(void (*function) (), caddr_t arg)
231 if (on_shmem_exit_index >= MAX_ON_EXITS)
234 on_shmem_exit_list[on_shmem_exit_index].function = function;
235 on_shmem_exit_list[on_shmem_exit_index].arg = arg;
237 ++on_shmem_exit_index;
242 /* ----------------------------------------------------------------
245 * this function clears all proc_exit() registered functions.
246 * ----------------------------------------------------------------
251 on_shmem_exit_index = 0;
252 on_proc_exit_index = 0;
255 /****************************************************************************/
256 /* IPCPrivateSemaphoreKill(status, semId) */
258 /****************************************************************************/
260 IPCPrivateSemaphoreKill(int status,
261 int semId) /* caddr_t */
265 semctl(semId, 0, IPC_RMID, semun);
269 /****************************************************************************/
270 /* IPCPrivateMemoryKill(status, shmId) */
272 /****************************************************************************/
274 IPCPrivateMemoryKill(int status,
275 int shmId) /* caddr_t */
277 if (UsePrivateMemory)
279 /* free ( IpcPrivateMem[shmId].memptr ); */
283 if (shmctl(shmId, IPC_RMID, (struct shmid_ds *) NULL) < 0)
285 elog(NOTICE, "IPCPrivateMemoryKill: shmctl(%d, %d, 0) failed: %m",
292 /****************************************************************************/
293 /* IpcSemaphoreCreate(semKey, semNum, permission, semStartValue) */
295 /* - returns a semaphore identifier: */
297 /* if key doesn't exist: return a new id, status:= IpcSemIdNotExist */
298 /* if key exists: return the old id, status:= IpcSemIdExist */
299 /* if semNum > MAX : return # of argument, status:=IpcInvalidArgument */
301 /****************************************************************************/
305 * XXX This should be split into two different calls. One should
306 * XXX be used to create a semaphore set. The other to "attach" a
307 * XXX existing set. It should be an error for the semaphore set
308 * XXX to to already exist or for it not to, respectively.
310 * Currently, the semaphore sets are "attached" and an error
311 * is detected only when a later shared memory attach fails.
315 IpcSemaphoreCreate(IpcSemaphoreKey semKey,
325 u_short array[IPC_NMAXSEM];
328 /* get a semaphore if non-existent */
329 /* check arguments */
330 if (semNum > IPC_NMAXSEM || semNum <= 0)
332 *status = IpcInvalidArgument;
333 return 2; /* returns the number of the invalid
337 semId = semget(semKey, 0, 0);
341 *status = IpcSemIdNotExist; /* there doesn't exist a semaphore */
343 EPRINTF("calling semget with %d, %d , %d\n",
346 IPC_CREAT | permission);
348 semId = semget(semKey, semNum, IPC_CREAT | permission);
352 EPRINTF("IpcSemaphoreCreate: semget failed (%s) "
353 "key=%d, num=%d, permission=%o",
354 strerror(errno), semKey, semNum, permission);
357 for (i = 0; i < semNum; i++)
358 array[i] = semStartValue;
360 errStatus = semctl(semId, 0, SETALL, semun);
363 EPRINTF("IpcSemaphoreCreate: semctl failed (%s) id=%d",
364 strerror(errno), semId);
368 on_shmem_exit(IPCPrivateSemaphoreKill, (caddr_t) semId);
373 /* there is a semaphore id for this key */
374 *status = IpcSemIdExist;
378 EPRINTF("\nIpcSemaphoreCreate, status %d, returns %d\n",
388 /****************************************************************************/
389 /* IpcSemaphoreSet() - sets the initial value of the semaphore */
391 /* note: the xxx_return variables are only used for debugging. */
392 /****************************************************************************/
394 static int IpcSemaphoreSet_return;
397 IpcSemaphoreSet(int semId, int semno, int value)
403 errStatus = semctl(semId, semno, SETVAL, semun);
404 IpcSemaphoreSet_return = errStatus;
408 EPRINTF("IpcSemaphoreSet: semctl failed (%s) id=%d",
409 strerror(errno), semId);
415 /****************************************************************************/
416 /* IpcSemaphoreKill(key) - removes a semaphore */
418 /****************************************************************************/
420 IpcSemaphoreKill(IpcSemaphoreKey key)
425 /* kill semaphore if existent */
427 semId = semget(key, 0, 0);
429 semctl(semId, 0, IPC_RMID, semun);
432 /****************************************************************************/
433 /* IpcSemaphoreLock(semId, sem, lock) - locks a semaphore */
435 /* note: the xxx_return variables are only used for debugging. */
436 /****************************************************************************/
437 static int IpcSemaphoreLock_return;
440 IpcSemaphoreLock(IpcSemaphoreId semId, int sem, int lock)
451 * Note: if errStatus is -1 and errno == EINTR then it means we
452 * returned from the operation prematurely because we were
453 * sent a signal. So we try and lock the semaphore again.
454 * I am not certain this is correct, but the semantics aren't
455 * clear it fixes problems with parallel abort synchronization,
456 * namely that after processing an abort signal, the semaphore
457 * call returns with -1 (and errno == EINTR) before it should.
463 errStatus = semop(semId, &sops, 1);
464 } while (errStatus == -1 && errno == EINTR);
466 IpcSemaphoreLock_return = errStatus;
470 EPRINTF("IpcSemaphoreLock: semop failed (%s) id=%d",
471 strerror(errno), semId);
476 /****************************************************************************/
477 /* IpcSemaphoreUnlock(semId, sem, lock) - unlocks a semaphore */
479 /* note: the xxx_return variables are only used for debugging. */
480 /****************************************************************************/
481 static int IpcSemaphoreUnlock_return;
484 IpcSemaphoreUnlock(IpcSemaphoreId semId, int sem, int lock)
496 * Note: if errStatus is -1 and errno == EINTR then it means we
497 * returned from the operation prematurely because we were
498 * sent a signal. So we try and lock the semaphore again.
499 * I am not certain this is correct, but the semantics aren't
500 * clear it fixes problems with parallel abort synchronization,
501 * namely that after processing an abort signal, the semaphore
502 * call returns with -1 (and errno == EINTR) before it should.
508 errStatus = semop(semId, &sops, 1);
509 } while (errStatus == -1 && errno == EINTR);
511 IpcSemaphoreUnlock_return = errStatus;
515 EPRINTF("IpcSemaphoreUnlock: semop failed (%s) id=%d",
516 strerror(errno), semId);
522 IpcSemaphoreGetCount(IpcSemaphoreId semId, int sem)
525 union semun dummy; /* for Solaris */
527 semncnt = semctl(semId, sem, GETNCNT, dummy);
532 IpcSemaphoreGetValue(IpcSemaphoreId semId, int sem)
535 union semun dummy; /* for Solaris */
537 semval = semctl(semId, sem, GETVAL, dummy);
541 /****************************************************************************/
542 /* IpcMemoryCreate(memKey) */
544 /* - returns the memory identifier, if creation succeeds */
545 /* returns IpcMemCreationFailed, if failure */
546 /****************************************************************************/
549 IpcMemoryCreate(IpcMemoryKey memKey, uint32 size, int permission)
553 if (memKey == PrivateIPCKey)
556 shmid = PrivateMemoryCreate(memKey, size);
559 shmid = shmget(memKey, size, IPC_CREAT | permission);
563 EPRINTF("IpcMemoryCreate: shmget failed (%s) "
564 "key=%d, size=%d, permission=%o",
565 strerror(errno), memKey, size, permission);
566 return IpcMemCreationFailed;
569 /* if (memKey == PrivateIPCKey) */
570 on_shmem_exit(IPCPrivateMemoryKill, (caddr_t) shmid);
575 /****************************************************************************/
576 /* IpcMemoryIdGet(memKey, size) returns the shared memory Id */
577 /* or IpcMemIdGetFailed */
578 /****************************************************************************/
580 IpcMemoryIdGet(IpcMemoryKey memKey, uint32 size)
584 shmid = shmget(memKey, size, 0);
588 EPRINTF("IpcMemoryIdGet: shmget failed (%s) "
589 "key=%d, size=%d, permission=%o",
590 strerror(errno), memKey, size, 0);
591 return IpcMemIdGetFailed;
597 /****************************************************************************/
598 /* IpcMemoryDetach(status, shmaddr) removes a shared memory segment */
599 /* from a backend address space */
600 /* (only called by backends running under the postmaster) */
601 /****************************************************************************/
603 IpcMemoryDetach(int status, char *shmaddr)
605 if (shmdt(shmaddr) < 0)
606 elog(NOTICE, "IpcMemoryDetach: shmdt(0x%x): %m", shmaddr);
609 /****************************************************************************/
610 /* IpcMemoryAttach(memId) returns the adress of shared memory */
611 /* or IpcMemAttachFailed */
613 /* CALL IT: addr = (struct <MemoryStructure> *) IpcMemoryAttach(memId); */
615 /****************************************************************************/
617 IpcMemoryAttach(IpcMemoryId memId)
621 if (UsePrivateMemory)
622 memAddress = (char *) PrivateMemoryAttach(memId);
624 memAddress = (char *) shmat(memId, 0, 0);
626 /* if ( *memAddress == -1) { XXX ??? */
627 if (memAddress == (char *) -1)
629 EPRINTF("IpcMemoryAttach: shmat failed (%s) id=%d",
630 strerror(errno), memId);
631 return IpcMemAttachFailed;
634 if (!UsePrivateMemory)
635 on_shmem_exit(IpcMemoryDetach, (caddr_t) memAddress);
637 return (char *) memAddress;
641 /****************************************************************************/
642 /* IpcMemoryKill(memKey) removes a shared memory segment */
643 /* (only called by the postmaster and standalone backends) */
644 /****************************************************************************/
646 IpcMemoryKill(IpcMemoryKey memKey)
650 if (!UsePrivateMemory && (shmid = shmget(memKey, 0, 0)) >= 0)
652 if (shmctl(shmid, IPC_RMID, (struct shmid_ds *) NULL) < 0)
654 elog(NOTICE, "IpcMemoryKill: shmctl(%d, %d, 0) failed: %m",
660 #ifdef HAS_TEST_AND_SET
661 /* ------------------
662 * use hardware locks to replace semaphores for sequent machines
663 * to avoid costs of swapping processes and to provide unlimited
669 SLock *SLockArray = NULL;
671 static SLock **FreeSLockPP;
672 static int *UnusedSLockIP;
673 static slock_t *SLockMemoryLock;
674 static IpcMemoryId SLockMemoryId = -1;
677 { /* to get alignment/size right */
681 SLock slocks[NSLOCKS];
683 static int SLockMemorySize = sizeof(struct ipcdummy);
686 CreateAndInitSLockMemory(IPCKey key)
691 SLockMemoryId = IpcMemoryCreate(key,
694 AttachSLockMemory(key);
696 *UnusedSLockIP = (int) FIRSTFREELOCKID;
697 for (id = 0; id < (int) FIRSTFREELOCKID; id++)
699 slckP = &(SLockArray[id]);
700 S_INIT_LOCK(&(slckP->locklock));
701 slckP->flag = NOLOCK;
703 S_INIT_LOCK(&(slckP->shlock));
704 S_INIT_LOCK(&(slckP->exlock));
705 S_INIT_LOCK(&(slckP->comlock));
712 AttachSLockMemory(IPCKey key)
714 struct ipcdummy *slockM;
716 if (SLockMemoryId == -1)
717 SLockMemoryId = IpcMemoryIdGet(key, SLockMemorySize);
718 if (SLockMemoryId == -1)
719 elog(FATAL, "SLockMemory not in shared memory");
720 slockM = (struct ipcdummy *) IpcMemoryAttach(SLockMemoryId);
721 if (slockM == IpcMemAttachFailed)
722 elog(FATAL, "AttachSLockMemory: could not attach segment");
723 FreeSLockPP = (SLock **) &(slockM->free);
724 UnusedSLockIP = (int *) &(slockM->unused);
725 SLockMemoryLock = (slock_t *) &(slockM->memlock);
726 S_INIT_LOCK(SLockMemoryLock);
727 SLockArray = (SLock *) &(slockM->slocks[0]);
733 LockIsFree(int lockid)
735 return SLockArray[lockid].flag == NOLOCK;
740 #endif /* HAS_TEST_AND_SET */
745 fprintf(stderr, "This type of error is usually caused by improper\n");
746 fprintf(stderr, "shared memory or System V IPC semaphore configuration.\n");
747 fprintf(stderr, "See the FAQ for more detailed information\n");