Header And Logo

PostgreSQL
| The world's most advanced open source database.

posix_sema.c

Go to the documentation of this file.
00001 /*-------------------------------------------------------------------------
00002  *
00003  * posix_sema.c
00004  *    Implement PGSemaphores using POSIX semaphore facilities
00005  *
00006  * We prefer the unnamed style of POSIX semaphore (the kind made with
00007  * sem_init).  We can cope with the kind made with sem_open, however.
00008  *
00009  *
00010  * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
00011  * Portions Copyright (c) 1994, Regents of the University of California
00012  *
00013  * IDENTIFICATION
00014  *    src/backend/port/posix_sema.c
00015  *
00016  *-------------------------------------------------------------------------
00017  */
00018 #include "postgres.h"
00019 
00020 #include <fcntl.h>
00021 #include <signal.h>
00022 #include <unistd.h>
00023 
00024 #include "miscadmin.h"
00025 #include "storage/ipc.h"
00026 #include "storage/pg_sema.h"
00027 
00028 
00029 #ifdef USE_NAMED_POSIX_SEMAPHORES
00030 /* PGSemaphore is pointer to pointer to sem_t */
00031 #define PG_SEM_REF(x)   (*(x))
00032 #else
00033 /* PGSemaphore is pointer to sem_t */
00034 #define PG_SEM_REF(x)   (x)
00035 #endif
00036 
00037 
00038 #define IPCProtection   (0600)  /* access/modify by user only */
00039 
00040 static sem_t **mySemPointers;   /* keep track of created semaphores */
00041 static int  numSems;            /* number of semas acquired so far */
00042 static int  maxSems;            /* allocated size of mySemaPointers array */
00043 static int  nextSemKey;         /* next name to try */
00044 
00045 
00046 static void ReleaseSemaphores(int status, Datum arg);
00047 
00048 
00049 #ifdef USE_NAMED_POSIX_SEMAPHORES
00050 
00051 /*
00052  * PosixSemaphoreCreate
00053  *
00054  * Attempt to create a new named semaphore.
00055  *
00056  * If we fail with a failure code other than collision-with-existing-sema,
00057  * print out an error and abort.  Other types of errors suggest nonrecoverable
00058  * problems.
00059  */
00060 static sem_t *
00061 PosixSemaphoreCreate(void)
00062 {
00063     int         semKey;
00064     char        semname[64];
00065     sem_t      *mySem;
00066 
00067     for (;;)
00068     {
00069         semKey = nextSemKey++;
00070 
00071         snprintf(semname, sizeof(semname), "/pgsql-%d", semKey);
00072 
00073         mySem = sem_open(semname, O_CREAT | O_EXCL,
00074                          (mode_t) IPCProtection, (unsigned) 1);
00075 
00076 #ifdef SEM_FAILED
00077         if (mySem != (sem_t *) SEM_FAILED)
00078             break;
00079 #else
00080         if (mySem != (sem_t *) (-1))
00081             break;
00082 #endif
00083 
00084         /* Loop if error indicates a collision */
00085         if (errno == EEXIST || errno == EACCES || errno == EINTR)
00086             continue;
00087 
00088         /*
00089          * Else complain and abort
00090          */
00091         elog(FATAL, "sem_open(\"%s\") failed: %m", semname);
00092     }
00093 
00094     /*
00095      * Unlink the semaphore immediately, so it can't be accessed externally.
00096      * This also ensures that it will go away if we crash.
00097      */
00098     sem_unlink(semname);
00099 
00100     return mySem;
00101 }
00102 #else                           /* !USE_NAMED_POSIX_SEMAPHORES */
00103 
00104 /*
00105  * PosixSemaphoreCreate
00106  *
00107  * Attempt to create a new unnamed semaphore.
00108  */
00109 static void
00110 PosixSemaphoreCreate(sem_t * sem)
00111 {
00112     if (sem_init(sem, 1, 1) < 0)
00113         elog(FATAL, "sem_init failed: %m");
00114 }
00115 #endif   /* USE_NAMED_POSIX_SEMAPHORES */
00116 
00117 
00118 /*
00119  * PosixSemaphoreKill   - removes a semaphore
00120  */
00121 static void
00122 PosixSemaphoreKill(sem_t * sem)
00123 {
00124 #ifdef USE_NAMED_POSIX_SEMAPHORES
00125     /* Got to use sem_close for named semaphores */
00126     if (sem_close(sem) < 0)
00127         elog(LOG, "sem_close failed: %m");
00128 #else
00129     /* Got to use sem_destroy for unnamed semaphores */
00130     if (sem_destroy(sem) < 0)
00131         elog(LOG, "sem_destroy failed: %m");
00132 #endif
00133 }
00134 
00135 
00136 /*
00137  * PGReserveSemaphores --- initialize semaphore support
00138  *
00139  * This is called during postmaster start or shared memory reinitialization.
00140  * It should do whatever is needed to be able to support up to maxSemas
00141  * subsequent PGSemaphoreCreate calls.  Also, if any system resources
00142  * are acquired here or in PGSemaphoreCreate, register an on_shmem_exit
00143  * callback to release them.
00144  *
00145  * The port number is passed for possible use as a key (for Posix, we use
00146  * it to generate the starting semaphore name).  In a standalone backend,
00147  * zero will be passed.
00148  *
00149  * In the Posix implementation, we acquire semaphores on-demand; the
00150  * maxSemas parameter is just used to size the array that keeps track of
00151  * acquired semas for subsequent releasing.
00152  */
00153 void
00154 PGReserveSemaphores(int maxSemas, int port)
00155 {
00156     mySemPointers = (sem_t **) malloc(maxSemas * sizeof(sem_t *));
00157     if (mySemPointers == NULL)
00158         elog(PANIC, "out of memory");
00159     numSems = 0;
00160     maxSems = maxSemas;
00161     nextSemKey = port * 1000;
00162 
00163     on_shmem_exit(ReleaseSemaphores, 0);
00164 }
00165 
00166 /*
00167  * Release semaphores at shutdown or shmem reinitialization
00168  *
00169  * (called as an on_shmem_exit callback, hence funny argument list)
00170  */
00171 static void
00172 ReleaseSemaphores(int status, Datum arg)
00173 {
00174     int         i;
00175 
00176     for (i = 0; i < numSems; i++)
00177         PosixSemaphoreKill(mySemPointers[i]);
00178     free(mySemPointers);
00179 }
00180 
00181 /*
00182  * PGSemaphoreCreate
00183  *
00184  * Initialize a PGSemaphore structure to represent a sema with count 1
00185  */
00186 void
00187 PGSemaphoreCreate(PGSemaphore sema)
00188 {
00189     sem_t      *newsem;
00190 
00191     /* Can't do this in a backend, because static state is postmaster's */
00192     Assert(!IsUnderPostmaster);
00193 
00194     if (numSems >= maxSems)
00195         elog(PANIC, "too many semaphores created");
00196 
00197 #ifdef USE_NAMED_POSIX_SEMAPHORES
00198     *sema = newsem = PosixSemaphoreCreate();
00199 #else
00200     PosixSemaphoreCreate(sema);
00201     newsem = sema;
00202 #endif
00203 
00204     /* Remember new sema for ReleaseSemaphores */
00205     mySemPointers[numSems++] = newsem;
00206 }
00207 
00208 /*
00209  * PGSemaphoreReset
00210  *
00211  * Reset a previously-initialized PGSemaphore to have count 0
00212  */
00213 void
00214 PGSemaphoreReset(PGSemaphore sema)
00215 {
00216     /*
00217      * There's no direct API for this in POSIX, so we have to ratchet the
00218      * semaphore down to 0 with repeated trywait's.
00219      */
00220     for (;;)
00221     {
00222         if (sem_trywait(PG_SEM_REF(sema)) < 0)
00223         {
00224             if (errno == EAGAIN || errno == EDEADLK)
00225                 break;          /* got it down to 0 */
00226             if (errno == EINTR)
00227                 continue;       /* can this happen? */
00228             elog(FATAL, "sem_trywait failed: %m");
00229         }
00230     }
00231 }
00232 
00233 /*
00234  * PGSemaphoreLock
00235  *
00236  * Lock a semaphore (decrement count), blocking if count would be < 0
00237  */
00238 void
00239 PGSemaphoreLock(PGSemaphore sema, bool interruptOK)
00240 {
00241     int         errStatus;
00242 
00243     /*
00244      * See notes in sysv_sema.c's implementation of PGSemaphoreLock. Just as
00245      * that code does for semop(), we handle both the case where sem_wait()
00246      * returns errno == EINTR after a signal, and the case where it just keeps
00247      * waiting.
00248      */
00249     do
00250     {
00251         ImmediateInterruptOK = interruptOK;
00252         CHECK_FOR_INTERRUPTS();
00253         errStatus = sem_wait(PG_SEM_REF(sema));
00254         ImmediateInterruptOK = false;
00255     } while (errStatus < 0 && errno == EINTR);
00256 
00257     if (errStatus < 0)
00258         elog(FATAL, "sem_wait failed: %m");
00259 }
00260 
00261 /*
00262  * PGSemaphoreUnlock
00263  *
00264  * Unlock a semaphore (increment count)
00265  */
00266 void
00267 PGSemaphoreUnlock(PGSemaphore sema)
00268 {
00269     int         errStatus;
00270 
00271     /*
00272      * Note: if errStatus is -1 and errno == EINTR then it means we returned
00273      * from the operation prematurely because we were sent a signal.  So we
00274      * try and unlock the semaphore again. Not clear this can really happen,
00275      * but might as well cope.
00276      */
00277     do
00278     {
00279         errStatus = sem_post(PG_SEM_REF(sema));
00280     } while (errStatus < 0 && errno == EINTR);
00281 
00282     if (errStatus < 0)
00283         elog(FATAL, "sem_post failed: %m");
00284 }
00285 
00286 /*
00287  * PGSemaphoreTryLock
00288  *
00289  * Lock a semaphore only if able to do so without blocking
00290  */
00291 bool
00292 PGSemaphoreTryLock(PGSemaphore sema)
00293 {
00294     int         errStatus;
00295 
00296     /*
00297      * Note: if errStatus is -1 and errno == EINTR then it means we returned
00298      * from the operation prematurely because we were sent a signal.  So we
00299      * try and lock the semaphore again.
00300      */
00301     do
00302     {
00303         errStatus = sem_trywait(PG_SEM_REF(sema));
00304     } while (errStatus < 0 && errno == EINTR);
00305 
00306     if (errStatus < 0)
00307     {
00308         if (errno == EAGAIN || errno == EDEADLK)
00309             return false;       /* failed to lock it */
00310         /* Otherwise we got trouble */
00311         elog(FATAL, "sem_trywait failed: %m");
00312     }
00313 
00314     return true;
00315 }