Skip to content

Commit 4db8718

Browse files
committed
Add SET statement_timeout capability. Timeout is in ms. A value of
zero turns off the timer.
1 parent ccb3f90 commit 4db8718

File tree

8 files changed

+200
-43
lines changed

8 files changed

+200
-43
lines changed

doc/src/sgml/runtime.sgml

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<!--
2-
$Header: /cvsroot/pgsql/doc/src/sgml/runtime.sgml,v 1.120 2002/07/05 01:17:20 momjian Exp $
2+
$Header: /cvsroot/pgsql/doc/src/sgml/runtime.sgml,v 1.121 2002/07/13 01:02:14 momjian Exp $
33
-->
44

55
<Chapter Id="runtime">
@@ -1584,6 +1584,16 @@ dynamic_library_path = '/usr/local/lib/postgresql:/home/my_project/lib:$libdir'
15841584
</listitem>
15851585
</varlistentry>
15861586

1587+
<varlistentry>
1588+
<term><varname>STATEMENT_TIMEOUT</varname> (<type>integer</type>)</term>
1589+
<listitem>
1590+
<para>
1591+
Aborts any statement that takes over the specified number of
1592+
microseconds. A value of zero turns off the timer.
1593+
</para>
1594+
</listitem>
1595+
</varlistentry>
1596+
15871597
<varlistentry>
15881598
<term><varname>SHARED_BUFFERS</varname> (<type>integer</type>)</term>
15891599
<listitem>

src/backend/postmaster/postmaster.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
*
3838
*
3939
* IDENTIFICATION
40-
* $Header: /cvsroot/pgsql/src/backend/postmaster/postmaster.c,v 1.280 2002/06/20 20:29:33 momjian Exp $
40+
* $Header: /cvsroot/pgsql/src/backend/postmaster/postmaster.c,v 1.281 2002/07/13 01:02:14 momjian Exp $
4141
*
4242
* NOTES
4343
*
@@ -2105,7 +2105,7 @@ DoBackend(Port *port)
21052105
* after a time delay, so that a broken client can't hog a connection
21062106
* indefinitely. PreAuthDelay doesn't count against the time limit.
21072107
*/
2108-
if (!enable_sigalrm_interrupt(AuthenticationTimeout * 1000))
2108+
if (!enable_sig_alarm(AuthenticationTimeout * 1000, false))
21092109
elog(FATAL, "DoBackend: Unable to set timer for auth timeout");
21102110

21112111
/*
@@ -2134,7 +2134,7 @@ DoBackend(Port *port)
21342134
* Done with authentication. Disable timeout, and prevent
21352135
* SIGTERM/SIGQUIT again until backend startup is complete.
21362136
*/
2137-
if (!disable_sigalrm_interrupt())
2137+
if (!disable_sig_alarm(false))
21382138
elog(FATAL, "DoBackend: Unable to disable timer for auth timeout");
21392139
PG_SETMASK(&BlockSig);
21402140

src/backend/storage/lmgr/proc.c

Lines changed: 155 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $Header: /cvsroot/pgsql/src/backend/storage/lmgr/proc.c,v 1.121 2002/06/20 20:29:35 momjian Exp $
11+
* $Header: /cvsroot/pgsql/src/backend/storage/lmgr/proc.c,v 1.122 2002/07/13 01:02:14 momjian Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -52,8 +52,10 @@
5252
#include "storage/sinval.h"
5353
#include "storage/spin.h"
5454

55-
5655
int DeadlockTimeout = 1000;
56+
int StatementTimeout = 0;
57+
int RemainingStatementTimeout = 0;
58+
bool alarm_is_statement_timeout = false;
5759

5860
PGPROC *MyProc = NULL;
5961

@@ -319,7 +321,7 @@ LockWaitCancel(void)
319321
waitingForLock = false;
320322

321323
/* Turn off the deadlock timer, if it's still running (see ProcSleep) */
322-
disable_sigalrm_interrupt();
324+
disable_sig_alarm(false);
323325

