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

Generated by: LCOV version 1.11