postgres/src/backend/port/win32_sema.c
Andres Freund d06995710b Remove the option to service interrupts during PGSemaphoreLock().
The remaining caller (lwlocks) doesn't need that facility, and we plan
to remove ImmedidateInterruptOK entirely. That means that interrupts
can't be serviced race-free and portably anyway, so there's little
reason for keeping the feature.

Reviewed-By: Heikki Linnakangas
2015-02-03 23:25:00 +01:00

220 lines
5.1 KiB
C

/*-------------------------------------------------------------------------
*
* win32_sema.c
* Microsoft Windows Win32 Semaphores Emulation
*
* Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
*
* IDENTIFICATION
* src/backend/port/win32_sema.c
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "miscadmin.h"
#include "storage/ipc.h"
#include "storage/pg_sema.h"
static HANDLE *mySemSet; /* IDs of sema sets acquired so far */
static int numSems; /* number of sema sets acquired so far */
static int maxSems; /* allocated size of mySemaSet array */
static void ReleaseSemaphores(int code, Datum arg);
/*
* PGReserveSemaphores --- initialize semaphore support
*
* In the Win32 implementation, we acquire semaphores on-demand; the
* maxSemas parameter is just used to size the array that keeps track of
* acquired semas for subsequent releasing. We use anonymous semaphores
* so the semaphores are automatically freed when the last referencing
* process exits.
*/
void
PGReserveSemaphores(int maxSemas, int port)
{
mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
if (mySemSet == NULL)
elog(PANIC, "out of memory");
numSems = 0;
maxSems = maxSemas;
on_shmem_exit(ReleaseSemaphores, 0);
}
/*
* Release semaphores at shutdown or shmem reinitialization
*
* (called as an on_shmem_exit callback, hence funny argument list)
*/
static void
ReleaseSemaphores(int code, Datum arg)
{
int i;
for (i = 0; i < numSems; i++)
CloseHandle(mySemSet[i]);
free(mySemSet);
}
/*
* PGSemaphoreCreate
*
* Initialize a PGSemaphore structure to represent a sema with count 1
*/
void
PGSemaphoreCreate(PGSemaphore sema)
{
HANDLE cur_handle;
SECURITY_ATTRIBUTES sec_attrs;
/* Can't do this in a backend, because static state is postmaster's */
Assert(!IsUnderPostmaster);
if (numSems >= maxSems)
elog(PANIC, "too many semaphores created");
ZeroMemory(&sec_attrs, sizeof(sec_attrs));
sec_attrs.nLength = sizeof(sec_attrs);
sec_attrs.lpSecurityDescriptor = NULL;
sec_attrs.bInheritHandle = TRUE;
/* We don't need a named semaphore */
cur_handle = CreateSemaphore(&sec_attrs, 1, 32767, NULL);
if (cur_handle)
{
/* Successfully done */
*sema = cur_handle;
mySemSet[numSems++] = cur_handle;
}
else
ereport(PANIC,
(errmsg("could not create semaphore: error code %lu", GetLastError())));
}
/*
* PGSemaphoreReset
*
* Reset a previously-initialized PGSemaphore to have count 0
*/
void
PGSemaphoreReset(PGSemaphore sema)
{
/*
* There's no direct API for this in Win32, so we have to ratchet the
* semaphore down to 0 with repeated trylock's.
*/
while (PGSemaphoreTryLock(sema));
}
/*
* PGSemaphoreLock
*
* Lock a semaphore (decrement count), blocking if count would be < 0.
* Serve the interrupt if interruptOK is true.
*/
void
PGSemaphoreLock(PGSemaphore sema)
{
HANDLE wh[2];
bool done = false;
/*
* Note: pgwin32_signal_event should be first to ensure that it will be
* reported when multiple events are set. We want to guarantee that
* pending signals are serviced.
*/
wh[0] = pgwin32_signal_event;
wh[1] = *sema;
/*
* As in other implementations of PGSemaphoreLock, we need to check for
* cancel/die interrupts each time through the loop. But here, there is
* no hidden magic about whether the syscall will internally service a
* signal --- we do that ourselves.
*/
while (!done)
{
DWORD rc;
CHECK_FOR_INTERRUPTS();
rc = WaitForMultipleObjectsEx(2, wh, FALSE, INFINITE, TRUE);
switch (rc)
{
case WAIT_OBJECT_0:
/* Signal event is set - we have a signal to deliver */
pgwin32_dispatch_queued_signals();
break;
case WAIT_OBJECT_0 + 1:
/* We got it! */
done = true;
break;
case WAIT_IO_COMPLETION:
/*
* The system interrupted the wait to execute an I/O
* completion routine or asynchronous procedure call in this
* thread. PostgreSQL does not provoke either of these, but
* atypical loaded DLLs or even other processes might do so.
* Now, resume waiting.
*/
break;
case WAIT_FAILED:
ereport(FATAL,
(errmsg("could not lock semaphore: error code %lu",
GetLastError())));
break;
default:
elog(FATAL, "unexpected return code from WaitForMultipleObjectsEx(): %lu", rc);
break;
}
}
}
/*
* PGSemaphoreUnlock
*
* Unlock a semaphore (increment count)
*/
void
PGSemaphoreUnlock(PGSemaphore sema)
{
if (!ReleaseSemaphore(*sema, 1, NULL))
ereport(FATAL,
(errmsg("could not unlock semaphore: error code %lu", GetLastError())));
}
/*
* PGSemaphoreTryLock
*
* Lock a semaphore only if able to do so without blocking
*/
bool
PGSemaphoreTryLock(PGSemaphore sema)
{
DWORD ret;
ret = WaitForSingleObject(*sema, 0);
if (ret == WAIT_OBJECT_0)
{
/* We got it! */
return true;
}
else if (ret == WAIT_TIMEOUT)
{
/* Can't get it */
errno = EAGAIN;
return false;
}
/* Otherwise we are in trouble */
ereport(FATAL,
(errmsg("could not try-lock semaphore: error code %lu", GetLastError())));
/* keep compiler quiet */
return false;
}