324326
/* Unlink myself from the wait queue, if on it (might not be anymore!) */
325327
LWLockAcquire(LockMgrLock, LW_EXCLUSIVE);
@@ -600,7 +602,7 @@ ProcSleep(LOCKMETHODTABLE *lockMethodTable,
600602

601603
/*
602604
* If we detected deadlock, give up without waiting. This must agree
603-
* with HandleDeadLock's recovery code, except that we shouldn't
605+
* with CheckDeadLock's recovery code, except that we shouldn't
604606
* release the semaphore since we haven't tried to lock it yet.
605607
*/
606608
if (early_deadlock)
@@ -632,13 +634,13 @@ ProcSleep(LOCKMETHODTABLE *lockMethodTable,
632634
* By delaying the check until we've waited for a bit, we can avoid
633635
* running the rather expensive deadlock-check code in most cases.
634636
*/
635-
if (!enable_sigalrm_interrupt(DeadlockTimeout))
637+
if (!enable_sig_alarm(DeadlockTimeout, false))
636638
elog(FATAL, "ProcSleep: Unable to set timer for process wakeup");
637639

638640
/*
639641
* If someone wakes us between LWLockRelease and PGSemaphoreLock,
640642
* PGSemaphoreLock will not block. The wakeup is "saved" by the
641-
* semaphore implementation. Note also that if HandleDeadLock is
643+
* semaphore implementation. Note also that if CheckDeadLock is
642644
* invoked but does not detect a deadlock, PGSemaphoreLock() will
643645
* continue to wait. There used to be a loop here, but it was useless
644646
* code...
@@ -654,7 +656,7 @@ ProcSleep(LOCKMETHODTABLE *lockMethodTable,
654656
/*
655657
* Disable the timer, if it's still running
656658
*/
657-
if (!disable_sigalrm_interrupt())
659+
if (!disable_sig_alarm(false))
658660
elog(FATAL, "ProcSleep: Unable to disable timer for process wakeup");
659661

660662
/*
@@ -785,7 +787,7 @@ ProcLockWakeup(LOCKMETHODTABLE *lockMethodTable, LOCK *lock)
785787
* --------------------
786788
*/
787789
void
788-
HandleDeadLock(SIGNAL_ARGS)
790+
CheckDeadLock(void)
789791
{
790792
int save_errno = errno;
791793

@@ -921,52 +923,180 @@ ProcSendSignal(BackendId procId)
921923
* Delay is given in milliseconds. Caller should be sure a SIGALRM
922924
* signal handler is installed before this is called.
923925
*
926+
* This code properly handles multiple alarms when the statement_timeout
927+
* alarm is specified first.
928+
*
924929
* Returns TRUE if okay, FALSE on failure.
925930
*/
926931
bool
927-
enable_sigalrm_interrupt(int delayms)
932+
enable_sig_alarm(int delayms, bool is_statement_timeout)
928933
{
929934
#ifndef __BEOS__
930-
struct itimerval timeval,
931-
dummy;
935+
struct itimerval timeval, remaining;
936+
#else
937+
bigtime_t time_interval, remaining;
938+
#endif
932939

940+
/* Don't set timer if the statement timeout scheduled before next alarm. */
941+
if (alarm_is_statement_timeout &&
942+
!is_statement_timeout &&
943+
RemainingStatementTimeout <= delayms)
944+
return true;
945+
946+
#ifndef __BEOS__
933947
MemSet(&timeval, 0, sizeof(struct itimerval));
934948
timeval.it_value.tv_sec = delayms / 1000;
935949
timeval.it_value.tv_usec = (delayms % 1000) * 1000;
936-
if (setitimer(ITIMER_REAL, &timeval, &dummy))
950+
if (setitimer(ITIMER_REAL, &timeval, &remaining))
937951
return false;
938952
#else
939953
/* BeOS doesn't have setitimer, but has set_alarm */
940-
bigtime_t time_interval;
941-
942954
time_interval = delayms * 1000; /* usecs */
943-
if (set_alarm(time_interval, B_ONE_SHOT_RELATIVE_ALARM) < 0)
955+
if ((remaining = set_alarm(time_interval, B_ONE_SHOT_RELATIVE_ALARM)) < 0)
944956
return false;
945957
#endif
946958

959+
if (is_statement_timeout)
960+
RemainingStatementTimeout = StatementTimeout;
961+
else
962+
{
963+
/* Switching to non-statement-timeout alarm, get remaining time */
964+
if (alarm_is_statement_timeout)
965+
{
966+
#ifndef __BEOS__
967+
/* We lose precision here because we convert to milliseconds */
968+
RemainingStatementTimeout = remaining.it_value.tv_sec * 1000 +
969+
remaining.it_value.tv_usec / 1000;
970+
#else
971+
RemainingStatementTimeout = remaining / 1000;
972+
#endif
973+
/* Rounding could cause a zero */
974+
if (RemainingStatementTimeout == 0)
975+
RemainingStatementTimeout = 1;
976+
}
977+
978+
if (RemainingStatementTimeout)
979+
{
980+
/* Remaining timeout alarm < delayms? */
981+
if (RemainingStatementTimeout <= delayms)
982+
{
983+
/* reinstall statement timeout alarm */
984+
alarm_is_statement_timeout = true;
985+
#ifndef __BEOS__
986+
remaining.it_value.tv_sec = RemainingStatementTimeout / 1000;
987+
remaining.it_value.tv_usec = (RemainingStatementTimeout % 1000) * 1000;
988+
if (setitimer(ITIMER_REAL, &remaining, &timeval))
989+
return false;
990+
else
991+
return true;
992+
#else
993+
remaining = RemainingStatementTimeout * 1000;
994+
if ((timeval = set_alarm(remaining, B_ONE_SHOT_RELATIVE_ALARM)) < 0)
995+
return false;
996+
else
997+
return true;
998+
#endif
999+
}
1000+
else
1001+
RemainingStatementTimeout -= delayms;
1002+
}
1003+
}
1004+
1005+
if (is_statement_timeout)
1006+
alarm_is_statement_timeout = true;
1007+
else
1008+
alarm_is_statement_timeout = false;
1009+
9471010
return true;
9481011
}
9491012

9501013
/*
951-
* Disable the SIGALRM interrupt, if it has not yet fired
1014+
* Cancel the SIGALRM timer.
1015+
*
1016+
* This is also called if the timer has fired to reschedule
1017+
* the statement_timeout timer.
9521018
*
9531019
* Returns TRUE if okay, FALSE on failure.
9541020
*/
9551021
bool
956-
disable_sigalrm_interrupt(void)
1022+
disable_sig_alarm(bool is_statement_timeout)
9571023
{
9581024
#ifndef __BEOS__
959-
struct itimerval timeval,
960-
dummy;
961-
1025+
struct itimerval timeval, remaining;
9621026
MemSet(&timeval, 0, sizeof(struct itimerval));
963-
if (setitimer(ITIMER_REAL, &timeval, &dummy))
964-
return false;
9651027
#else
966-
/* BeOS doesn't have setitimer, but has set_alarm */
967-
if (set_alarm(B_INFINITE_TIMEOUT, B_PERIODIC_ALARM) < 0)
968-
return false;
1028+
bigtime_t time_interval = 0;
9691029
#endif
9701030

1031+
if (!is_statement_timeout && RemainingStatementTimeout)
1032+
{
1033+
#ifndef __BEOS__
1034+
/* turn off timer and get remaining time, if any */
1035+
if (setitimer(ITIMER_REAL, &timeval, &remaining))
1036+
return false;
1037+
/* Add remaining time back because the timer didn't complete */
1038+
RemainingStatementTimeout += remaining.it_value.tv_sec * 1000 +
1039+
remaining.it_value.tv_usec / 1000;
1040+
/* Prepare to set timer */
1041+
timeval.it_value.tv_sec = RemainingStatementTimeout / 1000;
1042+
timeval.it_value.tv_usec = (RemainingStatementTimeout % 1000) * 1000;
1043+
#else
1044+
/* BeOS doesn't have setitimer, but has set_alarm */
1045+
if ((time_interval = set_alarm(B_INFINITE_TIMEOUT, B_PERIODIC_ALARM)) < 0)
1046+
return false;
1047+
RemainingStatementTimeout += time_interval / 1000;
1048+
time_interval = RemainingStatementTimeout * 1000;
1049+
#endif
1050+
/* Restore remaining statement timeout value */
1051+
alarm_is_statement_timeout = true;
1052+
}
1053+
/*
1054+
* Optimization: is_statement_timeout && RemainingStatementTimeout == 0
1055+
* does nothing. This is for cases where no timeout was set.
1056+
*/
1057+
if (!is_statement_timeout || RemainingStatementTimeout)
1058+
{
1059+
#ifndef __BEOS__
1060+
if (setitimer(ITIMER_REAL, &timeval, &remaining))
1061+
return false;
1062+
#else
1063+
if (time_interval)
1064+
{
1065+
if (set_alarm(time_interval, B_ONE_SHOT_RELATIVE_ALARM) < 0)
1066+
return false;
1067+
}
1068+
else
1069+
{
1070+
if (set_alarm(B_INFINITE_TIMEOUT, B_PERIODIC_ALARM) < 0)
1071+
return false;
1072+
}
1073+
#endif
1074+
}
1075+
1076+
if (is_statement_timeout)
1077+
RemainingStatementTimeout = 0;
1078+
9711079
return true;
9721080
}
1081+
1082+
1083+
/*
1084+
* Call alarm handler, either StatementCancel or Deadlock checker.
1085+
*/
1086+
void
1087+
handle_sig_alarm(SIGNAL_ARGS)
1088+
{
1089+
if (alarm_is_statement_timeout)
1090+
{
1091+
RemainingStatementTimeout = 0;
1092+
alarm_is_statement_timeout = false;
1093+
kill(MyProcPid, SIGINT);
1094+
}
1095+
else
1096+
{
1097+
CheckDeadLock();
1098+
/* Reactivate any statement_timeout alarm. */
1099+
disable_sig_alarm(false);
1100+
}
1101+
}
1102+

0 commit comments

Comments
 (0)