mirror of
https://git.postgresql.org/git/postgresql.git
synced 2024-09-14 04:29:46 +02:00
2746e5f21d
wait until it is set. Latches can be used to reliably wait until a signal arrives, which is hard otherwise because signals don't interrupt select() on some platforms, and even when they do, there's race conditions. On Unix, latches use the so called self-pipe trick under the covers to implement the sleep until the latch is set, without race conditions. On Windows, Windows events are used. Use the new latch abstraction to sleep in walsender, so that as soon as a transaction finishes, walsender is woken up to immediately send the WAL to the standby. This reduces the latency between master and standby, which is good. Preliminary work by Fujii Masao. The latch implementation is by me, with helpful comments from many people.
285 lines
6.8 KiB
C
285 lines
6.8 KiB
C
/*-------------------------------------------------------------------------
|
|
*
|
|
* win32_latch.c
|
|
* Windows implementation of latches.
|
|
*
|
|
* The Windows implementation uses Windows events. See unix_latch.c for
|
|
* information on usage.
|
|
*
|
|
* Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
|
*
|
|
* IDENTIFICATION
|
|
* $PostgreSQL: pgsql/src/backend/port/win32_latch.c,v 1.1 2010/09/11 15:48:04 heikki Exp $
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
#include "postgres.h"
|
|
|
|
#include <fcntl.h>
|
|
#include <signal.h>
|
|
#include <unistd.h>
|
|
|
|
#include "miscadmin.h"
|
|
#include "replication/walsender.h"
|
|
#include "storage/latch.h"
|
|
#include "storage/shmem.h"
|
|
#include "storage/spin.h"
|
|
|
|
/*
|
|
* Shared latches are implemented with Windows events that are shared by
|
|
* all postmaster child processes. At postmaster startup we create enough
|
|
* Event objects, and mark them as inheritable so that they are accessible
|
|
* in child processes. The handles are stored in sharedHandles.
|
|
*/
|
|
typedef struct
|
|
{
|
|
slock_t mutex; /* protects all the other fields */
|
|
|
|
int maxhandles; /* number of shared handles created */
|
|
int nfreehandles; /* number of free handles in array */
|
|
HANDLE handles[1]; /* free handles, variable length */
|
|
} SharedEventHandles;
|
|
|
|
static SharedEventHandles *sharedHandles;
|
|
|
|
void
|
|
InitLatch(volatile Latch *latch)
|
|
{
|
|
latch->event = CreateEvent(NULL, TRUE, FALSE, NULL);
|
|
latch->is_shared = false;
|
|
latch->is_set = false;
|
|
}
|
|
|
|
void
|
|
InitSharedLatch(volatile Latch *latch)
|
|
{
|
|
latch->is_shared = true;
|
|
latch->is_set = false;
|
|
latch->event = NULL;
|
|
}
|
|
|
|
void
|
|
OwnLatch(volatile Latch *latch)
|
|
{
|
|
HANDLE event;
|
|
|
|
/* Sanity checks */
|
|
Assert(latch->is_shared);
|
|
if (latch->event != 0)
|
|
elog(ERROR, "latch already owned");
|
|
|
|
/* Reserve an event handle from the shared handles array */
|
|
SpinLockAcquire(&sharedHandles->mutex);
|
|
if (sharedHandles->nfreehandles <= 0)
|
|
{
|
|
SpinLockRelease(&sharedHandles->mutex);
|
|
elog(ERROR, "out of shared event objects");
|
|
}
|
|
sharedHandles->nfreehandles--;
|
|
event = sharedHandles->handles[sharedHandles->nfreehandles];
|
|
SpinLockRelease(&sharedHandles->mutex);
|
|
|
|
latch->event = event;
|
|
}
|
|
|
|
void
|
|
DisownLatch(volatile Latch *latch)
|
|
{
|
|
Assert(latch->is_shared);
|
|
Assert(latch->event != NULL);
|
|
|
|
/* Put the event handle back to the pool */
|
|
SpinLockAcquire(&sharedHandles->mutex);
|
|
if (sharedHandles->nfreehandles >= sharedHandles->maxhandles)
|
|
{
|
|
SpinLockRelease(&sharedHandles->mutex);
|
|
elog(PANIC, "too many free event handles");
|
|
}
|
|
sharedHandles->handles[sharedHandles->nfreehandles] = latch->event;
|
|
sharedHandles->nfreehandles++;
|
|
SpinLockRelease(&sharedHandles->mutex);
|
|
|
|
latch->event = NULL;
|
|
}
|
|
|
|
bool
|
|
WaitLatch(volatile Latch *latch, long timeout)
|
|
{
|
|
return WaitLatchOrSocket(latch, PGINVALID_SOCKET, timeout) > 0;
|
|
}
|
|
|
|
int
|
|
WaitLatchOrSocket(volatile Latch *latch, SOCKET sock, long timeout)
|
|
{
|
|
DWORD rc;
|
|
HANDLE events[3];
|
|
HANDLE latchevent;
|
|
HANDLE sockevent;
|
|
int numevents;
|
|
int result = 0;
|
|
|
|
latchevent = latch->event;
|
|
|
|
events[0] = latchevent;
|
|
events[1] = pgwin32_signal_event;
|
|
numevents = 2;
|
|
if (sock != PGINVALID_SOCKET)
|
|
{
|
|
sockevent = WSACreateEvent();
|
|
WSAEventSelect(sock, sockevent, FD_READ);
|
|
events[numevents++] = sockevent;
|
|
}
|
|
|
|
for (;;)
|
|
{
|
|
/*
|
|
* 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 %d", (int) GetLastError());
|
|
if (latch->is_set)
|
|
{
|
|
result = 1;
|
|
break;
|
|
}
|
|
|
|
rc = WaitForMultipleObjects(numevents, events, FALSE,
|
|
(timeout >= 0) ? (timeout / 1000) : INFINITE);
|
|
if (rc == WAIT_FAILED)
|
|
elog(ERROR, "WaitForMultipleObjects() failed: error code %d", (int) GetLastError());
|
|
else if (rc == WAIT_TIMEOUT)
|
|
{
|
|
result = 0;
|
|
break;
|
|
}
|
|
else if (rc == WAIT_OBJECT_0 + 1)
|
|
pgwin32_dispatch_queued_signals();
|
|
else if (rc == WAIT_OBJECT_0 + 2)
|
|
{
|
|
Assert(sock != PGINVALID_SOCKET);
|
|
result = 2;
|
|
break;
|
|
}
|
|
else if (rc != WAIT_OBJECT_0)
|
|
elog(ERROR, "unexpected return code from WaitForMultipleObjects(): %d", rc);
|
|
}
|
|
|
|
/* Clean up the handle we created for the socket */
|
|
if (sock != PGINVALID_SOCKET)
|
|
{
|
|
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 in case the
|
|
* latch is just disowned between the test and the SetEvent call, and
|
|
* event field set to NULL.
|
|
*
|
|
* Fetch handle field only once, in case the owner simultaneously
|
|
* disowns the latch and clears handle. This assumes that HANDLE is
|
|
* atomic, which isn't guaranteed to be true! In practice, it should be,
|
|
* and in the worst case we end up calling SetEvent with a bogus handle,
|
|
* and SetEvent will return an error with no harm done.
|
|
*/
|
|
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)
|
|
{
|
|
latch->is_set = false;
|
|
}
|
|
|
|
/*
|
|
* Number of shared latches, used to allocate the right number of shared
|
|
* Event handles at postmaster startup. You must update this if you
|
|
* introduce a new shared latch!
|
|
*/
|
|
static int
|
|
NumSharedLatches(void)
|
|
{
|
|
int numLatches = 0;
|
|
|
|
/* Each walsender needs one latch */
|
|
numLatches += max_wal_senders;
|
|
|
|
return numLatches;
|
|
}
|
|
|
|
/*
|
|
* LatchShmemSize
|
|
* Compute space needed for latch's shared memory
|
|
*/
|
|
Size
|
|
LatchShmemSize(void)
|
|
{
|
|
return offsetof(SharedEventHandles, handles) +
|
|
NumSharedLatches() * sizeof(HANDLE);
|
|
}
|
|
|
|
/*
|
|
* LatchShmemInit
|
|
* Allocate and initialize shared memory needed for latches
|
|
*/
|
|
void
|
|
LatchShmemInit(void)
|
|
{
|
|
Size size = LatchShmemSize();
|
|
bool found;
|
|
|
|
sharedHandles = ShmemInitStruct("SharedEventHandles", size, &found);
|
|
|
|
/* If we're first, initialize the struct and allocate handles */
|
|
if (!found)
|
|
{
|
|
int i;
|
|
SECURITY_ATTRIBUTES sa;
|
|
|
|
/*
|
|
* Set up security attributes to specify that the events are
|
|
* inherited.
|
|
*/
|
|
ZeroMemory(&sa, sizeof(sa));
|
|
sa.nLength = sizeof(sa);
|
|
sa.bInheritHandle = TRUE;
|
|
|
|
SpinLockInit(&sharedHandles->mutex);
|
|
sharedHandles->maxhandles = NumSharedLatches();
|
|
sharedHandles->nfreehandles = sharedHandles->maxhandles;
|
|
for (i = 0; i < sharedHandles->maxhandles; i++)
|
|
{
|
|
sharedHandles->handles[i] = CreateEvent(&sa, TRUE, FALSE, NULL);
|
|
if (sharedHandles->handles[i] == NULL)
|
|
elog(ERROR, "CreateEvent failed: error code %d", (int) GetLastError());
|
|
}
|
|
}
|
|
}
|