/* * 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; } } }
/* Sleep function that can be interrupted by signals */ void pgwin32_backend_usleep(long microsec) { if (WaitForSingleObject(pgwin32_signal_event, (microsec < 500 ? 1 : (microsec + 500) / 1000)) == WAIT_OBJECT_0) { pgwin32_dispatch_queued_signals(); errno = EINTR; return; } }
static int pgwin32_poll_signals(void) { if (UNBLOCKED_SIGNAL_QUEUE()) { pgwin32_dispatch_queued_signals(); errno = EINTR; return 1; } return 0; }
/* * PGSemaphoreLock * * Lock a semaphore (decrement count), blocking if count would be < 0. * Serve the interrupt if interruptOK is true. */ void PGSemaphoreLock(PGSemaphore sema, bool interruptOK) { DWORD ret; HANDLE wh[2]; /* * 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. */ do { ImmediateInterruptOK = interruptOK; CHECK_FOR_INTERRUPTS(); ret = WaitForMultipleObjectsEx(2, wh, FALSE, INFINITE, TRUE); if (ret == WAIT_OBJECT_0) { /* Signal event is set - we have a signal to deliver */ pgwin32_dispatch_queued_signals(); errno = EINTR; } else if (ret == WAIT_OBJECT_0 + 1) { /* We got it! */ errno = 0; } else /* Otherwise we are in trouble */ errno = EIDRM; ImmediateInterruptOK = false; } while (errno == EINTR); if (errno != 0) ereport(FATAL, (errmsg("could not lock semaphore: error code %lu", GetLastError()))); }
/* signal masking. Only called on main thread, no sync required */ int pqsigsetmask(int mask) { int prevmask; prevmask = pg_signal_mask; pg_signal_mask = mask; /* * Dispatch any signals queued up right away, in case we have unblocked * one or more signals previously queued */ pgwin32_dispatch_queued_signals(); return prevmask; }
int pgwin32_waitforsinglesocket(SOCKET s, int what) { static HANDLE waitevent = INVALID_HANDLE_VALUE; HANDLE events[2]; int r; if (waitevent == INVALID_HANDLE_VALUE) { waitevent = CreateEvent(NULL, TRUE, FALSE, NULL); if (waitevent == INVALID_HANDLE_VALUE) ereport(ERROR, (errmsg_internal("Failed to create socket waiting event: %i", (int) GetLastError()))); } else if (!ResetEvent(waitevent)) ereport(ERROR, (errmsg_internal("Failed to reset socket waiting event: %i", (int) GetLastError()))); if (WSAEventSelect(s, waitevent, what) == SOCKET_ERROR) { TranslateSocketError(); return 0; } events[0] = pgwin32_signal_event; events[1] = waitevent; r = WaitForMultipleObjectsEx(2, events, FALSE, INFINITE, TRUE); if (r == WAIT_OBJECT_0 || r == WAIT_IO_COMPLETION) { pgwin32_dispatch_queued_signals(); errno = EINTR; return 0; } if (r == WAIT_OBJECT_0 + 1) return 1; ereport(ERROR, (errmsg_internal("Bad return from WaitForMultipleObjects: %i (%i)", r, (int) GetLastError()))); return 0; }
/* * PGSemaphoreLock * * Lock a semaphore (decrement count), blocking if count would be < 0. * Serve the interrupt if interruptOK is true. */ void PGSemaphoreLock(PGSemaphore sema, bool interruptOK) { DWORD ret; HANDLE wh[2]; wh[0] = *sema; wh[1] = pgwin32_signal_event; do { ImmediateInterruptOK = interruptOK; CHECK_FOR_INTERRUPTS(); errno = 0; ret = WaitForMultipleObjectsEx(2, wh, FALSE, INFINITE, TRUE); if (ret == WAIT_OBJECT_0) { /* We got it! */ return; } else if (ret == WAIT_OBJECT_0 + 1) { /* Signal event is set - we have a signal to deliver */ pgwin32_dispatch_queued_signals(); errno = EINTR; } else /* Otherwise we are in trouble */ errno = EIDRM; ImmediateInterruptOK = false; } while (errno == EINTR); if (errno != 0) ereport(FATAL, (errmsg("could not lock semaphore: error code %d", (int) GetLastError()))); }
void pg_usleep(long microsec) { if (program_mode == CLIENT) { if (microsec > 0) SleepEx((microsec < 500 ? 1 : (microsec + 500) / 1000), FALSE); } else { /* * In a Windows backend, we use implementation signal-aware * version from src/backend/port/win32/signal.c. */ HANDLE pgwin32_signal_event; if (WaitForSingleObject( pgwin32_signal_event, (microsec < 500 ? 1 : (microsec + 500) / 1000)) == WAIT_OBJECT_0) { pgwin32_dispatch_queued_signals(); errno = EINTR; return; } } }
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 */ /* Cannot specify WL_SOCKET_WRITEABLE without WL_SOCKET_READABLE */ Assert((wakeEvents & (WL_SOCKET_READABLE | WL_SOCKET_WRITEABLE)) != WL_SOCKET_WRITEABLE); 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(). * * 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. */ latchevent = latch->event; events[0] = pgwin32_signal_event; events[1] = latchevent; numevents = 2; if (wakeEvents & (WL_SOCKET_READABLE | WL_SOCKET_WRITEABLE)) { /* Need an event object to represent events on the socket */ int flags = 0; if (wakeEvents & WL_SOCKET_READABLE) flags |= (FD_READ | FD_CLOSE); if (wakeEvents & WL_SOCKET_WRITEABLE) flags |= FD_WRITE; sockevent = WSACreateEvent(); if (sockevent == WSA_INVALID_EVENT) elog(ERROR, "failed to create event for socket: error code %u", WSAGetLastError()); if (WSAEventSelect(sock, sockevent, flags) != 0) elog(ERROR, "failed to set up event for socket: error code %u", WSAGetLastError()); events[numevents++] = sockevent; } if (wakeEvents & WL_POSTMASTER_DEATH) { pmdeath_eventno = numevents; events[numevents++] = PostmasterHandle; } /* Ensure that signals are serviced even if latch is already set */ pgwin32_dispatch_queued_signals(); 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()); else if (rc == WAIT_TIMEOUT) { result |= WL_TIMEOUT; } else if (rc == WAIT_OBJECT_0) { /* Service newly-arrived signals */ pgwin32_dispatch_queued_signals(); } else if (rc == WAIT_OBJECT_0 + 1) { /* Latch is set, we'll handle that on next iteration of loop */ } 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) != 0) elog(ERROR, "failed to enumerate network events: error code %u", WSAGetLastError()); if ((wakeEvents & WL_SOCKET_READABLE) && (resEvents.lNetworkEvents & (FD_READ | FD_CLOSE))) { result |= WL_SOCKET_READABLE; } if ((wakeEvents & WL_SOCKET_WRITEABLE) && (resEvents.lNetworkEvents & FD_WRITE)) { result |= WL_SOCKET_WRITEABLE; } } else if ((wakeEvents & WL_POSTMASTER_DEATH) && rc == WAIT_OBJECT_0 + pmdeath_eventno) { /* * Postmaster apparently died. Since the consequences of falsely * returning WL_POSTMASTER_DEATH could be pretty unpleasant, we * take the trouble to positively verify this with * PostmasterIsAlive(), even though there is no known reason to * think that the event could be falsely set on Windows. */ if (!PostmasterIsAlive()) result |= WL_POSTMASTER_DEATH; } else elog(ERROR, "unexpected return code from WaitForMultipleObjects(): %lu", rc); } while (result == 0); /* Clean up the event object we created for the socket */ if (sockevent != WSA_INVALID_EVENT) { WSAEventSelect(sock, NULL, 0); WSACloseEvent(sockevent); } return result; }
/* * Wait for events added to the set to happen, or until the timeout is * reached. At most nevents occurred events are returned. * * If timeout = -1, block until an event occurs; if 0, check sockets for * readiness, but don't block; if > 0, block for at most timeout miliseconds. * * Returns the number of events occurred, or 0 if the timeout was reached. * * Returned events will have the fd, pos, user_data fields set to the * values associated with the registered event. */ int WaitEventSetWait(WaitEventSet *set, long timeout, WaitEvent *occurred_events, int nevents) { int returned_events = 0; instr_time start_time; instr_time cur_time; long cur_timeout = -1; Assert(nevents > 0); /* * Initialize timeout if requested. We must record the current time so * that we can determine the remaining timeout if interrupted. */ if (timeout >= 0) { INSTR_TIME_SET_CURRENT(start_time); Assert(timeout >= 0 && timeout <= INT_MAX); cur_timeout = timeout; } #ifndef WIN32 waiting = true; #else /* Ensure that signals are serviced even if latch is already set */ pgwin32_dispatch_queued_signals(); #endif while (returned_events == 0) { int rc; /* * Check if the latch is set already. If so, leave the loop * immediately, avoid blocking again. We don't attempt to report any * other events that might also be satisfied. * * If someone sets the latch between this and the * WaitEventSetWaitBlock() below, the setter will write a byte to the * pipe (or signal us and the signal handler will do that), and the * readiness routine will return immediately. * * On unix, If there's a pending byte in the self pipe, we'll notice * whenever blocking. Only clearing the pipe in that case avoids * having to drain it every time WaitLatchOrSocket() is used. Should * the pipe-buffer fill up we're still ok, because the pipe is in * nonblocking mode. It's unlikely for that to happen, because the * self pipe isn't filled unless we're blocking (waiting = true), or * from inside a signal handler in latch_sigusr1_handler(). * * On windows, we'll also notice if there's a pending event for the * latch when blocking, but there's no danger of anything filling up, * as "Setting an event that is already set has no effect.". * * Note: we assume that the kernel calls involved in latch management * will provide adequate synchronization on machines with weak memory * ordering, so that we cannot miss seeing is_set if a notification * has already been queued. */ if (set->latch && set->latch->is_set) { occurred_events->fd = PGINVALID_SOCKET; occurred_events->pos = set->latch_pos; occurred_events->user_data = set->events[set->latch_pos].user_data; occurred_events->events = WL_LATCH_SET; occurred_events++; returned_events++; break; } /* * Wait for events using the readiness primitive chosen at the top of * this file. If -1 is returned, a timeout has occurred, if 0 we have * to retry, everything >= 1 is the number of returned events. */ rc = WaitEventSetWaitBlock(set, cur_timeout, occurred_events, nevents); if (rc == -1) break; /* timeout occurred */ else returned_events = rc; /* If we're not done, update cur_timeout for next iteration */ if (returned_events == 0 && timeout >= 0) { INSTR_TIME_SET_CURRENT(cur_time); INSTR_TIME_SUBTRACT(cur_time, start_time); cur_timeout = timeout - (long) INSTR_TIME_GET_MILLISEC(cur_time); if (cur_timeout <= 0) break; } } #ifndef WIN32 waiting = false; #endif return returned_events; }
/* * Wait using Windows' WaitForMultipleObjects(). * * Unfortunately this will only ever return a single readiness notification at * a time. Note that while the official documentation for * WaitForMultipleObjects is ambiguous about multiple events being "consumed" * with a single bWaitAll = FALSE call, * https://blogs.msdn.microsoft.com/oldnewthing/20150409-00/?p=44273 confirms * that only one event is "consumed". */ static inline int WaitEventSetWaitBlock(WaitEventSet *set, int cur_timeout, WaitEvent *occurred_events, int nevents) { int returned_events = 0; DWORD rc; WaitEvent *cur_event; /* * Sleep. * * Need to wait for ->nevents + 1, because signal handle is in [0]. */ rc = WaitForMultipleObjects(set->nevents + 1, set->handles, FALSE, cur_timeout); /* Check return code */ if (rc == WAIT_FAILED) elog(ERROR, "WaitForMultipleObjects() failed: error code %lu", GetLastError()); else if (rc == WAIT_TIMEOUT) { /* timeout exceeded */ return -1; } if (rc == WAIT_OBJECT_0) { /* Service newly-arrived signals */ pgwin32_dispatch_queued_signals(); return 0; /* retry */ } /* * With an offset of one, due to the always present pgwin32_signal_event, * the handle offset directly corresponds to a wait event. */ cur_event = (WaitEvent *) &set->events[rc - WAIT_OBJECT_0 - 1]; occurred_events->pos = cur_event->pos; occurred_events->user_data = cur_event->user_data; occurred_events->events = 0; if (cur_event->events == WL_LATCH_SET) { if (!ResetEvent(set->latch->event)) elog(ERROR, "ResetEvent failed: error code %lu", GetLastError()); if (set->latch->is_set) { occurred_events->fd = PGINVALID_SOCKET; occurred_events->events = WL_LATCH_SET; occurred_events++; returned_events++; } } else if (cur_event->events == WL_POSTMASTER_DEATH) { /* * Postmaster apparently died. Since the consequences of falsely * returning WL_POSTMASTER_DEATH could be pretty unpleasant, we take * the trouble to positively verify this with PostmasterIsAlive(), * even though there is no known reason to think that the event could * be falsely set on Windows. */ if (!PostmasterIsAlive()) { occurred_events->fd = PGINVALID_SOCKET; occurred_events->events = WL_POSTMASTER_DEATH; occurred_events++; returned_events++; } } else if (cur_event->events & (WL_SOCKET_READABLE | WL_SOCKET_WRITEABLE)) { WSANETWORKEVENTS resEvents; HANDLE handle = set->handles[cur_event->pos + 1]; Assert(cur_event->fd); occurred_events->fd = cur_event->fd; ZeroMemory(&resEvents, sizeof(resEvents)); if (WSAEnumNetworkEvents(cur_event->fd, handle, &resEvents) != 0) elog(ERROR, "failed to enumerate network events: error code %u", WSAGetLastError()); if ((cur_event->events & WL_SOCKET_READABLE) && (resEvents.lNetworkEvents & FD_READ)) { /* data available in socket */ occurred_events->events |= WL_SOCKET_READABLE; } if ((cur_event->events & WL_SOCKET_WRITEABLE) && (resEvents.lNetworkEvents & FD_WRITE)) { /* writeable */ occurred_events->events |= WL_SOCKET_WRITEABLE; } if (resEvents.lNetworkEvents & FD_CLOSE) { /* EOF */ if (cur_event->events & WL_SOCKET_READABLE) occurred_events->events |= WL_SOCKET_READABLE; if (cur_event->events & WL_SOCKET_WRITEABLE) occurred_events->events |= WL_SOCKET_WRITEABLE; } if (occurred_events->events != 0) { occurred_events++; returned_events++; } } return returned_events; }
/* * Wait for activity on one or more sockets. * While waiting, allow signals to run * * NOTE! Currently does not implement exceptfds check, * since it is not used in postgresql! */ int pgwin32_select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, const struct timeval * timeout) { WSAEVENT events[FD_SETSIZE * 2]; /* worst case is readfds totally * different from writefds, so * 2*FD_SETSIZE sockets */ SOCKET sockets[FD_SETSIZE * 2]; int numevents = 0; int i; int r; DWORD timeoutval = WSA_INFINITE; FD_SET outreadfds; FD_SET outwritefds; int nummatches = 0; Assert(exceptfds == NULL); if (pgwin32_poll_signals()) return -1; FD_ZERO(&outreadfds); FD_ZERO(&outwritefds); /* * Write FDs are different in the way that it is only flagged by * WSASelectEvent() if we have tried to write to them first. So try an * empty write */ if (writefds) { for (i = 0; i < writefds->fd_count; i++) { char c; WSABUF buf; DWORD sent; buf.buf = &c; buf.len = 0; r = WSASend(writefds->fd_array[i], &buf, 1, &sent, 0, NULL, NULL); if (r == 0) /* Completed - means things are fine! */ FD_SET(writefds->fd_array[i], &outwritefds); else { /* Not completed */ if (WSAGetLastError() != WSAEWOULDBLOCK) /* * Not completed, and not just "would block", so an error * occurred */ FD_SET(writefds->fd_array[i], &outwritefds); } } if (outwritefds.fd_count > 0) { memcpy(writefds, &outwritefds, sizeof(fd_set)); if (readfds) FD_ZERO(readfds); return outwritefds.fd_count; } } /* Now set up for an actual select */ if (timeout != NULL) { /* timeoutval is in milliseconds */ timeoutval = timeout->tv_sec * 1000 + timeout->tv_usec / 1000; } if (readfds != NULL) { for (i = 0; i < readfds->fd_count; i++) { events[numevents] = WSACreateEvent(); sockets[numevents] = readfds->fd_array[i]; numevents++; } } if (writefds != NULL) { for (i = 0; i < writefds->fd_count; i++) { if (!readfds || !FD_ISSET(writefds->fd_array[i], readfds)) { /* If the socket is not in the read list */ events[numevents] = WSACreateEvent(); sockets[numevents] = writefds->fd_array[i]; numevents++; } } } for (i = 0; i < numevents; i++) { int flags = 0; if (readfds && FD_ISSET(sockets[i], readfds)) flags |= FD_READ | FD_ACCEPT | FD_CLOSE; if (writefds && FD_ISSET(sockets[i], writefds)) flags |= FD_WRITE | FD_CLOSE; if (WSAEventSelect(sockets[i], events[i], flags) != 0) { TranslateSocketError(); /* release already-assigned event objects */ while (--i >= 0) WSAEventSelect(sockets[i], NULL, 0); for (i = 0; i < numevents; i++) WSACloseEvent(events[i]); return -1; } } events[numevents] = pgwin32_signal_event; r = WaitForMultipleObjectsEx(numevents + 1, events, FALSE, timeoutval, TRUE); if (r != WAIT_TIMEOUT && r != WAIT_IO_COMPLETION && r != (WAIT_OBJECT_0 + numevents)) { /* * We scan all events, even those not signalled, in case more than one * event has been tagged but Wait.. can only return one. */ WSANETWORKEVENTS resEvents; for (i = 0; i < numevents; i++) { ZeroMemory(&resEvents, sizeof(resEvents)); if (WSAEnumNetworkEvents(sockets[i], events[i], &resEvents) != 0) elog(ERROR, "failed to enumerate network events: error code %u", WSAGetLastError()); /* Read activity? */ if (readfds && FD_ISSET(sockets[i], readfds)) { if ((resEvents.lNetworkEvents & FD_READ) || (resEvents.lNetworkEvents & FD_ACCEPT) || (resEvents.lNetworkEvents & FD_CLOSE)) { FD_SET(sockets[i], &outreadfds); nummatches++; } } /* Write activity? */ if (writefds && FD_ISSET(sockets[i], writefds)) { if ((resEvents.lNetworkEvents & FD_WRITE) || (resEvents.lNetworkEvents & FD_CLOSE)) { FD_SET(sockets[i], &outwritefds); nummatches++; } } } } /* Clean up all the event objects */ for (i = 0; i < numevents; i++) { WSAEventSelect(sockets[i], NULL, 0); WSACloseEvent(events[i]); } if (r == WSA_WAIT_TIMEOUT) { if (readfds) FD_ZERO(readfds); if (writefds) FD_ZERO(writefds); return 0; } /* Signal-like events. */ if (r == WAIT_OBJECT_0 + numevents || r == WAIT_IO_COMPLETION) { pgwin32_dispatch_queued_signals(); errno = EINTR; if (readfds) FD_ZERO(readfds); if (writefds) FD_ZERO(writefds); return -1; } /* Overwrite socket sets with our resulting values */ if (readfds) memcpy(readfds, &outreadfds, sizeof(fd_set)); if (writefds) memcpy(writefds, &outwritefds, sizeof(fd_set)); return nummatches; }
int pgwin32_waitforsinglesocket(SOCKET s, int what, int timeout) { static HANDLE waitevent = INVALID_HANDLE_VALUE; static SOCKET current_socket = INVALID_SOCKET; static int isUDP = 0; HANDLE events[2]; int r; /* Create an event object just once and use it on all future calls */ if (waitevent == INVALID_HANDLE_VALUE) { waitevent = CreateEvent(NULL, TRUE, FALSE, NULL); if (waitevent == INVALID_HANDLE_VALUE) ereport(ERROR, (errmsg_internal("could not create socket waiting event: error code %lu", GetLastError()))); } else if (!ResetEvent(waitevent)) ereport(ERROR, (errmsg_internal("could not reset socket waiting event: error code %lu", GetLastError()))); /* * Track whether socket is UDP or not. (NB: most likely, this is both * useless and wrong; there is no reason to think that the behavior of * WSAEventSelect is different for TCP and UDP.) */ if (current_socket != s) isUDP = isDataGram(s); current_socket = s; /* * Attach event to socket. NOTE: we must detach it again before * returning, since other bits of code may try to attach other events to * the socket. */ if (WSAEventSelect(s, waitevent, what) != 0) { TranslateSocketError(); return 0; } events[0] = pgwin32_signal_event; events[1] = waitevent; /* * Just a workaround of unknown locking problem with writing in UDP socket * under high load: Client's pgsql backend sleeps infinitely in * WaitForMultipleObjectsEx, pgstat process sleeps in pgwin32_select(). * So, we will wait with small timeout(0.1 sec) and if socket is still * blocked, try WSASend (see comments in pgwin32_select) and wait again. */ if ((what & FD_WRITE) && isUDP) { for (;;) { r = WaitForMultipleObjectsEx(2, events, FALSE, 100, TRUE); if (r == WAIT_TIMEOUT) { char c; WSABUF buf; DWORD sent; buf.buf = &c; buf.len = 0; r = WSASend(s, &buf, 1, &sent, 0, NULL, NULL); if (r == 0) /* Completed - means things are fine! */ { WSAEventSelect(s, NULL, 0); return 1; } else if (WSAGetLastError() != WSAEWOULDBLOCK) { TranslateSocketError(); WSAEventSelect(s, NULL, 0); return 0; } } else break; } } else r = WaitForMultipleObjectsEx(2, events, FALSE, timeout, TRUE); WSAEventSelect(s, NULL, 0); if (r == WAIT_OBJECT_0 || r == WAIT_IO_COMPLETION) { pgwin32_dispatch_queued_signals(); errno = EINTR; return 0; } if (r == WAIT_OBJECT_0 + 1) return 1; if (r == WAIT_TIMEOUT) { errno = EWOULDBLOCK; return 0; } ereport(ERROR, (errmsg_internal("unrecognized return value from WaitForMultipleObjects: %d (error code %lu)", r, GetLastError()))); return 0; }
static void interruptCallback() { if (UNBLOCKED_SIGNAL_QUEUE()) pgwin32_dispatch_queued_signals(); }
int pgwin32_waitforsinglesocket(SOCKET s, int what, int timeout) { static HANDLE waitevent = INVALID_HANDLE_VALUE; static SOCKET current_socket = -1; static int isUDP = 0; HANDLE events[2]; int r; if (waitevent == INVALID_HANDLE_VALUE) { waitevent = CreateEvent(NULL, TRUE, FALSE, NULL); if (waitevent == INVALID_HANDLE_VALUE) ereport(ERROR, (errmsg_internal("Failed to create socket waiting event: %i", (int) GetLastError()))); } else if (!ResetEvent(waitevent)) ereport(ERROR, (errmsg_internal("Failed to reset socket waiting event: %i", (int) GetLastError()))); /* * make sure we don't multiplex this kernel event object with a different * socket from a previous call */ if (current_socket != s) { if (current_socket != -1) WSAEventSelect(current_socket, waitevent, 0); isUDP = isDataGram(s); } current_socket = s; if (WSAEventSelect(s, waitevent, what) == SOCKET_ERROR) { TranslateSocketError(); return 0; } events[0] = pgwin32_signal_event; events[1] = waitevent; /* * Just a workaround of unknown locking problem with writing in UDP socket * under high load: Client's pgsql backend sleeps infinitely in * WaitForMultipleObjectsEx, pgstat process sleeps in pgwin32_select(). * So, we will wait with small timeout(0.1 sec) and if sockect is still * blocked, try WSASend (see comments in pgwin32_select) and wait again. */ if ((what & FD_WRITE) && isUDP) { for (;;) { r = WaitForMultipleObjectsEx(2, events, FALSE, 100, TRUE); if (r == WAIT_TIMEOUT) { char c; WSABUF buf; DWORD sent; buf.buf = &c; buf.len = 0; r = WSASend(s, &buf, 1, &sent, 0, NULL, NULL); if (r == 0) /* Completed - means things are fine! */ return 1; else if (WSAGetLastError() != WSAEWOULDBLOCK) { TranslateSocketError(); return 0; } } else break; } } else r = WaitForMultipleObjectsEx(2, events, FALSE, timeout, TRUE); if (r == WAIT_OBJECT_0 || r == WAIT_IO_COMPLETION) { pgwin32_dispatch_queued_signals(); errno = EINTR; return 0; } if (r == WAIT_OBJECT_0 + 1) return 1; if (r == WAIT_TIMEOUT) return 0; ereport(ERROR, (errmsg_internal("Bad return from WaitForMultipleObjects: %i (%i)", r, (int) GetLastError()))); return 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; }