Here's the latest win32 signals code, this time in the form of a patch

against the latest shapshot. It also includes the replacement of kill()
with pqkill() and sigsetmask() with pqsigsetmask().

Passes all tests fine on my linux machine once applied. Still doesn't
link completely on Win32 - there are a few things still required. But
much closer than before.

At Bruce's request, I'm goint to write up a README file about the method
of signals delivery chosen and why the others were rejected (basically a
summary of the mailinglist discussions). I'll finish that up once/if the
patch is accepted.


Magnus Hagander
This commit is contained in:
Bruce Momjian 2004-01-27 00:45:26 +00:00
parent eec08b95e7
commit 50491963cb
10 changed files with 404 additions and 26 deletions

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/async.c,v 1.107 2004/01/07 18:56:25 neilc Exp $
* $PostgreSQL: pgsql/src/backend/commands/async.c,v 1.108 2004/01/27 00:45:26 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@ -83,6 +83,7 @@
#include "commands/async.h"
#include "libpq/libpq.h"
#include "libpq/pqformat.h"
#include "libpq/pqsignal.h"
#include "miscadmin.h"
#include "storage/ipc.h"
#include "tcop/tcopprot.h"
@ -497,7 +498,7 @@ AtCommit_Notify(void)
* for some reason. It's OK to send the signal first, because
* the other guy can't read pg_listener until we unlock it.
*/
if (kill(listenerPID, SIGUSR2) < 0)
if (pqkill(listenerPID, SIGUSR2) < 0)
{
/*
* Get rid of pg_listener entry if it refers to a PID that

View File

@ -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

View File

@ -13,7 +13,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/main/main.c,v 1.71 2004/01/11 03:49:31 momjian Exp $
* $PostgreSQL: pgsql/src/backend/main/main.c,v 1.72 2004/01/27 00:45:26 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@ -40,6 +40,9 @@
#include "utils/help_config.h"
#include "utils/ps_status.h"
#include "pgstat.h"
#ifdef WIN32
#include "libpq/pqsignal.h"
#endif
@ -97,6 +100,8 @@ main(int argc, char *argv[])
argv[0], err);
exit(1);
}
pgwin32_signal_initialize();
}
#endif

View File

@ -8,7 +8,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/port/sysv_sema.c,v 1.12 2003/12/01 22:15:37 tgl Exp $
* $PostgreSQL: pgsql/src/backend/port/sysv_sema.c,v 1.13 2004/01/27 00:45:26 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@ -31,6 +31,7 @@
#include "miscadmin.h"
#include "storage/ipc.h"
#include "storage/pg_sema.h"
#include "libpq/pqsignal.h"
#ifndef HAVE_UNION_SEMUN
@ -232,7 +233,7 @@ IpcSemaphoreCreate(int numSems)
continue; /* oops, GETPID failed */
if (creatorPID != getpid())
{
if (kill(creatorPID, 0) == 0 ||
if (pqkill(creatorPID, 0) == 0 ||
errno != ESRCH)
continue; /* sema belongs to a live process */
}

View File

@ -10,7 +10,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/port/sysv_shmem.c,v 1.28 2004/01/07 18:56:27 neilc Exp $
* $PostgreSQL: pgsql/src/backend/port/sysv_shmem.c,v 1.29 2004/01/27 00:45:26 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@ -33,6 +33,7 @@
#include "miscadmin.h"
#include "storage/ipc.h"
#include "storage/pg_shmem.h"
#include "libpq/pqsignal.h"
typedef key_t IpcMemoryKey; /* shared memory key passed to shmget(2) */
@ -284,7 +285,7 @@ PGSharedMemoryCreate(uint32 size, bool makePrivate, int port)
hdr = (PGShmemHeader *) memAddress;
if (hdr->creatorPID != getpid())
{
if (kill(hdr->creatorPID, 0) == 0 || errno != ESRCH)
if (pqkill(hdr->creatorPID, 0) == 0 || errno != ESRCH)
{
shmdt(memAddress);
continue; /* segment belongs to a live process */

View File

@ -37,7 +37,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/postmaster/postmaster.c,v 1.362 2004/01/26 22:59:53 momjian Exp $
* $PostgreSQL: pgsql/src/backend/postmaster/postmaster.c,v 1.363 2004/01/27 00:45:26 momjian Exp $
*
* NOTES
*
@ -1566,7 +1566,7 @@ processCancelRequest(Port *port, void *pkt)
ereport(DEBUG2,
(errmsg_internal("processing cancel request: sending SIGINT to process %d",
backendPID)));
kill(bp->pid, SIGINT);
pqkill(bp->pid, SIGINT);
}
else
/* Right PID, wrong key: no way, Jose */
@ -1738,7 +1738,7 @@ SIGHUP_handler(SIGNAL_ARGS)
* will start a new one with a possibly changed config
*/
if (BgWriterPID != 0)
kill(BgWriterPID, SIGTERM);
pqkill(BgWriterPID, SIGTERM);
}
PG_SETMASK(&UnBlockSig);
@ -1772,7 +1772,7 @@ pmdie(SIGNAL_ARGS)
* Wait for children to end their work and ShutdownDataBase.
*/
if (BgWriterPID != 0)
kill(BgWriterPID, SIGTERM);
pqkill(BgWriterPID, SIGTERM);
if (Shutdown >= SmartShutdown)
break;
Shutdown = SmartShutdown;
@ -1806,7 +1806,7 @@ pmdie(SIGNAL_ARGS)
* and exit) and ShutdownDataBase when they are gone.
*/
if (BgWriterPID != 0)
kill(BgWriterPID, SIGTERM);
pqkill(BgWriterPID, SIGTERM);
if (Shutdown >= FastShutdown)
break;
ereport(LOG,
@ -1854,13 +1854,13 @@ pmdie(SIGNAL_ARGS)
* properly shutdown data base system.
*/
if (BgWriterPID != 0)
kill(BgWriterPID, SIGQUIT);
pqkill(BgWriterPID, SIGQUIT);
ereport(LOG,
(errmsg("received immediate shutdown request")));
if (ShutdownPID > 0)
kill(ShutdownPID, SIGQUIT);
pqkill(ShutdownPID, SIGQUIT);
if (StartupPID > 0)
kill(StartupPID, SIGQUIT);
pqkill(StartupPID, SIGQUIT);
if (DLGetHead(BackendList))
SignalChildren(SIGQUIT);
ExitPostmaster(0);
@ -2130,7 +2130,7 @@ CleanupProc(int pid,
(errmsg_internal("sending %s to process %d",
(SendStop ? "SIGSTOP" : "SIGQUIT"),
(int) bp->pid)));
kill(bp->pid, (SendStop ? SIGSTOP : SIGQUIT));
pqkill(bp->pid, (SendStop ? SIGSTOP : SIGQUIT));
}
}
else
@ -2225,7 +2225,7 @@ SignalChildren(int signal)
(errmsg_internal("sending signal %d to process %d",
signal,
(int) bp->pid)));
kill(bp->pid, signal);
pqkill(bp->pid, signal);
}
curr = next;

