LCOV - code coverage report
Current view: top level - src/backend/port - pg_sema.c (source / functions) Hit Total Coverage
Test: PostgreSQL 19devel Lines: 42 59 71.2 %
Date: 2025-11-22 16:17:05 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-2025, 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      286546 : PosixSemaphoreCreate(sem_t *sem)
     136             : {
     137      286546 :     if (sem_init(sem, 1, 1) < 0)
     138           0 :         elog(FATAL, "sem_init failed: %m");
     139      286546 : }
     140             : #endif                          /* USE_NAMED_POSIX_SEMAPHORES */
     141             : 
     142             : 
     143             : /*
     144             :  * PosixSemaphoreKill   - removes a semaphore
     145             :  */
     146             : static void
     147      286546 : 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      286546 :     if (sem_destroy(sem) < 0)
     156           0 :         elog(LOG, "sem_destroy failed: %m");
     157             : #endif
     158      286546 : }
     159             : 
     160             : 
     161             : /*
     162             :  * Report amount of shared memory needed for semaphores
     163             :  */
     164             : Size
     165        6300 : 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        6300 :     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        2200 : 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        2200 :     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        2200 :     sharedSemas = (PGSemaphore)
     219        2200 :         ShmemAlloc(PGSemaphoreShmemSize(maxSemas));
     220             : #endif
     221             : 
     222        2200 :     numSems = 0;
     223        2200 :     maxSems = maxSemas;
     224        2200 :     nextSemKey = statbuf.st_ino;
     225             : 
     226        2200 :     on_shmem_exit(ReleaseSemaphores, 0);
     227        2200 : }
     228             : 
     229             : /*
     230             :  * Release semaphores at shutdown or shmem reinitialization
     231             :  *
     232             :  * (called as an on_shmem_exit callback, hence funny argument list)
     233             :  */
     234             : static void
     235        2200 : ReleaseSemaphores(int status, Datum arg)
     236             : {
     237             :     int         i;
     238             : 
     239             : #ifdef USE_NAMED_POSIX_SEMAPHORES
     240             :     for (i = 0; i < numSems; i++)
     241             :         PosixSemaphoreKill(mySemPointers[i]);
     242             :     free(mySemPointers);
     243             : #endif
     244             : 
     245             : #ifdef USE_UNNAMED_POSIX_SEMAPHORES
     246      288746 :     for (i = 0; i < numSems; i++)
     247      286546 :         PosixSemaphoreKill(PG_SEM_REF(sharedSemas + i));
     248             : #endif
     249        2200 : }
     250             : 
     251             : /*
     252             :  * PGSemaphoreCreate
     253             :  *
     254             :  * Allocate a PGSemaphore structure with initial count 1
     255             :  */
     256             : PGSemaphore
     257      286546 : PGSemaphoreCreate(void)
     258             : {
     259             :     PGSemaphore sema;
     260             :     sem_t      *newsem;
     261             : 
     262             :     /* Can't do this in a backend, because static state is postmaster's */
     263             :     Assert(!IsUnderPostmaster);
     264             : 
     265      286546 :     if (numSems >= maxSems)
     266           0 :         elog(PANIC, "too many semaphores created");
     267             : 
     268             : #ifdef USE_NAMED_POSIX_SEMAPHORES
     269             :     newsem = PosixSemaphoreCreate();
     270             :     /* Remember new sema for ReleaseSemaphores */
     271             :     mySemPointers[numSems] = newsem;
     272             :     sema = (PGSemaphore) newsem;
     273             : #else
     274      286546 :     sema = &sharedSemas[numSems];
     275      286546 :     newsem = PG_SEM_REF(sema);
     276      286546 :     PosixSemaphoreCreate(newsem);
     277             : #endif
     278             : 
     279      286546 :     numSems++;
     280             : 
     281      286546 :     return sema;
     282             : }
     283             : 
     284             : /*
     285             :  * PGSemaphoreReset
     286             :  *
     287             :  * Reset a previously-initialized PGSemaphore to have count 0
     288             :  */
     289             : void
     290       44000 : PGSemaphoreReset(PGSemaphore sema)
     291             : {
     292             :     /*
     293             :      * There's no direct API for this in POSIX, so we have to ratchet the
     294             :      * semaphore down to 0 with repeated trywait's.
     295             :      */
     296             :     for (;;)
     297             :     {
     298       71200 :         if (sem_trywait(PG_SEM_REF(sema)) < 0)
     299             :         {
     300       44000 :             if (errno == EAGAIN || errno == EDEADLK)
     301             :                 break;          /* got it down to 0 */
     302           0 :             if (errno == EINTR)
     303           0 :                 continue;       /* can this happen? */
     304           0 :             elog(FATAL, "sem_trywait failed: %m");
     305             :         }
     306             :     }
     307       44000 : }
     308             : 
     309             : /*
     310             :  * PGSemaphoreLock
     311             :  *
     312             :  * Lock a semaphore (decrement count), blocking if count would be < 0
     313             :  */
     314             : void
     315     2246450 : PGSemaphoreLock(PGSemaphore sema)
     316             : {
     317             :     int         errStatus;
     318             : 
     319             :     /* See notes in sysv_sema.c's implementation of PGSemaphoreLock. */
     320             :     do
     321             :     {
     322     2246450 :         errStatus = sem_wait(PG_SEM_REF(sema));
     323     2246450 :     } while (errStatus < 0 && errno == EINTR);
     324             : 
     325     2246450 :     if (errStatus < 0)
     326           0 :         elog(FATAL, "sem_wait failed: %m");
     327     2246450 : }
     328             : 
     329             : /*
     330             :  * PGSemaphoreUnlock
     331             :  *
     332             :  * Unlock a semaphore (increment count)
     333             :  */
     334             : void
     335     2255748 : PGSemaphoreUnlock(PGSemaphore sema)
     336             : {
     337             :     int         errStatus;
     338             : 
     339             :     /*
     340             :      * Note: if errStatus is -1 and errno == EINTR then it means we returned
     341             :      * from the operation prematurely because we were sent a signal.  So we
     342             :      * try and unlock the semaphore again. Not clear this can really happen,
     343             :      * but might as well cope.
     344             :      */
     345             :     do
     346             :     {
     347     2255748 :         errStatus = sem_post(PG_SEM_REF(sema));
     348     2255748 :     } while (errStatus < 0 && errno == EINTR);
     349             : 
     350     2255748 :     if (errStatus < 0)
     351           0 :         elog(FATAL, "sem_post failed: %m");
     352     2255748 : }
     353             : 
     354             : /*
     355             :  * PGSemaphoreTryLock
     356             :  *
     357             :  * Lock a semaphore only if able to do so without blocking
     358             :  */
     359             : bool
     360           0 : PGSemaphoreTryLock(PGSemaphore sema)
     361             : {
     362             :     int         errStatus;
     363             : 
     364             :     /*
     365             :      * Note: if errStatus is -1 and errno == EINTR then it means we returned
     366             :      * from the operation prematurely because we were sent a signal.  So we
     367             :      * try and lock the semaphore again.
     368             :      */
     369             :     do
     370             :     {
     371           0 :         errStatus = sem_trywait(PG_SEM_REF(sema));
     372           0 :     } while (errStatus < 0 && errno == EINTR);
     373             : 
     374           0 :     if (errStatus < 0)
     375             :     {
     376           0 :         if (errno == EAGAIN || errno == EDEADLK)
     377           0 :             return false;       /* failed to lock it */
     378             :         /* Otherwise we got trouble */
     379           0 :         elog(FATAL, "sem_trywait failed: %m");
     380             :     }
     381             : 
     382           0 :     return true;
     383             : }

Generated by: LCOV version 1.16