diff -dcrpN postgresql/src/backend/postmaster/autovacuum.c postgresql.1/src/backend/postmaster/autovacuum.c *** postgresql/src/backend/postmaster/autovacuum.c 2012-06-26 09:10:21.275759379 +0200 --- postgresql.1/src/backend/postmaster/autovacuum.c 2012-06-27 08:34:13.982510939 +0200 *************** *** 89,94 **** --- 89,95 ---- #include "storage/proc.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 */ --- 485,491 ---- /* 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-26 09:10:21.277759395 +0200 --- postgresql.1/src/backend/postmaster/postmaster.c 2012-06-27 08:34:13.985510958 +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) *** 3437,3442 **** --- 3437,3450 ---- PG_SETMASK(&StartupBlockSig); /* + * Initialize timeout sources and register authentication timeout early. + */ + init_timeouts(); + register_timeout(AUTHENTICATION_TIMEOUT, false, + DummyTimeoutFunction, + GetCurrentTimestamp); + + /* * Get the remote host name and port for logging and status display. */ remote_host[0] = '\0'; *************** BackendInitialize(Port *port) *** 3488,3495 **** * 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"); /* * Receive the startup packet (which might turn out to be a cancel request --- 3496,3502 ---- * indefinitely. PreAuthDelay and any DNS interactions above don't count * against the time limit. */ ! enable_timeout(AUTHENTICATION_TIMEOUT, AuthenticationTimeout * 1000); /* * Receive the startup packet (which might turn out to be a cancel request *************** BackendInitialize(Port *port) *** 3526,3533 **** /* * 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); } --- 3533,3539 ---- /* * Disable the timeout, and prevent SIGTERM/SIGQUIT again. */ ! disable_timeout(AUTHENTICATION_TIMEOUT, false); 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-27 10:22:43.730344717 +0200 *************** *** 28,34 **** --- 28,36 ---- #include "storage/latch.h" #include "storage/pmsignal.h" #include "storage/proc.h" + #include "storage/timeout.h" #include "utils/guc.h" + #include "utils/timestamp.h" /* *************** StartupProcessMain(void) *** 195,201 **** 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); --- 197,203 ---- 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); *************** StartupProcessMain(void) *** 204,209 **** --- 206,222 ---- pqsignal(SIGUSR2, StartupProcTriggerHandler); /* + * Initialize timeouts and register timeouts needed for standby. + */ + init_timeouts(); + register_timeout(STANDBY_DEADLOCK_TIMEOUT, true, + StandbyDeadLockFunction, + GetCurrentTimestamp); + register_timeout(STANDBY_TIMEOUT, false, + StandbyTimeoutFunction, + GetCurrentTimestamp); + + /* * Reset some signals that are accepted by postmaster but not here */ pqsignal(SIGCHLD, SIG_DFL); 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-26 09:10:21.280759421 +0200 --- postgresql.1/src/backend/storage/ipc/standby.c 2012-06-27 08:34:13.986510964 +0200 *************** *** 27,32 **** --- 27,33 ---- #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; --- 395,400 ---- *************** ResolveRecoveryConflictWithBufferPin(voi *** 409,418 **** * 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) { --- 409,415 ---- * We're willing to wait forever for conflicts, so set timeout for * deadlock check (only) */ ! enable_timeout(STANDBY_DEADLOCK_TIMEOUT, DeadlockTimeout); } 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,445 ---- } 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 */ ! enable_timeout(STANDBY_DEADLOCK_TIMEOUT, DeadlockTimeout); ! ! TimestampDifference(now, ltime, &secs, &usecs); ! msecs = secs * 1000 + usecs / 1000; ! ! enable_timeout(STANDBY_TIMEOUT, msecs); } /* Wait to be signaled by UnpinBuffer() */ ProcWaitForSignal(); ! disable_all_timeouts(false); } 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-27 08:34:13.987510970 +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-26 09:10:21.282759438 +0200 --- postgresql.1/src/backend/storage/lmgr/proc.c 2012-06-27 09:21:15.411010772 +0200 *************** *** 48,53 **** --- 48,54 ---- #include "storage/procarray.h" #include "storage/procsignal.h" #include "storage/spin.h" + #include "storage/timeout.h" #include "utils/timestamp.h" *************** 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. --- 78,89 ---- /* 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) *** 653,659 **** 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 *** 1048,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"); /* * If someone wakes us between LWLockRelease and PGSemaphoreLock, --- 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. */ ! enable_timeout(DEADLOCK_TIMEOUT, DeadlockTimeout); /* * If someone wakes us between LWLockRelease and PGSemaphoreLock, *************** ProcSleep(LOCALLOCK *locallock, LockMeth *** 1138,1144 **** 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; --- 1123,1130 ---- 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 *** 1200,1207 **** /* * Disable the timer, if it's still running */ ! if (!disable_sig_alarm(false)) ! elog(FATAL, "could not disable timer for process wakeup"); /* * Re-acquire the lock table's partition lock. We have to do this to hold --- 1186,1192 ---- /* * Disable the timer, if it's still running */ ! disable_timeout(DEADLOCK_TIMEOUT, false); /* * Re-acquire the lock table's partition lock. We have to do this to hold *************** ProcLockWakeup(LockMethod lockMethodTabl *** 1344,1350 **** * 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; --- 1329,1335 ---- * 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) *** 1499,1816 **** 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; } /* --- 1484,1512 ---- PGSemaphoreUnlock(&proc->sem); } /* * Check for statement timeout. If the timeout time has come, ! * trigger a query-cancel interrupt. */ ! void ! StatementTimeoutFunction(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 ! StandbyDeadLockFunction(void) { ! SendRecoveryConflictWithBufferPin(PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK); } /* *************** disable_standby_sig_alarm(void) *** 1818,1898 **** * 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; } --- 1514,1521 ---- * SIGALRM handler. Timers will only be set when InHotStandby. * We simply ignore any signals unless the timer has been set. */ void ! StandbyTimeoutFunction(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-27 09:55:21.784879822 +0200 *************** *** 0 **** --- 1,448 ---- + /*------------------------------------------------------------------------- + * + * 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/timeout.h" + #include "utils/timestamp.h" + + /* + * 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_func)(void); + typedef TimestampTz (*timeout_start)(void); + + typedef struct timeout_params + { + TimeoutName index; + bool resched_next; + + /* volatile because it may be changed from the signal handler */ + volatile bool indicator; + + timeout_func timeout_func; + 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. InitializeBackend() and PostgresMain() initialize + * this array and register timeout sources. + */ + static bool base_timeouts_initialized = false; + static timeout_params base_timeouts[TIMEOUT_MAX]; + + /* + * 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 || index > n_timeouts) + elog(FATAL, "timeout index %d out of range 0..%d", index, n_timeouts); + + 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 || index > n_timeouts) + elog(FATAL, "timeout index %d out of range 0..%d", index, n_timeouts); + + 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 void + schedule_timeout(TimestampTz now) + { + long secs; + int usecs; + struct itimerval timeval; + + /* There is no active timeout, do nothing. */ + if (n_timeouts == 0) + return; + + 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) != 0) + elog(FATAL, "could not enable timer"); + } + + /***************************************************************************** + * Init, Destroy and callback functions for different timeouts. + *****************************************************************************/ + + /* + * 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; + } + + /* + * CheckDummyTimeout + * + * Do nothing. Usable for timeout sources that don't need + * extra processing, like authentication_timeout. + * The timeout indicator is set by the signal handler. + */ + void + DummyTimeoutFunction(void) + { + return; + } + + /***************************************************************************** + * Public API + *****************************************************************************/ + + /* + * Initialize + */ + void + init_timeouts(void) + { + int i; + + if (base_timeouts_initialized) + return; + + for (i = 0; i < TIMEOUT_MAX; i++) + { + base_timeouts[i].index = i; + base_timeouts[i].resched_next = false; + base_timeouts[i].indicator = false; + base_timeouts[i].timeout_func = NULL; + base_timeouts[i].timeout_start = NULL; + base_timeouts[i].start_time = 0; + base_timeouts[i].fin_time = 0; + } + + base_timeouts_initialized = true; + } + + /* + * Register a timeout source + */ + int + register_timeout(TimeoutName tn, bool resched_next, + timeout_func func, timeout_start start) + { + Assert(base_timeouts_initialized); + Assert(base_timeouts[tn].timeout_func == NULL); + + if (tn < 0) + { + for (tn = USER_TIMEOUT; tn < TIMEOUT_MAX; tn++) + if (base_timeouts[tn].timeout_func == 0) + break; + if (tn == TIMEOUT_MAX) + elog(FATAL, "Cannot add more timeout source"); + } + + base_timeouts[tn].resched_next = resched_next; + base_timeouts[tn].timeout_func = func; + base_timeouts[tn].timeout_start = start; + + return tn; + } + + /* + * 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. + */ + void + enable_timeout(TimeoutName tn, int delayms) + { + TimestampTz start_time; + TimestampTz fin_time; + int i; + + Assert(base_timeouts_initialized); + Assert(base_timeouts[tn].timeout_func != NULL); + Assert(!is_timeout_active(tn)); + + if (delayms <= 0) + return; + + 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 the timeout was inserted in the first position, set the timer. + */ + if (i == 0) + schedule_timeout(start_time); + } + + /* + * Cancel the SIGALRM timer for the specific timeout. + * If a timeout is canceled, any other active timeout remains in force. + * + * It's not an error to disable a timeout that is not enabled. + */ + void + disable_timeout(TimeoutName tn, bool keep_indicator) + { + int i; + + /* + * Always disable the timer 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 later in this function. + */ + if (n_timeouts > 0) + { + struct itimerval timeval; + + MemSet(&timeval, 0, sizeof(struct itimerval)); + if (setitimer(ITIMER_REAL, &timeval, NULL) != 0) + elog(FATAL, "could not disable timer"); + } + + /* Find the timeout and remove from the list. */ + i = find_active_timeout(tn); + if (i < 0) + return; + 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) + schedule_timeout(GetCurrentTimestamp()); + } + + /* + * Disable SIGALRM and remove all timeouts from the list and + * reset the timeout indicators. + */ + void + disable_all_timeouts(bool keep_indicators) + { + struct itimerval timeval; + int i; + + MemSet(&timeval, 0, sizeof(struct itimerval)); + if (setitimer(ITIMER_REAL, &timeval, NULL) != 0) + elog(FATAL, "could not disable timer"); + + n_timeouts = 0; + for (i = 0; i < TIMEOUT_MAX; i++) + DestroyTimeout(i, keep_indicators); + } + + /* + * 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. + * Cope with MyProc not being there, as the startup process also uses + * 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_func(); + + /* + * 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-26 09:10:21.283759446 +0200 --- postgresql.1/src/backend/tcop/postgres.c 2012-06-27 10:01:22.219982497 +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 *** 3617,3622 **** --- 3618,3634 ---- pqsignal(SIGALRM, handle_sig_alarm); /* timeout conditions */ /* + * Register timeout sources needed by backend operation. + */ + init_timeouts(); + register_timeout(DEADLOCK_TIMEOUT, true, + CheckDeadLock, + GetCurrentTimestamp); + register_timeout(STATEMENT_TIMEOUT, false, + StatementTimeoutFunction, + GetCurrentStatementStartTimestamp); + + /* * Ignore failure to write to frontend. Note: if frontend closes * connection, we will notice it and exit cleanly when control next * returns to outer loop. This seems safer than forcing exit in the *************** PostgresMain(int argc, char *argv[], con *** 3802,3811 **** /* * 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 */ /* --- 3814,3823 ---- /* * 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-06-26 09:10:21.287759479 +0200 --- postgresql.1/src/backend/utils/init/postinit.c 2012-06-27 08:34:13.992511002 +0200 *************** *** 41,52 **** #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/proc.h" #include "storage/sinvaladt.h" #include "storage/smgr.h" #include "tcop/tcopprot.h" #include "utils/acl.h" #include "utils/fmgroids.h" --- 41,52 ---- #include "storage/fd.h" #include "storage/ipc.h" #include "storage/lmgr.h" #include "storage/procarray.h" #include "storage/procsignal.h" #include "storage/proc.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) *** 205,212 **** * 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"); /* * Now perform authentication exchange. --- 205,211 ---- * during authentication. Since we're inside a transaction and might do * database access, we have to use the statement_timeout infrastructure. */ ! enable_timeout(STATEMENT_TIMEOUT, AuthenticationTimeout * 1000); /* * Now perform authentication exchange. *************** PerformAuthentication(Port *port) *** 216,223 **** /* * 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. */ ! disable_timeout(STATEMENT_TIMEOUT, false); if (Log_connections) { diff -dcrpN postgresql/src/include/storage/proc.h postgresql.1/src/include/storage/proc.h *** postgresql/src/include/storage/proc.h 2012-06-26 09:10:21.316759721 +0200 --- postgresql.1/src/include/storage/proc.h 2012-06-27 09:21:57.139257370 +0200 *************** extern int DeadlockTimeout; *** 222,230 **** extern int StatementTimeout; extern bool log_lock_waits; - extern volatile bool cancel_from_timeout; - - /* * Function Prototypes */ --- 222,227 ---- *************** 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 */ --- 249,258 ---- extern void ProcWaitForSignal(void); extern void ProcSendSignal(int pid); ! /* Timeout functions exported for register_timeout() */ ! extern void CheckDeadLock(void); ! extern void StatementTimeoutFunction(void); ! extern void StandbyDeadLockFunction(void); ! extern void StandbyTimeoutFunction(void); #endif /* PROC_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-27 09:49:16.789761575 +0200 *************** *** 0 **** --- 1,46 ---- + /*------------------------------------------------------------------------- + * + * 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" + + typedef enum TimeoutName { + AUTHENTICATION_TIMEOUT, + DEADLOCK_TIMEOUT, + STATEMENT_TIMEOUT, + STANDBY_DEADLOCK_TIMEOUT, + STANDBY_TIMEOUT, + /* First user timeout source */ + USER_TIMEOUT, + /* Maximum number of timeout sources */ + TIMEOUT_MAX = 16 + } TimeoutName; + + typedef void (*timeout_func)(void); + typedef TimestampTz (*timeout_start)(void); + + extern void init_timeouts(void); + extern int register_timeout(TimeoutName tn, bool resched_next, + timeout_func func, timeout_start start); + extern void enable_timeout(TimeoutName tn, int delayms); + extern void disable_timeout(TimeoutName tn, bool keep_indicator); + extern void 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); + + extern void DummyTimeoutFunction(void); + + #endif /* _TIMEOUT_H_ */