diff options
Diffstat (limited to 'src/backend/port/win32_latch.c')
-rw-r--r-- | src/backend/port/win32_latch.c | 284 |
1 files changed, 284 insertions, 0 deletions
diff --git a/src/backend/port/win32_latch.c b/src/backend/port/win32_latch.c new file mode 100644 index 00000000000..f6d4920e465 --- /dev/null +++ b/src/backend/port/win32_latch.c @@ -0,0 +1,284 @@ +/*------------------------------------------------------------------------- + * + * 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()); + } + } +} |