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