/* * PGSemaphoreCreate * * Allocate a PGSemaphore structure with initial count 1 */ PGSemaphore PGSemaphoreCreate(void) { PGSemaphore sema; /* Can't do this in a backend, because static state is postmaster's */ Assert(!IsUnderPostmaster); if (nextSemaNumber >= SEMAS_PER_SET) { /* Time to allocate another semaphore set */ if (numSemaSets >= maxSemaSets) elog(PANIC, "too many semaphores created"); mySemaSets[numSemaSets] = IpcSemaphoreCreate(SEMAS_PER_SET); numSemaSets++; nextSemaNumber = 0; } /* Use the next shared PGSemaphoreData */ if (numSharedSemas >= maxSharedSemas) elog(PANIC, "too many semaphores created"); sema = &sharedSemas[numSharedSemas++]; /* Assign the next free semaphore in the current set */ sema->semId = mySemaSets[numSemaSets - 1]; sema->semNum = nextSemaNumber++; /* Initialize it to count 1 */ IpcSemaphoreInitialize(sema->semId, sema->semNum, 1); return sema; }
/* * PGSemaphoreCreate * * Initialize a PGSemaphore structure to represent a sema with count 1 */ void PGSemaphoreCreate(PGSemaphore sema) { /* Can't do this in a backend, because static state is postmaster's */ Assert(!IsUnderPostmaster); if (nextSemaNumber >= SEMAS_PER_SET) { /* Time to allocate another semaphore set */ if (numSemaSets >= maxSemaSets) elog(PANIC, "too many semaphores created"); mySemaSets[numSemaSets] = IpcSemaphoreCreate(SEMAS_PER_SET); numSemaSets++; nextSemaNumber = 0; } /* Assign the next free semaphore in the current set */ sema->semId = mySemaSets[numSemaSets - 1]; sema->semNum = nextSemaNumber++; /* Initialize it to count 1 */ IpcSemaphoreInitialize(sema->semId, sema->semNum, 1); elog((Debug_print_semaphore_detail ? LOG : DEBUG5), "created SYSV semaphore semId %d, semNum %d", sema->semId, sema->semNum); }
/* * PGSemaphoreReset * * Reset a previously-initialized PGSemaphore to have count 0 */ void PGSemaphoreReset(PGSemaphore sema) { IpcSemaphoreInitialize(sema->semId, sema->semNum, 0); }
/* * Create a semaphore set with the given number of useful semaphores * (an additional sema is actually allocated to serve as identifier). * Dead Postgres sema sets are recycled if found, but we do not fail * upon collision with non-Postgres sema sets. * * The idea here is to detect and re-use keys that may have been assigned * by a crashed postmaster or backend. */ static IpcSemaphoreId IpcSemaphoreCreate(int numSems) { IpcSemaphoreId semId; union semun semun; PGSemaphoreData mysema; /* Loop till we find a free IPC key */ for (nextSemaKey++;; nextSemaKey++) { pid_t creatorPID; /* Try to create new semaphore set */ semId = InternalIpcSemaphoreCreate(nextSemaKey, numSems + 1); if (semId >= 0) break; /* successful create */ /* See if it looks to be leftover from a dead Postgres process */ semId = semget(nextSemaKey, numSems + 1, 0); if (semId < 0) continue; /* failed: must be some other app's */ if (IpcSemaphoreGetValue(semId, numSems) != PGSemaMagic) continue; /* sema belongs to a non-Postgres app */ /* * If the creator PID is my own PID or does not belong to any extant * process, it's safe to zap it. */ creatorPID = IpcSemaphoreGetLastPID(semId, numSems); if (creatorPID <= 0) continue; /* oops, GETPID failed */ if (creatorPID != getpid()) { if (kill(creatorPID, 0) == 0 || errno != ESRCH) continue; /* sema belongs to a live process */ } /* * The sema set appears to be from a dead Postgres process, or from a * previous cycle of life in this same process. Zap it, if possible. * This probably shouldn't fail, but if it does, assume the sema set * belongs to someone else after all, and continue quietly. */ semun.val = 0; /* unused, but keep compiler quiet */ if (semctl(semId, 0, IPC_RMID, semun) < 0) continue; /* * Now try again to create the sema set. */ semId = InternalIpcSemaphoreCreate(nextSemaKey, numSems + 1); if (semId >= 0) break; /* successful create */ /* * Can only get here if some other process managed to create the same * sema key before we did. Let him have that one, loop around to try * next key. */ } /* * OK, we created a new sema set. Mark it as created by this process. We * do this by setting the spare semaphore to PGSemaMagic-1 and then * incrementing it with semop(). That leaves it with value PGSemaMagic * and sempid referencing this process. */ IpcSemaphoreInitialize(semId, numSems, PGSemaMagic - 1); mysema.semId = semId; mysema.semNum = numSems; PGSemaphoreUnlock(&mysema); return semId; }