Code review for statement_timeout patch. Fix some race conditions
between signal handler and enable/disable code, avoid accumulation of timing error due to trying to maintain remaining-time instead of absolute-end-time, disable timeout before commit not after.
This commit is contained in:
parent
8a45a2e9b6
commit
55e4ef138c
@ -1,4 +1,4 @@
|
|||||||
$Header: /cvsroot/pgsql/src/backend/storage/lmgr/README,v 1.11 2002/07/19 00:17:40 momjian Exp $
|
$Header: /cvsroot/pgsql/src/backend/storage/lmgr/README,v 1.12 2002/10/31 21:34:16 tgl Exp $
|
||||||
|
|
||||||
|
|
||||||
LOCKING OVERVIEW
|
LOCKING OVERVIEW
|
||||||
@ -392,7 +392,7 @@ Miscellaneous notes:
|
|||||||
asynchronous invocation of deadlock checking. A deadlock cycle in the WFG
|
asynchronous invocation of deadlock checking. A deadlock cycle in the WFG
|
||||||
is formed when the last edge in the cycle is added; therefore the last
|
is formed when the last edge in the cycle is added; therefore the last
|
||||||
process in the cycle to wait (the one from which that edge is outgoing) is
|
process in the cycle to wait (the one from which that edge is outgoing) is
|
||||||
certain to detect and resolve the cycle when it later runs HandleDeadLock.
|
certain to detect and resolve the cycle when it later runs CheckDeadLock.
|
||||||
This holds even if that edge addition created multiple cycles; the process
|
This holds even if that edge addition created multiple cycles; the process
|
||||||
may indeed abort without ever noticing those additional cycles, but we
|
may indeed abort without ever noticing those additional cycles, but we
|
||||||
don't particularly care. The only other possible creation of deadlocks is
|
don't particularly care. The only other possible creation of deadlocks is
|
||||||
@ -402,7 +402,7 @@ it attempts to actually execute any rearrangement.
|
|||||||
|
|
||||||
2. It is not certain that a deadlock will be resolved by aborting the
|
2. It is not certain that a deadlock will be resolved by aborting the
|
||||||
last-to-wait process. If earlier waiters in the cycle have not yet run
|
last-to-wait process. If earlier waiters in the cycle have not yet run
|
||||||
HandleDeadLock, then the first one to do so will be the victim.
|
CheckDeadLock, then the first one to do so will be the victim.
|
||||||
|
|
||||||
3. No live (wakable) process can be missed by ProcLockWakeup, since it
|
3. No live (wakable) process can be missed by ProcLockWakeup, since it
|
||||||
examines every member of the wait queue (this was not true in the 7.0
|
examines every member of the wait queue (this was not true in the 7.0
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $Header: /cvsroot/pgsql/src/backend/storage/lmgr/lock.c,v 1.116 2002/09/26 05:18:30 momjian Exp $
|
* $Header: /cvsroot/pgsql/src/backend/storage/lmgr/lock.c,v 1.117 2002/10/31 21:34:16 tgl Exp $
|
||||||
*
|
*
|
||||||
* NOTES
|
* NOTES
|
||||||
* Outside modules can create a lock table and acquire/release
|
* Outside modules can create a lock table and acquire/release
|
||||||
@ -882,7 +882,7 @@ WaitOnLock(LOCKMETHOD lockmethod, LOCKMODE lockmode,
|
|||||||
/*
|
/*
|
||||||
* NOTE: Think not to put any shared-state cleanup after the call to
|
* NOTE: Think not to put any shared-state cleanup after the call to
|
||||||
* ProcSleep, in either the normal or failure path. The lock state
|
* ProcSleep, in either the normal or failure path. The lock state
|
||||||
* must be fully set by the lock grantor, or by HandleDeadLock if we
|
* must be fully set by the lock grantor, or by CheckDeadLock if we
|
||||||
* give up waiting for the lock. This is necessary because of the
|
* give up waiting for the lock. This is necessary because of the
|
||||||
* possibility that a cancel/die interrupt will interrupt ProcSleep
|
* possibility that a cancel/die interrupt will interrupt ProcSleep
|
||||||
* after someone else grants us the lock, but before we've noticed it.
|
* after someone else grants us the lock, but before we've noticed it.
|
||||||
@ -899,7 +899,7 @@ WaitOnLock(LOCKMETHOD lockmethod, LOCKMODE lockmode,
|
|||||||
holder) != STATUS_OK)
|
holder) != STATUS_OK)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* We failed as a result of a deadlock, see HandleDeadLock(). Quit
|
* We failed as a result of a deadlock, see CheckDeadLock(). Quit
|
||||||
* now. Removal of the holder and lock objects, if no longer
|
* now. Removal of the holder and lock objects, if no longer
|
||||||
* needed, will happen in xact cleanup (see above for motivation).
|
* needed, will happen in xact cleanup (see above for motivation).
|
||||||
*/
|
*/
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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/sinval.h"
|
||||||
#include "storage/spin.h"
|
#include "storage/spin.h"
|
||||||
|
|
||||||
|
/* GUC variables */
|
||||||
int DeadlockTimeout = 1000;
|
int DeadlockTimeout = 1000;
|
||||||
int StatementTimeout = 0;
|
int StatementTimeout = 0;
|
||||||
int RemainingStatementTimeout = 0;
|
|
||||||
bool alarm_is_statement_timeout = false;
|
|
||||||
|
|
||||||
|
/* Pointer to this process's PGPROC struct, if any */
|
||||||
PGPROC *MyProc = NULL;
|
PGPROC *MyProc = NULL;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -75,8 +75,16 @@ static PGPROC *DummyProc = NULL;
|
|||||||
static bool waitingForLock = false;
|
static bool waitingForLock = false;
|
||||||
static bool waitingForSignal = 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 ProcKill(void);
|
||||||
static void DummyProcKill(void);
|
static void DummyProcKill(void);
|
||||||
|
static bool CheckStatementTimeout(void);
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -796,14 +804,12 @@ ProcLockWakeup(LOCKMETHODTABLE *lockMethodTable, LOCK *lock)
|
|||||||
* and signal an error to ProcSleep.
|
* and signal an error to ProcSleep.
|
||||||
* --------------------
|
* --------------------
|
||||||
*/
|
*/
|
||||||
void
|
static void
|
||||||
CheckDeadLock(void)
|
CheckDeadLock(void)
|
||||||
{
|
{
|
||||||
int save_errno = errno;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Acquire locktable lock. Note that the SIGALRM interrupt had better
|
* Acquire locktable lock. Note that the deadlock check interrupt had
|
||||||
* not be enabled anywhere that this process itself holds the
|
* better not be enabled anywhere that this process itself holds the
|
||||||
* locktable lock, else this will wait forever. Also note that
|
* locktable lock, else this will wait forever. Also note that
|
||||||
* LWLockAcquire creates a critical section, so that this routine
|
* LWLockAcquire creates a critical section, so that this routine
|
||||||
* cannot be interrupted by cancel/die interrupts.
|
* cannot be interrupted by cancel/die interrupts.
|
||||||
@ -821,13 +827,11 @@ CheckDeadLock(void)
|
|||||||
* This is quicker than checking our semaphore's state, since no
|
* This is quicker than checking our semaphore's state, since no
|
||||||
* kernel call is needed, and it is safe because we hold the locktable
|
* kernel call is needed, and it is safe because we hold the locktable
|
||||||
* lock.
|
* lock.
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
if (MyProc->links.prev == INVALID_OFFSET ||
|
if (MyProc->links.prev == INVALID_OFFSET ||
|
||||||
MyProc->links.next == INVALID_OFFSET)
|
MyProc->links.next == INVALID_OFFSET)
|
||||||
{
|
{
|
||||||
LWLockRelease(LockMgrLock);
|
LWLockRelease(LockMgrLock);
|
||||||
errno = save_errno;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -840,7 +844,6 @@ CheckDeadLock(void)
|
|||||||
{
|
{
|
||||||
/* No deadlock, so keep waiting */
|
/* No deadlock, so keep waiting */
|
||||||
LWLockRelease(LockMgrLock);
|
LWLockRelease(LockMgrLock);
|
||||||
errno = save_errno;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -853,7 +856,7 @@ CheckDeadLock(void)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Set MyProc->errType to STATUS_ERROR so that ProcSleep will report
|
* 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;
|
MyProc->errType = STATUS_ERROR;
|
||||||
|
|
||||||
@ -874,7 +877,6 @@ CheckDeadLock(void)
|
|||||||
* RemoveFromWaitQueue took care of waking up any such processes.
|
* RemoveFromWaitQueue took care of waking up any such processes.
|
||||||
*/
|
*/
|
||||||
LWLockRelease(LockMgrLock);
|
LWLockRelease(LockMgrLock);
|
||||||
errno = save_errno;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -933,188 +935,218 @@ ProcSendSignal(BackendId procId)
|
|||||||
* Delay is given in milliseconds. Caller should be sure a SIGALRM
|
* Delay is given in milliseconds. Caller should be sure a SIGALRM
|
||||||
* signal handler is installed before this is called.
|
* signal handler is installed before this is called.
|
||||||
*
|
*
|
||||||
* This code properly handles multiple alarms when the statement_timeout
|
* This code properly handles nesting of deadlock timeout alarms within
|
||||||
* alarm is specified first.
|
* statement timeout alarms.
|
||||||
*
|
*
|
||||||
* Returns TRUE if okay, FALSE on failure.
|
* Returns TRUE if okay, FALSE on failure.
|
||||||
*/
|
*/
|
||||||
bool
|
bool
|
||||||
enable_sig_alarm(int delayms, bool is_statement_timeout)
|
enable_sig_alarm(int delayms, bool is_statement_timeout)
|
||||||
{
|
{
|
||||||
|
struct timeval fin_time;
|
||||||
#ifndef __BEOS__
|
#ifndef __BEOS__
|
||||||
struct itimerval timeval,
|
struct itimerval timeval;
|
||||||
remaining;
|
|
||||||
|
|
||||||
#else
|
#else
|
||||||
bigtime_t time_interval,
|
bigtime_t time_interval;
|
||||||
remaining;
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*
|
/* Compute target timeout time if we will need it */
|
||||||
* Don't set timer if the statement timeout scheduled before next
|
if (is_statement_timeout || statement_timeout_active)
|
||||||
* alarm.
|
|
||||||
*/
|
|
||||||
if (alarm_is_statement_timeout &&
|
|
||||||
!is_statement_timeout &&
|
|
||||||
RemainingStatementTimeout <= delayms)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
#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))
|
|
||||||
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)
|
|
||||||
return false;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (is_statement_timeout)
|
|
||||||
RemainingStatementTimeout = StatementTimeout;
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
/* Switching to non-statement-timeout alarm, get remaining time */
|
gettimeofday(&fin_time, NULL);
|
||||||
if (alarm_is_statement_timeout)
|
fin_time.tv_sec += delayms / 1000;
|
||||||
|
fin_time.tv_usec += (delayms % 1000) * 1000;
|
||||||
|
if (fin_time.tv_usec >= 1000000)
|
||||||
{
|
{
|
||||||
#ifndef __BEOS__
|
fin_time.tv_sec++;
|
||||||
/* We lose precision here because we convert to milliseconds */
|
fin_time.tv_usec -= 1000000;
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (RemainingStatementTimeout)
|
|
||||||
{
|
|
||||||
/* 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;
|
|
||||||
#else
|
|
||||||
remaining = RemainingStatementTimeout * 1000;
|
|
||||||
if ((timeval = set_alarm(remaining, B_ONE_SHOT_RELATIVE_ALARM)) < 0)
|
|
||||||
return false;
|
|
||||||
else
|
|
||||||
return true;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
else
|
|
||||||
RemainingStatementTimeout -= delayms;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_statement_timeout)
|
if (is_statement_timeout)
|
||||||
alarm_is_statement_timeout = true;
|
{
|
||||||
|
/* 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
|
else
|
||||||
alarm_is_statement_timeout = false;
|
{
|
||||||
|
/* 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, NULL))
|
||||||
|
return false;
|
||||||
|
#else
|
||||||
|
/* BeOS doesn't have setitimer, but has set_alarm */
|
||||||
|
time_interval = delayms * 1000; /* usecs */
|
||||||
|
if (set_alarm(time_interval, B_ONE_SHOT_RELATIVE_ALARM) < 0)
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Cancel the SIGALRM timer.
|
* Cancel the SIGALRM timer, either for a deadlock timeout or a statement
|
||||||
*
|
* timeout. If a deadlock timeout is canceled, any active statement timeout
|
||||||
* This is also called if the timer has fired to reschedule
|
* remains in force.
|
||||||
* the statement_timeout timer.
|
|
||||||
*
|
*
|
||||||
* Returns TRUE if okay, FALSE on failure.
|
* Returns TRUE if okay, FALSE on failure.
|
||||||
*/
|
*/
|
||||||
bool
|
bool
|
||||||
disable_sig_alarm(bool is_statement_timeout)
|
disable_sig_alarm(bool is_statement_timeout)
|
||||||
{
|
{
|
||||||
#ifndef __BEOS__
|
/*
|
||||||
struct itimerval timeval,
|
* Always disable the interrupt if it is active; this avoids being
|
||||||
remaining;
|
* interrupted by the signal handler and thereby possibly getting
|
||||||
|
* confused.
|
||||||
MemSet(&timeval, 0, sizeof(struct itimerval));
|
*
|
||||||
#else
|
* We will re-enable the interrupt if necessary in CheckStatementTimeout.
|
||||||
bigtime_t time_interval = 0;
|
*/
|
||||||
#endif
|
if (statement_timeout_active || deadlock_timeout_active)
|
||||||
|
|
||||||
if (!is_statement_timeout && RemainingStatementTimeout)
|
|
||||||
{
|
{
|
||||||
#ifndef __BEOS__
|
#ifndef __BEOS__
|
||||||
/* turn off timer and get remaining time, if any */
|
struct itimerval timeval;
|
||||||
if (setitimer(ITIMER_REAL, &timeval, &remaining))
|
|
||||||
|
MemSet(&timeval, 0, sizeof(struct itimerval));
|
||||||
|
if (setitimer(ITIMER_REAL, &timeval, NULL))
|
||||||
|
{
|
||||||
|
statement_timeout_active = deadlock_timeout_active = false;
|
||||||
return false;
|
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
|
#else
|
||||||
/* BeOS doesn't have setitimer, but has set_alarm */
|
/* BeOS doesn't have setitimer, but has set_alarm */
|
||||||
if ((time_interval = set_alarm(B_INFINITE_TIMEOUT, B_PERIODIC_ALARM)) < 0)
|
if (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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 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))
|
|
||||||
return false;
|
|
||||||
#else
|
|
||||||
if (time_interval)
|
|
||||||
{
|
{
|
||||||
if (set_alarm(time_interval, B_ONE_SHOT_RELATIVE_ALARM) < 0)
|
statement_timeout_active = deadlock_timeout_active = false;
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (set_alarm(B_INFINITE_TIMEOUT, B_PERIODIC_ALARM) < 0)
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Always cancel deadlock timeout, in case this is error cleanup */
|
||||||
|
deadlock_timeout_active = false;
|
||||||
|
|
||||||
|
/* Cancel or reschedule statement timeout */
|
||||||
if (is_statement_timeout)
|
if (is_statement_timeout)
|
||||||
RemainingStatementTimeout = 0;
|
statement_timeout_active = false;
|
||||||
|
else if (statement_timeout_active)
|
||||||
|
{
|
||||||
|
if (!CheckStatementTimeout())
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Call alarm handler, either StatementCancel or Deadlock checker.
|
* 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.
|
||||||
*/
|
*/
|
||||||
void
|
static bool
|
||||||
handle_sig_alarm(SIGNAL_ARGS)
|
CheckStatementTimeout(void)
|
||||||
{
|
{
|
||||||
if (alarm_is_statement_timeout)
|
struct timeval now;
|
||||||
|
|
||||||
|
if (!statement_timeout_active)
|
||||||
|
return true; /* do nothing if not active */
|
||||||
|
|
||||||
|
gettimeofday(&now, NULL);
|
||||||
|
|
||||||
|
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))
|
||||||
{
|
{
|
||||||
RemainingStatementTimeout = 0;
|
/* Time to die */
|
||||||
alarm_is_statement_timeout = false;
|
statement_timeout_active = false;
|
||||||
kill(MyProcPid, SIGINT);
|
kill(MyProcPid, SIGINT);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
CheckDeadLock();
|
/* Not time yet, so (re)schedule the interrupt */
|
||||||
/* Reactivate any statement_timeout alarm. */
|
#ifndef __BEOS__
|
||||||
disable_sig_alarm(false);
|
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;
|
||||||
|
#else
|
||||||
|
/* BeOS doesn't have setitimer, but has set_alarm */
|
||||||
|
bigtime_t time_interval;
|
||||||
|
|
||||||
|
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;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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)
|
||||||
|
{
|
||||||
|
int save_errno = errno;
|
||||||
|
|
||||||
|
if (deadlock_timeout_active)
|
||||||
|
{
|
||||||
|
deadlock_timeout_active = false;
|
||||||
|
CheckDeadLock();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (statement_timeout_active)
|
||||||
|
(void) CheckStatementTimeout();
|
||||||
|
|
||||||
|
errno = save_errno;
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.306 2002/10/24 23:19:13 tgl Exp $
|
* $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.307 2002/10/31 21:34:16 tgl Exp $
|
||||||
*
|
*
|
||||||
* NOTES
|
* NOTES
|
||||||
* this is the "main" module of the postgres backend and
|
* this is the "main" module of the postgres backend and
|
||||||
@ -75,8 +75,6 @@ char *debug_query_string; /* for pgmonitor and
|
|||||||
/* Note: whereToSendOutput is initialized for the bootstrap/standalone case */
|
/* Note: whereToSendOutput is initialized for the bootstrap/standalone case */
|
||||||
CommandDest whereToSendOutput = Debug;
|
CommandDest whereToSendOutput = Debug;
|
||||||
|
|
||||||
extern int StatementTimeout;
|
|
||||||
|
|
||||||
static bool dontExecute = false;
|
static bool dontExecute = false;
|
||||||
|
|
||||||
/* note: these declarations had better match tcopprot.h */
|
/* note: these declarations had better match tcopprot.h */
|
||||||
@ -582,9 +580,6 @@ pg_exec_query_string(StringInfo query_string, /* string to execute */
|
|||||||
start_xact_command();
|
start_xact_command();
|
||||||
xact_started = true;
|
xact_started = true;
|
||||||
|
|
||||||
if (StatementTimeout)
|
|
||||||
enable_sig_alarm(StatementTimeout, true);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* parse_context *must* be different from the execution memory
|
* parse_context *must* be different from the execution memory
|
||||||
* context, else the context reset at the bottom of the loop will
|
* context, else the context reset at the bottom of the loop will
|
||||||
@ -931,8 +926,6 @@ pg_exec_query_string(StringInfo query_string, /* string to execute */
|
|||||||
EndCommand(commandTag, dest);
|
EndCommand(commandTag, dest);
|
||||||
} /* end loop over parsetrees */
|
} /* end loop over parsetrees */
|
||||||
|
|
||||||
disable_sig_alarm(true);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Close down transaction statement, if one is open. (Note that this
|
* Close down transaction statement, if one is open. (Note that this
|
||||||
* will only happen if the querystring was empty.)
|
* will only happen if the querystring was empty.)
|
||||||
@ -964,6 +957,10 @@ start_xact_command(void)
|
|||||||
{
|
{
|
||||||
elog(DEBUG1, "StartTransactionCommand");
|
elog(DEBUG1, "StartTransactionCommand");
|
||||||
StartTransactionCommand(false);
|
StartTransactionCommand(false);
|
||||||
|
|
||||||
|
/* Set statement timeout running, if any */
|
||||||
|
if (StatementTimeout > 0)
|
||||||
|
enable_sig_alarm(StatementTimeout, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -972,6 +969,9 @@ finish_xact_command(bool forceCommit)
|
|||||||
/* Invoke IMMEDIATE constraint triggers */
|
/* Invoke IMMEDIATE constraint triggers */
|
||||||
DeferredTriggerEndQuery();
|
DeferredTriggerEndQuery();
|
||||||
|
|
||||||
|
/* Cancel any active statement timeout before committing */
|
||||||
|
disable_sig_alarm(true);
|
||||||
|
|
||||||
/* Now commit the command */
|
/* Now commit the command */
|
||||||
elog(DEBUG1, "CommitTransactionCommand");
|
elog(DEBUG1, "CommitTransactionCommand");
|
||||||
|
|
||||||
@ -1047,7 +1047,7 @@ die(SIGNAL_ARGS)
|
|||||||
/* until we are done getting ready for it */
|
/* until we are done getting ready for it */
|
||||||
InterruptHoldoffCount++;
|
InterruptHoldoffCount++;
|
||||||
DisableNotifyInterrupt();
|
DisableNotifyInterrupt();
|
||||||
/* Make sure HandleDeadLock won't run while shutting down... */
|
/* Make sure CheckDeadLock won't run while shutting down... */
|
||||||
LockWaitCancel();
|
LockWaitCancel();
|
||||||
InterruptHoldoffCount--;
|
InterruptHoldoffCount--;
|
||||||
ProcessInterrupts();
|
ProcessInterrupts();
|
||||||
@ -1648,8 +1648,7 @@ PostgresMain(int argc, char *argv[], const char *username)
|
|||||||
pqsignal(SIGINT, StatementCancelHandler); /* cancel current query */
|
pqsignal(SIGINT, StatementCancelHandler); /* cancel current query */
|
||||||
pqsignal(SIGTERM, die); /* cancel current query and exit */
|
pqsignal(SIGTERM, die); /* cancel current query and exit */
|
||||||
pqsignal(SIGQUIT, quickdie); /* hard crash time */
|
pqsignal(SIGQUIT, quickdie); /* hard crash time */
|
||||||
pqsignal(SIGALRM, handle_sig_alarm); /* check for deadlock
|
pqsignal(SIGALRM, handle_sig_alarm); /* timeout conditions */
|
||||||
* after timeout */
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Ignore failure to write to frontend. Note: if frontend closes
|
* Ignore failure to write to frontend. Note: if frontend closes
|
||||||
@ -1782,7 +1781,7 @@ PostgresMain(int argc, char *argv[], const char *username)
|
|||||||
if (!IsUnderPostmaster)
|
if (!IsUnderPostmaster)
|
||||||
{
|
{
|
||||||
puts("\nPOSTGRES backend interactive interface ");
|
puts("\nPOSTGRES backend interactive interface ");
|
||||||
puts("$Revision: 1.306 $ $Date: 2002/10/24 23:19:13 $\n");
|
puts("$Revision: 1.307 $ $Date: 2002/10/31 21:34:16 $\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1829,6 +1828,8 @@ PostgresMain(int argc, char *argv[], const char *username)
|
|||||||
QueryCancelPending = false;
|
QueryCancelPending = false;
|
||||||
InterruptHoldoffCount = 1;
|
InterruptHoldoffCount = 1;
|
||||||
CritSectionCount = 0; /* should be unnecessary, but... */
|
CritSectionCount = 0; /* should be unnecessary, but... */
|
||||||
|
disable_sig_alarm(true);
|
||||||
|
QueryCancelPending = false; /* again in case timeout occurred */
|
||||||
DisableNotifyInterrupt();
|
DisableNotifyInterrupt();
|
||||||
debug_query_string = NULL;
|
debug_query_string = NULL;
|
||||||
|
|
||||||
@ -1915,9 +1916,6 @@ PostgresMain(int argc, char *argv[], const char *username)
|
|||||||
QueryCancelPending = false; /* forget any earlier CANCEL
|
QueryCancelPending = false; /* forget any earlier CANCEL
|
||||||
* signal */
|
* signal */
|
||||||
|
|
||||||
/* Stop any statement timer */
|
|
||||||
disable_sig_alarm(true);
|
|
||||||
|
|
||||||
EnableNotifyInterrupt();
|
EnableNotifyInterrupt();
|
||||||
|
|
||||||
/* Allow "die" interrupt to be processed while waiting */
|
/* Allow "die" interrupt to be processed while waiting */
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
* command, configuration file, and command line options.
|
* command, configuration file, and command line options.
|
||||||
* See src/backend/utils/misc/README for more information.
|
* See src/backend/utils/misc/README for more information.
|
||||||
*
|
*
|
||||||
* $Header: /cvsroot/pgsql/src/backend/utils/misc/guc.c,v 1.97 2002/10/02 16:27:57 momjian Exp $
|
* $Header: /cvsroot/pgsql/src/backend/utils/misc/guc.c,v 1.98 2002/10/31 21:34:17 tgl Exp $
|
||||||
*
|
*
|
||||||
* Copyright 2000 by PostgreSQL Global Development Group
|
* Copyright 2000 by PostgreSQL Global Development Group
|
||||||
* Written by Peter Eisentraut <peter_e@gmx.net>.
|
* Written by Peter Eisentraut <peter_e@gmx.net>.
|
||||||
@ -56,7 +56,6 @@
|
|||||||
extern bool Log_connections;
|
extern bool Log_connections;
|
||||||
extern int PreAuthDelay;
|
extern int PreAuthDelay;
|
||||||
extern int AuthenticationTimeout;
|
extern int AuthenticationTimeout;
|
||||||
extern int StatementTimeout;
|
|
||||||
extern int CheckPointTimeout;
|
extern int CheckPointTimeout;
|
||||||
extern bool autocommit;
|
extern bool autocommit;
|
||||||
extern int CommitDelay;
|
extern int CommitDelay;
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $Id: proc.h,v 1.61 2002/10/21 18:57:34 petere Exp $
|
* $Id: proc.h,v 1.62 2002/10/31 21:34:17 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -86,8 +86,9 @@ typedef struct PROC_HDR
|
|||||||
} PROC_HDR;
|
} PROC_HDR;
|
||||||
|
|
||||||
|
|
||||||
/* configurable option */
|
/* configurable options */
|
||||||
extern int DeadlockTimeout;
|
extern int DeadlockTimeout;
|
||||||
|
extern int StatementTimeout;
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -105,7 +106,6 @@ extern int ProcSleep(LOCKMETHODTABLE *lockMethodTable, LOCKMODE lockmode,
|
|||||||
extern PGPROC *ProcWakeup(PGPROC *proc, int errType);
|
extern PGPROC *ProcWakeup(PGPROC *proc, int errType);
|
||||||
extern void ProcLockWakeup(LOCKMETHODTABLE *lockMethodTable, LOCK *lock);
|
extern void ProcLockWakeup(LOCKMETHODTABLE *lockMethodTable, LOCK *lock);
|
||||||
extern bool LockWaitCancel(void);
|
extern bool LockWaitCancel(void);
|
||||||
extern void CheckDeadLock(void);
|
|
||||||
|
|
||||||
extern void ProcWaitForSignal(void);
|
extern void ProcWaitForSignal(void);
|
||||||
extern void ProcCancelWaitForSignal(void);
|
extern void ProcCancelWaitForSignal(void);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user