WALInsertSlotAcquireOne(int slotno)
{
	volatile XLogInsertSlot *slot;
	PGPROC	   *proc = MyProc;
	bool		retry = false;
	int			extraWaits = 0;
	static int	slotToTry = -1;

	/*
	 * Try to use the slot we used last time. If the system isn't particularly
	 * busy, it's a good bet that it's available, and it's good to have some
	 * affinity to a particular slot so that you don't unnecessarily bounce
	 * cache lines between processes when there is no contention.
	 *
	 * If this is the first time through in this backend, pick a slot
	 * (semi-)randomly. This allows the slots to be used evenly if you have a
	 * lot of very short connections.
	 */
	if (slotno != -1)
		MySlotNo = slotno;
	else
	{
		if (slotToTry == -1)
			slotToTry = MyProc->pgprocno % num_xloginsert_slots;
		MySlotNo = slotToTry;
	}

	/*
	 * We can't wait if we haven't got a PGPROC.  This should only occur
	 * during bootstrap or shared memory initialization.  Put an Assert here
	 * to catch unsafe coding practices.
	 */
	Assert(MyProc != NULL);

	/*
	 * Lock out cancel/die interrupts until we exit the code section protected
	 * by the slot.  This ensures that interrupts will not interfere with
	 * manipulations of data structures in shared memory. There is no cleanup
	 * mechanism to release the slot if the backend dies while holding one,
	 * so make this a critical section.
	 */
	START_CRIT_SECTION();

	/*
	 * Loop here to try to acquire slot after each time we are signaled by
	 * WALInsertSlotRelease.
	 */
	for (;;)
	{
		bool		mustwait;

		slot = &XLogCtl->Insert.insertSlots[MySlotNo].slot;

		/* Acquire mutex.  Time spent holding mutex should be short! */
		SpinLockAcquire(&slot->mutex);

		/* If retrying, allow WALInsertSlotRelease to release waiters again */
		if (retry)
			slot->releaseOK = true;

		/* If I can get the slot, do so quickly. */
		if (slot->exclusive == 0)
		{
			slot->exclusive++;
			mustwait = false;
		}
		else
			mustwait = true;

		if (!mustwait)
			break;				/* got the lock */

		Assert(slot->owner != MyProc);

		/*
		 * Add myself to wait queue.
		 */
		proc->lwWaiting = true;
		proc->lwWaitMode = LW_EXCLUSIVE;
		proc->lwWaitLink = NULL;
		if (slot->head == NULL)
			slot->head = proc;
		else
			slot->tail->lwWaitLink = proc;
		slot->tail = proc;

		/* Can release the mutex now */
		SpinLockRelease(&slot->mutex);

		/*
		 * Wait until awakened.
		 *
		 * Since we share the process wait semaphore with the regular lock
		 * manager and ProcWaitForSignal, and we may need to acquire a slot
		 * while one of those is pending, it is possible that we get awakened
		 * for a reason other than being signaled by WALInsertSlotRelease. If
		 * so, loop back and wait again.  Once we've gotten the slot,
		 * re-increment the sema by the number of additional signals received,
		 * so that the lock manager or signal manager will see the received
		 * signal when it next waits.
		 */
		for (;;)
		{
			/* "false" means cannot accept cancel/die interrupt here. */
			PGSemaphoreLock(&proc->sem, false);
			if (!proc->lwWaiting)
				break;
			extraWaits++;
		}

		/* Now loop back and try to acquire lock again. */
		retry = true;
	}

	slot->owner = proc;

	/*
	 * Normally, we initialize the xlogInsertingAt value of the slot to 1,
	 * because we don't yet know where in the WAL we're going to insert. It's
	 * not critical what it points to right now - leaving it to a too small
	 * value just means that WaitXlogInsertionsToFinish() might wait on us
	 * unnecessarily, until we update the value (when we finish the insert or
	 * move to next page).
	 *
	 * If we're grabbing all the slots, however, stamp all but the last one
	 * with InvalidXLogRecPtr, meaning there is no insert in progress. The last
	 * slot is the one that we will update as we proceed with the insert, the
	 * rest are held just to keep off other inserters.
	 */
	if (slotno != -1 && slotno != num_xloginsert_slots - 1)
		slot->xlogInsertingAt = InvalidXLogRecPtr;
	else
		slot->xlogInsertingAt = 1;

	/* We are done updating shared state of the slot itself. */
	SpinLockRelease(&slot->mutex);

	/*
	 * Fix the process wait semaphore's count for any absorbed wakeups.
	 */
	while (extraWaits-- > 0)
		PGSemaphoreUnlock(&proc->sem);

	/*
	 * If we couldn't get the slot immediately, try another slot next time.
	 * On a system with more insertion slots than concurrent inserters, this
	 * causes all the inserters to eventually migrate to a slot that no-one
	 * else is using. On a system with more inserters than slots, it still
	 * causes the inserters to be distributed quite evenly across the slots.
	 */
	if (slotno != -1 && retry)
		slotToTry = (slotToTry + 1) % num_xloginsert_slots;
}