
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
220 lines
5.1 KiB
C
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;
|
|
}
|