1
0
mirror of https://github.com/postgres/postgres.git synced 2025-10-18 04:29:09 +03:00

Documentation improvement and minor code cleanups for the latch facility.

Improve the documentation around weak-memory-ordering risks, and do a pass
of general editorialization on the comments in the latch code.  Make the
Windows latch code more like the Unix latch code where feasible; in
particular provide the same Assert checks in both implementations.
Fix poorly-placed WaitLatch call in syncrep.c.

This patch resolves, for the moment, concerns around weak-memory-ordering
bugs in latch-related code: we have documented the restrictions and checked
that existing calls meet them.  In 9.2 I hope that we will install suitable
memory barrier instructions in SetLatch/ResetLatch, so that their callers
don't need to be quite so careful.
This commit is contained in:
Tom Lane
2011-08-09 15:30:45 -04:00
parent cff60f2dfa
commit 4e15a4db5e
5 changed files with 185 additions and 124 deletions

View File

@@ -1,9 +1,10 @@
/*-------------------------------------------------------------------------
*
* win32_latch.c
* Windows implementation of latches.
* Routines for inter-process latches
*
* See unix_latch.c for information on usage.
* 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.
@@ -24,7 +25,6 @@
#include "miscadmin.h"
#include "postmaster/postmaster.h"
#include "replication/walsender.h"
#include "storage/latch.h"
#include "storage/shmem.h"
@@ -89,7 +89,7 @@ WaitLatch(volatile Latch *latch, int wakeEvents, long timeout)
}
int
WaitLatchOrSocket(volatile Latch *latch, int wakeEvents, SOCKET sock,
WaitLatchOrSocket(volatile Latch *latch, int wakeEvents, pgsocket sock,
long timeout)
{
DWORD rc;
@@ -101,12 +101,15 @@ WaitLatchOrSocket(volatile Latch *latch, int wakeEvents, SOCKET sock,
int pmdeath_eventno = 0;
long timeout_ms;
Assert(wakeEvents != 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 milliseconds for WaitForMultipleObjects() */
if (wakeEvents & WL_TIMEOUT)
{
@@ -122,8 +125,8 @@ WaitLatchOrSocket(volatile Latch *latch, int wakeEvents, SOCKET sock,
events[0] = latchevent;
events[1] = pgwin32_signal_event;
numevents = 2;
if (((wakeEvents & WL_SOCKET_READABLE) ||
(wakeEvents & WL_SOCKET_WRITEABLE)))
if ((wakeEvents & WL_SOCKET_READABLE) ||
(wakeEvents & WL_SOCKET_WRITEABLE))
{
int flags = 0;
@@ -152,7 +155,7 @@ WaitLatchOrSocket(volatile Latch *latch, int wakeEvents, SOCKET sock,
*/
if (!ResetEvent(latchevent))
elog(ERROR, "ResetEvent failed: error code %d", (int) GetLastError());
if (latch->is_set && (wakeEvents & WL_LATCH_SET))
if ((wakeEvents & WL_LATCH_SET) && latch->is_set)
{
result |= WL_LATCH_SET;
/*
@@ -171,17 +174,17 @@ WaitLatchOrSocket(volatile Latch *latch, int wakeEvents, SOCKET sock,
else if (rc == WAIT_OBJECT_0 + 1)
pgwin32_dispatch_queued_signals();
else if ((wakeEvents & WL_POSTMASTER_DEATH) &&
rc == WAIT_OBJECT_0 + pmdeath_eventno)
{
/* Postmaster died */
result |= WL_POSTMASTER_DEATH;
}
else if (rc == WAIT_TIMEOUT)
{
result |= WL_TIMEOUT;
}
else if ((wakeEvents & (WL_SOCKET_READABLE | WL_SOCKET_WRITEABLE)) != 0 &&
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;
@@ -206,7 +209,7 @@ WaitLatchOrSocket(volatile Latch *latch, int wakeEvents, SOCKET sock,
else if (rc != WAIT_OBJECT_0)
elog(ERROR, "unexpected return code from WaitForMultipleObjects(): %d", (int) rc);
}
while(result == 0);
while (result == 0);
/* Clean up the handle we created for the socket */
if (sockevent != WSA_INVALID_EVENT)
@@ -231,15 +234,10 @@ SetLatch(volatile Latch *latch)
/*
* 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.
* we're in a signal handler.
*
* 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.
* Use a local variable here just in case somebody changes the event field
* concurrently (which really should not happen).
*/
handle = latch->event;
if (handle)
@@ -256,5 +254,8 @@ SetLatch(volatile Latch *latch)
void
ResetLatch(volatile Latch *latch)
{
/* Only the owner should reset the latch */
Assert(latch->owner_pid == MyProcPid);
latch->is_set = false;
}