diff options
author | alexv-smirnov <alex@ydb.tech> | 2022-12-20 00:50:48 +0300 |
---|---|---|
committer | alexv-smirnov <alex@ydb.tech> | 2022-12-20 00:50:48 +0300 |
commit | 84f2cfa253cc618438ed6e9d68b33fa7c0d88cb9 (patch) | |
tree | f0cf2236e0aafb3e437199f1ac7b559e7fad554a /contrib/libs/postgresql/src/backend/port/win32 | |
parent | bde6febc1ad3b826e72746de21d7250803e8e0b5 (diff) | |
download | ydb-84f2cfa253cc618438ed6e9d68b33fa7c0d88cb9.tar.gz |
add windows platform to ydb github export
Diffstat (limited to 'contrib/libs/postgresql/src/backend/port/win32')
4 files changed, 1348 insertions, 0 deletions
diff --git a/contrib/libs/postgresql/src/backend/port/win32/crashdump.c b/contrib/libs/postgresql/src/backend/port/win32/crashdump.c new file mode 100644 index 0000000000..45b6696ba1 --- /dev/null +++ b/contrib/libs/postgresql/src/backend/port/win32/crashdump.c @@ -0,0 +1,183 @@ +/*------------------------------------------------------------------------- + * + * crashdump.c + * Automatic crash dump creation for PostgreSQL on Windows + * + * The crashdump feature traps unhandled win32 exceptions produced by the + * backend, and tries to produce a Windows MiniDump crash + * dump for later debugging and analysis. The machine performing the dump + * doesn't need any special debugging tools; the user only needs to send + * the dump to somebody who has the same version of PostgreSQL and has debugging + * tools. + * + * crashdump module originally by Craig Ringer <ringerc@ringerc.id.au> + * + * LIMITATIONS + * =========== + * This *won't* work in hard OOM situations or stack overflows. + * + * For those, it'd be necessary to take a much more complicated approach where + * the handler switches to a new stack (if it can) and forks a helper process + * to debug it self. + * + * POSSIBLE FUTURE WORK + * ==================== + * For bonus points, the crash dump format permits embedding of user-supplied + * data. If there's anything else that should always be supplied with a crash + * dump (postgresql.conf? Last few lines of a log file?), it could potentially + * be added, though at the cost of a greater chance of the crash dump failing. + * + * + * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group + * + * IDENTIFICATION + * src/backend/port/win32/crashdump.c + * + *------------------------------------------------------------------------- + */ + +#include "postgres.h" + +#define WIN32_LEAN_AND_MEAN + +/* + * Some versions of the MS SDK contain "typedef enum { ... } ;" which the MS + * compiler quite sanely complains about. Well done, Microsoft. + * This pragma disables the warning just while we include the header. + * The pragma is known to work with all (as at the time of writing) supported + * versions of MSVC. + */ +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4091) +#endif +#include <dbghelp.h> +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +/* + * Much of the following code is based on CodeProject and MSDN examples, + * particularly + * http://www.codeproject.com/KB/debug/postmortemdebug_standalone1.aspx + * + * Useful MSDN articles: + * + * http://msdn.microsoft.com/en-us/library/ff805116(v=VS.85).aspx + * http://msdn.microsoft.com/en-us/library/ms679294(VS.85).aspx + * + * Other useful articles on working with minidumps: + * http://www.debuginfo.com/articles/effminidumps.html + */ + +typedef BOOL (WINAPI * MINIDUMPWRITEDUMP) (HANDLE hProcess, DWORD dwPid, HANDLE hFile, MINIDUMP_TYPE DumpType, + CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, + CONST PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam, + CONST PMINIDUMP_CALLBACK_INFORMATION CallbackParam +); + + +/* + * This function is the exception handler passed to SetUnhandledExceptionFilter. + * It's invoked only if there's an unhandled exception. The handler will use + * dbghelp.dll to generate a crash dump, then resume the normal unhandled + * exception process, which will generally exit with an error message from + * the runtime. + * + * This function is run under the unhandled exception handler, effectively + * in a crash context, so it should be careful with memory and avoid using + * any PostgreSQL functions. + */ +static LONG WINAPI +crashDumpHandler(struct _EXCEPTION_POINTERS *pExceptionInfo) +{ + /* + * We only write crash dumps if the "crashdumps" directory within the + * postgres data directory exists. + */ + DWORD attribs = GetFileAttributesA("crashdumps"); + + if (attribs != INVALID_FILE_ATTRIBUTES && (attribs & FILE_ATTRIBUTE_DIRECTORY)) + { + /* 'crashdumps' exists and is a directory. Try to write a dump' */ + HMODULE hDll = NULL; + MINIDUMPWRITEDUMP pDump = NULL; + MINIDUMP_TYPE dumpType; + char dumpPath[_MAX_PATH]; + HANDLE selfProcHandle = GetCurrentProcess(); + DWORD selfPid = GetProcessId(selfProcHandle); + HANDLE dumpFile; + DWORD systemTicks; + struct _MINIDUMP_EXCEPTION_INFORMATION ExInfo; + + ExInfo.ThreadId = GetCurrentThreadId(); + ExInfo.ExceptionPointers = pExceptionInfo; + ExInfo.ClientPointers = FALSE; + + /* Load the dbghelp.dll library and functions */ + hDll = LoadLibrary("dbghelp.dll"); + if (hDll == NULL) + { + write_stderr("could not load dbghelp.dll, cannot write crash dump\n"); + return EXCEPTION_CONTINUE_SEARCH; + } + + pDump = (MINIDUMPWRITEDUMP) (pg_funcptr_t) GetProcAddress(hDll, "MiniDumpWriteDump"); + + if (pDump == NULL) + { + write_stderr("could not load required functions in dbghelp.dll, cannot write crash dump\n"); + return EXCEPTION_CONTINUE_SEARCH; + } + + /* + * Dump as much as we can, except shared memory, code segments, and + * memory mapped files. Exactly what we can dump depends on the + * version of dbghelp.dll, see: + * http://msdn.microsoft.com/en-us/library/ms680519(v=VS.85).aspx + */ + dumpType = MiniDumpNormal | MiniDumpWithHandleData | + MiniDumpWithDataSegs; + + if (GetProcAddress(hDll, "EnumDirTree") != NULL) + { + /* If this function exists, we have version 5.2 or newer */ + dumpType |= MiniDumpWithIndirectlyReferencedMemory | + MiniDumpWithPrivateReadWriteMemory; + } + + systemTicks = GetTickCount(); + snprintf(dumpPath, _MAX_PATH, + "crashdumps\\postgres-pid%0i-%0i.mdmp", + (int) selfPid, (int) systemTicks); + dumpPath[_MAX_PATH - 1] = '\0'; + + dumpFile = CreateFile(dumpPath, GENERIC_WRITE, FILE_SHARE_WRITE, + NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, + NULL); + if (dumpFile == INVALID_HANDLE_VALUE) + { + write_stderr("could not open crash dump file \"%s\" for writing: error code %lu\n", + dumpPath, GetLastError()); + return EXCEPTION_CONTINUE_SEARCH; + } + + if ((*pDump) (selfProcHandle, selfPid, dumpFile, dumpType, &ExInfo, + NULL, NULL)) + write_stderr("wrote crash dump to file \"%s\"\n", dumpPath); + else + write_stderr("could not write crash dump to file \"%s\": error code %lu\n", + dumpPath, GetLastError()); + + CloseHandle(dumpFile); + } + + return EXCEPTION_CONTINUE_SEARCH; +} + + +void +pgwin32_install_crashdump_handler(void) +{ + SetUnhandledExceptionFilter(crashDumpHandler); +} diff --git a/contrib/libs/postgresql/src/backend/port/win32/signal.c b/contrib/libs/postgresql/src/backend/port/win32/signal.c new file mode 100644 index 0000000000..580a517f3f --- /dev/null +++ b/contrib/libs/postgresql/src/backend/port/win32/signal.c @@ -0,0 +1,344 @@ +/*------------------------------------------------------------------------- + * + * signal.c + * Microsoft Windows Win32 Signal Emulation Functions + * + * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group + * + * IDENTIFICATION + * src/backend/port/win32/signal.c + * + *------------------------------------------------------------------------- + */ + +#include "postgres.h" + +#include "libpq/pqsignal.h" + +/* + * These are exported for use by the UNBLOCKED_SIGNAL_QUEUE() macro. + * pg_signal_queue must be volatile since it is changed by the signal + * handling thread and inspected without any lock by the main thread. + * pg_signal_mask is only changed by main thread so shouldn't need it. + */ +volatile int pg_signal_queue; +int pg_signal_mask; + +HANDLE pgwin32_signal_event; +HANDLE pgwin32_initial_signal_pipe = INVALID_HANDLE_VALUE; + +/* + * pg_signal_crit_sec is used to protect only pg_signal_queue. That is the only + * variable that can be accessed from the signal sending threads! + */ +static CRITICAL_SECTION pg_signal_crit_sec; + +/* Note that array elements 0 are unused since they correspond to signal 0 */ +static pqsigfunc pg_signal_array[PG_SIGNAL_COUNT]; +static pqsigfunc pg_signal_defaults[PG_SIGNAL_COUNT]; + + +/* Signal handling thread functions */ +static DWORD WINAPI pg_signal_thread(LPVOID param); +static BOOL WINAPI pg_console_handler(DWORD dwCtrlType); + + +/* + * pg_usleep --- delay the specified number of microseconds, but + * stop waiting if a signal arrives. + * + * This replaces the non-signal-aware version provided by src/port/pgsleep.c. + */ +void +pg_usleep(long microsec) +{ + Assert(pgwin32_signal_event != NULL); + if (WaitForSingleObject(pgwin32_signal_event, + (microsec < 500 ? 1 : (microsec + 500) / 1000)) + == WAIT_OBJECT_0) + { + pgwin32_dispatch_queued_signals(); + errno = EINTR; + return; + } +} + + +/* Initialization */ +void +pgwin32_signal_initialize(void) +{ + int i; + HANDLE signal_thread_handle; + + InitializeCriticalSection(&pg_signal_crit_sec); + + for (i = 0; i < PG_SIGNAL_COUNT; i++) + { + pg_signal_array[i] = SIG_DFL; + pg_signal_defaults[i] = SIG_IGN; + } + pg_signal_mask = 0; + pg_signal_queue = 0; + + /* Create the global event handle used to flag signals */ + pgwin32_signal_event = CreateEvent(NULL, TRUE, FALSE, NULL); + if (pgwin32_signal_event == NULL) + ereport(FATAL, + (errmsg_internal("could not create signal event: error code %lu", GetLastError()))); + + /* Create thread for handling signals */ + signal_thread_handle = CreateThread(NULL, 0, pg_signal_thread, NULL, 0, NULL); + if (signal_thread_handle == NULL) + ereport(FATAL, + (errmsg_internal("could not create signal handler thread"))); + + /* Create console control handle to pick up Ctrl-C etc */ + if (!SetConsoleCtrlHandler(pg_console_handler, TRUE)) + ereport(FATAL, + (errmsg_internal("could not set console control handler"))); +} + +/* + * Dispatch all signals currently queued and not blocked + * Blocked signals are ignored, and will be fired at the time of + * the pqsigsetmask() call. + */ +void +pgwin32_dispatch_queued_signals(void) +{ + int exec_mask; + + Assert(pgwin32_signal_event != NULL); + EnterCriticalSection(&pg_signal_crit_sec); + while ((exec_mask = UNBLOCKED_SIGNAL_QUEUE()) != 0) + { + /* One or more unblocked signals queued for execution */ + int i; + + for (i = 1; i < PG_SIGNAL_COUNT; i++) + { + if (exec_mask & sigmask(i)) + { + /* Execute this signal */ + pqsigfunc sig = pg_signal_array[i]; + + if (sig == SIG_DFL) + sig = pg_signal_defaults[i]; + pg_signal_queue &= ~sigmask(i); + if (sig != SIG_ERR && sig != SIG_IGN && sig != SIG_DFL) + { + LeaveCriticalSection(&pg_signal_crit_sec); + sig(i); + EnterCriticalSection(&pg_signal_crit_sec); + break; /* Restart outer loop, in case signal mask or + * queue has been modified inside signal + * handler */ + } + } + } + } + ResetEvent(pgwin32_signal_event); + LeaveCriticalSection(&pg_signal_crit_sec); +} + +/* 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; +} + + +/* + * Unix-like signal handler installation + * + * Only called on main thread, no sync required + */ +pqsigfunc +pqsignal(int signum, pqsigfunc handler) +{ + pqsigfunc prevfunc; + + if (signum >= PG_SIGNAL_COUNT || signum < 0) + return SIG_ERR; + prevfunc = pg_signal_array[signum]; + pg_signal_array[signum] = handler; + return prevfunc; +} + +/* Create the signal listener pipe for specified PID */ +HANDLE +pgwin32_create_signal_listener(pid_t pid) +{ + char pipename[128]; + HANDLE pipe; + + snprintf(pipename, sizeof(pipename), "\\\\.\\pipe\\pgsignal_%u", (int) pid); + + pipe = CreateNamedPipe(pipename, PIPE_ACCESS_DUPLEX, + PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, + PIPE_UNLIMITED_INSTANCES, 16, 16, 1000, NULL); + + if (pipe == INVALID_HANDLE_VALUE) + ereport(ERROR, + (errmsg("could not create signal listener pipe for PID %d: error code %lu", + (int) pid, GetLastError()))); + + return pipe; +} + + +/* + * All functions below execute on the signal handler thread + * and must be synchronized as such! + * NOTE! The only global variable that can be used is + * pg_signal_queue! + */ + + +/* + * Queue a signal for the main thread, by setting the flag bit and event. + */ +void +pg_queue_signal(int signum) +{ + Assert(pgwin32_signal_event != NULL); + if (signum >= PG_SIGNAL_COUNT || signum <= 0) + return; /* ignore any bad signal number */ + + EnterCriticalSection(&pg_signal_crit_sec); + pg_signal_queue |= sigmask(signum); + LeaveCriticalSection(&pg_signal_crit_sec); + + SetEvent(pgwin32_signal_event); +} + +/* Signal handling thread */ +static DWORD WINAPI +pg_signal_thread(LPVOID param) +{ + char pipename[128]; + HANDLE pipe = pgwin32_initial_signal_pipe; + + /* Set up pipe name, in case we have to re-create the pipe. */ + snprintf(pipename, sizeof(pipename), "\\\\.\\pipe\\pgsignal_%lu", GetCurrentProcessId()); + + for (;;) + { + BOOL fConnected; + + /* Create a new pipe instance if we don't have one. */ + if (pipe == INVALID_HANDLE_VALUE) + { + pipe = CreateNamedPipe(pipename, PIPE_ACCESS_DUPLEX, + PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, + PIPE_UNLIMITED_INSTANCES, 16, 16, 1000, NULL); + + if (pipe == INVALID_HANDLE_VALUE) + { + write_stderr("could not create signal listener pipe: error code %lu; retrying\n", GetLastError()); + SleepEx(500, FALSE); + continue; + } + } + + /* + * Wait for a client to connect. If something connects before we + * reach here, we'll get back a "failure" with ERROR_PIPE_CONNECTED, + * which is actually a success (way to go, Microsoft). + */ + fConnected = ConnectNamedPipe(pipe, NULL) ? TRUE : (GetLastError() == ERROR_PIPE_CONNECTED); + if (fConnected) + { + /* + * We have a connection from a would-be signal sender. Process it. + */ + BYTE sigNum; + DWORD bytes; + + if (ReadFile(pipe, &sigNum, 1, &bytes, NULL) && + bytes == 1) + { + /* + * Queue the signal before responding to the client. In this + * way, it's guaranteed that once kill() has returned in the + * signal sender, the next CHECK_FOR_INTERRUPTS() in the + * signal recipient will see the signal. (This is a stronger + * guarantee than POSIX makes; maybe we don't need it? But + * without it, we've seen timing bugs on Windows that do not + * manifest on any known Unix.) + */ + pg_queue_signal(sigNum); + + /* + * Write something back to the client, allowing its + * CallNamedPipe() call to terminate. + */ + WriteFile(pipe, &sigNum, 1, &bytes, NULL); /* Don't care if it + * works or not */ + + /* + * We must wait for the client to read the data before we can + * disconnect, else the data will be lost. (If the WriteFile + * call failed, there'll be nothing in the buffer, so this + * shouldn't block.) + */ + FlushFileBuffers(pipe); + } + else + { + /* + * If we fail to read a byte from the client, assume it's the + * client's problem and do nothing. Perhaps it'd be better to + * force a pipe close and reopen? + */ + } + + /* Disconnect from client so that we can re-use the pipe. */ + DisconnectNamedPipe(pipe); + } + else + { + /* + * Connection failed. Cleanup and try again. + * + * This should never happen. If it does, there's a window where + * we'll miss signals until we manage to re-create the pipe. + * However, just trying to use the same pipe again is probably not + * going to work, so we have little choice. + */ + CloseHandle(pipe); + pipe = INVALID_HANDLE_VALUE; + } + } + return 0; +} + + +/* Console control handler will execute on a thread created + by the OS at the time of invocation */ +static BOOL WINAPI +pg_console_handler(DWORD dwCtrlType) +{ + if (dwCtrlType == CTRL_C_EVENT || + dwCtrlType == CTRL_BREAK_EVENT || + dwCtrlType == CTRL_CLOSE_EVENT || + dwCtrlType == CTRL_SHUTDOWN_EVENT) + { + pg_queue_signal(SIGINT); + return TRUE; + } + return FALSE; +} diff --git a/contrib/libs/postgresql/src/backend/port/win32/socket.c b/contrib/libs/postgresql/src/backend/port/win32/socket.c new file mode 100644 index 0000000000..af151e8470 --- /dev/null +++ b/contrib/libs/postgresql/src/backend/port/win32/socket.c @@ -0,0 +1,700 @@ +/*------------------------------------------------------------------------- + * + * socket.c + * Microsoft Windows Win32 Socket Functions + * + * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group + * + * IDENTIFICATION + * src/backend/port/win32/socket.c + * + *------------------------------------------------------------------------- + */ + +#include "postgres.h" + +/* + * Indicate if pgwin32_recv() and pgwin32_send() should operate + * in non-blocking mode. + * + * Since the socket emulation layer always sets the actual socket to + * non-blocking mode in order to be able to deliver signals, we must + * specify this in a separate flag if we actually need non-blocking + * operation. + * + * This flag changes the behaviour *globally* for all socket operations, + * so it should only be set for very short periods of time. + */ +int pgwin32_noblock = 0; + +/* Undef the macros defined in win32.h, so we can access system functions */ +#undef socket +#undef bind +#undef listen +#undef accept +#undef connect +#undef select +#undef recv +#undef send + +/* + * Blocking socket functions implemented so they listen on both + * the socket and the signal event, required for signal handling. + */ + +/* + * Convert the last socket error code into errno + * + * Note: where there is a direct correspondence between a WSAxxx error code + * and a Berkeley error symbol, this mapping is actually a no-op, because + * in win32.h we redefine the network-related Berkeley error symbols to have + * the values of their WSAxxx counterparts. The point of the switch is + * mostly to translate near-miss error codes into something that's sensible + * in the Berkeley universe. + */ +static void +TranslateSocketError(void) +{ + switch (WSAGetLastError()) + { + case WSAEINVAL: + case WSANOTINITIALISED: + case WSAEINVALIDPROVIDER: + case WSAEINVALIDPROCTABLE: + case WSAEDESTADDRREQ: + errno = EINVAL; + break; + case WSAEINPROGRESS: + errno = EINPROGRESS; + break; + case WSAEFAULT: + errno = EFAULT; + break; + case WSAEISCONN: + errno = EISCONN; + break; + case WSAEMSGSIZE: + errno = EMSGSIZE; + break; + case WSAEAFNOSUPPORT: + errno = EAFNOSUPPORT; + break; + case WSAEMFILE: + errno = EMFILE; + break; + case WSAENOBUFS: + errno = ENOBUFS; + break; + case WSAEPROTONOSUPPORT: + case WSAEPROTOTYPE: + case WSAESOCKTNOSUPPORT: + errno = EPROTONOSUPPORT; + break; + case WSAECONNABORTED: + errno = ECONNABORTED; + break; + case WSAECONNREFUSED: + errno = ECONNREFUSED; + break; + case WSAECONNRESET: + errno = ECONNRESET; + break; + case WSAEINTR: + errno = EINTR; + break; + case WSAENOTSOCK: + errno = ENOTSOCK; + break; + case WSAEOPNOTSUPP: + errno = EOPNOTSUPP; + break; + case WSAEWOULDBLOCK: + errno = EWOULDBLOCK; + break; + case WSAEACCES: + errno = EACCES; + break; + case WSAEADDRINUSE: + errno = EADDRINUSE; + break; + case WSAEADDRNOTAVAIL: + errno = EADDRNOTAVAIL; + break; + case WSAEHOSTDOWN: + errno = EHOSTDOWN; + break; + case WSAEHOSTUNREACH: + case WSAHOST_NOT_FOUND: + errno = EHOSTUNREACH; + break; + case WSAENETDOWN: + errno = ENETDOWN; + break; + case WSAENETUNREACH: + errno = ENETUNREACH; + break; + case WSAENETRESET: + errno = ENETRESET; + break; + case WSAENOTCONN: + case WSAESHUTDOWN: + case WSAEDISCON: + errno = ENOTCONN; + break; + default: + ereport(NOTICE, + (errmsg_internal("unrecognized win32 socket error code: %d", WSAGetLastError()))); + errno = EINVAL; + } +} + +static int +pgwin32_poll_signals(void) +{ + if (UNBLOCKED_SIGNAL_QUEUE()) + { + pgwin32_dispatch_queued_signals(); + errno = EINTR; + return 1; + } + return 0; +} + +static int +isDataGram(SOCKET s) +{ + int type; + int typelen = sizeof(type); + + if (getsockopt(s, SOL_SOCKET, SO_TYPE, (char *) &type, &typelen)) + return 1; + + return (type == SOCK_DGRAM) ? 1 : 0; +} + +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; +} + +/* + * Create a socket, setting it to overlapped and non-blocking + */ +SOCKET +pgwin32_socket(int af, int type, int protocol) +{ + SOCKET s; + unsigned long on = 1; + + s = WSASocket(af, type, protocol, NULL, 0, WSA_FLAG_OVERLAPPED); + if (s == INVALID_SOCKET) + { + TranslateSocketError(); + return INVALID_SOCKET; + } + + if (ioctlsocket(s, FIONBIO, &on)) + { + TranslateSocketError(); + return INVALID_SOCKET; + } + errno = 0; + + return s; +} + +int +pgwin32_bind(SOCKET s, struct sockaddr *addr, int addrlen) +{ + int res; + + res = bind(s, addr, addrlen); + if (res < 0) + TranslateSocketError(); + return res; +} + +int +pgwin32_listen(SOCKET s, int backlog) +{ + int res; + + res = listen(s, backlog); + if (res < 0) + TranslateSocketError(); + return res; +} + +SOCKET +pgwin32_accept(SOCKET s, struct sockaddr *addr, int *addrlen) +{ + SOCKET rs; + + /* + * Poll for signals, but don't return with EINTR, since we don't handle + * that in pqcomm.c + */ + pgwin32_poll_signals(); + + rs = WSAAccept(s, addr, addrlen, NULL, 0); + if (rs == INVALID_SOCKET) + { + TranslateSocketError(); + return INVALID_SOCKET; + } + return rs; +} + + +/* No signal delivery during connect. */ +int +pgwin32_connect(SOCKET s, const struct sockaddr *addr, int addrlen) +{ + int r; + + r = WSAConnect(s, addr, addrlen, NULL, NULL, NULL, NULL); + if (r == 0) + return 0; + + if (WSAGetLastError() != WSAEWOULDBLOCK) + { + TranslateSocketError(); + return -1; + } + + while (pgwin32_waitforsinglesocket(s, FD_CONNECT, INFINITE) == 0) + { + /* Loop endlessly as long as we are just delivering signals */ + } + + return 0; +} + +int +pgwin32_recv(SOCKET s, char *buf, int len, int f) +{ + WSABUF wbuf; + int r; + DWORD b; + DWORD flags = f; + int n; + + if (pgwin32_poll_signals()) + return -1; + + wbuf.len = len; + wbuf.buf = buf; + + r = WSARecv(s, &wbuf, 1, &b, &flags, NULL, NULL); + if (r != SOCKET_ERROR) + return b; /* success */ + + if (WSAGetLastError() != WSAEWOULDBLOCK) + { + TranslateSocketError(); + return -1; + } + + if (pgwin32_noblock) + { + /* + * No data received, and we are in "emulated non-blocking mode", so + * return indicating that we'd block if we were to continue. + */ + errno = EWOULDBLOCK; + return -1; + } + + /* We're in blocking mode, so wait for data */ + + for (n = 0; n < 5; n++) + { + if (pgwin32_waitforsinglesocket(s, FD_READ | FD_CLOSE | FD_ACCEPT, + INFINITE) == 0) + return -1; /* errno already set */ + + r = WSARecv(s, &wbuf, 1, &b, &flags, NULL, NULL); + if (r != SOCKET_ERROR) + return b; /* success */ + if (WSAGetLastError() != WSAEWOULDBLOCK) + { + TranslateSocketError(); + return -1; + } + + /* + * There seem to be cases on win2k (at least) where WSARecv can return + * WSAEWOULDBLOCK even when pgwin32_waitforsinglesocket claims the + * socket is readable. In this case, just sleep for a moment and try + * again. We try up to 5 times - if it fails more than that it's not + * likely to ever come back. + */ + pg_usleep(10000); + } + ereport(NOTICE, + (errmsg_internal("could not read from ready socket (after retries)"))); + errno = EWOULDBLOCK; + return -1; +} + +/* + * The second argument to send() is defined by SUS to be a "const void *" + * and so we use the same signature here to keep compilers happy when + * handling callers. + * + * But the buf member of a WSABUF struct is defined as "char *", so we cast + * the second argument to that here when assigning it, also to keep compilers + * happy. + */ + +int +pgwin32_send(SOCKET s, const void *buf, int len, int flags) +{ + WSABUF wbuf; + int r; + DWORD b; + + if (pgwin32_poll_signals()) + return -1; + + wbuf.len = len; + wbuf.buf = (char *) buf; + + /* + * Readiness of socket to send data to UDP socket may be not true: socket + * can become busy again! So loop until send or error occurs. + */ + for (;;) + { + r = WSASend(s, &wbuf, 1, &b, flags, NULL, NULL); + if (r != SOCKET_ERROR && b > 0) + /* Write succeeded right away */ + return b; + + if (r == SOCKET_ERROR && + WSAGetLastError() != WSAEWOULDBLOCK) + { + TranslateSocketError(); + return -1; + } + + if (pgwin32_noblock) + { + /* + * No data sent, and we are in "emulated non-blocking mode", so + * return indicating that we'd block if we were to continue. + */ + errno = EWOULDBLOCK; + return -1; + } + + /* No error, zero bytes (win2000+) or error+WSAEWOULDBLOCK (<=nt4) */ + + if (pgwin32_waitforsinglesocket(s, FD_WRITE | FD_CLOSE, INFINITE) == 0) + return -1; + } + + return -1; +} + + +/* + * 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); + + /* + * Windows does not guarantee to log an FD_WRITE network event indicating + * that more data can be sent unless the previous send() failed with + * WSAEWOULDBLOCK. While our caller might well have made such a call, we + * cannot assume that here. Therefore, if waiting for write-ready, force + * the issue by doing a dummy send(). If the dummy send() succeeds, + * assume that the socket is in fact write-ready, and return immediately. + * Also, if it fails with something other than WSAEWOULDBLOCK, return a + * write-ready indication to let our caller deal with the error condition. + */ + if (writefds != NULL) + { + 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 || WSAGetLastError() != WSAEWOULDBLOCK) + FD_SET(writefds->fd_array[i], &outwritefds); + } + + /* If we found any write-ready sockets, just return them immediately */ + 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 signaled, 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 %d", + 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; +} diff --git a/contrib/libs/postgresql/src/backend/port/win32/timer.c b/contrib/libs/postgresql/src/backend/port/win32/timer.c new file mode 100644 index 0000000000..53fdae9468 --- /dev/null +++ b/contrib/libs/postgresql/src/backend/port/win32/timer.c @@ -0,0 +1,121 @@ +/*------------------------------------------------------------------------- + * + * timer.c + * Microsoft Windows Win32 Timer Implementation + * + * Limitations of this implementation: + * + * - Does not support interval timer (value->it_interval) + * - Only supports ITIMER_REAL + * + * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group + * + * IDENTIFICATION + * src/backend/port/win32/timer.c + * + *------------------------------------------------------------------------- + */ + +#include "postgres.h" + + +/* Communication area for inter-thread communication */ +typedef struct timerCA +{ + struct itimerval value; + HANDLE event; + CRITICAL_SECTION crit_sec; +} timerCA; + +static timerCA timerCommArea; +static HANDLE timerThreadHandle = INVALID_HANDLE_VALUE; + + +/* Timer management thread */ +static DWORD WINAPI +pg_timer_thread(LPVOID param) +{ + DWORD waittime; + + Assert(param == NULL); + + waittime = INFINITE; + + for (;;) + { + int r; + + r = WaitForSingleObjectEx(timerCommArea.event, waittime, FALSE); + if (r == WAIT_OBJECT_0) + { + /* Event signaled from main thread, change the timer */ + EnterCriticalSection(&timerCommArea.crit_sec); + if (timerCommArea.value.it_value.tv_sec == 0 && + timerCommArea.value.it_value.tv_usec == 0) + waittime = INFINITE; /* Cancel the interrupt */ + else + { + /* WaitForSingleObjectEx() uses milliseconds, round up */ + waittime = (timerCommArea.value.it_value.tv_usec + 999) / 1000 + + timerCommArea.value.it_value.tv_sec * 1000; + } + ResetEvent(timerCommArea.event); + LeaveCriticalSection(&timerCommArea.crit_sec); + } + else if (r == WAIT_TIMEOUT) + { + /* Timeout expired, signal SIGALRM and turn it off */ + pg_queue_signal(SIGALRM); + waittime = INFINITE; + } + else + { + /* Should never happen */ + Assert(false); + } + } + + return 0; +} + +/* + * Win32 setitimer emulation by creating a persistent thread + * to handle the timer setting and notification upon timeout. + */ +int +setitimer(int which, const struct itimerval *value, struct itimerval *ovalue) +{ + Assert(value != NULL); + Assert(value->it_interval.tv_sec == 0 && value->it_interval.tv_usec == 0); + Assert(which == ITIMER_REAL); + + if (timerThreadHandle == INVALID_HANDLE_VALUE) + { + /* First call in this backend, create event and the timer thread */ + timerCommArea.event = CreateEvent(NULL, TRUE, FALSE, NULL); + if (timerCommArea.event == NULL) + ereport(FATAL, + (errmsg_internal("could not create timer event: error code %lu", + GetLastError()))); + + MemSet(&timerCommArea.value, 0, sizeof(struct itimerval)); + + InitializeCriticalSection(&timerCommArea.crit_sec); + + timerThreadHandle = CreateThread(NULL, 0, pg_timer_thread, NULL, 0, NULL); + if (timerThreadHandle == INVALID_HANDLE_VALUE) + ereport(FATAL, + (errmsg_internal("could not create timer thread: error code %lu", + GetLastError()))); + } + + /* Request the timer thread to change settings */ + EnterCriticalSection(&timerCommArea.crit_sec); + if (ovalue) + *ovalue = timerCommArea.value; + timerCommArea.value = *value; + LeaveCriticalSection(&timerCommArea.crit_sec); + SetEvent(timerCommArea.event); + + return 0; +} |