aboutsummaryrefslogtreecommitdiff
path: root/src/backend/storage/lmgr/proc.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/storage/lmgr/proc.c')
-rw-r--r--src/backend/storage/lmgr/proc.c300
1 files changed, 166 insertions, 134 deletions
diff --git a/src/backend/storage/lmgr/proc.c b/src/backend/storage/lmgr/proc.c
index 04f57707d19..4defa7ddbba 100644
--- a/src/backend/storage/lmgr/proc.c
+++ b/src/backend/storage/lmgr/proc.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/storage/lmgr/proc.c,v 1.126 2002/09/25 20:31:40 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/storage/lmgr/proc.c,v 1.127 2002/10/31 21:34:16 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -52,11 +52,11 @@
#include "storage/sinval.h"
#include "storage/spin.h"
+/* GUC variables */
int DeadlockTimeout = 1000;
int StatementTimeout = 0;
-int RemainingStatementTimeout = 0;
-bool alarm_is_statement_timeout = false;
+/* Pointer to this process's PGPROC struct, if any */
PGPROC *MyProc = NULL;
/*
@@ -75,8 +75,16 @@ static PGPROC *DummyProc = NULL;
static bool waitingForLock = false;
static bool waitingForSignal = false;
+/* Mark these volatile because they can be changed by signal handler */
+static volatile bool statement_timeout_active = false;
+static volatile bool deadlock_timeout_active = false;
+/* statement_fin_time is valid only if statement_timeout_active is true */
+static struct timeval statement_fin_time;
+
+
static void ProcKill(void);
static void DummyProcKill(void);
+static bool CheckStatementTimeout(void);
/*
@@ -796,14 +804,12 @@ ProcLockWakeup(LOCKMETHODTABLE *lockMethodTable, LOCK *lock)
* and signal an error to ProcSleep.
* --------------------
*/
-void
+static void
CheckDeadLock(void)
{
- int save_errno = errno;
-
/*
- * Acquire locktable lock. Note that the SIGALRM interrupt had better
- * not be enabled anywhere that this process itself holds the
+ * Acquire locktable lock. Note that the deadlock check interrupt had
+ * better not be enabled anywhere that this process itself holds the
* locktable lock, else this will wait forever. Also note that
* LWLockAcquire creates a critical section, so that this routine
* cannot be interrupted by cancel/die interrupts.
@@ -821,13 +827,11 @@ CheckDeadLock(void)
* This is quicker than checking our semaphore's state, since no
* kernel call is needed, and it is safe because we hold the locktable
* lock.
- *
*/
if (MyProc->links.prev == INVALID_OFFSET ||
MyProc->links.next == INVALID_OFFSET)
{
LWLockRelease(LockMgrLock);
- errno = save_errno;
return;
}
@@ -840,7 +844,6 @@ CheckDeadLock(void)
{
/* No deadlock, so keep waiting */
LWLockRelease(LockMgrLock);
- errno = save_errno;
return;
}
@@ -853,7 +856,7 @@ CheckDeadLock(void)
/*
* Set MyProc->errType to STATUS_ERROR so that ProcSleep will report
- * an error after we return from this signal handler.
+ * an error after we return from the signal handler.
*/
MyProc->errType = STATUS_ERROR;
@@ -874,7 +877,6 @@ CheckDeadLock(void)
* RemoveFromWaitQueue took care of waking up any such processes.
*/
LWLockRelease(LockMgrLock);
- errno = save_errno;
}
@@ -933,188 +935,218 @@ ProcSendSignal(BackendId procId)
* Delay is given in milliseconds. Caller should be sure a SIGALRM
* signal handler is installed before this is called.
*
- * This code properly handles multiple alarms when the statement_timeout
- * alarm is specified first.
+ * 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)
{
+ struct timeval fin_time;
#ifndef __BEOS__
- struct itimerval timeval,
- remaining;
-
+ struct itimerval timeval;
#else
- bigtime_t time_interval,
- remaining;
+ bigtime_t time_interval;
#endif
- /*
- * Don't set timer if the statement timeout scheduled before next
- * alarm.
- */
- if (alarm_is_statement_timeout &&
- !is_statement_timeout &&
- RemainingStatementTimeout <= delayms)
- return true;
+ /* Compute target timeout time if we will need it */
+ if (is_statement_timeout || statement_timeout_active)
+ {
+ gettimeofday(&fin_time, NULL);
+ fin_time.tv_sec += delayms / 1000;
+ fin_time.tv_usec += (delayms % 1000) * 1000;
+ if (fin_time.tv_usec >= 1000000)
+ {
+ fin_time.tv_sec++;
+ fin_time.tv_usec -= 1000000;
+ }
+ }
+
+ if (is_statement_timeout)
+ {
+ /* Begin statement-level timeout */
+ Assert(!deadlock_timeout_active);
+ statement_fin_time = fin_time;
+ 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.
+ */
+ deadlock_timeout_active = true;
+ if (fin_time.tv_sec > statement_fin_time.tv_sec ||
+ (fin_time.tv_sec == statement_fin_time.tv_sec &&
+ fin_time.tv_usec >= statement_fin_time.tv_usec))
+ return true;
+ }
+ else
+ {
+ /* Begin deadlock timeout with no statement-level timeout */
+ deadlock_timeout_active = true;
+ }
+ /* If we reach here, okay to set the timer interrupt */
#ifndef __BEOS__
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, &remaining))
+ if (setitimer(ITIMER_REAL, &timeval, NULL))
return false;
#else
/* BeOS doesn't have setitimer, but has set_alarm */
time_interval = delayms * 1000; /* usecs */
- if ((remaining = set_alarm(time_interval, B_ONE_SHOT_RELATIVE_ALARM)) < 0)
+ if (set_alarm(time_interval, B_ONE_SHOT_RELATIVE_ALARM) < 0)
return false;
#endif
- if (is_statement_timeout)
- RemainingStatementTimeout = StatementTimeout;
- else
+ 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)
{
- /* Switching to non-statement-timeout alarm, get remaining time */
- if (alarm_is_statement_timeout)
- {
#ifndef __BEOS__
- /* We lose precision here because we convert to milliseconds */
- RemainingStatementTimeout = remaining.it_value.tv_sec * 1000 +
- remaining.it_value.tv_usec / 1000;
-#else
- RemainingStatementTimeout = remaining / 1000;
-#endif
- /* Rounding could cause a zero */
- if (RemainingStatementTimeout == 0)
- RemainingStatementTimeout = 1;
- }
+ struct itimerval timeval;
- if (RemainingStatementTimeout)
+ MemSet(&timeval, 0, sizeof(struct itimerval));
+ if (setitimer(ITIMER_REAL, &timeval, NULL))
{
- /* Remaining timeout alarm < delayms? */
- if (RemainingStatementTimeout <= delayms)
- {
- /* reinstall statement timeout alarm */
- alarm_is_statement_timeout = true;
-#ifndef __BEOS__
- remaining.it_value.tv_sec = RemainingStatementTimeout / 1000;
- remaining.it_value.tv_usec = (RemainingStatementTimeout % 1000) * 1000;
- if (setitimer(ITIMER_REAL, &remaining, &timeval))
- return false;
- else
- return true;
+ statement_timeout_active = deadlock_timeout_active = false;
+ return false;
+ }
#else
- remaining = RemainingStatementTimeout * 1000;
- if ((timeval = set_alarm(remaining, B_ONE_SHOT_RELATIVE_ALARM)) < 0)
- return false;
- else
- return true;
-#endif
- }
- else
- RemainingStatementTimeout -= delayms;
+ /* BeOS doesn't have setitimer, but has set_alarm */
+ if (set_alarm(B_INFINITE_TIMEOUT, B_PERIODIC_ALARM) < 0)
+ {
+ statement_timeout_active = deadlock_timeout_active = false;
+ return false;
}
+#endif
}
+ /* Always cancel deadlock timeout, in case this is error cleanup */
+ deadlock_timeout_active = false;
+
+ /* Cancel or reschedule statement timeout */
if (is_statement_timeout)
- alarm_is_statement_timeout = true;
- else
- alarm_is_statement_timeout = false;
+ statement_timeout_active = false;
+ else if (statement_timeout_active)
+ {
+ if (!CheckStatementTimeout())
+ return false;
+ }
return true;
}
+
/*
- * Cancel the SIGALRM timer.
+ * 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.
*
- * This is also called if the timer has fired to reschedule
- * the statement_timeout timer.
- *
- * Returns TRUE if okay, FALSE on failure.
+ * Returns true if okay, false if failed to set the interrupt.
*/
-bool
-disable_sig_alarm(bool is_statement_timeout)
+static bool
+CheckStatementTimeout(void)
{
-#ifndef __BEOS__
- struct itimerval timeval,
- remaining;
+ struct timeval now;
- MemSet(&timeval, 0, sizeof(struct itimerval));
-#else
- bigtime_t time_interval = 0;
-#endif
+ if (!statement_timeout_active)
+ return true; /* do nothing if not active */
+
+ gettimeofday(&now, NULL);
- if (!is_statement_timeout && RemainingStatementTimeout)
+ if (now.tv_sec > statement_fin_time.tv_sec ||
+ (now.tv_sec == statement_fin_time.tv_sec &&
+ now.tv_usec >= statement_fin_time.tv_usec))
{
+ /* Time to die */
+ statement_timeout_active = false;
+ kill(MyProcPid, SIGINT);
+ }
+ else
+ {
+ /* Not time yet, so (re)schedule the interrupt */
#ifndef __BEOS__
- /* turn off timer and get remaining time, if any */
- if (setitimer(ITIMER_REAL, &timeval, &remaining))
+ struct itimerval timeval;
+
+ MemSet(&timeval, 0, sizeof(struct itimerval));
+ timeval.it_value.tv_sec = statement_fin_time.tv_sec - now.tv_sec;
+ timeval.it_value.tv_usec = statement_fin_time.tv_usec - now.tv_usec;
+ if (timeval.it_value.tv_usec < 0)
+ {
+ timeval.it_value.tv_sec--;
+ timeval.it_value.tv_usec += 1000000;
+ }
+ if (setitimer(ITIMER_REAL, &timeval, NULL))
return false;
- /* Add remaining time back because the timer didn't complete */
- RemainingStatementTimeout += remaining.it_value.tv_sec * 1000 +
- remaining.it_value.tv_usec / 1000;
- /* Prepare to set timer */
- timeval.it_value.tv_sec = RemainingStatementTimeout / 1000;
- timeval.it_value.tv_usec = (RemainingStatementTimeout % 1000) * 1000;
#else
/* BeOS doesn't have setitimer, but has set_alarm */
- if ((time_interval = set_alarm(B_INFINITE_TIMEOUT, B_PERIODIC_ALARM)) < 0)
- return false;
- RemainingStatementTimeout += time_interval / 1000;
- time_interval = RemainingStatementTimeout * 1000;
-#endif
- /* Restore remaining statement timeout value */
- alarm_is_statement_timeout = true;
- }
+ bigtime_t time_interval;
- /*
- * Optimization: is_statement_timeout && RemainingStatementTimeout ==
- * 0 does nothing. This is for cases where no timeout was set.
- */
- if (!is_statement_timeout || RemainingStatementTimeout)
- {
-#ifndef __BEOS__
- if (setitimer(ITIMER_REAL, &timeval, &remaining))
+ time_interval =
+ (statement_fin_time.tv_sec - now.tv_sec) * 1000000 +
+ (statement_fin_time.tv_usec - now.tv_usec);
+ if (set_alarm(time_interval, B_ONE_SHOT_RELATIVE_ALARM) < 0)
return false;
-#else
- if (time_interval)
- {
- if (set_alarm(time_interval, B_ONE_SHOT_RELATIVE_ALARM) < 0)
- return false;
- }
- else
- {
- if (set_alarm(B_INFINITE_TIMEOUT, B_PERIODIC_ALARM) < 0)
- return false;
- }
#endif
}
- if (is_statement_timeout)
- RemainingStatementTimeout = 0;
-
return true;
}
/*
- * Call alarm handler, either StatementCancel or Deadlock checker.
+ * Signal handler for SIGALRM
+ *
+ * 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)
{
- if (alarm_is_statement_timeout)
- {
- RemainingStatementTimeout = 0;
- alarm_is_statement_timeout = false;
- kill(MyProcPid, SIGINT);
- }
- else
+ int save_errno = errno;
+
+ if (deadlock_timeout_active)
{
+ deadlock_timeout_active = false;
CheckDeadLock();
- /* Reactivate any statement_timeout alarm. */
- disable_sig_alarm(false);
}
+
+ if (statement_timeout_active)
+ (void) CheckStatementTimeout();
+
+ errno = save_errno;
}