]> granicus.if.org Git - postgresql/blob - src/backend/storage/ipc/ipc.c
exit recursion fix from Massimo
[postgresql] / src / backend / storage / ipc / ipc.c
1  /*-------------------------------------------------------------------------
2  *
3  * ipc.c
4  *        POSTGRES inter-process communication definitions.
5  *
6  * Copyright (c) 1994, Regents of the University of California
7  *
8  *
9  * IDENTIFICATION
10  *        $Header: /cvsroot/pgsql/src/backend/storage/ipc/ipc.c,v 1.42 1999/11/06 19:46:57 momjian Exp $
11  *
12  * NOTES
13  *
14  *        Currently, semaphores are used (my understanding anyway) in two
15  *        different ways:
16  *              1. as mutexes on machines that don't have test-and-set (eg.
17  *                 mips R3000).
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.
23  *                                                                                                                -ay 4/95
24  *
25  *-------------------------------------------------------------------------
26  */
27 #include <sys/types.h>
28 #include <sys/file.h>
29 #include <errno.h>
30
31
32 #include "postgres.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 */
36 #include <sys/sem.h>
37 #include <sys/shm.h>
38 #include "utils/memutils.h"
39 #include "libpq/libpq.h"
40 #include "utils/trace.h"
41
42 #if defined(solaris_sparc)
43 #include <sys/ipc.h>
44 #endif
45
46 static int      UsePrivateMemory = 0;
47
48 static void IpcMemoryDetach(int status, char *shmaddr);
49 static void IpcConfigTip(void);
50
51 /* ----------------------------------------------------------------
52  *                                              exit() handling stuff
53  * ----------------------------------------------------------------
54  */
55
56 #define MAX_ON_EXITS 20
57
58 static struct ONEXIT
59 {
60         void            (*function) ();
61         caddr_t         arg;
62 }                       on_proc_exit_list[MAX_ON_EXITS], on_shmem_exit_list[MAX_ON_EXITS];
63
64 static int      on_proc_exit_index,
65                         on_shmem_exit_index;
66
67 typedef struct _PrivateMemStruct
68 {
69         int                     id;
70         char       *memptr;
71 } PrivateMem;
72
73 PrivateMem      IpcPrivateMem[16];
74
75 static int
76 PrivateMemoryCreate(IpcMemoryKey memKey,
77                                         uint32 size)
78 {
79         static int      memid = 0;
80
81         UsePrivateMemory = 1;
82
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 */
88
89         return memid++;
90 }
91
92 static char *
93 PrivateMemoryAttach(IpcMemoryId memid)
94 {
95         return IpcPrivateMem[memid].memptr;
96 }
97
98
99 /* ----------------------------------------------------------------
100  *              proc_exit
101  *
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().
105  *              -cim 2/6/90
106  * ----------------------------------------------------------------
107  */
108 static int      proc_exit_inprogress = 0;
109
110 void
111 proc_exit(int code)
112 {
113         int                     i;
114
115         TPRINTF(TRACE_VERBOSE, "proc_exit(%d) [#%d]", code, proc_exit_inprogress);
116
117         /*
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.
120          */
121
122         if (++proc_exit_inprogress == 9)
123                 elog(ERROR, "infinite recursion in proc_exit");
124         if (proc_exit_inprogress >= 9)
125                 goto exit;
126
127         /* ----------------
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.
131          * ----------------
132          */
133         if (proc_exit_inprogress > 1)
134                 return;
135
136         /* do our shared memory exits first */
137         shmem_exit(code);
138
139         /* ----------------
140          *      call all the callbacks registered before calling exit().
141          * ----------------
142          */
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);
145
146 exit:
147         TPRINTF(TRACE_VERBOSE, "exit(%d)", code);
148         exit(code);
149 }
150
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
155  * ------------------
156  */
157 static int      shmem_exit_inprogress = 0;
158
159 void
160 shmem_exit(int code)
161 {
162         int                     i;
163
164         TPRINTF(TRACE_VERBOSE, "shmem_exit(%d) [#%d]",
165                         code, shmem_exit_inprogress);
166
167         /*
168          * If shmem_exit is called too many times something bad is happenig,
169          * so exit immediately.
170          */
171         if (shmem_exit_inprogress > 9)
172         {
173                 elog(ERROR, "infinite recursion in shmem_exit");
174                 exit(-1);
175         }
176
177         /* ----------------
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.
181          * ----------------
182          */
183         if (shmem_exit_inprogress++)
184                 return;
185
186         /* ----------------
187          *      call all the callbacks registered before calling exit().
188          * ----------------
189          */
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);
192
193         on_shmem_exit_index = 0;
194         shmem_exit_inprogress = 0;
195 }
196
197 /* ----------------------------------------------------------------
198  *              on_proc_exit
199  *
200  *              this function adds a callback function to the list of
201  *              functions invoked by proc_exit().       -cim 2/6/90
202  * ----------------------------------------------------------------
203  */
204 int
205                         on_proc_exit(void (*function) (), caddr_t arg)
206 {
207         if (on_proc_exit_index >= MAX_ON_EXITS)
208                 return -1;
209
210         on_proc_exit_list[on_proc_exit_index].function = function;
211         on_proc_exit_list[on_proc_exit_index].arg = arg;
212
213         ++on_proc_exit_index;
214
215         return 0;
216 }
217
218 /* ----------------------------------------------------------------
219  *              on_shmem_exit
220  *
221  *              this function adds a callback function to the list of
222  *              functions invoked by shmem_exit().      -cim 2/6/90
223  * ----------------------------------------------------------------
224  */
225 int
226                         on_shmem_exit(void (*function) (), caddr_t arg)
227 {
228         if (on_shmem_exit_index >= MAX_ON_EXITS)
229                 return -1;
230
231         on_shmem_exit_list[on_shmem_exit_index].function = function;
232         on_shmem_exit_list[on_shmem_exit_index].arg = arg;
233
234         ++on_shmem_exit_index;
235
236         return 0;
237 }
238
239 /* ----------------------------------------------------------------
240  *              on_exit_reset
241  *
242  *              this function clears all proc_exit() registered functions.
243  * ----------------------------------------------------------------
244  */
245 void
246 on_exit_reset(void)
247 {
248         on_shmem_exit_index = 0;
249         on_proc_exit_index = 0;
250 }
251
252 /****************************************************************************/
253 /*       IPCPrivateSemaphoreKill(status, semId)                                                                 */
254 /*                                                                                                                                                      */
255 /****************************************************************************/
256 static void
257 IPCPrivateSemaphoreKill(int status,
258                                                 int semId)              /* caddr_t */
259 {
260         union semun semun;
261
262         semctl(semId, 0, IPC_RMID, semun);
263 }
264
265
266 /****************************************************************************/
267 /*       IPCPrivateMemoryKill(status, shmId)                                                                    */
268 /*                                                                                                                                                      */
269 /****************************************************************************/
270 static void
271 IPCPrivateMemoryKill(int status,
272                                          int shmId) /* caddr_t */
273 {
274         if (UsePrivateMemory)
275         {
276                 /* free ( IpcPrivateMem[shmId].memptr ); */
277         }
278         else
279         {
280                 if (shmctl(shmId, IPC_RMID, (struct shmid_ds *) NULL) < 0)
281                 {
282                         elog(NOTICE, "IPCPrivateMemoryKill: shmctl(%d, %d, 0) failed: %m",
283                                  shmId, IPC_RMID);
284                 }
285         }
286 }
287
288 /*
289  * Note:
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.
294  *
295  *              Currently, the semaphore sets are "attached" and an error
296  *              is detected only when a later shared memory attach fails.
297  */
298
299 IpcSemaphoreId
300 IpcSemaphoreCreate(IpcSemaphoreKey semKey,
301                                    int semNum,
302                                    int permission,
303                                    int semStartValue,
304                                    int removeOnExit)
305 {
306         int                     i;
307         int                     errStatus;
308         int                     semId;
309         u_short         array[IPC_NMAXSEM];
310         union semun semun;
311
312         /* check arguments      */
313         if (semNum > IPC_NMAXSEM || semNum <= 0)
314                 return(-1);
315
316         semId = semget(semKey, 0, 0);
317
318         if (semId == -1)
319         {
320 #ifdef DEBUG_IPC
321                 EPRINTF("calling semget with %d, %d , %d\n",
322                                 semKey,
323                                 semNum,
324                                 IPC_CREAT | permission);
325 #endif
326                 semId = semget(semKey, semNum, IPC_CREAT | permission);
327
328                 if (semId < 0)
329                 {
330                         EPRINTF("IpcSemaphoreCreate: semget failed (%s) "
331                                         "key=%d, num=%d, permission=%o",
332                                         strerror(errno), semKey, semNum, permission);
333                         IpcConfigTip();
334                         return(-1);
335                 }
336                 for (i = 0; i < semNum; i++)
337                         array[i] = semStartValue;
338                 semun.array = array;
339                 errStatus = semctl(semId, 0, SETALL, semun);
340                 if (errStatus == -1)
341                 {
342                         EPRINTF("IpcSemaphoreCreate: semctl failed (%s) id=%d",
343                                         strerror(errno), semId);
344                         semctl(semId, 0, IPC_RMID, semun);
345                         IpcConfigTip();
346                         return(-1);
347                 }
348
349                 if (removeOnExit)
350                         on_shmem_exit(IPCPrivateSemaphoreKill, (caddr_t) semId);
351         }
352
353 #ifdef DEBUG_IPC
354         EPRINTF("\nIpcSemaphoreCreate, returns %d\n", semId);
355         fflush(stdout);
356         fflush(stderr);
357 #endif
358         return semId;
359 }
360
361
362 /****************************************************************************/
363 /*       IpcSemaphoreSet()                      - sets the initial value of the semaphore       */
364 /*                                                                                                                                                      */
365 /*              note: the xxx_return variables are only used for debugging.                     */
366 /****************************************************************************/
367 #ifdef NOT_USED
368 static int      IpcSemaphoreSet_return;
369
370 void
371 IpcSemaphoreSet(int semId, int semno, int value)
372 {
373         int                     errStatus;
374         union semun semun;
375
376         semun.val = value;
377         errStatus = semctl(semId, semno, SETVAL, semun);
378         IpcSemaphoreSet_return = errStatus;
379
380         if (errStatus == -1)
381         {
382                 EPRINTF("IpcSemaphoreSet: semctl failed (%s) id=%d",
383                                 strerror(errno), semId);
384         }
385 }
386
387 #endif
388
389 /****************************************************************************/
390 /*       IpcSemaphoreKill(key)          - removes a semaphore                                           */
391 /*                                                                                                                                                      */
392 /****************************************************************************/
393 void
394 IpcSemaphoreKill(IpcSemaphoreKey key)
395 {
396         int                     semId;
397         union semun semun;
398
399         /* kill semaphore if existent */
400
401         semId = semget(key, 0, 0);
402         if (semId != -1)
403                 semctl(semId, 0, IPC_RMID, semun);
404 }
405
406 /****************************************************************************/
407 /*       IpcSemaphoreLock(semId, sem, lock) - locks a semaphore                                 */
408 /*                                                                                                                                                      */
409 /*              note: the xxx_return variables are only used for debugging.                     */
410 /****************************************************************************/
411 static int      IpcSemaphoreLock_return;
412
413 void
414 IpcSemaphoreLock(IpcSemaphoreId semId, int sem, int lock)
415 {
416         extern int      errno;
417         int                     errStatus;
418         struct sembuf sops;
419
420         sops.sem_op = lock;
421         sops.sem_flg = 0;
422         sops.sem_num = sem;
423
424         /* ----------------
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.
432          *                -cim 3/28/90
433          * ----------------
434          */
435         do
436         {
437                 errStatus = semop(semId, &sops, 1);
438         } while (errStatus == -1 && errno == EINTR);
439
440         IpcSemaphoreLock_return = errStatus;
441
442         if (errStatus == -1)
443         {
444                 EPRINTF("IpcSemaphoreLock: semop failed (%s) id=%d",
445                                 strerror(errno), semId);
446                 proc_exit(255);
447         }
448 }
449
450 /****************************************************************************/
451 /*       IpcSemaphoreUnlock(semId, sem, lock)           - unlocks a semaphore           */
452 /*                                                                                                                                                      */
453 /*              note: the xxx_return variables are only used for debugging.                     */
454 /****************************************************************************/
455 static int      IpcSemaphoreUnlock_return;
456
457 void
458 IpcSemaphoreUnlock(IpcSemaphoreId semId, int sem, int lock)
459 {
460         extern int      errno;
461         int                     errStatus;
462         struct sembuf sops;
463
464         sops.sem_op = -lock;
465         sops.sem_flg = 0;
466         sops.sem_num = sem;
467
468
469         /* ----------------
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.
477          *                -cim 3/28/90
478          * ----------------
479          */
480         do
481         {
482                 errStatus = semop(semId, &sops, 1);
483         } while (errStatus == -1 && errno == EINTR);
484
485         IpcSemaphoreUnlock_return = errStatus;
486
487         if (errStatus == -1)
488         {
489                 EPRINTF("IpcSemaphoreUnlock: semop failed (%s) id=%d",
490                                 strerror(errno), semId);
491                 proc_exit(255);
492         }
493 }
494
495 int
496 IpcSemaphoreGetCount(IpcSemaphoreId semId, int sem)
497 {
498         int                     semncnt;
499         union semun dummy;                      /* for Solaris */
500
501         semncnt = semctl(semId, sem, GETNCNT, dummy);
502         return semncnt;
503 }
504
505 int
506 IpcSemaphoreGetValue(IpcSemaphoreId semId, int sem)
507 {
508         int                     semval;
509         union semun dummy;                      /* for Solaris */
510
511         semval = semctl(semId, sem, GETVAL, dummy);
512         return semval;
513 }
514
515 /****************************************************************************/
516 /*       IpcMemoryCreate(memKey)                                                                                                */
517 /*                                                                                                                                                      */
518 /*        - returns the memory identifier, if creation succeeds                                 */
519 /*              returns IpcMemCreationFailed, if failure                                                        */
520 /****************************************************************************/
521
522 IpcMemoryId
523 IpcMemoryCreate(IpcMemoryKey memKey, uint32 size, int permission)
524 {
525         IpcMemoryId shmid;
526
527         if (memKey == PrivateIPCKey)
528         {
529                 /* private */
530                 shmid = PrivateMemoryCreate(memKey, size);
531         }
532         else
533                 shmid = shmget(memKey, size, IPC_CREAT | permission);
534
535         if (shmid < 0)
536         {
537                 EPRINTF("IpcMemoryCreate: shmget failed (%s) "
538                                 "key=%d, size=%d, permission=%o",
539                                 strerror(errno), memKey, size, permission);
540                 IpcConfigTip();
541                 return IpcMemCreationFailed;
542         }
543
544         /* if (memKey == PrivateIPCKey) */
545         on_shmem_exit(IPCPrivateMemoryKill, (caddr_t) shmid);
546
547         return shmid;
548 }
549
550 /****************************************************************************/
551 /*      IpcMemoryIdGet(memKey, size)    returns the shared memory Id                    */
552 /*                                                                      or IpcMemIdGetFailed                                    */
553 /****************************************************************************/
554 IpcMemoryId
555 IpcMemoryIdGet(IpcMemoryKey memKey, uint32 size)
556 {
557         IpcMemoryId shmid;
558
559         shmid = shmget(memKey, size, 0);
560
561         if (shmid < 0)
562         {
563                 EPRINTF("IpcMemoryIdGet: shmget failed (%s) "
564                                 "key=%d, size=%d, permission=%o",
565                                 strerror(errno), memKey, size, 0);
566                 return IpcMemIdGetFailed;
567         }
568
569         return shmid;
570 }
571
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 /****************************************************************************/
577 static void
578 IpcMemoryDetach(int status, char *shmaddr)
579 {
580         if (shmdt(shmaddr) < 0)
581                 elog(NOTICE, "IpcMemoryDetach: shmdt(0x%x): %m", shmaddr);
582 }
583
584 /****************************************************************************/
585 /*      IpcMemoryAttach(memId)    returns the adress of shared memory                   */
586 /*                                                        or IpcMemAttachFailed                                                 */
587 /*                                                                                                                                                      */
588 /* CALL IT:  addr = (struct <MemoryStructure> *) IpcMemoryAttach(memId);        */
589 /*                                                                                                                                                      */
590 /****************************************************************************/
591 char *
592 IpcMemoryAttach(IpcMemoryId memId)
593 {
594         char       *memAddress;
595
596         if (UsePrivateMemory)
597                 memAddress = (char *) PrivateMemoryAttach(memId);
598         else
599                 memAddress = (char *) shmat(memId, 0, 0);
600
601         /* if ( *memAddress == -1) { XXX ??? */
602         if (memAddress == (char *) -1)
603         {
604                 EPRINTF("IpcMemoryAttach: shmat failed (%s) id=%d",
605                                 strerror(errno), memId);
606                 return IpcMemAttachFailed;
607         }
608
609         if (!UsePrivateMemory)
610                 on_shmem_exit(IpcMemoryDetach, (caddr_t) memAddress);
611
612         return (char *) memAddress;
613 }
614
615
616 /****************************************************************************/
617 /*      IpcMemoryKill(memKey)                           removes a shared memory segment         */
618 /*      (only called by the postmaster and standalone backends)                                 */
619 /****************************************************************************/
620 void
621 IpcMemoryKill(IpcMemoryKey memKey)
622 {
623         IpcMemoryId shmid;
624
625         if (!UsePrivateMemory && (shmid = shmget(memKey, 0, 0)) >= 0)
626         {
627                 if (shmctl(shmid, IPC_RMID, (struct shmid_ds *) NULL) < 0)
628                 {
629                         elog(NOTICE, "IpcMemoryKill: shmctl(%d, %d, 0) failed: %m",
630                                  shmid, IPC_RMID);
631                 }
632         }
633 }
634
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
639  *      supply of locks.
640  * ------------------
641  */
642
643 /* used in spin.c */
644 SLock      *SLockArray = NULL;
645
646 static SLock **FreeSLockPP;
647 static int *UnusedSLockIP;
648 static slock_t *SLockMemoryLock;
649 static IpcMemoryId SLockMemoryId = -1;
650
651 struct ipcdummy
652 {                                                               /* to get alignment/size right */
653         SLock      *free;
654         int                     unused;
655         slock_t         memlock;
656         SLock           slocks[MAX_SPINS + 1];
657 };
658
659 #define SLOCKMEMORYSIZE         sizeof(struct ipcdummy)
660
661 void
662 CreateAndInitSLockMemory(IPCKey key)
663 {
664         int                     id;
665         SLock      *slckP;
666
667         SLockMemoryId = IpcMemoryCreate(key,
668                                                                         SLOCKMEMORYSIZE,
669                                                                         0700);
670         AttachSLockMemory(key);
671         *FreeSLockPP = NULL;
672         *UnusedSLockIP = (int) FIRSTFREELOCKID;
673         for (id = 0; id < (int) FIRSTFREELOCKID; id++)
674         {
675                 slckP = &(SLockArray[id]);
676                 S_INIT_LOCK(&(slckP->locklock));
677                 slckP->flag = NOLOCK;
678                 slckP->nshlocks = 0;
679                 S_INIT_LOCK(&(slckP->shlock));
680                 S_INIT_LOCK(&(slckP->exlock));
681                 S_INIT_LOCK(&(slckP->comlock));
682                 slckP->next = NULL;
683         }
684         return;
685 }
686
687 void
688 AttachSLockMemory(IPCKey key)
689 {
690         struct ipcdummy *slockM;
691
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]);
704         return;
705 }
706
707 #ifdef NOT_USED
708 bool
709 LockIsFree(int lockid)
710 {
711         return SLockArray[lockid].flag == NOLOCK;
712 }
713
714 #endif
715
716 #endif   /* HAS_TEST_AND_SET */
717
718 static void
719 IpcConfigTip(void)
720 {
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");
726 }