View File

@ -8,7 +8,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/storage/ipc/pmsignal.c,v 1.11 2004/01/26 22:59:53 momjian Exp $
* $PostgreSQL: pgsql/src/backend/storage/ipc/pmsignal.c,v 1.12 2004/01/27 00:45:26 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@ -20,6 +20,7 @@
#include "miscadmin.h"
#include "storage/pmsignal.h"
#include "storage/shmem.h"
#include "libpq/pqsignal.h"
/*
@ -64,7 +65,7 @@ SendPostmasterSignal(PMSignalReason reason)
/* Atomically set the proper flag */
PMSignalFlags[reason] = true;
/* Send signal to postmaster */
kill(PostmasterPid, SIGUSR1);
pqkill(PostmasterPid, SIGUSR1);
}
/*

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/storage/lmgr/proc.c,v 1.144 2004/01/07 18:56:27 neilc Exp $
* $PostgreSQL: pgsql/src/backend/storage/lmgr/proc.c,v 1.145 2004/01/27 00:45:26 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@ -51,6 +51,7 @@
#include "storage/proc.h"
#include "storage/sinval.h"
#include "storage/spin.h"
#include "libpq/pqsignal.h"
/* GUC variables */
int DeadlockTimeout = 1000;
@ -1130,7 +1131,7 @@ CheckStatementTimeout(void)
{
/* Time to die */
statement_timeout_active = false;
kill(MyProcPid, SIGINT);
pqkill(MyProcPid, SIGINT);
}
else
{

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/init/miscinit.c,v 1.120 2004/01/26 22:35:32 tgl Exp $
* $PostgreSQL: pgsql/src/backend/utils/init/miscinit.c,v 1.121 2004/01/27 00:45:26 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@ -32,6 +32,7 @@
#include "catalog/catname.h"
#include "catalog/pg_shadow.h"
#include "libpq/libpq-be.h"
#include "libpq/pqsignal.h"
#include "miscadmin.h"
#include "storage/ipc.h"
#include "storage/pg_shmem.h"
@ -531,7 +532,7 @@ CreateLockFile(const char *filename, bool amPostmaster,
*/
if (other_pid != my_pid)
{
if (kill(other_pid, 0) == 0 ||
if (pqkill(other_pid, 0) == 0 ||
(errno != ESRCH
#ifdef __BEOS__
&& errno != EINVAL

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/libpq/pqsignal.h,v 1.23 2003/11/29 22:41:03 pgsql Exp $
* $PostgreSQL: pgsql/src/include/libpq/pqsignal.h,v 1.24 2004/01/27 00:45:26 momjian Exp $
*
* NOTES
* This shouldn't be in libpq, but the monitor and some other
@ -18,7 +18,18 @@
#ifndef PQSIGNAL_H
#define PQSIGNAL_H
#ifndef WIN32
#include <signal.h>
#endif
#ifndef WIN32
#define pqkill(pid,sig) kill(pid,sig)
#define pqsigsetmask(mask) sigsetmask(mask)
#else
int pqkill(int pid, int sig);
int pqsigsetmask(int mask);
#endif
#ifdef HAVE_SIGPROCMASK
extern sigset_t UnBlockSig,
@ -31,7 +42,7 @@ extern int UnBlockSig,
BlockSig,
AuthBlockSig;
#define PG_SETMASK(mask) sigsetmask(*((int*)(mask)))
#define PG_SETMASK(mask) pqsigsetmask(*((int*)(mask)))
#endif
typedef void (*pqsigfunc) (int);
@ -40,4 +51,119 @@ extern void pqinitmask(void);
extern pqsigfunc pqsignal(int signo, pqsigfunc func);
#ifdef WIN32
#define sigmask(sig) ( 1 << (sig-1) )
void pgwin32_signal_initialize(void);
extern HANDLE pgwin32_main_thread_handle;
#define PG_POLL_SIGNALS() WaitForSingleObjectEx(pgwin32_main_thread_handle,0,TRUE);
/* Define signal numbers. Override system values, since they are not
complete anyway */
#undef SIGHUP
#define SIGHUP 1 /* hangup */
#undef SIGINT
#define SIGINT 2 /* interrupt */
#undef SIGQUIT
#define SIGQUIT 3 /* quit */
#undef SIGILL
#define SIGILL 4 /* illegal instruction (not reset when caught) */
#undef SIGTRAP
#define SIGTRAP 5 /* trace trap (not reset when caught) */
#undef SIGABRT
#define SIGABRT 6 /* abort(void) */
#undef SIGIOT
#define SIGIOT SIGABRT /* compatibility */
#undef SIGEMT
#define SIGEMT 7 /* EMT instruction */
#undef SIGFPE
#define SIGFPE 8 /* floating point exception */
#undef SIGKILL
#define SIGKILL 9 /* kill (cannot be caught or ignored) */
#undef SIGBUS
#define SIGBUS 10 /* bus error */
#undef SIGSEGV
#define SIGSEGV 11 /* segmentation violation */
#undef SIGSYS
#define SIGSYS 12 /* non-existent system call invoked */
#undef SIGSYS
#define SIGPIPE 13 /* write on a pipe with no one to read it */
#undef SIGALRM
#define SIGALRM 14 /* alarm clock */
#undef SIGTERM
#define SIGTERM 15 /* software termination signal from kill */
#undef SIGURG
#define SIGURG 16 /* urgent condition on IO channel */
#undef SIGSTOP
#define SIGSTOP 17 /* sendable stop signal not from tty */
#undef SIGTSTP
#define SIGTSTP 18 /* stop signal from tty */
#undef SIGCONT
#define SIGCONT 19 /* continue a stopped process */
#undef SIGCHLD
#define SIGCHLD 20 /* to parent on child stop or exit */
#undef SIGTTIN
#define SIGTTIN 21 /* to readers pgrp upon background tty read */
#undef SIGTTOU
#define SIGTTOU 22 /* like TTIN for output if (tp->t_local&LTOSTOP) */
#undef SIGIO
#define SIGIO 23 /* input/output possible signal */
#undef SIGXCPU
#define SIGXCPU 24 /* exceeded CPU time limit */
#undef SIGXFSZ
#define SIGXFSZ 25 /* exceeded file size limit */
#undef SIGVTALR
#define SIGVTALRM 26 /* virtual time alarm */
#undef SIGPROF
#define SIGPROF 27 /* profiling time alarm */
#undef SIGWINCH
#define SIGWINCH 28 /* window size changes */
#undef SIGINFO
#define SIGINFO 29 /* information request */
#undef SIGUSR1
#define SIGUSR1 30 /* user defined signal 1 */
#undef SIGUSR2
#define SIGUSR2 31 /* user defined signal 2 */
#undef SIG_DFL
#undef SIG_ERR
#undef SIG_IGN
#define SIG_DFL ((pqsigfunc)0)
#define SIG_ERR ((pqsigfunc)-1)
#define SIG_IGN ((pqsigfunc)1)
#endif
#endif /* PQSIGNAL_H */