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

Generated by: LCOV version 1.14