LCOV - code coverage report
Current view: top level - src/backend/port - pg_sema.c (source / functions) Hit Total Coverage
Test: PostgreSQL 18devel Lines: 42 59 71.2 %
Date: 2024-11-21 08:14:44 Functions: 9 10 90.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * posix_sema.c
       4             :  *    Implement PGSemaphores using POSIX semaphore facilities
       5             :  *
       6             :  * We prefer the unnamed style of POSIX semaphore (the kind made with
       7             :  * sem_init).  We can cope with the kind made with sem_open, however.
       8             :  *
       9             :  * In either implementation, typedef PGSemaphore is equivalent to "sem_t *".
      10             :  * With unnamed semaphores, the sem_t structs live in an array in shared
      11             :  * memory.  With named semaphores, that's not true because we cannot persuade
      12             :  * sem_open to do its allocation there.  Therefore, the named-semaphore code
      13             :  * *does not cope with EXEC_BACKEND*.  The sem_t structs will just be in the
      14             :  * postmaster's private memory, where they are successfully inherited by
      15             :  * forked backends, but they could not be accessed by exec'd backends.
      16             :  *
      17             :  *
      18             :  * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
      19             :  * Portions Copyright (c) 1994, Regents of the University of California
      20             :  *
      21             :  * IDENTIFICATION
      22             :  *    src/backend/port/posix_sema.c
      23             :  *
      24             :  *-------------------------------------------------------------------------
      25             :  */
      26             : #include "postgres.h"
      27             : 
      28             : #include <fcntl.h>
      29             : #include <semaphore.h>
      30             : #include <signal.h>
      31             : #include <unistd.h>
      32             : #include <sys/stat.h>
      33             : 
      34             : #include "miscadmin.h"
      35             : #include "storage/ipc.h"
      36             : #include "storage/pg_sema.h"
      37             : #include "storage/shmem.h"
      38             : 
      39             : 
      40             : /* see file header comment */
      41             : #if defined(USE_NAMED_POSIX_SEMAPHORES) && defined(EXEC_BACKEND)
      42             : #error cannot use named POSIX semaphores with EXEC_BACKEND
      43             : #endif
      44             : 
      45             : typedef union SemTPadded
      46             : {
      47             :     sem_t       pgsem;
      48             :     char        pad[PG_CACHE_LINE_SIZE];
      49             : } SemTPadded;
      50             : 
      51             : /* typedef PGSemaphore is equivalent to pointer to sem_t */
      52             : typedef struct PGSemaphoreData
      53             : {
      54             :     SemTPadded  sem_padded;
      55             : } PGSemaphoreData;
      56             : 
      57             : #define PG_SEM_REF(x)   (&(x)->sem_padded.pgsem)
      58             : 
      59             : #define IPCProtection   (0600)  /* access/modify by user only */
      60             : 
      61             : #ifdef USE_NAMED_POSIX_SEMAPHORES
      62             : static sem_t **mySemPointers;   /* keep track of created semaphores */
      63             : #else
      64             : static PGSemaphore sharedSemas; /* array of PGSemaphoreData in shared memory */
      65             : #endif
      66             : static int  numSems;            /* number of semas acquired so far */
      67             : static int  maxSems;            /* allocated size of above arrays */
      68             : static int  nextSemKey;         /* next name to try */
      69             : 
      70             : 
      71             : static void ReleaseSemaphores(int status, Datum arg);
      72             : 
      73             : 
      74             : #ifdef USE_NAMED_POSIX_SEMAPHORES
      75             : 
      76             : /*
      77             :  * PosixSemaphoreCreate
      78             :  *
      79             :  * Attempt to create a new named semaphore.
      80             :  *
      81             :  * If we fail with a failure code other than collision-with-existing-sema,
      82             :  * print out an error and abort.  Other types of errors suggest nonrecoverable
      83             :  * problems.
      84             :  */
      85             : static sem_t *
      86             : PosixSemaphoreCreate(void)
      87             : {
      88             :     int         semKey;
      89             :     char        semname[64];
      90             :     sem_t      *mySem;
      91             : 
      92             :     for (;;)
      93             :     {
      94             :         semKey = nextSemKey++;
      95             : 
      96             :         snprintf(semname, sizeof(semname), "/pgsql-%d", semKey);
      97             : 
      98             :         mySem = sem_open(semname, O_CREAT | O_EXCL,
      99             :                          (mode_t) IPCProtection, (unsigned) 1);
     100             : 
     101             : #ifdef SEM_FAILED
     102             :         if (mySem != (sem_t *) SEM_FAILED)
     103             :             break;
     104             : #else
     105             :         if (mySem != (sem_t *) (-1))
     106             :             break;
     107             : #endif
     108             : 
     109             :         /* Loop if error indicates a collision */
     110             :         if (errno == EEXIST || errno == EACCES || errno == EINTR)
     111             :             continue;
     112             : 
     113             :         /*
     114             :          * Else complain and abort
     115             :          */
     116             :         elog(FATAL, "sem_open(\"%s\") failed: %m", semname);
     117             :     }
     118             : 
     119             :     /*
     120             :      * Unlink the semaphore immediately, so it can't be accessed externally.
     121             :      * This also ensures that it will go away if we crash.
     122             :      */
     123             :     sem_unlink(semname);
     124             : 
     125             :     return mySem;
     126             : }
     127             : #else                           /* !USE_NAMED_POSIX_SEMAPHORES */
     128             : 
     129             : /*
     130             :  * PosixSemaphoreCreate
     131             :  *
     132             :  * Attempt to create a new unnamed semaphore.
     133             :  */
     134             : static void
     135      169538 : PosixSemaphoreCreate(sem_t *sem)
     136             : {
     137      169538 :     if (sem_init(sem, 1, 1) < 0)
     138           0 :         elog(FATAL, "sem_init failed: %m");
     139      169538 : }
     140             : #endif                          /* USE_NAMED_POSIX_SEMAPHORES */
     141             : 
     142             : 
     143             : /*
     144             :  * PosixSemaphoreKill   - removes a semaphore
     145             :  */
     146             : static void
     147      168830 : PosixSemaphoreKill(sem_t *sem)
     148             : {
     149             : #ifdef USE_NAMED_POSIX_SEMAPHORES
     150             :     /* Got to use sem_close for named semaphores */
     151             :     if (sem_close(sem) < 0)
     152             :         elog(LOG, "sem_close failed: %m");
     153             : #else
     154             :     /* Got to use sem_destroy for unnamed semaphores */
     155      168830 :     if (sem_destroy(sem) < 0)
     156           0 :         elog(LOG, "sem_destroy failed: %m");
     157             : #endif
     158      168830 : }
     159             : 
     160             : 
     161             : /*
     162             :  * Report amount of shared memory needed for semaphores
     163             :  */
     164             : Size
     165        5436 : PGSemaphoreShmemSize(int maxSemas)
     166             : {
     167             : #ifdef USE_NAMED_POSIX_SEMAPHORES
     168             :     /* No shared memory needed in this case */
     169             :     return 0;
     170             : #else
     171             :     /* Need a PGSemaphoreData per semaphore */
     172        5436 :     return mul_size(maxSemas, sizeof(PGSemaphoreData));
     173             : #endif
     174             : }
     175             : 
     176             : /*
     177             :  * PGReserveSemaphores --- initialize semaphore support
     178             :  *
     179             :  * This is called during postmaster start or shared memory reinitialization.
     180             :  * It should do whatever is needed to be able to support up to maxSemas
     181             :  * subsequent PGSemaphoreCreate calls.  Also, if any system resources
     182             :  * are acquired here or in PGSemaphoreCreate, register an on_shmem_exit
     183             :  * callback to release them.
     184             :  *
     185             :  * In the Posix implementation, we acquire semaphores on-demand; the
     186             :  * maxSemas parameter is just used to size the arrays.  For unnamed
     187             :  * semaphores, there is an array of PGSemaphoreData structs in shared memory.
     188             :  * For named semaphores, we keep a postmaster-local array of sem_t pointers,
     189             :  * which we use for releasing the semaphores when done.
     190             :  * (This design minimizes the dependency of postmaster shutdown on the
     191             :  * contents of shared memory, which a failed backend might have clobbered.
     192             :  * We can't do much about the possibility of sem_destroy() crashing, but
     193             :  * we don't have to expose the counters to other processes.)
     194             :  */
     195             : void
     196        1902 : PGReserveSemaphores(int maxSemas)
     197             : {
     198             :     struct stat statbuf;
     199             : 
     200             :     /*
     201             :      * We use the data directory's inode number to seed the search for free
     202             :      * semaphore keys.  This minimizes the odds of collision with other
     203             :      * postmasters, while maximizing the odds that we will detect and clean up
     204             :      * semaphores left over from a crashed postmaster in our own directory.
     205             :      */
     206        1902 :     if (stat(DataDir, &statbuf) < 0)
     207           0 :         ereport(FATAL,
     208             :                 (errcode_for_file_access(),
     209             :                  errmsg("could not stat data directory \"%s\": %m",
     210             :                         DataDir)));
     211             : 
     212             : #ifdef USE_NAMED_POSIX_SEMAPHORES
     213             :     mySemPointers = (sem_t **) malloc(maxSemas * sizeof(sem_t *));
     214             :     if (mySemPointers == NULL)
     215             :         elog(PANIC, "out of memory");
     216             : #else
     217             : 
     218             :     /*
     219             :      * We must use ShmemAllocUnlocked(), since the spinlock protecting
     220             :      * ShmemAlloc() won't be ready yet.
     221             :      */
     222        1902 :     sharedSemas = (PGSemaphore)
     223        1902 :         ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
     224             : #endif
     225             : 
     226        1902 :     numSems = 0;
     227        1902 :     maxSems = maxSemas;
     228        1902 :     nextSemKey = statbuf.st_ino;
     229             : 
     230        1902 :     on_shmem_exit(ReleaseSemaphores, 0);
     231        1902 : }
     232             : 
     233             : /*
     234             :  * Release semaphores at shutdown or shmem reinitialization
     235             :  *
     236             :  * (called as an on_shmem_exit callback, hence funny argument list)
     237             :  */
     238             : static void
     239        1896 : ReleaseSemaphores(int status, Datum arg)
     240             : {
     241             :     int         i;
     242             : 
     243             : #ifdef USE_NAMED_POSIX_SEMAPHORES
     244             :     for (i = 0; i < numSems; i++)
     245             :         PosixSemaphoreKill(mySemPointers[i]);
     246             :     free(mySemPointers);
     247             : #endif
     248             : 
     249             : #ifdef USE_UNNAMED_POSIX_SEMAPHORES
     250      170726 :     for (i = 0; i < numSems; i++)
     251      168830 :         PosixSemaphoreKill(PG_SEM_REF(sharedSemas + i));
     252             : #endif
     253        1896 : }
     254             : 
     255             : /*
     256             :  * PGSemaphoreCreate
     257             :  *
     258             :  * Allocate a PGSemaphore structure with initial count 1
     259             :  */
     260             : PGSemaphore
     261      169538 : PGSemaphoreCreate(void)
     262             : {
     263             :     PGSemaphore sema;
     264             :     sem_t      *newsem;
     265             : 
     266             :     /* Can't do this in a backend, because static state is postmaster's */
     267             :     Assert(!IsUnderPostmaster);
     268             : 
     269      169538 :     if (numSems >= maxSems)
     270           0 :         elog(PANIC, "too many semaphores created");
     271             : 
     272             : #ifdef USE_NAMED_POSIX_SEMAPHORES
     273             :     newsem = PosixSemaphoreCreate();
     274             :     /* Remember new sema for ReleaseSemaphores */
     275             :     mySemPointers[numSems] = newsem;
     276             :     sema = (PGSemaphore) newsem;
     277             : #else
     278      169538 :     sema = &sharedSemas[numSems];
     279      169538 :     newsem = PG_SEM_REF(sema);
     280      169538 :     PosixSemaphoreCreate(newsem);
     281             : #endif
     282             : 
     283      169538 :     numSems++;
     284             : 
     285      169538 :     return sema;
     286             : }
     287             : 
     288             : /*
     289             :  * PGSemaphoreReset
     290             :  *
     291             :  * Reset a previously-initialized PGSemaphore to have count 0
     292             :  */
     293             : void
     294       55646 : PGSemaphoreReset(PGSemaphore sema)
     295             : {
     296             :     /*
     297             :      * There's no direct API for this in POSIX, so we have to ratchet the
     298             :      * semaphore down to 0 with repeated trywait's.
     299             :      */
     300             :     for (;;)
     301             :     {
     302       55646 :         if (sem_trywait(PG_SEM_REF(sema)) < 0)
     303             :         {
     304       35542 :             if (errno == EAGAIN || errno == EDEADLK)
     305             :                 break;          /* got it down to 0 */
     306           0 :             if (errno == EINTR)
     307           0 :                 continue;       /* can this happen? */
     308           0 :             elog(FATAL, "sem_trywait failed: %m");
     309             :         }
     310             :     }
     311       35542 : }
     312             : 
     313             : /*
     314             :  * PGSemaphoreLock
     315             :  *
     316             :  * Lock a semaphore (decrement count), blocking if count would be < 0
     317             :  */
     318             : void
     319     3439910 : PGSemaphoreLock(PGSemaphore sema)
     320             : {
     321             :     int         errStatus;
     322             : 
     323             :     /* See notes in sysv_sema.c's implementation of PGSemaphoreLock. */
     324             :     do
     325             :     {
     326     3439910 :         errStatus = sem_wait(PG_SEM_REF(sema));
     327     3439910 :     } while (errStatus < 0 && errno == EINTR);
     328             : 
     329     3439910 :     if (errStatus < 0)
     330           0 :         elog(FATAL, "sem_wait failed: %m");
     331     3439910 : }
     332             : 
     333             : /*
     334             :  * PGSemaphoreUnlock
     335             :  *
     336             :  * Unlock a semaphore (increment count)
     337             :  */
     338             : void
     339     3439968 : PGSemaphoreUnlock(PGSemaphore sema)
     340             : {
     341             :     int         errStatus;
     342             : 
     343             :     /*
     344             :      * Note: if errStatus is -1 and errno == EINTR then it means we returned
     345             :      * from the operation prematurely because we were sent a signal.  So we
     346             :      * try and unlock the semaphore again. Not clear this can really happen,
     347             :      * but might as well cope.
     348             :      */
     349             :     do
     350             :     {
     351     3439968 :         errStatus = sem_post(PG_SEM_REF(sema));
     352     3439968 :     } while (errStatus < 0 && errno == EINTR);
     353             : 
     354     3439968 :     if (errStatus < 0)
     355           0 :         elog(FATAL, "sem_post failed: %m");
     356     3439968 : }
     357             : 
     358             : /*
     359             :  * PGSemaphoreTryLock
     360             :  *
     361             :  * Lock a semaphore only if able to do so without blocking
     362             :  */
     363             : bool
     364           0 : PGSemaphoreTryLock(PGSemaphore sema)
     365             : {
     366             :     int         errStatus;
     367             : 
     368             :     /*
     369             :      * Note: if errStatus is -1 and errno == EINTR then it means we returned
     370             :      * from the operation prematurely because we were sent a signal.  So we
     371             :      * try and lock the semaphore again.
     372             :      */
     373             :     do
     374             :     {
     375           0 :         errStatus = sem_trywait(PG_SEM_REF(sema));
     376           0 :     } while (errStatus < 0 && errno == EINTR);
     377             : 
     378           0 :     if (errStatus < 0)
     379             :     {
     380           0 :         if (errno == EAGAIN || errno == EDEADLK)
     381           0 :             return false;       /* failed to lock it */
     382             :         /* Otherwise we got trouble */
     383           0 :         elog(FATAL, "sem_trywait failed: %m");
     384             :     }
     385             : 
     386           0 :     return true;
     387             : }

Generated by: LCOV version 1.14