diff options
Diffstat (limited to 'src/backend/storage/lmgr/proc.c')
-rw-r--r-- | src/backend/storage/lmgr/proc.c | 300 |
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; } |