/* * PGSemaphoreCreate * * Allocate a PGSemaphore structure with initial count 1 */ PGSemaphore PGSemaphoreCreate(void) { PGSemaphore sema; sem_t *newsem; /* Can't do this in a backend, because static state is postmaster's */ Assert(!IsUnderPostmaster); if (numSems >= maxSems) elog(PANIC, "too many semaphores created"); #ifdef USE_NAMED_POSIX_SEMAPHORES newsem = PosixSemaphoreCreate(); /* Remember new sema for ReleaseSemaphores */ mySemPointers[numSems] = newsem; sema = (PGSemaphore) newsem; #else sema = &sharedSemas[numSems]; newsem = PG_SEM_REF(sema); PosixSemaphoreCreate(newsem); #endif numSems++; return sema; }
/* * PGSemaphoreTryLock * * Lock a semaphore only if able to do so without blocking */ bool PGSemaphoreTryLock(PGSemaphore sema) { int errStatus; /* * Note: if errStatus is -1 and errno == EINTR then it means we returned * from the operation prematurely because we were sent a signal. So we * try and lock the semaphore again. */ do { errStatus = sem_trywait(PG_SEM_REF(sema)); } while (errStatus < 0 && errno == EINTR); if (errStatus < 0) { if (errno == EAGAIN || errno == EDEADLK) return false; /* failed to lock it */ /* Otherwise we got trouble */ elog(FATAL, "sem_trywait failed: %m"); } return true; }
/* * PGSemaphoreLock * * Lock a semaphore (decrement count), blocking if count would be < 0 */ void PGSemaphoreLock(PGSemaphore sema) { int errStatus; /* See notes in sysv_sema.c's implementation of PGSemaphoreLock. */ do { errStatus = sem_wait(PG_SEM_REF(sema)); } while (errStatus < 0 && errno == EINTR); if (errStatus < 0) elog(FATAL, "sem_wait failed: %m"); }
/* * PGSemaphoreLock * * Lock a semaphore (decrement count), blocking if count would be < 0 */ void PGSemaphoreLock(PGSemaphore sema, bool interruptOK) { int errStatus; /* * Note: if errStatus is -1 and errno == EINTR then it means we * returned from the operation prematurely because we were sent a * signal. So we try and lock the semaphore again. * * Each time around the loop, we check for a cancel/die interrupt. We * assume that if such an interrupt comes in while we are waiting, it * will cause the sem_wait() call to exit with errno == EINTR, so that * we will be able to service the interrupt (if not in a critical * section already). * * Once we acquire the lock, we do NOT check for an interrupt before * returning. The caller needs to be able to record ownership of the * lock before any interrupt can be accepted. * * There is a window of a few instructions between CHECK_FOR_INTERRUPTS * and entering the sem_wait() call. If a cancel/die interrupt occurs * in that window, we would fail to notice it until after we acquire * the lock (or get another interrupt to escape the sem_wait()). We * can avoid this problem by temporarily setting ImmediateInterruptOK * to true before we do CHECK_FOR_INTERRUPTS; then, a die() interrupt * in this interval will execute directly. However, there is a huge * pitfall: there is another window of a few instructions after the * sem_wait() before we are able to reset ImmediateInterruptOK. If an * interrupt occurs then, we'll lose control, which means that the * lock has been acquired but our caller did not get a chance to * record the fact. Therefore, we only set ImmediateInterruptOK if the * caller tells us it's OK to do so, ie, the caller does not need to * record acquiring the lock. (This is currently true for lockmanager * locks, since the process that granted us the lock did all the * necessary state updates. It's not true for Posix semaphores used to * implement LW locks or emulate spinlocks --- but the wait time for * such locks should not be very long, anyway.) */ do { ImmediateInterruptOK = interruptOK; CHECK_FOR_INTERRUPTS(); errStatus = sem_wait(PG_SEM_REF(sema)); ImmediateInterruptOK = false; } while (errStatus < 0 && errno == EINTR); if (errStatus < 0) elog(FATAL, "sem_wait failed: %m"); }
/* * PGSemaphoreLockInterruptable * * Lock a semaphore (decrement count), blocking if count would be < 0. * Return true if the lock obtained or false if an interrupt occurred. */ bool PGSemaphoreLockInterruptable(PGSemaphore sema) { int errStatus; errStatus = sem_wait(PG_SEM_REF(sema)); if (errStatus < 0) { if (errno == EINTR) return false; elog(FATAL, "sem_wait failed: %m"); } return true; }
/* * Release semaphores at shutdown or shmem reinitialization * * (called as an on_shmem_exit callback, hence funny argument list) */ static void ReleaseSemaphores(int status, Datum arg) { int i; #ifdef USE_NAMED_POSIX_SEMAPHORES for (i = 0; i < numSems; i++) PosixSemaphoreKill(mySemPointers[i]); free(mySemPointers); #endif #ifdef USE_UNNAMED_POSIX_SEMAPHORES for (i = 0; i < numSems; i++) PosixSemaphoreKill(PG_SEM_REF(sharedSemas + i)); #endif }
/* * PGSemaphoreUnlock * * Unlock a semaphore (increment count) */ void PGSemaphoreUnlock(PGSemaphore sema) { int errStatus; /* * Note: if errStatus is -1 and errno == EINTR then it means we returned * from the operation prematurely because we were sent a signal. So we * try and unlock the semaphore again. Not clear this can really happen, * but might as well cope. */ do { errStatus = sem_post(PG_SEM_REF(sema)); } while (errStatus < 0 && errno == EINTR); if (errStatus < 0) elog(FATAL, "sem_post failed: %m"); }
/* * PGSemaphoreReset * * Reset a previously-initialized PGSemaphore to have count 0 */ void PGSemaphoreReset(PGSemaphore sema) { /* * There's no direct API for this in POSIX, so we have to ratchet the * semaphore down to 0 with repeated trywait's. */ for (;;) { if (sem_trywait(PG_SEM_REF(sema)) < 0) { if (errno == EAGAIN || errno == EDEADLK) break; /* got it down to 0 */ if (errno == EINTR) continue; /* can this happen? */ elog(FATAL, "sem_trywait failed: %m"); } } }
/* * PGSemaphoreLock * * Lock a semaphore (decrement count), blocking if count would be < 0 */ void PGSemaphoreLock(PGSemaphore sema, bool interruptOK) { int errStatus; /* * See notes in sysv_sema.c's implementation of PGSemaphoreLock. Just as * that code does for semop(), we handle both the case where sem_wait() * returns errno == EINTR after a signal, and the case where it just keeps * waiting. */ do { ImmediateInterruptOK = interruptOK; CHECK_FOR_INTERRUPTS(); errStatus = sem_wait(PG_SEM_REF(sema)); ImmediateInterruptOK = false; } while (errStatus < 0 && errno == EINTR); if (errStatus < 0) elog(FATAL, "sem_wait failed: %m"); }