#include "utils/rel.h"
#include "utils/snapmgr.h"
#include "utils/syscache.h"
+#include "utils/timeout.h"
#include "utils/timestamp.h"
#include "utils/tqual.h"
pqsignal(SIGTERM, avl_sigterm_handler);
pqsignal(SIGQUIT, quickdie);
- pqsignal(SIGALRM, handle_sig_alarm);
+ InitializeTimeouts(); /* establishes SIGALRM handler */
pqsignal(SIGPIPE, SIG_IGN);
pqsignal(SIGUSR1, procsignal_sigusr1_handler);
/* Prevents interrupts while cleaning up */
HOLD_INTERRUPTS();
- /* Forget any pending QueryCancel request */
+ /* Forget any pending QueryCancel or timeout request */
QueryCancelPending = false;
- disable_sig_alarm(true);
+ disable_all_timeouts(false);
QueryCancelPending = false; /* again in case timeout occurred */
/* Report the error to the server log */
pqsignal(SIGINT, StatementCancelHandler);
pqsignal(SIGTERM, die);
pqsignal(SIGQUIT, quickdie);
- pqsignal(SIGALRM, handle_sig_alarm);
+ InitializeTimeouts(); /* establishes SIGALRM handler */
pqsignal(SIGPIPE, SIG_IGN);
pqsignal(SIGUSR1, procsignal_sigusr1_handler);
#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"
#include "utils/memutils.h"
#include "utils/ps_status.h"
+#include "utils/timeout.h"
#ifdef EXEC_BACKEND
#include "storage/spin.h"
static void sigusr1_handler(SIGNAL_ARGS);
static void startup_die(SIGNAL_ARGS);
static void dummy_handler(SIGNAL_ARGS);
+static void StartupPacketTimeoutHandler(void);
static void CleanupBackend(int pid, int exitstatus);
static void HandleChildCrash(int pid, int exitstatus, const char *procname);
static void LogChildExit(int lev, const char *procname,
*/
pqsignal(SIGTERM, startup_die);
pqsignal(SIGQUIT, startup_die);
- pqsignal(SIGALRM, startup_die);
+ InitializeTimeouts(); /* establishes SIGALRM handler */
PG_SETMASK(&StartupBlockSig);
/*
* 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.
+ *
+ * Note: AuthenticationTimeout is applied here while waiting for the
+ * startup packet, and then again in InitPostgres for the duration of any
+ * authentication operations. So a hostile client could tie up the
+ * process for nearly twice AuthenticationTimeout before we kick him off.
+ *
+ * Note: because PostgresMain will call InitializeTimeouts again, the
+ * registration of STARTUP_PACKET_TIMEOUT will be lost. This is okay
+ * since we never use it again after this function.
*/
- if (!enable_sig_alarm(AuthenticationTimeout * 1000, false))
- elog(FATAL, "could not set timer for startup packet timeout");
+ RegisterTimeout(STARTUP_PACKET_TIMEOUT, StartupPacketTimeoutHandler);
+ enable_timeout_after(STARTUP_PACKET_TIMEOUT, AuthenticationTimeout * 1000);
/*
* Receive the startup packet (which might turn out to be a cancel request
/*
* Disable the timeout, and prevent SIGTERM/SIGQUIT again.
*/
- if (!disable_sig_alarm(false))
- elog(FATAL, "could not disable timer for startup packet timeout");
+ disable_timeout(STARTUP_PACKET_TIMEOUT, false);
PG_SETMASK(&BlockSig);
}
}
/*
- * Timeout or shutdown signal from postmaster while processing startup packet.
- * Cleanup and exit(1).
+ * SIGTERM or SIGQUIT while processing startup packet.
+ * Clean up and exit(1).
*
* XXX: possible future improvement: try to send a message indicating
* why we are disconnecting. Problem is to be sure we don't block while
{
}
+/*
+ * Timeout while processing startup packet.
+ * As for startup_die(), we clean up and exit(1).
+ */
+static void
+StartupPacketTimeoutHandler(void)
+{
+ proc_exit(1);
+}
+
+
/*
* RandomSalt
*/
#include "storage/ipc.h"
#include "storage/latch.h"
#include "storage/pmsignal.h"
-#include "storage/proc.h"
+#include "storage/standby.h"
#include "utils/guc.h"
+#include "utils/timeout.h"
/*
/*
* Properly accept or ignore signals the postmaster might send us.
- *
- * Note: ideally we'd not enable handle_standby_sig_alarm unless actually
- * doing hot standby, but we don't know that yet. Rely on it to not do
- * anything if it shouldn't.
*/
pqsignal(SIGHUP, StartupProcSigHupHandler); /* reload config file */
pqsignal(SIGINT, SIG_IGN); /* ignore query cancel */
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);
+ InitializeTimeouts(); /* establishes SIGALRM handler */
pqsignal(SIGPIPE, SIG_IGN);
pqsignal(SIGUSR1, StartupProcSigUsr1Handler);
pqsignal(SIGUSR2, StartupProcTriggerHandler);
pqsignal(SIGCONT, SIG_DFL);
pqsignal(SIGWINCH, SIG_DFL);
+ /*
+ * Register timeouts needed for standby mode
+ */
+ RegisterTimeout(STANDBY_DEADLOCK_TIMEOUT, StandbyDeadLockHandler);
+ RegisterTimeout(STANDBY_TIMEOUT, StandbyTimeoutHandler);
+
/*
* Unblock signals (they were blocked when the postmaster forked us)
*/
PG_SETMASK(&UnBlockSig);
+ /*
+ * Do what we came for.
+ */
StartupXLOG();
/*
#include "utils/memutils.h"
#include "utils/ps_status.h"
#include "utils/resowner.h"
+#include "utils/timeout.h"
#include "utils/timestamp.h"
pqsignal(SIGINT, SIG_IGN); /* not used */
pqsignal(SIGTERM, WalSndShutdownHandler); /* request shutdown */
pqsignal(SIGQUIT, WalSndQuickDieHandler); /* hard crash time */
- pqsignal(SIGALRM, SIG_IGN);
+ InitializeTimeouts(); /* establishes SIGALRM handler */
pqsignal(SIGPIPE, SIG_IGN);
pqsignal(SIGUSR1, WalSndXLogSendHandler); /* request WAL sending */
pqsignal(SIGUSR2, WalSndLastCycleHandler); /* request a last cycle and
#include "storage/sinvaladt.h"
#include "storage/standby.h"
#include "utils/ps_status.h"
+#include "utils/timeout.h"
#include "utils/timestamp.h"
/* User-settable GUC parameters */
static void ResolveRecoveryConflictWithVirtualXIDs(VirtualTransactionId *waitlist,
ProcSignalReason reason);
static void ResolveRecoveryConflictWithLock(Oid dbOid, Oid relOid);
+static void SendRecoveryConflictWithBufferPin(ProcSignalReason reason);
static void LogCurrentRunningXacts(RunningTransactions CurrRunningXacts);
static void LogAccessExclusiveLocks(int nlocks, xl_standby_lock *locks);
* ResolveRecoveryConflictWithBufferPin is called from LockBufferForCleanup()
* to resolve conflicts with other backends holding buffer pins.
*
- * We either resolve conflicts immediately or set a SIGALRM to wake us at
- * the limit of our patience. The sleep in LockBufferForCleanup() is
- * performed here, for code clarity.
+ * The ProcWaitForSignal() sleep normally done in LockBufferForCleanup()
+ * (when not InHotStandby) is performed here, for code clarity.
+ *
+ * We either resolve conflicts immediately or set a timeout to wake us at
+ * the limit of our patience.
*
* Resolve conflicts by sending a PROCSIG signal to all backends to check if
* they hold one of the buffer pins that is blocking Startup process. If so,
- * backends will take an appropriate error action, ERROR or FATAL.
+ * those backends will take an appropriate error action, ERROR or FATAL.
*
* We also must check for deadlocks. Deadlocks occur because if queries
* wait on a lock, that must be behind an AccessExclusiveLock, which can only
*
* Deadlocks are extremely rare, and relatively expensive to check for,
* so we don't do a deadlock check right away ... only if we have had to wait
- * at least deadlock_timeout. Most of the logic about that is in proc.c.
+ * at least deadlock_timeout.
*/
void
ResolveRecoveryConflictWithBufferPin(void)
{
- bool sig_alarm_enabled = false;
TimestampTz ltime;
- TimestampTz now;
Assert(InHotStandby);
ltime = GetStandbyLimitTime();
- now = GetCurrentTimestamp();
- if (!ltime)
+ if (ltime == 0)
{
/*
* We're willing to wait forever for conflicts, so set timeout for
- * deadlock check (only)
+ * 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");
+ enable_timeout_after(STANDBY_DEADLOCK_TIMEOUT, DeadlockTimeout);
}
- else if (now >= ltime)
+ else if (GetCurrentTimestamp() >= ltime)
{
/*
* We're already behind, so clear a path as quickly as possible.
* 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");
+ enable_timeout_after(STANDBY_DEADLOCK_TIMEOUT, DeadlockTimeout);
+ enable_timeout_at(STANDBY_TIMEOUT, ltime);
}
/* 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");
- }
+ /*
+ * Clear any timeout requests established above. We assume here that
+ * the Startup process doesn't have any other timeouts than what this
+ * function uses. If that stops being true, we could cancel the
+ * timeouts individually, but that'd be slower.
+ */
+ disable_all_timeouts(false);
}
-void
+static void
SendRecoveryConflictWithBufferPin(ProcSignalReason reason)
{
Assert(reason == PROCSIG_RECOVERY_CONFLICT_BUFFERPIN ||
errdetail("User transaction caused buffer deadlock with recovery.")));
}
+
+/* --------------------------------
+ * timeout handler routines
+ * --------------------------------
+ */
+
+/*
+ * StandbyDeadLockHandler() will be called if STANDBY_DEADLOCK_TIMEOUT
+ * occurs before STANDBY_TIMEOUT. Send out a request for hot-standby
+ * backends to check themselves for deadlocks.
+ */
+void
+StandbyDeadLockHandler(void)
+{
+ SendRecoveryConflictWithBufferPin(PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK);
+}
+
+/*
+ * StandbyTimeoutHandler() will be called if STANDBY_TIMEOUT is exceeded.
+ * Send out a request to release conflicting buffer pins unconditionally,
+ * so we can press ahead with applying changes in recovery.
+ */
+void
+StandbyTimeoutHandler(void)
+{
+ /* forget any pending STANDBY_DEADLOCK_TIMEOUT request */
+ disable_timeout(STANDBY_DEADLOCK_TIMEOUT, false);
+
+ SendRecoveryConflictWithBufferPin(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN);
+}
+
+
/*
* -----------------------------------------------------
* Locking in Recovery Mode
#include "storage/procarray.h"
#include "storage/procsignal.h"
#include "storage/spin.h"
+#include "utils/timeout.h"
#include "utils/timestamp.h"
/* 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;
+/* Mark this volatile because it can be changed by signal handler */
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);
/*
return;
/* Turn off the deadlock timer, if it's still running (see ProcSleep) */
- disable_sig_alarm(false);
+ disable_timeout(DEADLOCK_TIMEOUT, false);
/* Unlink myself from the wait queue, if on it (might not be anymore!) */
partitionLock = LockHashPartitionLock(lockAwaited->hashcode);
if (RecoveryInProgress() && !InRecovery)
CheckRecoveryConflictDeadlock();
- /* Reset deadlock_state before enabling the signal handler */
+ /* Reset deadlock_state before enabling the timeout handler */
deadlock_state = DS_NOT_YET_CHECKED;
/*
* 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");
+ enable_timeout_after(DEADLOCK_TIMEOUT, DeadlockTimeout);
/*
* If someone wakes us between LWLockRelease and PGSemaphoreLock,
* that we don't mind losing control to a cancel/die interrupt here. We
* don't, because we have no shared-state-change work to do after being
* granted the lock (the grantor did it all). We do have to worry about
- * updating the locallock table, but if we lose control to an error,
- * LockErrorCleanup will fix that up.
+ * canceling the deadlock timeout and updating the locallock table, but if
+ * we lose control to an error, LockErrorCleanup will fix that up.
*/
do
{
DescribeLockTag(&buf, &locallock->tag.lock);
modename = GetLockmodeName(locallock->tag.lock.locktag_lockmethodid,
lockmode);
- TimestampDifference(timeout_start_time, GetCurrentTimestamp(),
+ TimestampDifference(get_timeout_start_time(DEADLOCK_TIMEOUT),
+ GetCurrentTimestamp(),
&secs, &usecs);
msecs = secs * 1000 + usecs / 1000;
usecs = usecs % 1000;
/*
* Disable the timer, if it's still running
*/
- if (!disable_sig_alarm(false))
- elog(FATAL, "could not disable timer for process wakeup");
+ disable_timeout(DEADLOCK_TIMEOUT, false);
/*
* Re-acquire the lock table's partition lock. We have to do this to hold
/*
* CheckDeadLock
*
- * We only get to this routine if we got SIGALRM after DeadlockTimeout
+ * We only get to this routine if the DEADLOCK_TIMEOUT fired
* while waiting for a lock to be released by some other process. Look
* to see if there's a deadlock; if not, just return and continue waiting.
* (But signal ProcSleep to log a message, if log_lock_waits is true.)
* NB: this is run inside a signal handler, so be very wary about what is done
* here or in called routines.
*/
-static void
+void
CheckDeadLock(void)
{
int i;
if (proc != NULL)
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;
-}
-
-/*
- * CheckStandbyTimeout() runs unconditionally in the Startup process
- * 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;
-}
#include "utils/memutils.h"
#include "utils/ps_status.h"
#include "utils/snapmgr.h"
+#include "utils/timeout.h"
#include "utils/timestamp.h"
#include "mb/pg_wchar.h"
/* 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);
+ enable_timeout_after(STATEMENT_TIMEOUT, StatementTimeout);
else
- cancel_from_timeout = false;
+ disable_timeout(STATEMENT_TIMEOUT, false);
xact_started = true;
}
if (xact_started)
{
/* Cancel any active statement timeout before committing */
- disable_sig_alarm(true);
+ disable_timeout(STATEMENT_TIMEOUT, false);
/* Now commit the command */
ereport(DEBUG3,
(errcode(ERRCODE_QUERY_CANCELED),
errmsg("canceling authentication due to timeout")));
}
- if (cancel_from_timeout)
+ if (get_timeout_indicator(STATEMENT_TIMEOUT))
{
ImmediateInterruptOK = false; /* not idle anymore */
DisableNotifyInterrupt();
pqsignal(SIGQUIT, quickdie); /* hard crash time */
else
pqsignal(SIGQUIT, die); /* cancel current query and exit */
- pqsignal(SIGALRM, handle_sig_alarm); /* timeout conditions */
+ InitializeTimeouts(); /* establishes SIGALRM handler */
/*
* Ignore failure to write to frontend. Note: if frontend closes
/*
* Forget any pending QueryCancel request, since we're returning to
- * the idle loop anyway, and cancel the statement timer if running.
+ * the idle loop anyway, and cancel any active timeout requests.
*/
QueryCancelPending = false;
- disable_sig_alarm(true);
+ disable_all_timeouts(false);
QueryCancelPending = false; /* again in case timeout occurred */
/*
#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 "utils/ps_status.h"
#include "utils/snapmgr.h"
#include "utils/syscache.h"
+#include "utils/timeout.h"
#include "utils/tqual.h"
static void CheckMyDatabase(const char *name, bool am_superuser);
static void InitCommunication(void);
static void ShutdownPostgres(int code, Datum arg);
+static void StatementTimeoutHandler(void);
static bool ThereIsAtLeastOneRole(void);
static void process_startup_options(Port *port, bool am_superuser);
static void process_settings(Oid databaseid, Oid roleid);
* 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");
+ enable_timeout_after(STATEMENT_TIMEOUT, AuthenticationTimeout * 1000);
/*
* Now perform authentication exchange.
/*
* Done with authentication. Disable the timeout, and log if needed.
*/
- if (!disable_sig_alarm(true))
- elog(FATAL, "could not disable timer for authorization timeout");
+ disable_timeout(STATEMENT_TIMEOUT, false);
if (Log_connections)
{
/* Now that we have a BackendId, we can participate in ProcSignal */
ProcSignalInit(MyBackendId);
+ /*
+ * Also set up timeout handlers needed for backend operation. We need
+ * these in every case except bootstrap.
+ */
+ if (!bootstrap)
+ {
+ RegisterTimeout(DEADLOCK_TIMEOUT, CheckDeadLock);
+ RegisterTimeout(STATEMENT_TIMEOUT, StatementTimeoutHandler);
+ }
+
/*
* bufmgr needs another initialization call too
*/
}
+/*
+ * STATEMENT_TIMEOUT handler: trigger a query-cancel interrupt.
+ */
+static void
+StatementTimeoutHandler(void)
+{
+#ifdef HAVE_SETSID
+ /* try to signal whole process group */
+ kill(-MyProcPid, SIGINT);
+#endif
+ kill(MyProcPid, SIGINT);
+}
+
+
/*
* Returns true if at least one role is defined in this database cluster.
*/
override CPPFLAGS := -I. -I$(srcdir) $(CPPFLAGS)
-OBJS = guc.o help_config.o pg_rusage.o ps_status.o superuser.o tzparser.o \
- rbtree.o
+OBJS = guc.o help_config.o pg_rusage.o ps_status.o rbtree.o \
+ superuser.o timeout.o tzparser.o
# This location might depend on the installation directories. Therefore
# we can't subsitute it into pg_config.h.
--- /dev/null
+/*-------------------------------------------------------------------------
+ *
+ * timeout.c
+ * Routines to multiplex SIGALRM interrupts for multiple timeout reasons.
+ *
+ * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/backend/utils/misc/timeout.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include <sys/time.h>
+
+#include "libpq/pqsignal.h"
+#include "storage/proc.h"
+#include "utils/timeout.h"
+#include "utils/timestamp.h"
+
+
+/* Data about any one timeout reason */
+typedef struct timeout_params
+{
+ TimeoutId index; /* identifier of timeout reason */
+
+ /* volatile because it may be changed from the signal handler */
+ volatile bool indicator; /* true if timeout has occurred */
+
+ /* callback function for timeout, or NULL if timeout not registered */
+ timeout_handler timeout_handler;
+
+ TimestampTz start_time; /* time that timeout was last activated */
+ TimestampTz fin_time; /* if active, time it is due to fire */
+} timeout_params;
+
+/*
+ * List of possible timeout reasons in the order of enum TimeoutId.
+ */
+static timeout_params all_timeouts[MAX_TIMEOUTS];
+static bool all_timeouts_initialized = false;
+
+/*
+ * List of active timeouts ordered by their fin_time and priority.
+ * This list is subject to change by the interrupt handler, so it's volatile.
+ */
+static volatile int num_active_timeouts = 0;
+static timeout_params *volatile active_timeouts[MAX_TIMEOUTS];
+
+
+/*****************************************************************************
+ * Internal helper functions
+ *
+ * For all of these, it is caller's responsibility to protect them from
+ * interruption by the signal handler.
+ *****************************************************************************/
+
+/*
+ * Find the index of a given timeout reason in the active array.
+ * If it's not there, return -1.
+ */
+static int
+find_active_timeout(TimeoutId id)
+{
+ int i;
+
+ for (i = 0; i < num_active_timeouts; i++)
+ {
+ if (active_timeouts[i]->index == id)
+ return i;
+ }
+
+ return -1;
+}
+
+/*
+ * Insert specified timeout reason into the list of active timeouts
+ * at the given index.
+ */
+static void
+insert_timeout(TimeoutId id, int index)
+{
+ int i;
+
+ if (index < 0 || index > num_active_timeouts)
+ elog(FATAL, "timeout index %d out of range 0..%d", index,
+ num_active_timeouts);
+
+ for (i = num_active_timeouts - 1; i >= index; i--)
+ active_timeouts[i + 1] = active_timeouts[i];
+
+ active_timeouts[index] = &all_timeouts[id];
+
+ /* NB: this must be the last step, see comments in enable_timeout */
+ num_active_timeouts++;
+}
+
+/*
+ * Remove the index'th element from the timeout list.
+ */
+static void
+remove_timeout_index(int index)
+{
+ int i;
+
+ if (index < 0 || index >= num_active_timeouts)
+ elog(FATAL, "timeout index %d out of range 0..%d", index,
+ num_active_timeouts - 1);
+
+ for (i = index + 1; i < num_active_timeouts; i++)
+ active_timeouts[i - 1] = active_timeouts[i];
+
+ num_active_timeouts--;
+}
+
+/*
+ * Schedule alarm for the next active timeout, if any
+ *
+ * We assume the caller has obtained the current time, or a close-enough
+ * approximation.
+ */
+static void
+schedule_alarm(TimestampTz now)
+{
+ if (num_active_timeouts > 0)
+ {
+ struct itimerval timeval;
+ long secs;
+ int usecs;
+
+ MemSet(&timeval, 0, sizeof(struct itimerval));
+
+ /* Get the time remaining till the nearest pending timeout */
+ TimestampDifference(now, active_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;
+
+ timeval.it_value.tv_sec = secs;
+ timeval.it_value.tv_usec = usecs;
+
+ /* Set the alarm timer */
+ if (setitimer(ITIMER_REAL, &timeval, NULL) != 0)
+ elog(FATAL, "could not enable SIGALRM timer: %m");
+ }
+}
+
+
+/*****************************************************************************
+ * Signal handler
+ *****************************************************************************/
+
+/*
+ * Signal handler for SIGALRM
+ *
+ * Process any active timeout reasons and then reschedule the interrupt
+ * as needed.
+ */
+static void
+handle_sig_alarm(SIGNAL_ARGS)
+{
+ int save_errno = errno;
+
+ /*
+ * SIGALRM is always 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);
+
+ /*
+ * Fire any pending timeouts.
+ */
+ if (num_active_timeouts > 0)
+ {
+ TimestampTz now = GetCurrentTimestamp();
+
+ /* While the first pending timeout has been reached ... */
+ while (num_active_timeouts > 0 &&
+ now >= active_timeouts[0]->fin_time)
+ {
+ timeout_params *this_timeout = active_timeouts[0];
+
+ /* Remove it from the active list */
+ remove_timeout_index(0);
+
+ /* Mark it as fired */
+ this_timeout->indicator = true;
+
+ /* And call its handler function */
+ (*this_timeout->timeout_handler) ();
+
+ /*
+ * The handler might not take negligible time (CheckDeadLock for
+ * instance isn't too cheap), so let's update our idea of "now"
+ * after each one.
+ */
+ now = GetCurrentTimestamp();
+ }
+
+ /* Done firing timeouts, so reschedule next interrupt if any */
+ schedule_alarm(now);
+ }
+
+ errno = save_errno;
+}
+
+
+/*****************************************************************************
+ * Public API
+ *****************************************************************************/
+
+/*
+ * Initialize timeout module.
+ *
+ * This must be called in every process that wants to use timeouts.
+ *
+ * If the process was forked from another one that was also using this
+ * module, be sure to call this before re-enabling signals; else handlers
+ * meant to run in the parent process might get invoked in this one.
+ */
+void
+InitializeTimeouts(void)
+{
+ int i;
+
+ /* Initialize, or re-initialize, all local state */
+ num_active_timeouts = 0;
+
+ for (i = 0; i < MAX_TIMEOUTS; i++)
+ {
+ all_timeouts[i].index = i;
+ all_timeouts[i].indicator = false;
+ all_timeouts[i].timeout_handler = NULL;
+ all_timeouts[i].start_time = 0;
+ all_timeouts[i].fin_time = 0;
+ }
+
+ all_timeouts_initialized = true;
+
+ /* Now establish the signal handler */
+ pqsignal(SIGALRM, handle_sig_alarm);
+}
+
+/*
+ * Register a timeout reason
+ *
+ * For predefined timeouts, this just registers the callback function.
+ *
+ * For user-defined timeouts, pass id == USER_TIMEOUT; we then allocate and
+ * return a timeout ID.
+ */
+TimeoutId
+RegisterTimeout(TimeoutId id, timeout_handler handler)
+{
+ Assert(all_timeouts_initialized);
+
+ if (id >= USER_TIMEOUT)
+ {
+ /* Allocate a user-defined timeout reason */
+ for (id = USER_TIMEOUT; id < MAX_TIMEOUTS; id++)
+ if (all_timeouts[id].timeout_handler == NULL)
+ break;
+ if (id >= MAX_TIMEOUTS)
+ ereport(FATAL,
+ (errcode(ERRCODE_CONFIGURATION_LIMIT_EXCEEDED),
+ errmsg("cannot add more timeout reasons")));
+ }
+
+ Assert(all_timeouts[id].timeout_handler == NULL);
+
+ all_timeouts[id].timeout_handler = handler;
+
+ return id;
+}
+
+/*
+ * Enable the specified timeout reason
+ */
+static void
+enable_timeout(TimeoutId id, TimestampTz now, TimestampTz fin_time)
+{
+ struct itimerval timeval;
+ int i;
+
+ /* Assert request is sane */
+ Assert(all_timeouts_initialized);
+ Assert(all_timeouts[id].timeout_handler != NULL);
+
+ /*
+ * Disable the timer if it is active; this avoids getting interrupted by
+ * the signal handler and thereby possibly getting confused. We will
+ * re-enable the interrupt below.
+ *
+ * If num_active_timeouts is zero, we don't have to call setitimer. There
+ * should not be any pending interrupt, and even if there is, the worst
+ * possible case is that the signal handler fires during schedule_alarm.
+ * (If it fires at any point before insert_timeout has incremented
+ * num_active_timeouts, it will do nothing.) In that case we could end up
+ * scheduling a useless interrupt ... but when the interrupt does happen,
+ * the signal handler will do nothing, so it's all good.
+ */
+ if (num_active_timeouts > 0)
+ {
+ MemSet(&timeval, 0, sizeof(struct itimerval));
+ if (setitimer(ITIMER_REAL, &timeval, NULL) != 0)
+ elog(FATAL, "could not disable SIGALRM timer: %m");
+ }
+
+ /*
+ * If this timeout was already active, momentarily disable it. We
+ * interpret the call as a directive to reschedule the timeout.
+ */
+ i = find_active_timeout(id);
+ if (i >= 0)
+ remove_timeout_index(i);
+
+ /*
+ * Find out the index where to insert the new timeout. We sort by
+ * fin_time, and for equal fin_time by priority.
+ */
+ for (i = 0; i < num_active_timeouts; i++)
+ {
+ timeout_params *old_timeout = active_timeouts[i];
+
+ if (fin_time < old_timeout->fin_time)
+ break;
+ if (fin_time == old_timeout->fin_time && id < old_timeout->index)
+ break;
+ }
+
+ /*
+ * Activate the timeout.
+ */
+ all_timeouts[id].indicator = false;
+ all_timeouts[id].start_time = now;
+ all_timeouts[id].fin_time = fin_time;
+ insert_timeout(id, i);
+
+ /*
+ * Set the timer.
+ */
+ schedule_alarm(now);
+}
+
+/*
+ * Enable the specified timeout to fire after the specified delay.
+ *
+ * Delay is given in milliseconds.
+ */
+void
+enable_timeout_after(TimeoutId id, int delay_ms)
+{
+ TimestampTz now;
+ TimestampTz fin_time;
+
+ now = GetCurrentTimestamp();
+ fin_time = TimestampTzPlusMilliseconds(now, delay_ms);
+
+ enable_timeout(id, now, fin_time);
+}
+
+/*
+ * Enable the specified timeout to fire at the specified time.
+ *
+ * This is provided to support cases where there's a reason to calculate
+ * the timeout by reference to some point other than "now". If there isn't,
+ * use enable_timeout_after(), to avoid calling GetCurrentTimestamp() twice.
+ */
+void
+enable_timeout_at(TimeoutId id, TimestampTz fin_time)
+{
+ enable_timeout(id, GetCurrentTimestamp(), fin_time);
+}
+
+/*
+ * Cancel the specified timeout.
+ *
+ * The timeout's I've-been-fired indicator is reset,
+ * unless keep_indicator is true.
+ *
+ * When 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(TimeoutId id, bool keep_indicator)
+{
+ struct itimerval timeval;
+ int i;
+
+ /* Assert request is sane */
+ Assert(all_timeouts_initialized);
+ Assert(all_timeouts[id].timeout_handler != NULL);
+
+ /*
+ * Disable the timer if it is active; this avoids getting interrupted by
+ * the signal handler and thereby possibly getting confused. We will
+ * re-enable the interrupt if necessary below.
+ *
+ * If num_active_timeouts is zero, we don't have to call setitimer. There
+ * should not be any pending interrupt, and even if there is, the signal
+ * handler will not do anything. In this situation the only thing we
+ * really have to do is reset the timeout's indicator.
+ */
+ if (num_active_timeouts > 0)
+ {
+ MemSet(&timeval, 0, sizeof(struct itimerval));
+ if (setitimer(ITIMER_REAL, &timeval, NULL) != 0)
+ elog(FATAL, "could not disable SIGALRM timer: %m");
+ }
+
+ /* Find the timeout and remove it from the active list. */
+ i = find_active_timeout(id);
+ if (i >= 0)
+ remove_timeout_index(i);
+
+ /* Mark it inactive, whether it was active or not. */
+ if (!keep_indicator)
+ all_timeouts[id].indicator = false;
+
+ /* Now re-enable the timer, if necessary. */
+ if (num_active_timeouts > 0)
+ schedule_alarm(GetCurrentTimestamp());
+}
+
+/*
+ * Disable SIGALRM and remove all timeouts from the active list,
+ * and optionally reset their 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 SIGALRM timer: %m");
+
+ num_active_timeouts = 0;
+
+ if (!keep_indicators)
+ {
+ for (i = 0; i < MAX_TIMEOUTS; i++)
+ all_timeouts[i].indicator = false;
+ }
+}
+
+/*
+ * Return the timeout's I've-been-fired indicator
+ */
+bool
+get_timeout_indicator(TimeoutId id)
+{
+ return all_timeouts[id].indicator;
+}
+
+/*
+ * Return the time when the timeout was most recently activated
+ *
+ * Note: will return 0 if timeout has never been activated in this process.
+ * However, we do *not* reset the start_time when a timeout occurs, so as
+ * not to create a race condition if SIGALRM fires just as some code is
+ * about to fetch the value.
+ */
+TimestampTz
+get_timeout_start_time(TimeoutId id)
+{
+ return all_timeouts[id].start_time;
+}
#define _PROC_H_
#include "access/xlogdefs.h"
-#include "datatype/timestamp.h"
#include "storage/latch.h"
#include "storage/lock.h"
#include "storage/pg_sema.h"
extern int StatementTimeout;
extern bool log_lock_waits;
-extern volatile bool cancel_from_timeout;
-
/*
* Function Prototypes
extern int ProcSleep(LOCALLOCK *locallock, LockMethod lockMethodTable);
extern PGPROC *ProcWakeup(PGPROC *proc, int waitStatus);
extern void ProcLockWakeup(LockMethod lockMethodTable, LOCK *lock);
+extern void CheckDeadLock(void);
extern bool IsWaitingForLock(void);
extern void LockErrorCleanup(void);
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 */
extern void ResolveRecoveryConflictWithDatabase(Oid dbid);
extern void ResolveRecoveryConflictWithBufferPin(void);
-extern void SendRecoveryConflictWithBufferPin(ProcSignalReason reason);
extern void CheckRecoveryConflictDeadlock(void);
+extern void StandbyDeadLockHandler(void);
+extern void StandbyTimeoutHandler(void);
/*
* Standby Rmgr (RM_STANDBY_ID)
--- /dev/null
+/*-------------------------------------------------------------------------
+ *
+ * timeout.h
+ * Routines to multiplex SIGALRM interrupts for multiple timeout reasons.
+ *
+ *
+ * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/utils/timeout.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef TIMEOUT_H
+#define TIMEOUT_H
+
+#include "datatype/timestamp.h"
+
+/*
+ * Identifiers for timeout reasons. Note that in case multiple timeouts
+ * trigger at the same time, they are serviced in the order of this enum.
+ */
+typedef enum TimeoutId
+{
+ /* Predefined timeout reasons */
+ STARTUP_PACKET_TIMEOUT,
+ DEADLOCK_TIMEOUT,
+ STATEMENT_TIMEOUT,
+ STANDBY_DEADLOCK_TIMEOUT,
+ STANDBY_TIMEOUT,
+ /* First user-definable timeout reason */
+ USER_TIMEOUT,
+ /* Maximum number of timeout reasons */
+ MAX_TIMEOUTS = 16
+} TimeoutId;
+
+/* callback function signature */
+typedef void (*timeout_handler) (void);
+
+/* timeout setup */
+extern void InitializeTimeouts(void);
+extern TimeoutId RegisterTimeout(TimeoutId id, timeout_handler handler);
+
+/* timeout operation */
+extern void enable_timeout_after(TimeoutId id, int delay_ms);
+extern void enable_timeout_at(TimeoutId id, TimestampTz fin_time);
+extern void disable_timeout(TimeoutId id, bool keep_indicator);
+extern void disable_all_timeouts(bool keep_indicators);
+
+/* accessors */
+extern bool get_timeout_indicator(TimeoutId id);
+extern TimestampTz get_timeout_start_time(TimeoutId id);
+
+#endif /* TIMEOUT_H */