Hi, On Mon, Dec 8, 2008 at 11:39 PM, Heikki Linnakangas <[EMAIL PROTECTED]> wrote: > Tom Lane wrote: >> >> Heikki Linnakangas <[EMAIL PROTECTED]> writes: >>> >>> To set or clear the flag from PGPROC, to send or handle a signal, we have >>> to acquire ProcArrayLock. Is that safe to do in a signal handler? >> >> No. If it's trying to do that then it's broken. In fact, if it's >> trying to do much of anything beyond setting a "volatile" flag variable >> in a signal handler, it's broken --- unless there are special provisions >> to limit where the signal trap can occur, which would be pretty much >> unacceptable for a multiplexed-signal implementation. > > Ok, I was afraid so. > > I think we'll need to replace the proposed bitmask with an array of > sig_atomic_t flags then, and do without locking.
Thanks! I updated the patch so (based on signal_handling_v2-heikki-1.patch). Regards, -- Fujii Masao NIPPON TELEGRAPH AND TELEPHONE CORPORATION NTT Open Source Software Center
diff -rc base/src/backend/access/transam/twophase.c new/src/backend/access/transam/twophase.c *** base/src/backend/access/transam/twophase.c 2008-12-09 11:45:21.000000000 +0900 --- new/src/backend/access/transam/twophase.c 2008-12-09 12:38:52.000000000 +0900 *************** *** 287,292 **** --- 287,293 ---- gxact->proc.databaseId = databaseid; gxact->proc.roleId = owner; gxact->proc.inCommit = false; + MemSet(gxact->proc.signalFlags, 0, NUM_PROCSIGNALS * sizeof(sig_atomic_t)); gxact->proc.vacuumFlags = 0; gxact->proc.lwWaiting = false; gxact->proc.lwExclusive = false; diff -rc base/src/backend/commands/async.c new/src/backend/commands/async.c *** base/src/backend/commands/async.c 2008-12-09 11:45:21.000000000 +0900 --- new/src/backend/commands/async.c 2008-12-09 11:45:43.000000000 +0900 *************** *** 915,923 **** * a frontend command. Signal handler execution of inbound notifies * is disabled until the next EnableNotifyInterrupt call. * ! * The SIGUSR1 signal handler also needs to call this, so as to ! * prevent conflicts if one signal interrupts the other. So we ! * must return the previous state of the flag. */ bool DisableNotifyInterrupt(void) --- 915,924 ---- * a frontend command. Signal handler execution of inbound notifies * is disabled until the next EnableNotifyInterrupt call. * ! * This also needs to be called when SIGUSR1 with ! * PROCSIG_CATCHUP_INTERRUPT is received, so as to prevent conflicts ! * if one signal interrupts the other. So we must return the previous ! * state of the flag. */ bool DisableNotifyInterrupt(void) *************** *** 954,960 **** nulls[Natts_pg_listener]; bool catchup_enabled; ! /* Must prevent SIGUSR1 interrupt while I am running */ catchup_enabled = DisableCatchupInterrupt(); if (Trace_notify) --- 955,961 ---- nulls[Natts_pg_listener]; bool catchup_enabled; ! /* Must prevent catchup interrupt while I am running */ catchup_enabled = DisableCatchupInterrupt(); if (Trace_notify) diff -rc base/src/backend/postmaster/autovacuum.c new/src/backend/postmaster/autovacuum.c *** base/src/backend/postmaster/autovacuum.c 2008-12-09 11:45:21.000000000 +0900 --- new/src/backend/postmaster/autovacuum.c 2008-12-09 11:45:43.000000000 +0900 *************** *** 1477,1483 **** pqsignal(SIGALRM, handle_sig_alarm); pqsignal(SIGPIPE, SIG_IGN); ! pqsignal(SIGUSR1, CatchupInterruptHandler); /* We don't listen for async notifies */ pqsignal(SIGUSR2, SIG_IGN); pqsignal(SIGFPE, FloatExceptionHandler); --- 1477,1483 ---- pqsignal(SIGALRM, handle_sig_alarm); pqsignal(SIGPIPE, SIG_IGN); ! pqsignal(SIGUSR1, proc_sigusr1_handler); /* We don't listen for async notifies */ pqsignal(SIGUSR2, SIG_IGN); pqsignal(SIGFPE, FloatExceptionHandler); diff -rc base/src/backend/storage/ipc/sinval.c new/src/backend/storage/ipc/sinval.c *** base/src/backend/storage/ipc/sinval.c 2008-12-09 11:45:21.000000000 +0900 --- new/src/backend/storage/ipc/sinval.c 2008-12-09 11:45:43.000000000 +0900 *************** *** 27,33 **** * need a way to give an idle backend a swift kick in the rear and make * it catch up before the sinval queue overflows and forces it to go * through a cache reset exercise. This is done by sending SIGUSR1 ! * to any backend that gets too far behind. * * State for catchup events consists of two flags: one saying whether * the signal handler is currently allowed to call ProcessCatchupEvent --- 27,34 ---- * need a way to give an idle backend a swift kick in the rear and make * it catch up before the sinval queue overflows and forces it to go * through a cache reset exercise. This is done by sending SIGUSR1 ! * with PROCSIG_CATCHUP_INTERRUPT to any backend that gets too far ! * behind. * * State for catchup events consists of two flags: one saying whether * the signal handler is currently allowed to call ProcessCatchupEvent *************** *** 144,152 **** /* ! * CatchupInterruptHandler * ! * This is the signal handler for SIGUSR1. * * If we are idle (catchupInterruptEnabled is set), we can safely * invoke ProcessCatchupEvent directly. Otherwise, just set a flag --- 145,154 ---- /* ! * HandleCatchupInterrupt * ! * This is called when SIGUSR1 with PROCSIG_CATCHUP_INTERRUPT is ! * received. * * If we are idle (catchupInterruptEnabled is set), we can safely * invoke ProcessCatchupEvent directly. Otherwise, just set a flag *************** *** 156,168 **** * since there's no longer any reason to do anything.) */ void ! CatchupInterruptHandler(SIGNAL_ARGS) { - int save_errno = errno; - /* ! * Note: this is a SIGNAL HANDLER. You must be very wary what you do ! * here. */ /* Don't joggle the elbow of proc_exit */ --- 158,168 ---- * since there's no longer any reason to do anything.) */ void ! HandleCatchupInterrupt(void) { /* ! * Note: this is called by a SIGNAL HANDLER. ! * You must be very wary what you do here. */ /* Don't joggle the elbow of proc_exit */ *************** *** 216,223 **** */ catchupInterruptOccurred = 1; } - - errno = save_errno; } /* --- 216,221 ---- *************** *** 289,295 **** /* * ProcessCatchupEvent * ! * Respond to a catchup event (SIGUSR1) from another backend. * * This is called either directly from the SIGUSR1 signal handler, * or the next time control reaches the outer idle loop (assuming --- 287,294 ---- /* * ProcessCatchupEvent * ! * Respond to a catchup event (SIGUSR1 with PROCSIG_CATCHUP_INTERRUPT) ! * from another backend. * * This is called either directly from the SIGUSR1 signal handler, * or the next time control reaches the outer idle loop (assuming diff -rc base/src/backend/storage/ipc/sinvaladt.c new/src/backend/storage/ipc/sinvaladt.c *** base/src/backend/storage/ipc/sinvaladt.c 2008-12-09 11:45:21.000000000 +0900 --- new/src/backend/storage/ipc/sinvaladt.c 2008-12-09 11:45:43.000000000 +0900 *************** *** 21,26 **** --- 21,27 ---- #include "storage/backendid.h" #include "storage/ipc.h" #include "storage/proc.h" + #include "storage/procarray.h" #include "storage/shmem.h" #include "storage/sinvaladt.h" #include "storage/spin.h" *************** *** 136,144 **** /* Per-backend state in shared invalidation structure */ typedef struct ProcState { ! /* procPid is zero in an inactive ProcState array entry. */ ! pid_t procPid; /* PID of backend, for signaling */ ! /* nextMsgNum is meaningless if procPid == 0 or resetState is true. */ int nextMsgNum; /* next message number to read */ bool resetState; /* backend needs to reset its state */ bool signaled; /* backend has been sent catchup signal */ --- 137,145 ---- /* Per-backend state in shared invalidation structure */ typedef struct ProcState { ! /* proc is NULL in an inactive ProcState array entry. */ ! PGPROC *proc; /* PGPROC entry of backend, for signaling */ ! /* nextMsgNum is meaningless if proc == NULL or resetState is true. */ int nextMsgNum; /* next message number to read */ bool resetState; /* backend needs to reset its state */ bool signaled; /* backend has been sent catchup signal */ *************** *** 235,241 **** /* Mark all backends inactive, and initialize nextLXID */ for (i = 0; i < shmInvalBuffer->maxBackends; i++) { ! shmInvalBuffer->procState[i].procPid = 0; /* inactive */ shmInvalBuffer->procState[i].nextMsgNum = 0; /* meaningless */ shmInvalBuffer->procState[i].resetState = false; shmInvalBuffer->procState[i].signaled = false; --- 236,242 ---- /* Mark all backends inactive, and initialize nextLXID */ for (i = 0; i < shmInvalBuffer->maxBackends; i++) { ! shmInvalBuffer->procState[i].proc = NULL; /* inactive */ shmInvalBuffer->procState[i].nextMsgNum = 0; /* meaningless */ shmInvalBuffer->procState[i].resetState = false; shmInvalBuffer->procState[i].signaled = false; *************** *** 266,272 **** /* Look for a free entry in the procState array */ for (index = 0; index < segP->lastBackend; index++) { ! if (segP->procState[index].procPid == 0) /* inactive slot? */ { stateP = &segP->procState[index]; break; --- 267,273 ---- /* Look for a free entry in the procState array */ for (index = 0; index < segP->lastBackend; index++) { ! if (segP->procState[index].proc == NULL) /* inactive slot? */ { stateP = &segP->procState[index]; break; *************** *** 278,284 **** if (segP->lastBackend < segP->maxBackends) { stateP = &segP->procState[segP->lastBackend]; ! Assert(stateP->procPid == 0); segP->lastBackend++; } else --- 279,285 ---- if (segP->lastBackend < segP->maxBackends) { stateP = &segP->procState[segP->lastBackend]; ! Assert(stateP->proc == NULL); segP->lastBackend++; } else *************** *** 303,309 **** nextLocalTransactionId = stateP->nextLXID; /* mark myself active, with all extant messages already read */ ! stateP->procPid = MyProcPid; stateP->nextMsgNum = segP->maxMsgNum; stateP->resetState = false; stateP->signaled = false; --- 304,310 ---- nextLocalTransactionId = stateP->nextLXID; /* mark myself active, with all extant messages already read */ ! stateP->proc = MyProc; stateP->nextMsgNum = segP->maxMsgNum; stateP->resetState = false; stateP->signaled = false; *************** *** 341,347 **** stateP->nextLXID = nextLocalTransactionId; /* Mark myself inactive */ ! stateP->procPid = 0; stateP->nextMsgNum = 0; stateP->resetState = false; stateP->signaled = false; --- 342,348 ---- stateP->nextLXID = nextLocalTransactionId; /* Mark myself inactive */ ! stateP->proc = NULL; stateP->nextMsgNum = 0; stateP->resetState = false; stateP->signaled = false; *************** *** 349,355 **** /* Recompute index of last active backend */ for (i = segP->lastBackend; i > 0; i--) { ! if (segP->procState[i - 1].procPid != 0) break; } segP->lastBackend = i; --- 350,356 ---- /* Recompute index of last active backend */ for (i = segP->lastBackend; i > 0; i--) { ! if (segP->procState[i - 1].proc != NULL) break; } segP->lastBackend = i; *************** *** 374,380 **** { ProcState *stateP = &segP->procState[backendID - 1]; ! result = (stateP->procPid != 0); } else result = false; --- 375,381 ---- { ProcState *stateP = &segP->procState[backendID - 1]; ! result = (stateP->proc != NULL); } else result = false; *************** *** 590,596 **** int n = stateP->nextMsgNum; /* Ignore if inactive or already in reset state */ ! if (stateP->procPid == 0 || stateP->resetState) continue; /* --- 591,597 ---- int n = stateP->nextMsgNum; /* Ignore if inactive or already in reset state */ ! if (stateP->proc == NULL || stateP->resetState) continue; /* *************** *** 644,661 **** segP->nextThreshold = (numMsgs / CLEANUP_QUANTUM + 1) * CLEANUP_QUANTUM; /* ! * Lastly, signal anyone who needs a catchup interrupt. Since kill() ! * might not be fast, we don't want to hold locks while executing it. */ if (needSig) { ! pid_t his_pid = needSig->procPid; needSig->signaled = true; LWLockRelease(SInvalReadLock); LWLockRelease(SInvalWriteLock); ! elog(DEBUG4, "sending sinval catchup signal to PID %d", (int) his_pid); ! kill(his_pid, SIGUSR1); if (callerHasWriteLock) LWLockAcquire(SInvalWriteLock, LW_EXCLUSIVE); } --- 645,664 ---- segP->nextThreshold = (numMsgs / CLEANUP_QUANTUM + 1) * CLEANUP_QUANTUM; /* ! * Lastly, signal anyone who needs a catchup interrupt. Since ! * SendProcSignal() might not be fast, we don't want to hold locks while ! * executing it. */ if (needSig) { ! PGPROC *his_proc = needSig->proc; needSig->signaled = true; LWLockRelease(SInvalReadLock); LWLockRelease(SInvalWriteLock); ! elog(DEBUG4, "sending sinval catchup signal to PID %d", ! (int) his_proc->pid); ! SendProcSignal(his_proc, PROCSIG_CATCHUP_INTERRUPT); if (callerHasWriteLock) LWLockAcquire(SInvalWriteLock, LW_EXCLUSIVE); } diff -rc base/src/backend/storage/lmgr/proc.c new/src/backend/storage/lmgr/proc.c *** base/src/backend/storage/lmgr/proc.c 2008-12-09 11:45:21.000000000 +0900 --- new/src/backend/storage/lmgr/proc.c 2008-12-09 12:37:50.000000000 +0900 *************** *** 289,294 **** --- 289,295 ---- MyProc->databaseId = InvalidOid; MyProc->roleId = InvalidOid; MyProc->inCommit = false; + MemSet(MyProc->signalFlags, 0, NUM_PROCSIGNALS * sizeof(sig_atomic_t)); MyProc->vacuumFlags = 0; if (IsAutoVacuumWorkerProcess()) MyProc->vacuumFlags |= PROC_IS_AUTOVACUUM; *************** *** 428,433 **** --- 429,435 ---- MyProc->databaseId = InvalidOid; MyProc->roleId = InvalidOid; MyProc->inCommit = false; + MemSet(MyProc->signalFlags, 0, NUM_PROCSIGNALS * sizeof(sig_atomic_t)); /* we don't set the "is autovacuum" flag in the launcher */ MyProc->vacuumFlags = 0; MyProc->lwWaiting = false; *************** *** 1277,1282 **** --- 1279,1321 ---- PGSemaphoreUnlock(&proc->sem); } + /* + * SendProcSignal - send the signal with the reason to the process + * (such as backend, autovacuum worker and auxiliary process) + * identified by proc. + */ + void + SendProcSignal(PGPROC *proc, ProcSignalReason reason) + { + /* If the process has gone, do nothing */ + if (proc == NULL || proc->pid == 0) + return; + + /* Atomically set the proper flag */ + proc->signalFlags[reason] = true; + + /* Send SIGUSR1 to the process */ + kill(proc->pid, SIGUSR1); + } + + /* + * CheckProcSignal - check to see if the particular reason has been + * signaled, and clear the signal flag. Should be called after + * receiving SIGUSR1. + */ + bool + CheckProcSignal(ProcSignalReason reason) + { + /* Careful here --- don't clear flag if we haven't seen it set */ + if (MyProc->signalFlags[reason]) + { + MyProc->signalFlags[reason] = false; + return true; + } + + return false; + } + /***************************************************************************** * SIGALRM interrupt support diff -rc base/src/backend/tcop/postgres.c new/src/backend/tcop/postgres.c *** base/src/backend/tcop/postgres.c 2008-12-09 11:45:21.000000000 +0900 --- new/src/backend/tcop/postgres.c 2008-12-09 12:27:08.000000000 +0900 *************** *** 2437,2442 **** --- 2437,2464 ---- */ /* + * proc_sigusr1_handler - handle SIGUSR1 signal. + * + * SIGUSR1 is multiplexed to handle multiple different events. The signalFlags + * array in PGPROC indicates which events have been signaled. + */ + void + proc_sigusr1_handler(SIGNAL_ARGS) + { + int save_errno = errno; + + if (CheckProcSignal(PROCSIG_CATCHUP_INTERRUPT)) + { + /* + * Catchup interrupt has been sent. + */ + HandleCatchupInterrupt(); + } + + errno = save_errno; + } + + /* * quickdie() occurs when signalled SIGQUIT by the postmaster. * * Some backend has bought the farm, *************** *** 3180,3186 **** * of output during who-knows-what operation... */ pqsignal(SIGPIPE, SIG_IGN); ! pqsignal(SIGUSR1, CatchupInterruptHandler); pqsignal(SIGUSR2, NotifyInterruptHandler); pqsignal(SIGFPE, FloatExceptionHandler); --- 3202,3208 ---- * of output during who-knows-what operation... */ pqsignal(SIGPIPE, SIG_IGN); ! pqsignal(SIGUSR1, proc_sigusr1_handler); pqsignal(SIGUSR2, NotifyInterruptHandler); pqsignal(SIGFPE, FloatExceptionHandler); diff -rc base/src/include/storage/proc.h new/src/include/storage/proc.h *** base/src/include/storage/proc.h 2008-12-09 11:45:21.000000000 +0900 --- new/src/include/storage/proc.h 2008-12-09 13:01:25.000000000 +0900 *************** *** 14,19 **** --- 14,21 ---- #ifndef _PROC_H_ #define _PROC_H_ + #include <signal.h> + #include "storage/lock.h" #include "storage/pg_sema.h" *************** *** 38,43 **** --- 40,59 ---- TransactionId xids[PGPROC_MAX_CACHED_SUBXIDS]; }; + /* + * Reasons for signaling the process (such as backend, autovacuum worker + * and auxiliary process). We can cope with simultaneous signals for + * different reasons. If the same reason is signaled multiple times in + * quick succession, however, the postmaster is likely to observe only + * one notification of it. This is okay for the present uses. + */ + typedef enum + { + PROCSIG_CATCHUP_INTERRUPT, /* catchup interrupt */ + + NUM_PROCSIGNALS /* Must be last value of enum! */ + } ProcSignalReason; + /* Flags for PGPROC->vacuumFlags */ #define PROC_IS_AUTOVACUUM 0x01 /* is it an autovac worker? */ #define PROC_IN_VACUUM 0x02 /* currently running lazy vacuum */ *************** *** 91,96 **** --- 107,126 ---- bool inCommit; /* true if within commit critical section */ + /* + * The backend and auxiliary process are sent SIGUSR1 with the specific + * reason, which is communicated via flags in shared memory. We keep + * a boolean flag for each possible "reason", so that different reasons + * can be signaled by different processes at the same time. (However, + * if the same reason is signaled more than once simultaneously, the + * backend and auxiliary process will observe it only once.) + * + * The flags are actually declared as "volatile sig_atomic_t" for maximum + * portability. This should ensure that loads and stores of the flag + * values are atomic, allowing us to dispense with any explicit locking. + */ + volatile sig_atomic_t signalFlags[NUM_PROCSIGNALS]; + uint8 vacuumFlags; /* vacuum-related flags, see above */ /* Info about LWLock the process is currently waiting for, if any. */ *************** *** 171,176 **** --- 201,209 ---- extern void ProcWaitForSignal(void); extern void ProcSendSignal(int pid); + extern void SendProcSignal(PGPROC *proc, ProcSignalReason reason); + extern bool CheckProcSignal(ProcSignalReason reason); + extern bool enable_sig_alarm(int delayms, bool is_statement_timeout); extern bool disable_sig_alarm(bool is_statement_timeout); extern void handle_sig_alarm(SIGNAL_ARGS); diff -rc base/src/include/storage/sinval.h new/src/include/storage/sinval.h *** base/src/include/storage/sinval.h 2008-12-09 11:45:21.000000000 +0900 --- new/src/include/storage/sinval.h 2008-12-09 11:45:43.000000000 +0900 *************** *** 90,96 **** void (*resetFunction) (void)); /* signal handler for catchup events (SIGUSR1) */ ! extern void CatchupInterruptHandler(SIGNAL_ARGS); /* * enable/disable processing of catchup events directly from signal handler. --- 90,96 ---- void (*resetFunction) (void)); /* signal handler for catchup events (SIGUSR1) */ ! extern void HandleCatchupInterrupt(void); /* * enable/disable processing of catchup events directly from signal handler. diff -rc base/src/include/tcop/tcopprot.h new/src/include/tcop/tcopprot.h *** base/src/include/tcop/tcopprot.h 2008-12-09 11:45:21.000000000 +0900 --- new/src/include/tcop/tcopprot.h 2008-12-09 11:45:43.000000000 +0900 *************** *** 56,61 **** --- 56,62 ---- extern bool assign_max_stack_depth(int newval, bool doit, GucSource source); + extern void proc_sigusr1_handler(SIGNAL_ARGS); extern void die(SIGNAL_ARGS); extern void quickdie(SIGNAL_ARGS); extern void authdie(SIGNAL_ARGS);
-- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers