diff options
Diffstat (limited to 'src/backend/libpq/pqsignal.c')
-rw-r--r-- | src/backend/libpq/pqsignal.c | 243 |
1 files changed, 242 insertions, 1 deletions
diff --git a/src/backend/libpq/pqsignal.c b/src/backend/libpq/pqsignal.c index 497201e0a77..79d31b4ec73 100644 --- a/src/backend/libpq/pqsignal.c +++ b/src/backend/libpq/pqsignal.c @@ -9,7 +9,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/libpq/pqsignal.c,v 1.28 2003/11/29 19:51:49 pgsql Exp $ + * $PostgreSQL: pgsql/src/backend/libpq/pqsignal.c,v 1.29 2004/01/27 00:45:26 momjian Exp $ * * NOTES * This shouldn't be in libpq, but the monitor and some other @@ -38,9 +38,18 @@ * is to do signal-handler reinstallation, which doesn't work well * at all. * ------------------------------------------------------------------------*/ +#ifdef WIN32 +#define WIN32_LEAN_AND_MEAN +#define _WIN32_WINNT 0x0400 +#endif + #include "postgres.h" +#ifndef WIN32 #include <signal.h> +#else +#include <windows.h> +#endif #include "libpq/pqsignal.h" @@ -127,6 +136,7 @@ pqinitmask(void) } +#ifndef WIN32 /* * Set up a signal handler */ @@ -149,3 +159,234 @@ pqsignal(int signo, pqsigfunc func) return oact.sa_handler; #endif /* !HAVE_POSIX_SIGNALS */ } + + +#else + + +/* Win32 specific signals code */ + +/* 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; +static int pg_signal_queue; + +#define PG_SIGNAL_COUNT 32 +static pqsigfunc pg_signal_array[PG_SIGNAL_COUNT]; +static pqsigfunc pg_signal_defaults[PG_SIGNAL_COUNT]; +static int pg_signal_mask; + +HANDLE pgwin32_main_thread_handle; + +/* Signal handling thread function */ +static DWORD WINAPI pg_signal_thread(LPVOID param); + +/* 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; + + /* Get handle to main thread so we can post calls to it later */ + if (!DuplicateHandle(GetCurrentProcess(),GetCurrentThread(), + GetCurrentProcess(),&pgwin32_main_thread_handle, + 0,FALSE,DUPLICATE_SAME_ACCESS)) { + fprintf(stderr,gettext("Failed to get main thread handle!\n")); + exit(1); + } + + /* Create thread for handling signals */ + signal_thread_handle = CreateThread(NULL,0,pg_signal_thread,NULL,0,NULL); + if (signal_thread_handle == NULL) { + fprintf(stderr,gettext("Failed to create signal handler thread!\n")); + exit(1); + } +} + + +/* Dispatch all signals currently queued and not blocked + * Blocked signals are ignored, and will be fired at the time of + * the sigsetmask() call. */ +static void dispatch_queued_signals(void) { + int i; + + EnterCriticalSection(&pg_signal_crit_sec); + while (pg_signal_queue & ~pg_signal_mask) { + /* One or more unblocked signals queued for execution */ + + int exec_mask = pg_signal_queue & ~pg_signal_mask; + + for (i = 0; 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 */ + } + } + } + } + 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 */ + dispatch_queued_signals(); + + return prevmask; +} + + +/* signal manipulation. 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; +} + +/* signal sending */ +int pqkill(int pid, int sig) { + char pipename[128]; + BYTE sigData = sig; + BYTE sigRet = 0; + DWORD bytes; + + if (sig >= PG_SIGNAL_COUNT || sig <= 0) { + errno = EINVAL; + return -1; + } + if (pid <= 0) { + /* No support for process groups */ + errno = EINVAL; + return -1; + } + wsprintf(pipename,"\\\\.\\pipe\\pgsignal_%i",pid); + if (!CallNamedPipe(pipename,&sigData,1,&sigRet,1,&bytes,1000)) { + if (GetLastError() == ERROR_FILE_NOT_FOUND) + errno = ESRCH; + else if (GetLastError() == ERROR_ACCESS_DENIED) + errno = EPERM; + else + errno = EINVAL; + return -1; + } + if (bytes != 1 || sigRet != sig) { + errno = ESRCH; + return -1; + } + + return 0; +} + +/* APC callback scheduled on main thread when signals are fired */ +static void CALLBACK pg_signal_apc(ULONG_PTR param) { + dispatch_queued_signals(); +} + +/* + * 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! + */ + + +static void pg_queue_signal(int signum) { + if (signum >= PG_SIGNAL_COUNT || signum < 0) + return; + + EnterCriticalSection(&pg_signal_crit_sec); + pg_signal_queue |= sigmask(signum); + LeaveCriticalSection(&pg_signal_crit_sec); + + QueueUserAPC(pg_signal_apc,pgwin32_main_thread_handle,(ULONG_PTR)NULL); +} + +/* Signal dispatching thread */ +static DWORD WINAPI pg_signal_dispatch_thread(LPVOID param) { + HANDLE pipe = (HANDLE)param; + BYTE sigNum; + DWORD bytes; + + if (!ReadFile(pipe,&sigNum,1,&bytes,NULL)) { + /* Client died before sending */ + CloseHandle(pipe); + return 0; + } + if (bytes != 1) { + /* Received <bytes> bytes over signal pipe (should be 1) */ + CloseHandle(pipe); + return 0; + } + WriteFile(pipe,&sigNum,1,&bytes,NULL); /* Don't care if it works or not.. */ + FlushFileBuffers(pipe); + DisconnectNamedPipe(pipe); + CloseHandle(pipe); + + pg_queue_signal(sigNum); + return 0; +} + +/* Signal handling thread */ +static DWORD WINAPI pg_signal_thread(LPVOID param) { + char pipename[128]; + HANDLE pipe = INVALID_HANDLE_VALUE; + + wsprintf(pipename,"\\\\.\\pipe\\pgsignal_%i",GetCurrentProcessId()); + + for (;;) { + BOOL fConnected; + HANDLE hThread; + + 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) { + fprintf(stderr,gettext("Failed to create signal listener pipe: %i. Retrying.\n"),(int)GetLastError()); + SleepEx(500,TRUE); + continue; + } + + fConnected = ConnectNamedPipe(pipe, NULL) ? TRUE : (GetLastError() == ERROR_PIPE_CONNECTED); + if (fConnected) { + hThread = CreateThread(NULL, 0, + (LPTHREAD_START_ROUTINE)pg_signal_dispatch_thread, + (LPVOID)pipe,0,NULL); + if (hThread == INVALID_HANDLE_VALUE) { + fprintf(stderr,gettext("Failed to create signal dispatch thread: %i\n"),(int)GetLastError()); + } + else + CloseHandle(hThread); + } + else + /* Connection failed. Cleanup and try again */ + CloseHandle(pipe); + } + return 0; +} + + +#endif |