1
0
mirror of https://github.com/postgres/postgres.git synced 2025-10-18 04:29:09 +03:00
Files
postgres/src/backend/port/win32_latch.c
2012-01-01 18:01:58 -05:00

258 lines
6.2 KiB
C

/*-------------------------------------------------------------------------
*
* win32_latch.c
* Routines for inter-process latches
*
* See unix_latch.c for header comments for the exported functions;
* the API presented here is supposed to be the same as there.
*
* The Windows implementation uses Windows events that are inherited by
* all postmaster child processes.
*
* Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* src/backend/port/win32_latch.c
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include <fcntl.h>
#include <signal.h>
#include <unistd.h>
#include "miscadmin.h"
#include "postmaster/postmaster.h"
#include "storage/latch.h"
#include "storage/shmem.h"
void
InitLatch(volatile Latch *latch)
{
latch->is_set = false;
latch->owner_pid = MyProcPid;
latch->is_shared = false;
latch->event = CreateEvent(NULL, TRUE, FALSE, NULL);
if (latch->event == NULL)
elog(ERROR, "CreateEvent failed: error code %lu", GetLastError());
}
void
InitSharedLatch(volatile Latch *latch)
{
SECURITY_ATTRIBUTES sa;
latch->is_set = false;
latch->owner_pid = 0;
latch->is_shared = true;
/*
* Set up security attributes to specify that the events are inherited.
*/
ZeroMemory(&sa, sizeof(sa));
sa.nLength = sizeof(sa);
sa.bInheritHandle = TRUE;
latch->event = CreateEvent(&sa, TRUE, FALSE, NULL);
if (latch->event == NULL)
elog(ERROR, "CreateEvent failed: error code %lu", GetLastError());
}
void
OwnLatch(volatile Latch *latch)
{
/* Sanity checks */
Assert(latch->is_shared);
if (latch->owner_pid != 0)
elog(ERROR, "latch already owned");
latch->owner_pid = MyProcPid;
}
void
DisownLatch(volatile Latch *latch)
{
Assert(latch->is_shared);
Assert(latch->owner_pid == MyProcPid);
latch->owner_pid = 0;
}
int
WaitLatch(volatile Latch *latch, int wakeEvents, long timeout)
{
return WaitLatchOrSocket(latch, wakeEvents, PGINVALID_SOCKET, timeout);
}
int
WaitLatchOrSocket(volatile Latch *latch, int wakeEvents, pgsocket sock,
long timeout)
{
DWORD rc;
HANDLE events[4];
HANDLE latchevent;
HANDLE sockevent = WSA_INVALID_EVENT;
int numevents;
int result = 0;
int pmdeath_eventno = 0;
/* Ignore WL_SOCKET_* events if no valid socket is given */
if (sock == PGINVALID_SOCKET)
wakeEvents &= ~(WL_SOCKET_READABLE | WL_SOCKET_WRITEABLE);
Assert(wakeEvents != 0); /* must have at least one wake event */
if ((wakeEvents & WL_LATCH_SET) && latch->owner_pid != MyProcPid)
elog(ERROR, "cannot wait on a latch owned by another process");
/* Convert timeout to form used by WaitForMultipleObjects() */
if (wakeEvents & WL_TIMEOUT)
Assert(timeout >= 0);
else
timeout = INFINITE;
/* Construct an array of event handles for WaitforMultipleObjects() */
latchevent = latch->event;
events[0] = latchevent;
events[1] = pgwin32_signal_event;
numevents = 2;
if ((wakeEvents & WL_SOCKET_READABLE) ||
(wakeEvents & WL_SOCKET_WRITEABLE))
{
int flags = 0;
if (wakeEvents & WL_SOCKET_READABLE)
flags |= FD_READ;
if (wakeEvents & WL_SOCKET_WRITEABLE)
flags |= FD_WRITE;
sockevent = WSACreateEvent();
WSAEventSelect(sock, sockevent, flags);
events[numevents++] = sockevent;
}
if (wakeEvents & WL_POSTMASTER_DEATH)
{
pmdeath_eventno = numevents;
events[numevents++] = PostmasterHandle;
}
do
{
/*
* Reset the event, and check if the latch is set already. If someone
* sets the latch between this and the WaitForMultipleObjects() call
* below, the setter will set the event and WaitForMultipleObjects()
* will return immediately.
*/
if (!ResetEvent(latchevent))
elog(ERROR, "ResetEvent failed: error code %lu", GetLastError());
if ((wakeEvents & WL_LATCH_SET) && latch->is_set)
{
result |= WL_LATCH_SET;
/*
* Leave loop immediately, avoid blocking again. We don't attempt
* to report any other events that might also be satisfied.
*/
break;
}
rc = WaitForMultipleObjects(numevents, events, FALSE, timeout);
if (rc == WAIT_FAILED)
elog(ERROR, "WaitForMultipleObjects() failed: error code %lu", GetLastError());
/* Participate in Windows signal emulation */
else if (rc == WAIT_OBJECT_0 + 1)
pgwin32_dispatch_queued_signals();
else if (rc == WAIT_TIMEOUT)
{
result |= WL_TIMEOUT;
}
else if ((wakeEvents & WL_POSTMASTER_DEATH) &&
rc == WAIT_OBJECT_0 + pmdeath_eventno)
{
/* Postmaster died */
result |= WL_POSTMASTER_DEATH;
}
else if ((wakeEvents & (WL_SOCKET_READABLE | WL_SOCKET_WRITEABLE)) &&
rc == WAIT_OBJECT_0 + 2) /* socket is at event slot 2 */
{
WSANETWORKEVENTS resEvents;
ZeroMemory(&resEvents, sizeof(resEvents));
if (WSAEnumNetworkEvents(sock, sockevent, &resEvents) == SOCKET_ERROR)
ereport(FATAL,
(errmsg_internal("failed to enumerate network events: error code %lu", GetLastError())));
if ((wakeEvents & WL_SOCKET_READABLE) &&
(resEvents.lNetworkEvents & FD_READ))
{
result |= WL_SOCKET_READABLE;
}
if ((wakeEvents & WL_SOCKET_WRITEABLE) &&
(resEvents.lNetworkEvents & FD_WRITE))
{
result |= WL_SOCKET_WRITEABLE;
}
}
/* Otherwise it must be the latch event */
else if (rc != WAIT_OBJECT_0)
elog(ERROR, "unexpected return code from WaitForMultipleObjects(): %lu", rc);
}
while (result == 0);
/* Clean up the handle we created for the socket */
if (sockevent != WSA_INVALID_EVENT)
{
WSAEventSelect(sock, sockevent, 0);
WSACloseEvent(sockevent);
}
return result;
}
void
SetLatch(volatile Latch *latch)
{
HANDLE handle;
/* Quick exit if already set */
if (latch->is_set)
return;
latch->is_set = true;
/*
* See if anyone's waiting for the latch. It can be the current process if
* we're in a signal handler.
*
* Use a local variable here just in case somebody changes the event field
* concurrently (which really should not happen).
*/
handle = latch->event;
if (handle)
{
SetEvent(handle);
/*
* Note that we silently ignore any errors. We might be in a signal
* handler or other critical path where it's not safe to call elog().
*/
}
}
void
ResetLatch(volatile Latch *latch)
{
/* Only the owner should reset the latch */
Assert(latch->owner_pid == MyProcPid);
latch->is_set = false;
}