diff -dcrpN postgresql/src/backend/postmaster/autovacuum.c postgresql.1/src/backend/postmaster/autovacuum.c *** postgresql/src/backend/postmaster/autovacuum.c 2012-06-11 06:22:48.137921483 +0200 --- postgresql.1/src/backend/postmaster/autovacuum.c 2012-06-19 17:49:55.348479057 +0200 *************** *** 86,94 **** #include "storage/ipc.h" #include "storage/latch.h" #include "storage/pmsignal.h" - #include "storage/proc.h" #include "storage/procsignal.h" #include "storage/sinvaladt.h" #include "tcop/tcopprot.h" #include "utils/fmgroids.h" #include "utils/lsyscache.h" --- 86,94 ---- #include "storage/ipc.h" #include "storage/latch.h" #include "storage/pmsignal.h" #include "storage/procsignal.h" #include "storage/sinvaladt.h" + #include "storage/timeout.h" #include "tcop/tcopprot.h" #include "utils/fmgroids.h" #include "utils/lsyscache.h" *************** AutoVacLauncherMain(int argc, char *argv *** 484,490 **** /* Forget any pending QueryCancel request */ QueryCancelPending = false; ! disable_sig_alarm(true); QueryCancelPending = false; /* again in case timeout occurred */ /* Report the error to the server log */ --- 484,490 ---- /* Forget any pending QueryCancel request */ QueryCancelPending = false; ! disable_all_timeouts(false); QueryCancelPending = false; /* again in case timeout occurred */ /* Report the error to the server log */ diff -dcrpN postgresql/src/backend/postmaster/postmaster.c postgresql.1/src/backend/postmaster/postmaster.c *** postgresql/src/backend/postmaster/postmaster.c 2012-06-11 06:22:48.148921535 +0200 --- postgresql.1/src/backend/postmaster/postmaster.c 2012-06-19 17:49:55.351479077 +0200 *************** *** 112,118 **** #include "storage/ipc.h" #include "storage/pg_shmem.h" #include "storage/pmsignal.h" ! #include "storage/proc.h" #include "tcop/tcopprot.h" #include "utils/builtins.h" #include "utils/datetime.h" --- 112,118 ---- #include "storage/ipc.h" #include "storage/pg_shmem.h" #include "storage/pmsignal.h" ! #include "storage/timeout.h" #include "tcop/tcopprot.h" #include "utils/builtins.h" #include "utils/datetime.h" *************** BackendInitialize(Port *port) *** 3486,3494 **** * Ready to begin client interaction. We will give up and exit(1) after a * time delay, so that a broken client can't hog a connection * indefinitely. PreAuthDelay and any DNS interactions above don't count ! * against the time limit. */ ! if (!enable_sig_alarm(AuthenticationTimeout * 1000, false)) elog(FATAL, "could not set timer for startup packet timeout"); /* --- 3486,3494 ---- * Ready to begin client interaction. We will give up and exit(1) after a * time delay, so that a broken client can't hog a connection * indefinitely. PreAuthDelay and any DNS interactions above don't count ! * against the time limit. Use the deadlock timeout interface. */ ! if (!enable_timeout(DEADLOCK_TIMEOUT, AuthenticationTimeout * 1000)) elog(FATAL, "could not set timer for startup packet timeout"); /* *************** BackendInitialize(Port *port) *** 3526,3532 **** /* * Disable the timeout, and prevent SIGTERM/SIGQUIT again. */ ! if (!disable_sig_alarm(false)) elog(FATAL, "could not disable timer for startup packet timeout"); PG_SETMASK(&BlockSig); } --- 3526,3532 ---- /* * Disable the timeout, and prevent SIGTERM/SIGQUIT again. */ ! if (!disable_timeout(DEADLOCK_TIMEOUT, false)) elog(FATAL, "could not disable timer for startup packet timeout"); PG_SETMASK(&BlockSig); } diff -dcrpN postgresql/src/backend/postmaster/startup.c postgresql.1/src/backend/postmaster/startup.c *** postgresql/src/backend/postmaster/startup.c 2012-04-16 19:57:22.442915536 +0200 --- postgresql.1/src/backend/postmaster/startup.c 2012-06-19 17:49:55.351479077 +0200 *************** *** 27,33 **** #include "storage/ipc.h" #include "storage/latch.h" #include "storage/pmsignal.h" ! #include "storage/proc.h" #include "utils/guc.h" --- 27,33 ---- #include "storage/ipc.h" #include "storage/latch.h" #include "storage/pmsignal.h" ! #include "storage/timeout.h" #include "utils/guc.h" *************** StartupProcessMain(void) *** 195,202 **** pqsignal(SIGTERM, StartupProcShutdownHandler); /* request shutdown */ pqsignal(SIGQUIT, startupproc_quickdie); /* hard crash time */ if (EnableHotStandby) ! pqsignal(SIGALRM, handle_standby_sig_alarm); /* ignored unless ! * InHotStandby */ else pqsignal(SIGALRM, SIG_IGN); pqsignal(SIGPIPE, SIG_IGN); --- 195,202 ---- pqsignal(SIGTERM, StartupProcShutdownHandler); /* request shutdown */ pqsignal(SIGQUIT, startupproc_quickdie); /* hard crash time */ if (EnableHotStandby) ! pqsignal(SIGALRM, handle_sig_alarm); /* ignored unless ! * InHotStandby */ else pqsignal(SIGALRM, SIG_IGN); pqsignal(SIGPIPE, SIG_IGN); diff -dcrpN postgresql/src/backend/storage/ipc/standby.c postgresql.1/src/backend/storage/ipc/standby.c *** postgresql/src/backend/storage/ipc/standby.c 2012-06-11 06:22:48.154921564 +0200 --- postgresql.1/src/backend/storage/ipc/standby.c 2012-06-19 17:49:55.352479081 +0200 *************** *** 23,32 **** #include "miscadmin.h" #include "storage/bufmgr.h" #include "storage/lmgr.h" - #include "storage/proc.h" #include "storage/procarray.h" #include "storage/sinvaladt.h" #include "storage/standby.h" #include "utils/ps_status.h" #include "utils/timestamp.h" --- 23,32 ---- #include "miscadmin.h" #include "storage/bufmgr.h" #include "storage/lmgr.h" #include "storage/procarray.h" #include "storage/sinvaladt.h" #include "storage/standby.h" + #include "storage/timeout.h" #include "utils/ps_status.h" #include "utils/timestamp.h" *************** ResolveRecoveryConflictWithLock(Oid dbOi *** 394,400 **** void ResolveRecoveryConflictWithBufferPin(void) { - bool sig_alarm_enabled = false; TimestampTz ltime; TimestampTz now; --- 394,399 ---- *************** ResolveRecoveryConflictWithBufferPin(voi *** 409,417 **** * We're willing to wait forever for conflicts, so set timeout for * deadlock check (only) */ ! if (enable_standby_sig_alarm(now, now, true)) ! sig_alarm_enabled = true; ! else elog(FATAL, "could not set timer for process wakeup"); } else if (now >= ltime) --- 408,414 ---- * We're willing to wait forever for conflicts, so set timeout for * deadlock check (only) */ ! if (!enable_timeout(STANDBY_DEADLOCK_TIMEOUT, DeadlockTimeout)) elog(FATAL, "could not set timer for process wakeup"); } else if (now >= ltime) *************** ResolveRecoveryConflictWithBufferPin(voi *** 423,446 **** } else { /* * Wake up at ltime, and check for deadlocks as well if we will be * waiting longer than deadlock_timeout */ ! if (enable_standby_sig_alarm(now, ltime, false)) ! sig_alarm_enabled = true; ! else elog(FATAL, "could not set timer for process wakeup"); } /* Wait to be signaled by UnpinBuffer() */ ProcWaitForSignal(); ! if (sig_alarm_enabled) ! { ! if (!disable_standby_sig_alarm()) ! elog(FATAL, "could not disable timer for process wakeup"); ! } } void --- 420,447 ---- } else { + long secs, msecs; + int usecs; + /* * Wake up at ltime, and check for deadlocks as well if we will be * waiting longer than deadlock_timeout */ ! if (!enable_timeout(STANDBY_DEADLOCK_TIMEOUT, DeadlockTimeout)) ! elog(FATAL, "could not set timer for process wakeup"); ! ! TimestampDifference(now, ltime, &secs, &usecs); ! msecs = secs * 1000 + usecs / 1000; ! ! if (!enable_timeout(STANDBY_TIMEOUT, msecs)) elog(FATAL, "could not set timer for process wakeup"); } /* Wait to be signaled by UnpinBuffer() */ ProcWaitForSignal(); ! if (!disable_all_timeouts(false)) ! elog(FATAL, "could not disable timer for process wakeup"); } void diff -dcrpN postgresql/src/backend/storage/lmgr/Makefile postgresql.1/src/backend/storage/lmgr/Makefile *** postgresql/src/backend/storage/lmgr/Makefile 2012-04-16 19:57:22.458915722 +0200 --- postgresql.1/src/backend/storage/lmgr/Makefile 2012-06-19 17:49:55.353479085 +0200 *************** subdir = src/backend/storage/lmgr *** 12,18 **** top_builddir = ../../../.. include $(top_builddir)/src/Makefile.global ! OBJS = lmgr.o lock.o proc.o deadlock.o lwlock.o spin.o s_lock.o predicate.o include $(top_srcdir)/src/backend/common.mk --- 12,18 ---- top_builddir = ../../../.. include $(top_builddir)/src/Makefile.global ! OBJS = lmgr.o lock.o proc.o deadlock.o lwlock.o spin.o s_lock.o predicate.o timeout.o include $(top_srcdir)/src/backend/common.mk diff -dcrpN postgresql/src/backend/storage/lmgr/proc.c postgresql.1/src/backend/storage/lmgr/proc.c *** postgresql/src/backend/storage/lmgr/proc.c 2012-06-11 06:22:48.156921573 +0200 --- postgresql.1/src/backend/storage/lmgr/proc.c 2012-06-19 17:59:28.649869905 +0200 *************** *** 47,59 **** #include "storage/proc.h" #include "storage/procarray.h" #include "storage/procsignal.h" #include "storage/spin.h" #include "utils/timestamp.h" /* GUC variables */ - int DeadlockTimeout = 1000; - int StatementTimeout = 0; bool log_lock_waits = false; /* Pointer to this process's PGPROC and PGXACT structs, if any */ --- 47,59 ---- #include "storage/proc.h" #include "storage/procarray.h" #include "storage/procsignal.h" + #include "storage/proctimeout.h" #include "storage/spin.h" + #include "storage/timeout.h" #include "utils/timestamp.h" /* GUC variables */ bool log_lock_waits = false; /* Pointer to this process's PGPROC and PGXACT structs, if any */ *************** PGPROC *PreparedXactProcs = NULL; *** 77,103 **** /* If we are waiting for a lock, this points to the associated LOCALLOCK */ static LOCALLOCK *lockAwaited = NULL; ! /* Mark these volatile because they can be changed by signal handler */ ! static volatile bool standby_timeout_active = false; ! static volatile bool statement_timeout_active = false; ! static volatile bool deadlock_timeout_active = false; ! static volatile DeadLockState deadlock_state = DS_NOT_YET_CHECKED; ! volatile bool cancel_from_timeout = false; ! ! /* timeout_start_time is set when log_lock_waits is true */ ! static TimestampTz timeout_start_time; ! ! /* statement_fin_time is valid only if statement_timeout_active is true */ ! static TimestampTz statement_fin_time; ! static TimestampTz statement_fin_time2; /* valid only in recovery */ ! static void RemoveProcFromArray(int code, Datum arg); static void ProcKill(int code, Datum arg); static void AuxiliaryProcKill(int code, Datum arg); - static bool CheckStatementTimeout(void); - static bool CheckStandbyTimeout(void); - /* * Report shared-memory space needed by InitProcGlobal. --- 77,88 ---- /* If we are waiting for a lock, this points to the associated LOCALLOCK */ static LOCALLOCK *lockAwaited = NULL; ! /* Mark this volatile because it can be changed by signal handler */ ! static volatile DeadLockState deadlock_state; static void RemoveProcFromArray(int code, Datum arg); static void ProcKill(int code, Datum arg); static void AuxiliaryProcKill(int code, Datum arg); /* * Report shared-memory space needed by InitProcGlobal. *************** LockErrorCleanup(void) *** 654,660 **** return; /* Turn off the deadlock timer, if it's still running (see ProcSleep) */ ! disable_sig_alarm(false); /* Unlink myself from the wait queue, if on it (might not be anymore!) */ partitionLock = LockHashPartitionLock(lockAwaited->hashcode); --- 639,645 ---- return; /* Turn off the deadlock timer, if it's still running (see ProcSleep) */ ! disable_timeout(DEADLOCK_TIMEOUT, false); /* Unlink myself from the wait queue, if on it (might not be anymore!) */ partitionLock = LockHashPartitionLock(lockAwaited->hashcode); *************** ProcSleep(LOCALLOCK *locallock, LockMeth *** 1049,1055 **** * By delaying the check until we've waited for a bit, we can avoid * running the rather expensive deadlock-check code in most cases. */ ! if (!enable_sig_alarm(DeadlockTimeout, false)) elog(FATAL, "could not set timer for process wakeup"); /* --- 1034,1040 ---- * By delaying the check until we've waited for a bit, we can avoid * running the rather expensive deadlock-check code in most cases. */ ! if (!enable_timeout(DEADLOCK_TIMEOUT, DeadlockTimeout)) elog(FATAL, "could not set timer for process wakeup"); /* *************** ProcSleep(LOCALLOCK *locallock, LockMeth *** 1139,1145 **** DescribeLockTag(&buf, &locallock->tag.lock); modename = GetLockmodeName(locallock->tag.lock.locktag_lockmethodid, lockmode); ! TimestampDifference(timeout_start_time, GetCurrentTimestamp(), &secs, &usecs); msecs = secs * 1000 + usecs / 1000; usecs = usecs % 1000; --- 1124,1131 ---- DescribeLockTag(&buf, &locallock->tag.lock); modename = GetLockmodeName(locallock->tag.lock.locktag_lockmethodid, lockmode); ! TimestampDifference(get_timeout_start(DEADLOCK_TIMEOUT), ! GetCurrentTimestamp(), &secs, &usecs); msecs = secs * 1000 + usecs / 1000; usecs = usecs % 1000; *************** ProcSleep(LOCALLOCK *locallock, LockMeth *** 1201,1207 **** /* * Disable the timer, if it's still running */ ! if (!disable_sig_alarm(false)) elog(FATAL, "could not disable timer for process wakeup"); /* --- 1187,1193 ---- /* * Disable the timer, if it's still running */ ! if (!disable_timeout(DEADLOCK_TIMEOUT, false)) elog(FATAL, "could not disable timer for process wakeup"); /* *************** ProcLockWakeup(LockMethod lockMethodTabl *** 1345,1351 **** * NB: this is run inside a signal handler, so be very wary about what is done * here or in called routines. */ ! static void CheckDeadLock(void) { int i; --- 1331,1337 ---- * NB: this is run inside a signal handler, so be very wary about what is done * here or in called routines. */ ! void CheckDeadLock(void) { int i; *************** ProcSendSignal(int pid) *** 1500,1817 **** PGSemaphoreUnlock(&proc->sem); } - - /***************************************************************************** - * SIGALRM interrupt support - * - * Maybe these should be in pqsignal.c? - *****************************************************************************/ - - /* - * Enable the SIGALRM interrupt to fire after the specified delay - * - * Delay is given in milliseconds. Caller should be sure a SIGALRM - * signal handler is installed before this is called. - * - * This code properly handles nesting of deadlock timeout alarms within - * statement timeout alarms. - * - * Returns TRUE if okay, FALSE on failure. - */ - bool - enable_sig_alarm(int delayms, bool is_statement_timeout) - { - TimestampTz fin_time; - struct itimerval timeval; - - if (is_statement_timeout) - { - /* - * Begin statement-level timeout - * - * Note that we compute statement_fin_time with reference to the - * statement_timestamp, but apply the specified delay without any - * correction; that is, we ignore whatever time has elapsed since - * statement_timestamp was set. In the normal case only a small - * interval will have elapsed and so this doesn't matter, but there - * are corner cases (involving multi-statement query strings with - * embedded COMMIT or ROLLBACK) where we might re-initialize the - * statement timeout long after initial receipt of the message. In - * such cases the enforcement of the statement timeout will be a bit - * inconsistent. This annoyance is judged not worth the cost of - * performing an additional gettimeofday() here. - */ - Assert(!deadlock_timeout_active); - fin_time = GetCurrentStatementStartTimestamp(); - fin_time = TimestampTzPlusMilliseconds(fin_time, delayms); - statement_fin_time = fin_time; - cancel_from_timeout = false; - statement_timeout_active = true; - } - else if (statement_timeout_active) - { - /* - * Begin deadlock timeout with statement-level timeout active - * - * Here, we want to interrupt at the closer of the two timeout times. - * If fin_time >= statement_fin_time then we need not touch the - * existing timer setting; else set up to interrupt at the deadlock - * timeout time. - * - * NOTE: in this case it is possible that this routine will be - * interrupted by the previously-set timer alarm. This is okay - * because the signal handler will do only what it should do according - * to the state variables. The deadlock checker may get run earlier - * than normal, but that does no harm. - */ - timeout_start_time = GetCurrentTimestamp(); - fin_time = TimestampTzPlusMilliseconds(timeout_start_time, delayms); - deadlock_timeout_active = true; - if (fin_time >= statement_fin_time) - return true; - } - else - { - /* Begin deadlock timeout with no statement-level timeout */ - deadlock_timeout_active = true; - /* GetCurrentTimestamp can be expensive, so only do it if we must */ - if (log_lock_waits) - timeout_start_time = GetCurrentTimestamp(); - } - - /* If we reach here, okay to set the timer interrupt */ - MemSet(&timeval, 0, sizeof(struct itimerval)); - timeval.it_value.tv_sec = delayms / 1000; - timeval.it_value.tv_usec = (delayms % 1000) * 1000; - if (setitimer(ITIMER_REAL, &timeval, NULL)) - return false; - return true; - } - - /* - * Cancel the SIGALRM timer, either for a deadlock timeout or a statement - * timeout. If a deadlock timeout is canceled, any active statement timeout - * remains in force. - * - * Returns TRUE if okay, FALSE on failure. - */ - bool - disable_sig_alarm(bool is_statement_timeout) - { - /* - * Always disable the interrupt if it is active; this avoids being - * interrupted by the signal handler and thereby possibly getting - * confused. - * - * We will re-enable the interrupt if necessary in CheckStatementTimeout. - */ - if (statement_timeout_active || deadlock_timeout_active) - { - struct itimerval timeval; - - MemSet(&timeval, 0, sizeof(struct itimerval)); - if (setitimer(ITIMER_REAL, &timeval, NULL)) - { - statement_timeout_active = false; - cancel_from_timeout = false; - deadlock_timeout_active = false; - return false; - } - } - - /* Always cancel deadlock timeout, in case this is error cleanup */ - deadlock_timeout_active = false; - - /* Cancel or reschedule statement timeout */ - if (is_statement_timeout) - { - statement_timeout_active = false; - cancel_from_timeout = false; - } - else if (statement_timeout_active) - { - if (!CheckStatementTimeout()) - return false; - } - return true; - } - - /* * Check for statement timeout. If the timeout time has come, ! * trigger a query-cancel interrupt; if not, reschedule the SIGALRM ! * interrupt to occur at the right time. ! * ! * Returns true if okay, false if failed to set the interrupt. */ ! static bool CheckStatementTimeout(void) { - TimestampTz now; - - if (!statement_timeout_active) - return true; /* do nothing if not active */ - - now = GetCurrentTimestamp(); - - if (now >= statement_fin_time) - { - /* Time to die */ - statement_timeout_active = false; - cancel_from_timeout = true; #ifdef HAVE_SETSID ! /* try to signal whole process group */ ! kill(-MyProcPid, SIGINT); #endif ! kill(MyProcPid, SIGINT); ! } ! else ! { ! /* Not time yet, so (re)schedule the interrupt */ ! long secs; ! int usecs; ! struct itimerval timeval; ! ! TimestampDifference(now, statement_fin_time, ! &secs, &usecs); ! ! /* ! * It's possible that the difference is less than a microsecond; ! * ensure we don't cancel, rather than set, the interrupt. ! */ ! if (secs == 0 && usecs == 0) ! usecs = 1; ! MemSet(&timeval, 0, sizeof(struct itimerval)); ! timeval.it_value.tv_sec = secs; ! timeval.it_value.tv_usec = usecs; ! if (setitimer(ITIMER_REAL, &timeval, NULL)) ! return false; ! } ! ! return true; } - /* ! * Signal handler for SIGALRM for normal user backends ! * ! * Process deadlock check and/or statement timeout check, as needed. ! * To avoid various edge cases, we must be careful to do nothing ! * when there is nothing to be done. We also need to be able to ! * reschedule the timer interrupt if called before end of statement. */ void ! handle_sig_alarm(SIGNAL_ARGS) ! { ! int save_errno = errno; ! ! /* SIGALRM is cause for waking anything waiting on the process latch */ ! if (MyProc) ! SetLatch(&MyProc->procLatch); ! ! if (deadlock_timeout_active) ! { ! deadlock_timeout_active = false; ! CheckDeadLock(); ! } ! ! if (statement_timeout_active) ! (void) CheckStatementTimeout(); ! ! errno = save_errno; ! } ! ! /* ! * Signal handler for SIGALRM in Startup process ! * ! * To avoid various edge cases, we must be careful to do nothing ! * when there is nothing to be done. We also need to be able to ! * reschedule the timer interrupt if called before end of statement. ! * ! * We set either deadlock_timeout_active or statement_timeout_active ! * or both. Interrupts are enabled if standby_timeout_active. ! */ ! bool ! enable_standby_sig_alarm(TimestampTz now, TimestampTz fin_time, bool deadlock_only) ! { ! TimestampTz deadlock_time = TimestampTzPlusMilliseconds(now, ! DeadlockTimeout); ! ! if (deadlock_only) ! { ! /* ! * Wake up at deadlock_time only, then wait forever ! */ ! statement_fin_time = deadlock_time; ! deadlock_timeout_active = true; ! statement_timeout_active = false; ! } ! else if (fin_time > deadlock_time) ! { ! /* ! * Wake up at deadlock_time, then again at fin_time ! */ ! statement_fin_time = deadlock_time; ! statement_fin_time2 = fin_time; ! deadlock_timeout_active = true; ! statement_timeout_active = true; ! } ! else ! { ! /* ! * Wake only at fin_time because its fairly soon ! */ ! statement_fin_time = fin_time; ! deadlock_timeout_active = false; ! statement_timeout_active = true; ! } ! ! if (deadlock_timeout_active || statement_timeout_active) ! { ! long secs; ! int usecs; ! struct itimerval timeval; ! ! TimestampDifference(now, statement_fin_time, ! &secs, &usecs); ! if (secs == 0 && usecs == 0) ! usecs = 1; ! MemSet(&timeval, 0, sizeof(struct itimerval)); ! timeval.it_value.tv_sec = secs; ! timeval.it_value.tv_usec = usecs; ! if (setitimer(ITIMER_REAL, &timeval, NULL)) ! return false; ! standby_timeout_active = true; ! } ! ! return true; ! } ! ! bool ! disable_standby_sig_alarm(void) { ! /* ! * Always disable the interrupt if it is active; this avoids being ! * interrupted by the signal handler and thereby possibly getting ! * confused. ! * ! * We will re-enable the interrupt if necessary in CheckStandbyTimeout. ! */ ! if (standby_timeout_active) ! { ! struct itimerval timeval; ! ! MemSet(&timeval, 0, sizeof(struct itimerval)); ! if (setitimer(ITIMER_REAL, &timeval, NULL)) ! { ! standby_timeout_active = false; ! return false; ! } ! } ! ! standby_timeout_active = false; ! ! return true; } /* --- 1486,1514 ---- PGSemaphoreUnlock(&proc->sem); } /* * Check for statement timeout. If the timeout time has come, ! * trigger a query-cancel interrupt. */ ! void CheckStatementTimeout(void) { #ifdef HAVE_SETSID ! /* try to signal whole process group */ ! kill(-MyProcPid, SIGINT); #endif ! kill(MyProcPid, SIGINT); } /* ! * CheckStandbyDeadLock() will trigger if the deadlock timeout ! * happens earlier than StandbyTimeout. If it triggers, ! * StandbyTimeout will still be rescheduled. */ void ! CheckStandbyDeadLock(void) { ! SendRecoveryConflictWithBufferPin(PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK); } /* *************** disable_standby_sig_alarm(void) *** 1819,1899 **** * SIGALRM handler. Timers will only be set when InHotStandby. * We simply ignore any signals unless the timer has been set. */ - static bool - CheckStandbyTimeout(void) - { - TimestampTz now; - bool reschedule = false; - - standby_timeout_active = false; - - now = GetCurrentTimestamp(); - - /* - * Reschedule the timer if its not time to wake yet, or if we have both - * timers set and the first one has just been reached. - */ - if (now >= statement_fin_time) - { - if (deadlock_timeout_active) - { - /* - * We're still waiting when we reach deadlock timeout, so send out - * a request to have other backends check themselves for deadlock. - * Then continue waiting until statement_fin_time, if that's set. - */ - SendRecoveryConflictWithBufferPin(PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK); - deadlock_timeout_active = false; - - /* - * Begin second waiting period if required. - */ - if (statement_timeout_active) - { - reschedule = true; - statement_fin_time = statement_fin_time2; - } - } - else - { - /* - * We've now reached statement_fin_time, so ask all conflicts to - * leave, so we can press ahead with applying changes in recovery. - */ - SendRecoveryConflictWithBufferPin(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN); - } - } - else - reschedule = true; - - if (reschedule) - { - long secs; - int usecs; - struct itimerval timeval; - - TimestampDifference(now, statement_fin_time, - &secs, &usecs); - if (secs == 0 && usecs == 0) - usecs = 1; - MemSet(&timeval, 0, sizeof(struct itimerval)); - timeval.it_value.tv_sec = secs; - timeval.it_value.tv_usec = usecs; - if (setitimer(ITIMER_REAL, &timeval, NULL)) - return false; - standby_timeout_active = true; - } - - return true; - } - void ! handle_standby_sig_alarm(SIGNAL_ARGS) { ! int save_errno = errno; ! ! if (standby_timeout_active) ! (void) CheckStandbyTimeout(); ! ! errno = save_errno; } --- 1516,1523 ---- * SIGALRM handler. Timers will only be set when InHotStandby. * We simply ignore any signals unless the timer has been set. */ void ! CheckStandbyTimeout(void) { ! SendRecoveryConflictWithBufferPin(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN); } diff -dcrpN postgresql/src/backend/storage/lmgr/timeout.c postgresql.1/src/backend/storage/lmgr/timeout.c *** postgresql/src/backend/storage/lmgr/timeout.c 1970-01-01 01:00:00.000000000 +0100 --- postgresql.1/src/backend/storage/lmgr/timeout.c 2012-06-19 18:57:10.735365663 +0200 *************** *** 0 **** --- 1,421 ---- + /*------------------------------------------------------------------------- + * + * timeout.c + * routines to manage timeout sources handled by SIGALRM + * + * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/backend/storage/lmgr/timeout.c + * + *------------------------------------------------------------------------- + */ + #include "postgres.h" + + #include + + #include "access/xact.h" + #include "storage/proc.h" + #include "storage/proctimeout.h" + #include "storage/timeout.h" + #include "utils/timestamp.h" + + /* GUC variables */ + int DeadlockTimeout = 1000; + int StatementTimeout = 0; + + /* + * Infrastructure for timeouts + */ + + static void InitTimeout(TimeoutName tn, TimestampTz start_time, TimestampTz fin_time); + static void DestroyTimeout(TimeoutName tn, bool keep_indicator); + + typedef void (*timeout_check)(void); + typedef TimestampTz (*timeout_start)(void); + + typedef struct { + TimeoutName index; + bool resched_next; + + /* volatile because it may be changed from the signal handler */ + volatile bool indicator; + + timeout_check timeout_check; + timeout_start timeout_start; + + TimestampTz start_time; + TimestampTz fin_time; + } timeout_params; + + /* + * List of possible timeout reasons in the order of enum TimeoutName. + * The priority of timeouts (in case two of them would trigger at the + * same time) is determined by this order: the earlier one in the list + * has higher priority. + */ + static timeout_params base_timeouts[TIMEOUT_MAX] = { + { + DEADLOCK_TIMEOUT, true, false, + CheckDeadLock, GetCurrentTimestamp, + 0 + }, + + { + STATEMENT_TIMEOUT, false, false, + CheckStatementTimeout, GetCurrentStatementStartTimestamp, + 0 + }, + + { + STANDBY_DEADLOCK_TIMEOUT, true, false, + CheckStandbyDeadLock, GetCurrentTimestamp, + 0 + }, + + { + STANDBY_TIMEOUT, false, false, + CheckStandbyTimeout, GetCurrentTimestamp, + 0 + } + }; + + /* + * List of active timeouts ordered by their fin_time and priority. + */ + static int n_timeouts = 0; + static timeout_params *timeouts[TIMEOUT_MAX]; + + /***************************************************************************** + * Internal helper functions + *****************************************************************************/ + + /* + * Find the index of a given timeout type in the active array + */ + static int + find_active_timeout(TimeoutName tn) + { + int i; + + for (i = 0; i < n_timeouts; i++) + { + if (timeouts[i]->index == tn) + return i; + } + + return -1; + } + + #define is_timeout_active(tn) (find_active_timeout(tn) >= 0) + + /* + * Insert tn'th timeout into the list of timeouts at the given index + * if the previous timeout allows rescheduling the next one in the list. + */ + static void + insert_timeout(TimeoutName tn, int index) + { + int i; + + if (index > 0 && !timeouts[index-1]->resched_next) + return; + + for (i = n_timeouts - 1; i >= index; i--) + timeouts[i+1] = timeouts[i]; + + timeouts[index] = &base_timeouts[tn]; + n_timeouts++; + } + + /* + * Remove the index'th element from the timeout list. + */ + static void + remove_timeout_index(int index) + { + int i; + + if (index < 0) + return; + + for (i = index + 1; i < n_timeouts; i++) + timeouts[i-1] = timeouts[i]; + + if (n_timeouts > 0 && index < n_timeouts) + n_timeouts--; + } + + /* + * (Re)schedule the next active timeout + */ + static bool + schedule_timeout(TimestampTz now) + { + long secs; + int usecs; + struct itimerval timeval; + + /* There is no active timeout, do nothing. */ + if (n_timeouts == 0) + return true; + + TimestampDifference(now, timeouts[0]->fin_time, + &secs, &usecs); + + /* + * It's possible that the difference is less than a microsecond; + * ensure we don't cancel, rather than set, the interrupt. + */ + if (secs == 0 && usecs == 0) + usecs = 1; + MemSet(&timeval, 0, sizeof(struct itimerval)); + timeval.it_value.tv_sec = secs; + timeval.it_value.tv_usec = usecs; + if (setitimer(ITIMER_REAL, &timeval, NULL)) + return false; + return true; + } + + /***************************************************************************** + * Init, Destroy and Check functions for different timeouts. + * + * NB: all Check* functions are run inside a signal handler, so be very wary + * about what is done in them or in called routines. + *****************************************************************************/ + + /* + * Common Init and Destroy functions + */ + + static void + InitTimeout(TimeoutName tn, TimestampTz start_time, TimestampTz fin_time) + { + base_timeouts[tn].indicator = false; + base_timeouts[tn].start_time = start_time; + base_timeouts[tn].fin_time = fin_time; + } + + static void + DestroyTimeout(TimeoutName tn, bool keep_indicator) + { + if (!keep_indicator) + base_timeouts[tn].indicator = false; + base_timeouts[tn].start_time = 0; + base_timeouts[tn].fin_time = 0; + } + + /***************************************************************************** + * Public API + *****************************************************************************/ + + /* + * Enable the SIGALRM interrupt to fire after the specified delay + * + * Delay is given in milliseconds. Caller should be sure a SIGALRM + * signal handler is installed before this is called. + * + * This code properly handles nesting of different timeout alarms. + * + * Returns TRUE if okay, FALSE on failure to set the timer. + */ + bool + enable_timeout(TimeoutName tn, int delayms) + { + TimestampTz start_time; + TimestampTz fin_time; + int i; + + Assert(!is_timeout_active(tn)); + + if (delayms <= 0) + return true; + + start_time = base_timeouts[tn].timeout_start(); + fin_time = TimestampTzPlusMilliseconds(start_time, delayms); + + /* Find out the index where to insert the new timeout. */ + for (i = 0; i < n_timeouts; i++) + { + /* + * The new timeout triggers earlier than + * a previously added one: insert here. + */ + if (fin_time < timeouts[i]->fin_time) + break; + /* + * The new timeout triggers at the same time + * as the previously added one but has greater priority. + */ + if (fin_time == timeouts[i]->fin_time && tn < timeouts[i]->index) + break; + } + + /* + * Initialize the timeout parameters + */ + InitTimeout(tn, start_time, fin_time); + + insert_timeout(tn, i); + + if (i > 0) + return true; + + /* If we reach here, okay to set the timer interrupt */ + if (!schedule_timeout(start_time)) + return false; + return true; + } + + /* + * Cancel the SIGALRM timer for the specific timeout. + * If a timeout is canceled, any other active timeout remains in force. + * + * Returns TRUE if okay, FALSE on failure to set the timer for + * the next timeout source. + */ + bool + disable_timeout(TimeoutName tn, bool keep_indicator) + { + int i; + + /* + * Always disable the interrupt if it is active; this avoids being + * interrupted by the signal handler and thereby possibly getting + * confused. + * + * We will re-enable the interrupt if necessary in ->check_timeout(). + */ + if (n_timeouts > 0) + { + struct itimerval timeval; + + MemSet(&timeval, 0, sizeof(struct itimerval)); + if (setitimer(ITIMER_REAL, &timeval, NULL)) + { + n_timeouts = 0; + for (i = 0; i < TIMEOUT_MAX; i++) + DestroyTimeout(i, false); + + return false; + } + } + + /* Find the timeout and remove from the list. */ + i = find_active_timeout(tn); + remove_timeout_index(i); + + /* Do cleanup. */ + DestroyTimeout(tn, keep_indicator); + + /* + * If the first timeout was removed from the list and there is + * at least one active left, reschedule it. + */ + if (i == 0 && n_timeouts > 0) + if (!schedule_timeout(GetCurrentTimestamp())) + return false; + + return true; + } + + /* + * Disable SIGALRM and remove all timeouts from the list and + * reset the timeout indicators. + */ + bool + disable_all_timeouts(bool keep_indicators) + { + struct itimerval timeval; + int i; + bool ret; + + MemSet(&timeval, 0, sizeof(struct itimerval)); + ret = (setitimer(ITIMER_REAL, &timeval, NULL) == 0); + + n_timeouts = 0; + for (i = 0; i < TIMEOUT_MAX; i++) + DestroyTimeout(i, keep_indicators); + + return ret; + } + + /* + * Return the timeout indicator + */ + bool + get_timeout_indicator(TimeoutName tn) + { + return base_timeouts[tn].indicator; + } + + /* + * Return the start of the timer for this timeout + */ + TimestampTz + get_timeout_start(TimeoutName tn) + { + return base_timeouts[tn].start_time; + } + + /* + * Signal handler for SIGALRM + * + * Process the check for the currently active timeout source and + * reschedule the next as needed. To avoid various edge cases, + * we must be careful to do nothing when there is nothing to be done. + */ + void + handle_sig_alarm(SIGNAL_ARGS) + { + int save_errno = errno; + + /* + * SIGALRM is cause for waking anything waiting on the process latch. + * Recovery (the startup process) doesn't have MyProc set so + * it can also use this signal handler. + */ + if (MyProc) + SetLatch(&MyProc->procLatch); + + if (n_timeouts > 0) + { + TimestampTz now; + bool reschedule; + + now = GetCurrentTimestamp(); + + /* If our time has not come yet, do nothing. */ + if (now < timeouts[0]->fin_time) + goto out; + + /* + * Set the trigger indicator before calling the checker function. + * Setting if after may have side effects that lead to detecting + * a different event, like "user pressed Ctrl-C" is detected instead + * of the statement timeout having triggered. + */ + timeouts[0]->indicator = true; + + /* Call the timeout checker. */ + timeouts[0]->timeout_check(); + + /* + * Remove the current timeout source and reschedule + * the next if needed. Short circuit disable_timeout(..., true) + * for the timeout source that just triggered. + */ + reschedule = timeouts[0]->resched_next; + remove_timeout_index(0); + + if (reschedule) + schedule_timeout(now); + else + disable_all_timeouts(true); + } + + out: + errno = save_errno; + } diff -dcrpN postgresql/src/backend/tcop/postgres.c postgresql.1/src/backend/tcop/postgres.c *** postgresql/src/backend/tcop/postgres.c 2012-06-11 06:22:48.157921579 +0200 --- postgresql.1/src/backend/tcop/postgres.c 2012-06-19 17:49:55.358479113 +0200 *************** *** 64,69 **** --- 64,70 ---- #include "storage/proc.h" #include "storage/procsignal.h" #include "storage/sinval.h" + #include "storage/timeout.h" #include "tcop/fastpath.h" #include "tcop/pquery.h" #include "tcop/tcopprot.h" *************** start_xact_command(void) *** 2396,2404 **** /* Set statement timeout running, if any */ /* NB: this mustn't be enabled until we are within an xact */ if (StatementTimeout > 0) ! enable_sig_alarm(StatementTimeout, true); else ! cancel_from_timeout = false; xact_started = true; } --- 2397,2405 ---- /* Set statement timeout running, if any */ /* NB: this mustn't be enabled until we are within an xact */ if (StatementTimeout > 0) ! enable_timeout(STATEMENT_TIMEOUT, StatementTimeout); else ! disable_timeout(STATEMENT_TIMEOUT, false); xact_started = true; } *************** finish_xact_command(void) *** 2410,2416 **** if (xact_started) { /* Cancel any active statement timeout before committing */ ! disable_sig_alarm(true); /* Now commit the command */ ereport(DEBUG3, --- 2411,2417 ---- if (xact_started) { /* Cancel any active statement timeout before committing */ ! disable_all_timeouts(false); /* Now commit the command */ ereport(DEBUG3, *************** ProcessInterrupts(void) *** 2891,2897 **** (errcode(ERRCODE_QUERY_CANCELED), errmsg("canceling authentication due to timeout"))); } ! if (cancel_from_timeout) { ImmediateInterruptOK = false; /* not idle anymore */ DisableNotifyInterrupt(); --- 2892,2898 ---- (errcode(ERRCODE_QUERY_CANCELED), errmsg("canceling authentication due to timeout"))); } ! if (get_timeout_indicator(STATEMENT_TIMEOUT)) { ImmediateInterruptOK = false; /* not idle anymore */ DisableNotifyInterrupt(); *************** PostgresMain(int argc, char *argv[], con *** 3799,3808 **** /* * Forget any pending QueryCancel request, since we're returning to ! * the idle loop anyway, and cancel the statement timer if running. */ QueryCancelPending = false; ! disable_sig_alarm(true); QueryCancelPending = false; /* again in case timeout occurred */ /* --- 3800,3809 ---- /* * Forget any pending QueryCancel request, since we're returning to ! * the idle loop anyway, and cancel the timer if running. */ QueryCancelPending = false; ! disable_all_timeouts(false); QueryCancelPending = false; /* again in case timeout occurred */ /* diff -dcrpN postgresql/src/backend/utils/init/postinit.c postgresql.1/src/backend/utils/init/postinit.c *** postgresql/src/backend/utils/init/postinit.c 2012-04-16 19:57:22.490916093 +0200 --- postgresql.1/src/backend/utils/init/postinit.c 2012-06-19 17:49:55.360479125 +0200 *************** *** 41,51 **** #include "storage/fd.h" #include "storage/ipc.h" #include "storage/lmgr.h" - #include "storage/proc.h" #include "storage/procarray.h" #include "storage/procsignal.h" #include "storage/sinvaladt.h" #include "storage/smgr.h" #include "tcop/tcopprot.h" #include "utils/acl.h" #include "utils/fmgroids.h" --- 41,51 ---- #include "storage/fd.h" #include "storage/ipc.h" #include "storage/lmgr.h" #include "storage/procarray.h" #include "storage/procsignal.h" #include "storage/sinvaladt.h" #include "storage/smgr.h" + #include "storage/timeout.h" #include "tcop/tcopprot.h" #include "utils/acl.h" #include "utils/fmgroids.h" *************** PerformAuthentication(Port *port) *** 204,210 **** * during authentication. Since we're inside a transaction and might do * database access, we have to use the statement_timeout infrastructure. */ ! if (!enable_sig_alarm(AuthenticationTimeout * 1000, true)) elog(FATAL, "could not set timer for authorization timeout"); /* --- 204,210 ---- * during authentication. Since we're inside a transaction and might do * database access, we have to use the statement_timeout infrastructure. */ ! if (!enable_timeout(STATEMENT_TIMEOUT, AuthenticationTimeout * 1000)) elog(FATAL, "could not set timer for authorization timeout"); /* *************** PerformAuthentication(Port *port) *** 215,221 **** /* * Done with authentication. Disable the timeout, and log if needed. */ ! if (!disable_sig_alarm(true)) elog(FATAL, "could not disable timer for authorization timeout"); if (Log_connections) --- 215,221 ---- /* * Done with authentication. Disable the timeout, and log if needed. */ ! if (!disable_timeout(STATEMENT_TIMEOUT, false)) elog(FATAL, "could not disable timer for authorization timeout"); if (Log_connections) diff -dcrpN postgresql/src/backend/utils/misc/guc.c postgresql.1/src/backend/utils/misc/guc.c *** postgresql/src/backend/utils/misc/guc.c 2012-06-11 06:22:48.193921753 +0200 --- postgresql.1/src/backend/utils/misc/guc.c 2012-06-19 17:49:55.364479146 +0200 *************** *** 63,68 **** --- 63,69 ---- #include "storage/standby.h" #include "storage/fd.h" #include "storage/predicate.h" + #include "storage/timeout.h" #include "tcop/tcopprot.h" #include "tsearch/ts_cache.h" #include "utils/builtins.h" diff -dcrpN postgresql/src/include/storage/proc.h postgresql.1/src/include/storage/proc.h *** postgresql/src/include/storage/proc.h 2012-06-11 06:22:48.223921899 +0200 --- postgresql.1/src/include/storage/proc.h 2012-06-19 17:49:55.365479152 +0200 *************** extern PGPROC *PreparedXactProcs; *** 218,230 **** /* configurable options */ - extern int DeadlockTimeout; - extern int StatementTimeout; extern bool log_lock_waits; - extern volatile bool cancel_from_timeout; - - /* * Function Prototypes */ --- 218,225 ---- *************** extern void LockErrorCleanup(void); *** 252,264 **** extern void ProcWaitForSignal(void); extern void ProcSendSignal(int pid); - 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); - - extern bool enable_standby_sig_alarm(TimestampTz now, - TimestampTz fin_time, bool deadlock_only); - extern bool disable_standby_sig_alarm(void); - extern void handle_standby_sig_alarm(SIGNAL_ARGS); - #endif /* PROC_H */ --- 247,250 ---- diff -dcrpN postgresql/src/include/storage/proctimeout.h postgresql.1/src/include/storage/proctimeout.h *** postgresql/src/include/storage/proctimeout.h 1970-01-01 01:00:00.000000000 +0100 --- postgresql.1/src/include/storage/proctimeout.h 2012-06-19 18:32:03.951063303 +0200 *************** *** 0 **** --- 1,22 ---- + /*------------------------------------------------------------------------- + * + * proctimeout.h + * declaration of timeout checker functions + * + * + * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/include/storage/proctimeout.h + * + *------------------------------------------------------------------------- + */ + #ifndef _PROCTIMEOUT_H_ + #define _PROCTIMEOUT_H_ + + extern void CheckDeadLock(void); + extern void CheckStatementTimeout(void); + extern void CheckStandbyDeadLock(void); + extern void CheckStandbyTimeout(void); + + #endif /* _PROCTIMEOUT_H_ */ diff -dcrpN postgresql/src/include/storage/timeout.h postgresql.1/src/include/storage/timeout.h *** postgresql/src/include/storage/timeout.h 1970-01-01 01:00:00.000000000 +0100 --- postgresql.1/src/include/storage/timeout.h 2012-06-19 17:49:55.365479152 +0200 *************** *** 0 **** --- 1,38 ---- + /*------------------------------------------------------------------------- + * + * timeout.h + * SIGALRM timeout API + * + * + * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/include/storage/timeout.h + * + *------------------------------------------------------------------------- + */ + #ifndef _TIMEOUT_H_ + #define _TIMEOUT_H_ + + #include "datatype/timestamp.h" + + /* configurable options */ + extern int DeadlockTimeout; + extern int StatementTimeout; + + typedef enum TimeoutName { + DEADLOCK_TIMEOUT, + STATEMENT_TIMEOUT, + STANDBY_DEADLOCK_TIMEOUT, + STANDBY_TIMEOUT, + TIMEOUT_MAX + } TimeoutName; + + extern bool enable_timeout(TimeoutName tn, int delayms); + extern bool disable_timeout(TimeoutName tn, bool keep_indicator); + extern bool disable_all_timeouts(bool keep_indicators); + extern bool get_timeout_indicator(TimeoutName tn); + extern TimestampTz get_timeout_start(TimeoutName tn); + extern void handle_sig_alarm(SIGNAL_ARGS); + + #endif /* _TIMEOUT_H_ */