pg92-common-lock-framework-lock_timeout-v4.patch
text/x-patch
Filename: pg92-common-lock-framework-lock_timeout-v4.patch
Type: text/x-patch
Part: 0
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: context
Series: patch v4
| File | + | − |
|---|---|---|
| doc/src/sgml/config.sgml | 32 | 1 |
| doc/src/sgml/ref/lock.sgml | 5 | 2 |
| doc/src/sgml/ref/select.sgml | 7 | 0 |
| src/backend/port/posix_sema.c | 26 | 0 |
| src/backend/port/sysv_sema.c | 31 | 0 |
| src/backend/port/win32_sema.c | 56 | 0 |
| src/backend/postmaster/autovacuum.c | 2 | 2 |
| src/backend/postmaster/postmaster.c | 4 | 4 |
| src/backend/postmaster/startup.c | 3 | 3 |
| src/backend/storage/ipc/standby.c | 14 | 13 |
| src/backend/storage/lmgr/lmgr.c | 87 | 9 |
| src/backend/storage/lmgr/lock.c | 53 | 20 |
| src/backend/storage/lmgr/Makefile | 1 | 1 |
| src/backend/storage/lmgr/proc.c | 48 | 542 |
| src/backend/storage/lmgr/timeout.c | 722 | 0 |
| src/backend/tcop/postgres.c | 7 | 6 |
| src/backend/utils/adt/lockfuncs.c | 40 | 8 |
| src/backend/utils/init/postinit.c | 3 | 3 |
| src/backend/utils/misc/guc.c | 12 | 0 |
| src/backend/utils/misc/postgresql.conf.sample | 3 | 0 |
| src/include/storage/pg_sema.h | 3 | 0 |
| src/include/storage/proc.h | 0 | 14 |
| src/include/storage/timeout.h | 40 | 0 |
| src/test/regress/expected/prepared_xacts.out | 14 | 0 |
| src/test/regress/sql/prepared_xacts.sql | 10 | 0 |
diff -dcrpN postgresql.orig/doc/src/sgml/config.sgml postgresql/doc/src/sgml/config.sgml
*** postgresql.orig/doc/src/sgml/config.sgml 2012-03-28 10:54:25.177368339 +0200
--- postgresql/doc/src/sgml/config.sgml 2012-04-04 17:11:20.197061781 +0200
*************** COPY postgres_log FROM '/full/path/to/lo
*** 4944,4950 ****
milliseconds, starting from the time the command arrives at the server
from the client. If <varname>log_min_error_statement</> is set to
<literal>ERROR</> or lower, the statement that timed out will also be
! logged. A value of zero (the default) turns this off.
</para>
<para>
--- 4944,4953 ----
milliseconds, starting from the time the command arrives at the server
from the client. If <varname>log_min_error_statement</> is set to
<literal>ERROR</> or lower, the statement that timed out will also be
! logged. The timeout may happen any time, i.e. while waiting for locks
! on database objects or in case of a large result set, during data
! retrieval from the server after all locks were successfully acquired.
! A value of zero (the default) turns this off.
</para>
<para>
*************** COPY postgres_log FROM '/full/path/to/lo
*** 4955,4960 ****
--- 4958,4991 ----
</listitem>
</varlistentry>
+ <varlistentry id="guc-lock-timeout" xreflabel="lock_timeout">
+ <term><varname>lock_timeout</varname> (<type>integer</type>)</term>
+ <indexterm>
+ <primary><varname>lock_timeout</> configuration parameter</primary>
+ </indexterm>
+ <listitem>
+ <para>
+ Abort any statement that tries to acquire a heavy-weight lock on rows,
+ pages, tables, indices or other objects and the lock has to wait more
+ than the specified number of milliseconds, starting from the time the
+ command arrives at the server from the client. If the statement involves
+ more than one such lock, the timeout applies to every one of them.
+ This makes the statement possibly wait for up to N * <varname>lock_timeout</>
+ time in the worst case where N is the number of locks attempted to acquire.
+ As opposed to <varname>statement_timeout</>, this timeout (and the error)
+ may only occur while waiting for locks. If <varname>log_min_error_statement</>
+ is set to <literal>ERROR</> or lower, the statement that timed out will
+ also be logged. A value of zero (the default) turns off the limitation.
+ </para>
+
+ <para>
+ Setting <varname>lock_timeout</> in
+ <filename>postgresql.conf</> is not recommended because it
+ affects all sessions.
+ </para>
+ </listitem>
+ </varlistentry>
+
<varlistentry id="guc-vacuum-freeze-table-age" xreflabel="vacuum_freeze_table_age">
<term><varname>vacuum_freeze_table_age</varname> (<type>integer</type>)</term>
<indexterm>
diff -dcrpN postgresql.orig/doc/src/sgml/ref/lock.sgml postgresql/doc/src/sgml/ref/lock.sgml
*** postgresql.orig/doc/src/sgml/ref/lock.sgml 2011-08-07 11:29:16.004256905 +0200
--- postgresql/doc/src/sgml/ref/lock.sgml 2012-04-03 15:54:26.543224868 +0200
*************** LOCK [ TABLE ] [ ONLY ] <replaceable cla
*** 39,46 ****
<literal>NOWAIT</literal> is specified, <command>LOCK
TABLE</command> does not wait to acquire the desired lock: if it
cannot be acquired immediately, the command is aborted and an
! error is emitted. Once obtained, the lock is held for the
! remainder of the current transaction. (There is no <command>UNLOCK
TABLE</command> command; locks are always released at transaction
end.)
</para>
--- 39,49 ----
<literal>NOWAIT</literal> is specified, <command>LOCK
TABLE</command> does not wait to acquire the desired lock: if it
cannot be acquired immediately, the command is aborted and an
! error is emitted. If <varname>lock_timeout</varname> is set to a value
! higher than 0, and the lock cannot be acquired under the specified
! timeout value in milliseconds, the command is aborted and an error
! is emitted. Once obtained, the lock is held for the remainder of
! the current transaction. (There is no <command>UNLOCK
TABLE</command> command; locks are always released at transaction
end.)
</para>
diff -dcrpN postgresql.orig/doc/src/sgml/ref/select.sgml postgresql/doc/src/sgml/ref/select.sgml
*** postgresql.orig/doc/src/sgml/ref/select.sgml 2012-01-29 20:33:53.893650856 +0100
--- postgresql/doc/src/sgml/ref/select.sgml 2012-04-04 10:30:35.035960738 +0200
*************** FOR SHARE [ OF <replaceable class="param
*** 1199,1204 ****
--- 1199,1211 ----
</para>
<para>
+ If <literal>NOWAIT</> option is not specified and <varname>lock_timeout</varname>
+ is set to a value higher than 0, and the lock needs to wait more than
+ the specified value in milliseconds, the command reports an error after
+ timing out, rather than waiting indefinitely.
+ </para>
+
+ <para>
If specific tables are named in <literal>FOR UPDATE</literal>
or <literal>FOR SHARE</literal>,
then only rows coming from those tables are locked; any other
diff -dcrpN postgresql.orig/src/backend/port/posix_sema.c postgresql/src/backend/port/posix_sema.c
*** postgresql.orig/src/backend/port/posix_sema.c 2012-01-02 12:35:11.502186906 +0100
--- postgresql/src/backend/port/posix_sema.c 2012-04-04 15:01:25.781802645 +0200
***************
*** 24,29 ****
--- 24,30 ----
#include "miscadmin.h"
#include "storage/ipc.h"
#include "storage/pg_sema.h"
+ #include "storage/timeout.h"
#ifdef USE_NAMED_POSIX_SEMAPHORES
*************** PGSemaphoreTryLock(PGSemaphore sema)
*** 313,315 ****
--- 314,341 ----
return true;
}
+
+ /*
+ * PGSemaphoreTimedLock
+ *
+ * Lock a semaphore (decrement count), blocking if count would be < 0
+ * Return if lock_timeout expired
+ */
+ void
+ PGSemaphoreTimedLock(PGSemaphore sema, bool interruptOK)
+ {
+ int errStatus;
+
+ do
+ {
+ ImmediateInterruptOK = interruptOK;
+ CHECK_FOR_INTERRUPTS();
+ errStatus = sem_wait(PG_SEM_REF(sema));
+ ImmediateInterruptOK = false;
+ } while (errStatus < 0 && errno == EINTR && !get_timeout_indicator(LOCK_TIMEOUT));
+
+ if (get_timeout_indicator(LOCK_TIMEOUT))
+ return;
+ if (errStatus < 0)
+ elog(FATAL, "sem_wait failed: %m");
+ }
diff -dcrpN postgresql.orig/src/backend/port/sysv_sema.c postgresql/src/backend/port/sysv_sema.c
*** postgresql.orig/src/backend/port/sysv_sema.c 2012-01-02 12:35:11.503186847 +0100
--- postgresql/src/backend/port/sysv_sema.c 2012-04-04 15:01:16.902757977 +0200
***************
*** 30,35 ****
--- 30,36 ----
#include "miscadmin.h"
#include "storage/ipc.h"
#include "storage/pg_sema.h"
+ #include "storage/timeout.h"
#ifndef HAVE_UNION_SEMUN
*************** PGSemaphoreTryLock(PGSemaphore sema)
*** 495,497 ****
--- 496,528 ----
return true;
}
+
+ /*
+ * PGSemaphoreTimedLock
+ *
+ * Lock a semaphore (decrement count), blocking if count would be < 0
+ * Return if lock_timeout expired
+ */
+ void
+ PGSemaphoreTimedLock(PGSemaphore sema, bool interruptOK)
+ {
+ int errStatus;
+ struct sembuf sops;
+
+ sops.sem_op = -1; /* decrement */
+ sops.sem_flg = 0;
+ sops.sem_num = sema->semNum;
+
+ do
+ {
+ ImmediateInterruptOK = interruptOK;
+ CHECK_FOR_INTERRUPTS();
+ errStatus = semop(sema->semId, &sops, 1);
+ ImmediateInterruptOK = false;
+ } while (errStatus < 0 && errno == EINTR && !get_timeout_indicator(LOCK_TIMEOUT));
+
+ if (get_timeout_indicator(LOCK_TIMEOUT))
+ return;
+ if (errStatus < 0)
+ elog(FATAL, "semop(id=%d) failed: %m", sema->semId);
+ }
diff -dcrpN postgresql.orig/src/backend/port/win32_sema.c postgresql/src/backend/port/win32_sema.c
*** postgresql.orig/src/backend/port/win32_sema.c 2012-01-02 12:35:11.504186789 +0100
--- postgresql/src/backend/port/win32_sema.c 2012-04-04 15:01:35.197850015 +0200
***************
*** 16,21 ****
--- 16,22 ----
#include "miscadmin.h"
#include "storage/ipc.h"
#include "storage/pg_sema.h"
+ #include "storage/timeout.h"
static HANDLE *mySemSet; /* IDs of sema sets acquired so far */
static int numSems; /* number of sema sets acquired so far */
*************** PGSemaphoreTryLock(PGSemaphore sema)
*** 205,207 ****
--- 206,263 ----
/* keep compiler quiet */
return false;
}
+
+ /*
+ * PGSemaphoreTimedLock
+ *
+ * Lock a semaphore (decrement count), blocking if count would be < 0.
+ * Serve the interrupt if interruptOK is true.
+ * Return if lock_timeout expired.
+ */
+ void
+ PGSemaphoreTimedLock(PGSemaphore sema, bool interruptOK)
+ {
+ DWORD ret;
+ HANDLE wh[2];
+
+ wh[0] = *sema;
+ wh[1] = pgwin32_signal_event;
+
+ /*
+ * As in other implementations of PGSemaphoreLock, we need to check for
+ * cancel/die interrupts each time through the loop. But here, there is
+ * no hidden magic about whether the syscall will internally service a
+ * signal --- we do that ourselves.
+ */
+ do
+ {
+ ImmediateInterruptOK = interruptOK;
+ CHECK_FOR_INTERRUPTS();
+
+ errno = 0;
+ ret = WaitForMultipleObjectsEx(2, wh, FALSE, INFINITE, TRUE);
+
+ if (ret == WAIT_OBJECT_0)
+ {
+ /* We got it! */
+ return;
+ }
+ else if (ret == WAIT_OBJECT_0 + 1)
+ {
+ /* Signal event is set - we have a signal to deliver */
+ pgwin32_dispatch_queued_signals();
+ errno = EINTR;
+ }
+ else
+ /* Otherwise we are in trouble */
+ errno = EIDRM;
+
+ ImmediateInterruptOK = false;
+ } while (errno == EINTR && !get_timeout_indicator(LOCK_TIMEOUT));
+
+ if (get_timeout_indicator(LOCK_TIMEOUT))
+ return;
+ if (errno != 0)
+ ereport(FATAL,
+ (errmsg("could not lock semaphore: error code %d", (int) GetLastError())));
+ }
diff -dcrpN postgresql.orig/src/backend/postmaster/autovacuum.c postgresql/src/backend/postmaster/autovacuum.c
*** postgresql.orig/src/backend/postmaster/autovacuum.c 2012-01-27 10:29:06.483649830 +0100
--- postgresql/src/backend/postmaster/autovacuum.c 2012-04-04 15:03:20.447379486 +0200
***************
*** 86,94 ****
#include "storage/ipc.h"
#include "storage/latch.h"
#include "storage/pmsignal.h"
- #include "storage/proc.h"
#include "storage/procsignal.h"
#include "storage/sinvaladt.h"
#include "tcop/tcopprot.h"
#include "utils/fmgroids.h"
#include "utils/lsyscache.h"
--- 86,94 ----
#include "storage/ipc.h"
#include "storage/latch.h"
#include "storage/pmsignal.h"
#include "storage/procsignal.h"
#include "storage/sinvaladt.h"
+ #include "storage/timeout.h"
#include "tcop/tcopprot.h"
#include "utils/fmgroids.h"
#include "utils/lsyscache.h"
*************** AutoVacLauncherMain(int argc, char *argv
*** 484,490 ****
/* Forget any pending QueryCancel request */
QueryCancelPending = false;
! disable_sig_alarm(true);
QueryCancelPending = false; /* again in case timeout occurred */
/* Report the error to the server log */
--- 484,490 ----
/* Forget any pending QueryCancel request */
QueryCancelPending = false;
! disable_all_timeouts(false);
QueryCancelPending = false; /* again in case timeout occurred */
/* Report the error to the server log */
diff -dcrpN postgresql.orig/src/backend/postmaster/postmaster.c postgresql/src/backend/postmaster/postmaster.c
*** postgresql.orig/src/backend/postmaster/postmaster.c 2012-03-29 08:36:59.775468454 +0200
--- postgresql/src/backend/postmaster/postmaster.c 2012-04-04 15:03:54.927552935 +0200
***************
*** 112,118 ****
#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"
--- 112,118 ----
#include "storage/ipc.h"
#include "storage/pg_shmem.h"
#include "storage/pmsignal.h"
! #include "storage/timeout.h"
#include "tcop/tcopprot.h"
#include "utils/builtins.h"
#include "utils/datetime.h"
*************** BackendInitialize(Port *port)
*** 3466,3474 ****
* Ready to begin client interaction. We will give up and exit(1) after a
* 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.
*/
! if (!enable_sig_alarm(AuthenticationTimeout * 1000, false))
elog(FATAL, "could not set timer for startup packet timeout");
/*
--- 3466,3474 ----
* Ready to begin client interaction. We will give up and exit(1) after a
* 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. Use the deadlock timeout interface.
*/
! if (!enable_timeout(DEADLOCK_TIMEOUT, AuthenticationTimeout * 1000))
elog(FATAL, "could not set timer for startup packet timeout");
/*
*************** BackendInitialize(Port *port)
*** 3506,3512 ****
/*
* Disable the timeout, and prevent SIGTERM/SIGQUIT again.
*/
! if (!disable_sig_alarm(false))
elog(FATAL, "could not disable timer for startup packet timeout");
PG_SETMASK(&BlockSig);
}
--- 3506,3512 ----
/*
* Disable the timeout, and prevent SIGTERM/SIGQUIT again.
*/
! if (!disable_timeout(DEADLOCK_TIMEOUT, false))
elog(FATAL, "could not disable timer for startup packet timeout");
PG_SETMASK(&BlockSig);
}
diff -dcrpN postgresql.orig/src/backend/postmaster/startup.c postgresql/src/backend/postmaster/startup.c
*** postgresql.orig/src/backend/postmaster/startup.c 2012-01-02 12:35:11.508186555 +0100
--- postgresql/src/backend/postmaster/startup.c 2012-04-04 15:04:32.902743966 +0200
***************
*** 27,33 ****
#include "storage/ipc.h"
#include "storage/latch.h"
#include "storage/pmsignal.h"
! #include "storage/proc.h"
#include "utils/guc.h"
--- 27,33 ----
#include "storage/ipc.h"
#include "storage/latch.h"
#include "storage/pmsignal.h"
! #include "storage/timeout.h"
#include "utils/guc.h"
*************** StartupProcessMain(void)
*** 195,202 ****
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);
pqsignal(SIGPIPE, SIG_IGN);
--- 195,202 ----
pqsignal(SIGTERM, StartupProcShutdownHandler); /* request shutdown */
pqsignal(SIGQUIT, startupproc_quickdie); /* hard crash time */
if (EnableHotStandby)
! pqsignal(SIGALRM, handle_sig_alarm); /* ignored unless
! * InHotStandby */
else
pqsignal(SIGALRM, SIG_IGN);
pqsignal(SIGPIPE, SIG_IGN);
diff -dcrpN postgresql.orig/src/backend/storage/ipc/standby.c postgresql/src/backend/storage/ipc/standby.c
*** postgresql.orig/src/backend/storage/ipc/standby.c 2012-02-05 12:28:36.003281960 +0100
--- postgresql/src/backend/storage/ipc/standby.c 2012-04-04 15:05:29.887030606 +0200
***************
*** 23,32 ****
#include "miscadmin.h"
#include "storage/bufmgr.h"
#include "storage/lmgr.h"
- #include "storage/proc.h"
#include "storage/procarray.h"
#include "storage/sinvaladt.h"
#include "storage/standby.h"
#include "utils/ps_status.h"
#include "utils/timestamp.h"
--- 23,32 ----
#include "miscadmin.h"
#include "storage/bufmgr.h"
#include "storage/lmgr.h"
#include "storage/procarray.h"
#include "storage/sinvaladt.h"
#include "storage/standby.h"
+ #include "storage/timeout.h"
#include "utils/ps_status.h"
#include "utils/timestamp.h"
*************** ResolveRecoveryConflictWithLock(Oid dbOi
*** 394,400 ****
void
ResolveRecoveryConflictWithBufferPin(void)
{
- bool sig_alarm_enabled = false;
TimestampTz ltime;
TimestampTz now;
--- 394,399 ----
*************** ResolveRecoveryConflictWithBufferPin(voi
*** 409,417 ****
* We're willing to wait forever for conflicts, so set timeout for
* 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");
}
else if (now >= ltime)
--- 408,414 ----
* We're willing to wait forever for conflicts, so set timeout for
* deadlock check (only)
*/
! if (!enable_timeout(STANDBY_DEADLOCK_TIMEOUT, DeadlockTimeout))
elog(FATAL, "could not set timer for process wakeup");
}
else if (now >= ltime)
*************** ResolveRecoveryConflictWithBufferPin(voi
*** 423,446 ****
}
else
{
/*
* 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");
}
/* 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");
! }
}
void
--- 420,447 ----
}
else
{
+ long secs, msecs;
+ int usecs;
+
/*
* Wake up at ltime, and check for deadlocks as well if we will be
* waiting longer than deadlock_timeout
*/
! if (!enable_timeout(STANDBY_DEADLOCK_TIMEOUT, DeadlockTimeout))
! elog(FATAL, "could not set timer for process wakeup");
!
! TimestampDifference(now, ltime, &secs, &usecs);
! msecs = secs * 1000 + usecs / 1000;
!
! if (!enable_timeout(STANDBY_TIMEOUT, msecs))
elog(FATAL, "could not set timer for process wakeup");
}
/* Wait to be signaled by UnpinBuffer() */
ProcWaitForSignal();
! if (!disable_all_timeouts(false))
! elog(FATAL, "could not disable timer for process wakeup");
}
void
diff -dcrpN postgresql.orig/src/backend/storage/lmgr/lmgr.c postgresql/src/backend/storage/lmgr/lmgr.c
*** postgresql.orig/src/backend/storage/lmgr/lmgr.c 2012-01-02 12:35:11.517186027 +0100
--- postgresql/src/backend/storage/lmgr/lmgr.c 2012-04-03 15:55:39.981617113 +0200
***************
*** 19,26 ****
--- 19,29 ----
#include "access/transam.h"
#include "access/xact.h"
#include "catalog/catalog.h"
+ #include "catalog/pg_database.h"
#include "miscadmin.h"
#include "storage/lmgr.h"
+ #include "utils/lsyscache.h"
+ #include "storage/proc.h"
#include "storage/procarray.h"
#include "utils/inval.h"
*************** LockRelationOid(Oid relid, LOCKMODE lock
*** 78,83 ****
--- 81,101 ----
res = LockAcquire(&tag, lockmode, false, false);
+ if (res == LOCKACQUIRE_NOT_AVAIL)
+ {
+ char *relname = get_rel_name(relid);
+ if (relname)
+ ereport(ERROR,
+ (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
+ errmsg("could not obtain lock on relation \"%s\"",
+ relname)));
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
+ errmsg("could not obtain lock on relation with OID %u",
+ relid)));
+ }
+
/*
* Now that we have the lock, check for invalidation messages, so that we
* will update or flush any stale relcache entry before we try to use it.
*************** LockRelation(Relation relation, LOCKMODE
*** 174,179 ****
--- 192,203 ----
res = LockAcquire(&tag, lockmode, false, false);
+ if (res == LOCKACQUIRE_NOT_AVAIL)
+ ereport(ERROR,
+ (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
+ errmsg("could not obtain lock on relation \"%s\"",
+ RelationGetRelationName(relation))));
+
/*
* Now that we have the lock, check for invalidation messages; see notes
* in LockRelationOid.
*************** LockRelationIdForSession(LockRelId *reli
*** 251,257 ****
SET_LOCKTAG_RELATION(tag, relid->dbId, relid->relId);
! (void) LockAcquire(&tag, lockmode, true, false);
}
/*
--- 275,294 ----
SET_LOCKTAG_RELATION(tag, relid->dbId, relid->relId);
! if (LockAcquire(&tag, lockmode, true, false) == LOCKACQUIRE_NOT_AVAIL)
! {
! char *relname = get_rel_name(relid->relId);
! if (relname)
! ereport(ERROR,
! (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
! errmsg("could not obtain lock on relation \"%s\"",
! relname)));
! else
! ereport(ERROR,
! (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
! errmsg("could not obtain lock on relation with OID %u",
! relid->relId)));
! }
}
/*
*************** LockRelationForExtension(Relation relati
*** 286,292 ****
relation->rd_lockInfo.lockRelId.dbId,
relation->rd_lockInfo.lockRelId.relId);
! (void) LockAcquire(&tag, lockmode, false, false);
}
/*
--- 323,333 ----
relation->rd_lockInfo.lockRelId.dbId,
relation->rd_lockInfo.lockRelId.relId);
! if (LockAcquire(&tag, lockmode, false, false) == LOCKACQUIRE_NOT_AVAIL)
! ereport(ERROR,
! (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
! errmsg("could not obtain lock on index \"%s\"",
! RelationGetRelationName(relation))));
}
/*
*************** LockPage(Relation relation, BlockNumber
*** 320,326 ****
relation->rd_lockInfo.lockRelId.relId,
blkno);
! (void) LockAcquire(&tag, lockmode, false, false);
}
/*
--- 361,371 ----
relation->rd_lockInfo.lockRelId.relId,
blkno);
! if (LockAcquire(&tag, lockmode, false, false) == LOCKACQUIRE_NOT_AVAIL)
! ereport(ERROR,
! (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
! errmsg("could not obtain lock on page %u of relation \"%s\"",
! blkno, RelationGetRelationName(relation))));
}
/*
*************** LockTuple(Relation relation, ItemPointer
*** 376,382 ****
ItemPointerGetBlockNumber(tid),
ItemPointerGetOffsetNumber(tid));
! (void) LockAcquire(&tag, lockmode, false, false);
}
/*
--- 421,431 ----
ItemPointerGetBlockNumber(tid),
ItemPointerGetOffsetNumber(tid));
! if (LockAcquire(&tag, lockmode, false, false) == LOCKACQUIRE_NOT_AVAIL)
! ereport(ERROR,
! (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
! errmsg("could not obtain lock on row in relation \"%s\"",
! RelationGetRelationName(relation))));
}
/*
*************** XactLockTableInsert(TransactionId xid)
*** 430,436 ****
SET_LOCKTAG_TRANSACTION(tag, xid);
! (void) LockAcquire(&tag, ExclusiveLock, false, false);
}
/*
--- 479,488 ----
SET_LOCKTAG_TRANSACTION(tag, xid);
! if (LockAcquire(&tag, ExclusiveLock, false, false) == LOCKACQUIRE_NOT_AVAIL)
! ereport(ERROR,
! (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
! errmsg("could not obtain lock on transaction with ID %u", xid)));
}
/*
*************** XactLockTableWait(TransactionId xid)
*** 474,480 ****
SET_LOCKTAG_TRANSACTION(tag, xid);
! (void) LockAcquire(&tag, ShareLock, false, false);
LockRelease(&tag, ShareLock, false);
--- 526,535 ----
SET_LOCKTAG_TRANSACTION(tag, xid);
! if (LockAcquire(&tag, ShareLock, false, false) == LOCKACQUIRE_NOT_AVAIL)
! ereport(ERROR,
! (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
! errmsg("could not obtain lock on transaction with ID %u", xid)));
LockRelease(&tag, ShareLock, false);
*************** LockDatabaseObject(Oid classid, Oid obji
*** 535,541 ****
objid,
objsubid);
! (void) LockAcquire(&tag, lockmode, false, false);
/* Make sure syscaches are up-to-date with any changes we waited for */
AcceptInvalidationMessages();
--- 590,600 ----
objid,
objsubid);
! if (LockAcquire(&tag, lockmode, false, false) == LOCKACQUIRE_NOT_AVAIL)
! ereport(ERROR,
! (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
! errmsg("could not obtain lock on class:object: %u:%u",
! classid, objid)));
/* Make sure syscaches are up-to-date with any changes we waited for */
AcceptInvalidationMessages();
*************** LockSharedObject(Oid classid, Oid objid,
*** 576,582 ****
objid,
objsubid);
! (void) LockAcquire(&tag, lockmode, false, false);
/* Make sure syscaches are up-to-date with any changes we waited for */
AcceptInvalidationMessages();
--- 635,645 ----
objid,
objsubid);
! if (LockAcquire(&tag, lockmode, false, false) == LOCKACQUIRE_NOT_AVAIL)
! ereport(ERROR,
! (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
! errmsg("could not obtain lock on class:object: %u:%u",
! classid, objid)));
/* Make sure syscaches are up-to-date with any changes we waited for */
AcceptInvalidationMessages();
*************** LockSharedObjectForSession(Oid classid,
*** 618,624 ****
objid,
objsubid);
! (void) LockAcquire(&tag, lockmode, true, false);
}
/*
--- 681,702 ----
objid,
objsubid);
! if (LockAcquire(&tag, lockmode, true, false) == LOCKACQUIRE_NOT_AVAIL)
! switch(classid)
! {
! case DatabaseRelationId:
! ereport(ERROR,
! (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
! errmsg("could not obtain lock on database with ID %u",
! objid)));
! break;
! default:
! ereport(ERROR,
! (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
! errmsg("could not obtain lock on class:object: %u:%u",
! classid, objid)));
! break;
! }
}
/*
diff -dcrpN postgresql.orig/src/backend/storage/lmgr/lock.c postgresql/src/backend/storage/lmgr/lock.c
*** postgresql.orig/src/backend/storage/lmgr/lock.c 2012-01-25 06:45:42.322747976 +0100
--- postgresql/src/backend/storage/lmgr/lock.c 2012-04-04 10:47:12.072945214 +0200
*************** static void RemoveLocalLock(LOCALLOCK *l
*** 338,344 ****
static PROCLOCK *SetupLockInTable(LockMethod lockMethodTable, PGPROC *proc,
const LOCKTAG *locktag, uint32 hashcode, LOCKMODE lockmode);
static void GrantLockLocal(LOCALLOCK *locallock, ResourceOwner owner);
! static void WaitOnLock(LOCALLOCK *locallock, ResourceOwner owner);
static void ReleaseLockForOwner(LOCALLOCK *locallock, ResourceOwner owner);
static bool UnGrantLock(LOCK *lock, LOCKMODE lockmode,
PROCLOCK *proclock, LockMethod lockMethodTable);
--- 338,344 ----
static PROCLOCK *SetupLockInTable(LockMethod lockMethodTable, PGPROC *proc,
const LOCKTAG *locktag, uint32 hashcode, LOCKMODE lockmode);
static void GrantLockLocal(LOCALLOCK *locallock, ResourceOwner owner);
! static int WaitOnLock(LOCALLOCK *locallock, ResourceOwner owner);
static void ReleaseLockForOwner(LOCALLOCK *locallock, ResourceOwner owner);
static bool UnGrantLock(LOCK *lock, LOCKMODE lockmode,
PROCLOCK *proclock, LockMethod lockMethodTable);
*************** ProcLockHashCode(const PROCLOCKTAG *proc
*** 544,550 ****
* dontWait: if true, don't wait to acquire lock
*
* Returns one of:
! * LOCKACQUIRE_NOT_AVAIL lock not available, and dontWait=true
* LOCKACQUIRE_OK lock successfully acquired
* LOCKACQUIRE_ALREADY_HELD incremented count for lock already held
*
--- 544,550 ----
* dontWait: if true, don't wait to acquire lock
*
* Returns one of:
! * LOCKACQUIRE_NOT_AVAIL lock not available, either dontWait=true or timeout
* LOCKACQUIRE_OK lock successfully acquired
* LOCKACQUIRE_ALREADY_HELD incremented count for lock already held
*
*************** LockAcquireExtended(const LOCKTAG *lockt
*** 863,869 ****
locktag->locktag_type,
lockmode);
! WaitOnLock(locallock, owner);
TRACE_POSTGRESQL_LOCK_WAIT_DONE(locktag->locktag_field1,
locktag->locktag_field2,
--- 863,869 ----
locktag->locktag_type,
lockmode);
! status = WaitOnLock(locallock, owner);
TRACE_POSTGRESQL_LOCK_WAIT_DONE(locktag->locktag_field1,
locktag->locktag_field2,
*************** LockAcquireExtended(const LOCKTAG *lockt
*** 878,897 ****
* done when the lock was granted to us --- see notes in WaitOnLock.
*/
! /*
! * Check the proclock entry status, in case something in the ipc
! * communication doesn't work correctly.
! */
! if (!(proclock->holdMask & LOCKBIT_ON(lockmode)))
{
! PROCLOCK_PRINT("LockAcquire: INCONSISTENT", proclock);
! LOCK_PRINT("LockAcquire: INCONSISTENT", lock, lockmode);
! /* Should we retry ? */
! LWLockRelease(partitionLock);
! elog(ERROR, "LockAcquire failed");
}
- PROCLOCK_PRINT("LockAcquire: granted", proclock);
- LOCK_PRINT("LockAcquire: granted", lock, lockmode);
}
LWLockRelease(partitionLock);
--- 878,909 ----
* done when the lock was granted to us --- see notes in WaitOnLock.
*/
! switch (status)
{
! case STATUS_OK:
! /*
! * Check the proclock entry status, in case something in the ipc
! * communication doesn't work correctly.
! */
! if (!(proclock->holdMask & LOCKBIT_ON(lockmode)))
! {
! PROCLOCK_PRINT("LockAcquire: INCONSISTENT", proclock);
! LOCK_PRINT("LockAcquire: INCONSISTENT", lock, lockmode);
! /* Should we retry ? */
! LWLockRelease(partitionLock);
! elog(ERROR, "LockAcquire failed");
! }
! PROCLOCK_PRINT("LockAcquire: granted", proclock);
! LOCK_PRINT("LockAcquire: granted", lock, lockmode);
! break;
! case STATUS_WAITING:
! PROCLOCK_PRINT("LockAcquire: timed out", proclock);
! LOCK_PRINT("LockAcquire: timed out", lock, lockmode);
! break;
! default:
! elog(ERROR, "LockAcquire invalid status");
! break;
}
}
LWLockRelease(partitionLock);
*************** LockAcquireExtended(const LOCKTAG *lockt
*** 911,917 ****
locktag->locktag_field2);
}
! return LOCKACQUIRE_OK;
}
/*
--- 923,929 ----
locktag->locktag_field2);
}
! return (status == STATUS_OK ? LOCKACQUIRE_OK : LOCKACQUIRE_NOT_AVAIL);
}
/*
*************** GrantAwaitedLock(void)
*** 1371,1384 ****
* Caller must have set MyProc->heldLocks to reflect locks already held
* on the lockable object by this process.
*
* The appropriate partition lock must be held at entry.
*/
! static void
WaitOnLock(LOCALLOCK *locallock, ResourceOwner owner)
{
LOCKMETHODID lockmethodid = LOCALLOCK_LOCKMETHOD(*locallock);
LockMethod lockMethodTable = LockMethods[lockmethodid];
char *volatile new_status = NULL;
LOCK_PRINT("WaitOnLock: sleeping on lock",
locallock->lock, locallock->tag.mode);
--- 1383,1402 ----
* Caller must have set MyProc->heldLocks to reflect locks already held
* on the lockable object by this process.
*
+ * Result: returns value of ProcSleep()
+ * STATUS_OK if we acquired the lock
+ * STATUS_ERROR if not (deadlock)
+ * STATUS_WAITING if not (timeout)
+ *
* The appropriate partition lock must be held at entry.
*/
! static int
WaitOnLock(LOCALLOCK *locallock, ResourceOwner owner)
{
LOCKMETHODID lockmethodid = LOCALLOCK_LOCKMETHOD(*locallock);
LockMethod lockMethodTable = LockMethods[lockmethodid];
char *volatile new_status = NULL;
+ int wait_status;
LOCK_PRINT("WaitOnLock: sleeping on lock",
locallock->lock, locallock->tag.mode);
*************** WaitOnLock(LOCALLOCK *locallock, Resourc
*** 1420,1427 ****
*/
PG_TRY();
{
! if (ProcSleep(locallock, lockMethodTable) != STATUS_OK)
{
/*
* We failed as a result of a deadlock, see CheckDeadLock(). Quit
* now.
--- 1438,1450 ----
*/
PG_TRY();
{
! wait_status = ProcSleep(locallock, lockMethodTable);
! switch (wait_status)
{
+ case STATUS_OK:
+ case STATUS_WAITING:
+ break;
+ default:
/*
* We failed as a result of a deadlock, see CheckDeadLock(). Quit
* now.
*************** WaitOnLock(LOCALLOCK *locallock, Resourc
*** 1466,1473 ****
pfree(new_status);
}
! LOCK_PRINT("WaitOnLock: wakeup on lock",
locallock->lock, locallock->tag.mode);
}
/*
--- 1489,1502 ----
pfree(new_status);
}
! if (wait_status == STATUS_OK)
! LOCK_PRINT("WaitOnLock: wakeup on lock",
! locallock->lock, locallock->tag.mode);
! else if (wait_status == STATUS_WAITING)
! LOCK_PRINT("WaitOnLock: timeout on lock",
locallock->lock, locallock->tag.mode);
+
+ return wait_status;
}
/*
*************** VirtualXactLock(VirtualTransactionId vxi
*** 3739,3745 ****
LWLockRelease(proc->backendLock);
/* Time to wait. */
! (void) LockAcquire(&tag, ShareLock, false, false);
LockRelease(&tag, ShareLock, false);
return true;
--- 3768,3778 ----
LWLockRelease(proc->backendLock);
/* Time to wait. */
! if (LockAcquire(&tag, ShareLock, false, false) == LOCKACQUIRE_NOT_AVAIL)
! ereport(ERROR,
! (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
! errmsg("could not obtain lock on virtual transaction with ID %u",
! vxid.localTransactionId)));
LockRelease(&tag, ShareLock, false);
return true;
diff -dcrpN postgresql.orig/src/backend/storage/lmgr/Makefile postgresql/src/backend/storage/lmgr/Makefile
*** postgresql.orig/src/backend/storage/lmgr/Makefile 2011-02-10 10:36:32.248685484 +0100
--- postgresql/src/backend/storage/lmgr/Makefile 2012-04-04 14:31:15.547760162 +0200
*************** subdir = src/backend/storage/lmgr
*** 12,18 ****
top_builddir = ../../../..
include $(top_builddir)/src/Makefile.global
! OBJS = lmgr.o lock.o proc.o deadlock.o lwlock.o spin.o s_lock.o predicate.o
include $(top_srcdir)/src/backend/common.mk
--- 12,18 ----
top_builddir = ../../../..
include $(top_builddir)/src/Makefile.global
! OBJS = lmgr.o lock.o proc.o deadlock.o lwlock.o spin.o s_lock.o predicate.o timeout.o
include $(top_srcdir)/src/backend/common.mk
diff -dcrpN postgresql.orig/src/backend/storage/lmgr/proc.c postgresql/src/backend/storage/lmgr/proc.c
*** postgresql.orig/src/backend/storage/lmgr/proc.c 2012-03-22 11:00:51.723002056 +0100
--- postgresql/src/backend/storage/lmgr/proc.c 2012-04-04 17:04:07.824916865 +0200
***************
*** 48,59 ****
#include "storage/procarray.h"
#include "storage/procsignal.h"
#include "storage/spin.h"
#include "utils/timestamp.h"
/* GUC variables */
- int DeadlockTimeout = 1000;
- int StatementTimeout = 0;
bool log_lock_waits = false;
/* Pointer to this process's PGPROC struct, if any */
--- 48,58 ----
#include "storage/procarray.h"
#include "storage/procsignal.h"
#include "storage/spin.h"
+ #include "storage/timeout.h"
#include "utils/timestamp.h"
/* GUC variables */
bool log_lock_waits = false;
/* Pointer to this process's PGPROC struct, if any */
*************** PGPROC *PreparedXactProcs = NULL;
*** 77,103 ****
/* 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;
! 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);
-
/*
* Report shared-memory space needed by InitProcGlobal.
--- 76,87 ----
/* If we are waiting for a lock, this points to the associated LOCALLOCK */
static LOCALLOCK *lockAwaited = NULL;
! /* Declared in timeout.c */
! extern volatile DeadLockState deadlock_state;
static void RemoveProcFromArray(int code, Datum arg);
static void ProcKill(int code, Datum arg);
static void AuxiliaryProcKill(int code, Datum arg);
/*
* Report shared-memory space needed by InitProcGlobal.
*************** LockWaitCancel(void)
*** 651,657 ****
return;
/* Turn off the deadlock timer, if it's still running (see ProcSleep) */
! disable_sig_alarm(false);
/* Unlink myself from the wait queue, if on it (might not be anymore!) */
partitionLock = LockHashPartitionLock(lockAwaited->hashcode);
--- 635,641 ----
return;
/* Turn off the deadlock timer, if it's still running (see ProcSleep) */
! disable_timeout(DEADLOCK_TIMEOUT, false);
/* Unlink myself from the wait queue, if on it (might not be anymore!) */
partitionLock = LockHashPartitionLock(lockAwaited->hashcode);
*************** ProcQueueInit(PROC_QUEUE *queue)
*** 885,891 ****
* The lock table's partition lock must be held at entry, and will be held
* at exit.
*
! * Result: STATUS_OK if we acquired the lock, STATUS_ERROR if not (deadlock).
*
* ASSUME: that no one will fiddle with the queue until after
* we release the partition lock.
--- 869,878 ----
* The lock table's partition lock must be held at entry, and will be held
* at exit.
*
! * Result:
! * STATUS_OK if we acquired the lock
! * STATUS_ERROR if not (deadlock)
! * STATUS_WAITING if not (timeout)
*
* ASSUME: that no one will fiddle with the queue until after
* we release the partition lock.
*************** ProcSleep(LOCALLOCK *locallock, LockMeth
*** 907,912 ****
--- 894,900 ----
LOCKMASK myHeldLocks = MyProc->heldLocks;
bool early_deadlock = false;
bool allow_autovacuum_cancel = true;
+ bool timeout_detected;
int myWaitStatus;
PGPROC *proc;
int i;
*************** ProcSleep(LOCALLOCK *locallock, LockMeth
*** 1044,1055 ****
* 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");
/*
! * If someone wakes us between LWLockRelease and PGSemaphoreLock,
! * PGSemaphoreLock will not block. The wakeup is "saved" by the semaphore
* implementation. While this is normally good, there are cases where a
* saved wakeup might be leftover from a previous operation (for example,
* we aborted ProcWaitForSignal just before someone did ProcSendSignal).
--- 1032,1051 ----
* 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_timeout(DEADLOCK_TIMEOUT, DeadlockTimeout))
elog(FATAL, "could not set timer for process wakeup");
/*
! * Reset timer so we are awaken in case of lock timeout.
! * This doesn't modify the timer for deadlock check in case
! * the deadlock check happens earlier.
! */
! if (!enable_timeout(LOCK_TIMEOUT, LockTimeout))
! elog(FATAL, "could not set timer for process wakeup");
!
! /*
! * If someone wakes us between LWLockRelease and PGSemaphoreTimedLock,
! * PGSemaphoreTimedLock will not block. The wakeup is "saved" by the semaphore
* implementation. While this is normally good, there are cases where a
* saved wakeup might be leftover from a previous operation (for example,
* we aborted ProcWaitForSignal just before someone did ProcSendSignal).
*************** ProcSleep(LOCALLOCK *locallock, LockMeth
*** 1066,1072 ****
*/
do
{
! PGSemaphoreLock(&MyProc->sem, true);
/*
* waitStatus could change from STATUS_WAITING to something else
--- 1062,1071 ----
*/
do
{
! PGSemaphoreTimedLock(&MyProc->sem, true);
!
! if (get_timeout_indicator(LOCK_TIMEOUT))
! break;
/*
* waitStatus could change from STATUS_WAITING to something else
*************** ProcSleep(LOCALLOCK *locallock, LockMeth
*** 1134,1140 ****
DescribeLockTag(&buf, &locallock->tag.lock);
modename = GetLockmodeName(locallock->tag.lock.locktag_lockmethodid,
lockmode);
! TimestampDifference(timeout_start_time, GetCurrentTimestamp(),
&secs, &usecs);
msecs = secs * 1000 + usecs / 1000;
usecs = usecs % 1000;
--- 1133,1140 ----
DescribeLockTag(&buf, &locallock->tag.lock);
modename = GetLockmodeName(locallock->tag.lock.locktag_lockmethodid,
lockmode);
! TimestampDifference(get_timeout_start(DEADLOCK_TIMEOUT),
! GetCurrentTimestamp(),
&secs, &usecs);
msecs = secs * 1000 + usecs / 1000;
usecs = usecs % 1000;
*************** ProcSleep(LOCALLOCK *locallock, LockMeth
*** 1194,1202 ****
} while (myWaitStatus == STATUS_WAITING);
/*
! * Disable the timer, if it's still running
*/
! if (!disable_sig_alarm(false))
elog(FATAL, "could not disable timer for process wakeup");
/*
--- 1194,1210 ----
} while (myWaitStatus == STATUS_WAITING);
/*
! * Disable the deadlock timer, if it's still running
*/
! if (!disable_timeout(DEADLOCK_TIMEOUT, false))
! elog(FATAL, "could not disable timer for process wakeup");
!
! /*
! * Disable the lock timeout timer, if it's still running
! * but keep the indicator for later checks.
! */
! timeout_detected = get_timeout_indicator(LOCK_TIMEOUT);
! if (!disable_timeout(LOCK_TIMEOUT, false))
elog(FATAL, "could not disable timer for process wakeup");
/*
*************** ProcSleep(LOCALLOCK *locallock, LockMeth
*** 1207,1212 ****
--- 1215,1229 ----
LWLockAcquire(partitionLock, LW_EXCLUSIVE);
/*
+ * If we're in timeout, so:
+ * 1. we're not waiting anymore and
+ * 2. we're not the one that the lock will be granted to,
+ * remove ourselves from the wait queue.
+ */
+ if (timeout_detected)
+ RemoveFromWaitQueue(MyProc, hashcode);
+
+ /*
* We no longer want LockWaitCancel to do anything.
*/
lockAwaited = NULL;
*************** ProcSleep(LOCALLOCK *locallock, LockMeth
*** 1220,1227 ****
/*
* We don't have to do anything else, because the awaker did all the
* necessary update of the lock table and MyProc.
*/
! return MyProc->waitStatus;
}
--- 1237,1246 ----
/*
* We don't have to do anything else, because the awaker did all the
* necessary update of the lock table and MyProc.
+ * RemoveFromWaitQueue() have set MyProc->waitStatus = STATUS_ERROR,
+ * we need to distinguish this case.
*/
! return (timeout_detected ? STATUS_WAITING : MyProc->waitStatus);
}
*************** ProcLockWakeup(LockMethod lockMethodTabl
*** 1328,1448 ****
}
/*
- * CheckDeadLock
- *
- * We only get to this routine if we got SIGALRM after DeadlockTimeout
- * 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.)
- * If we have a real deadlock, remove ourselves from the lock's wait queue
- * and signal an error to ProcSleep.
- *
- * NB: this is run inside a signal handler, so be very wary about what is done
- * here or in called routines.
- */
- static void
- CheckDeadLock(void)
- {
- int i;
-
- /*
- * Acquire exclusive lock on the entire shared lock data structures. Must
- * grab LWLocks in partition-number order to avoid LWLock deadlock.
- *
- * Note that the deadlock check interrupt had better not be enabled
- * anywhere that this process itself holds lock partition locks, else this
- * will wait forever. Also note that LWLockAcquire creates a critical
- * section, so that this routine cannot be interrupted by cancel/die
- * interrupts.
- */
- for (i = 0; i < NUM_LOCK_PARTITIONS; i++)
- LWLockAcquire(FirstLockMgrLock + i, LW_EXCLUSIVE);
-
- /*
- * Check to see if we've been awoken by anyone in the interim.
- *
- * If we have, we can return and resume our transaction -- happy day.
- * Before we are awoken the process releasing the lock grants it to us so
- * we know that we don't have to wait anymore.
- *
- * We check by looking to see if we've been unlinked from the wait queue.
- * This is quicker than checking our semaphore's state, since no kernel
- * call is needed, and it is safe because we hold the lock partition lock.
- */
- if (MyProc->links.prev == NULL ||
- MyProc->links.next == NULL)
- goto check_done;
-
- #ifdef LOCK_DEBUG
- if (Debug_deadlocks)
- DumpAllLocks();
- #endif
-
- /* Run the deadlock check, and set deadlock_state for use by ProcSleep */
- deadlock_state = DeadLockCheck(MyProc);
-
- if (deadlock_state == DS_HARD_DEADLOCK)
- {
- /*
- * Oops. We have a deadlock.
- *
- * Get this process out of wait state. (Note: we could do this more
- * efficiently by relying on lockAwaited, but use this coding to
- * preserve the flexibility to kill some other transaction than the
- * one detecting the deadlock.)
- *
- * RemoveFromWaitQueue sets MyProc->waitStatus to STATUS_ERROR, so
- * ProcSleep will report an error after we return from the signal
- * handler.
- */
- Assert(MyProc->waitLock != NULL);
- RemoveFromWaitQueue(MyProc, LockTagHashCode(&(MyProc->waitLock->tag)));
-
- /*
- * Unlock my semaphore so that the interrupted ProcSleep() call can
- * finish.
- */
- PGSemaphoreUnlock(&MyProc->sem);
-
- /*
- * We're done here. Transaction abort caused by the error that
- * ProcSleep will raise will cause any other locks we hold to be
- * released, thus allowing other processes to wake up; we don't need
- * to do that here. NOTE: an exception is that releasing locks we
- * hold doesn't consider the possibility of waiters that were blocked
- * behind us on the lock we just failed to get, and might now be
- * wakable because we're not in front of them anymore. However,
- * RemoveFromWaitQueue took care of waking up any such processes.
- */
- }
- else if (log_lock_waits || deadlock_state == DS_BLOCKED_BY_AUTOVACUUM)
- {
- /*
- * Unlock my semaphore so that the interrupted ProcSleep() call can
- * print the log message (we daren't do it here because we are inside
- * a signal handler). It will then sleep again until someone releases
- * the lock.
- *
- * If blocked by autovacuum, this wakeup will enable ProcSleep to send
- * the canceling signal to the autovacuum worker.
- */
- PGSemaphoreUnlock(&MyProc->sem);
- }
-
- /*
- * And release locks. We do this in reverse order for two reasons: (1)
- * Anyone else who needs more than one of the locks will be trying to lock
- * them in increasing order; we don't want to release the other process
- * until it can get all the locks it needs. (2) This avoids O(N^2)
- * behavior inside LWLockRelease.
- */
- check_done:
- for (i = NUM_LOCK_PARTITIONS; --i >= 0;)
- LWLockRelease(FirstLockMgrLock + i);
- }
-
-
- /*
* ProcWaitForSignal - wait for a signal from another backend.
*
* This can share the semaphore normally used for waiting for locks,
--- 1347,1352 ----
*************** ProcSendSignal(int pid)
*** 1494,1894 ****
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;
- }
--- 1398,1400 ----
diff -dcrpN postgresql.orig/src/backend/storage/lmgr/timeout.c postgresql/src/backend/storage/lmgr/timeout.c
*** postgresql.orig/src/backend/storage/lmgr/timeout.c 1970-01-01 01:00:00.000000000 +0100
--- postgresql/src/backend/storage/lmgr/timeout.c 2012-04-04 16:11:36.110068311 +0200
***************
*** 0 ****
--- 1,722 ----
+ /*-------------------------------------------------------------------------
+ *
+ * timeout.c
+ * routines to manage timeout sources handled by SIGALRM
+ *
+ * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/backend/storage/lmgr/timeout.c
+ *
+ *-------------------------------------------------------------------------
+ */
+ /*
+ * Interface:
+ * enable_timeout(),
+ * disable_timeout(), disable_all_timeouts()
+ * get_timeout_indicator(), get_timeout_start()
+ * and
+ * handle_sig_alarm()
+ */
+ #include "postgres.h"
+
+ #include <sys/time.h>
+
+ #include "access/xact.h"
+ #include "miscadmin.h"
+ #include "storage/proc.h"
+ #include "storage/standby.h"
+ #include "storage/timeout.h"
+ #include "utils/timestamp.h"
+
+ /* GUC variables */
+ int DeadlockTimeout = 1000;
+ int StatementTimeout = 0;
+ int LockTimeout = 0;
+
+ /*
+ * This is used by ProcSleep() in proc.c
+ * Mark this volatile because it can be changed by the signal handler.
+ */
+ volatile DeadLockState deadlock_state = DS_NOT_YET_CHECKED;
+
+
+ /*
+ * Infrastructure for timeouts
+ */
+
+ static void InitDeadLock(TimestampTz start_time, TimestampTz fin_time);
+ static void DestroyDeadLock(bool keep_indicator);
+ static bool CheckDeadLock(void);
+
+ static void InitLockTimeout(TimestampTz start_time, TimestampTz fin_time);
+ static void DestroyLockTimeout(bool keep_indicator);
+ static bool CheckLockTimeout(void);
+
+ static void InitStatementTimeout(TimestampTz start_time, TimestampTz fin_time);
+ static void DestroyStatementTimeout(bool keep_indicator);
+ static bool CheckStatementTimeout(void);
+
+ static void InitStandbyDeadLock(TimestampTz start_time, TimestampTz fin_time);
+ static void DestroyStandbyDeadLock(bool keep_indicator);
+ static bool CheckStandbyDeadLock(void);
+
+ static void InitStandbyTimeout(TimestampTz start_time, TimestampTz fin_time);
+ static void DestroyStandbyTimeout(bool keep_indicator);
+ static bool CheckStandbyTimeout(void);
+
+ typedef void (*timeout_init)(TimestampTz, TimestampTz);
+ typedef void (*timeout_destroy)(bool);
+ typedef bool (*timeout_check)(void);
+ typedef TimestampTz (*timeout_start)(void);
+
+ typedef struct {
+ TimeoutName index;
+ bool resched_next;
+
+ /* volatile because it may be changed from the signal handler */
+ volatile bool indicator;
+
+ timeout_init timeout_init;
+ timeout_destroy timeout_destroy;
+ timeout_check timeout_check;
+ timeout_start timeout_start;
+
+ TimestampTz start_time;
+ TimestampTz fin_time;
+ } timeout_params;
+
+ /*
+ * List of possible timeout reasons in the order of enum TimeoutName.
+ * The priority of timeouts (in case two of them would trigger at the
+ * same time) is determined by this order: the earlier one in the list
+ * has higher priority.
+ */
+ static timeout_params base_timeouts[TIMEOUT_MAX] = {
+ {
+ DEADLOCK_TIMEOUT, true, false,
+ InitDeadLock, DestroyDeadLock,
+ CheckDeadLock, GetCurrentTimestamp,
+ 0
+ },
+
+ {
+ LOCK_TIMEOUT, false, false,
+ InitLockTimeout, DestroyLockTimeout,
+ CheckLockTimeout, GetCurrentTimestamp,
+ 0
+ },
+
+ {
+ STATEMENT_TIMEOUT, false, false,
+ InitStatementTimeout, DestroyStatementTimeout,
+ CheckStatementTimeout, GetCurrentStatementStartTimestamp,
+ 0
+ },
+
+ {
+ STANDBY_DEADLOCK_TIMEOUT, true, false,
+ InitStandbyDeadLock, DestroyStandbyDeadLock,
+ CheckStandbyDeadLock, GetCurrentTimestamp,
+ 0
+ },
+
+ {
+ STANDBY_TIMEOUT, false, false,
+ InitStandbyTimeout, DestroyStandbyTimeout,
+ CheckStandbyTimeout, GetCurrentTimestamp,
+ 0
+ }
+ };
+
+ /*
+ * List of active timeouts ordered by their fin_time and priority.
+ */
+ static int n_timeouts = 0;
+ static timeout_params *timeouts[TIMEOUT_MAX];
+
+ /*****************************************************************************
+ * Internal helper functions
+ *****************************************************************************/
+
+ /*
+ * Find the index of a given timeout type in the active array
+ */
+ static int
+ find_active_timeout(TimeoutName tn)
+ {
+ int i;
+
+ for (i = 0; i < n_timeouts; i++)
+ {
+ if (timeouts[i]->index == tn)
+ return i;
+ }
+
+ return -1;
+ }
+
+ #define is_timeout_active(tn) (find_active_timeout(tn) >= 0)
+
+ /*
+ * Insert tn'th timeout into the list of timeouts at the given index
+ * if the previous timeout allows rescheduling the next one in the list.
+ */
+ static void
+ insert_timeout(TimeoutName tn, int index)
+ {
+ int i;
+
+ if (index > 0 && !timeouts[index-1]->resched_next)
+ return;
+
+ for (i = n_timeouts - 1; i >= index; i--)
+ timeouts[i+1] = timeouts[i];
+
+ timeouts[index] = &base_timeouts[tn];
+ n_timeouts++;
+ }
+
+ /*
+ * Remove the index'th element from the timeout list.
+ */
+ static void
+ remove_timeout_index(int index)
+ {
+ int i;
+
+ if (index < 0)
+ return;
+
+ for (i = index + 1; i < n_timeouts; i++)
+ timeouts[i-1] = timeouts[i];
+
+ if (n_timeouts > 0 && index < n_timeouts)
+ n_timeouts--;
+ }
+
+ /*
+ * (Re)schedule the next active timeout
+ */
+ static bool
+ schedule_timeout(TimestampTz now)
+ {
+ long secs;
+ int usecs;
+ struct itimerval timeval;
+
+ /* There is no active timeout, do nothing. */
+ if (n_timeouts == 0)
+ return true;
+
+ TimestampDifference(now, 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;
+ 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;
+ }
+
+ /*****************************************************************************
+ * Init, Destroy and Check functions for different timeouts.
+ *
+ * NB: all Check* functions are run inside a signal handler, so be very wary
+ * about what is done in them or in called routines.
+ *****************************************************************************/
+
+ /*
+ * Common Init and Destroy functions
+ */
+
+ static void
+ InitTimeout(TimeoutName tn, TimestampTz start_time, TimestampTz fin_time)
+ {
+ base_timeouts[tn].indicator = false;
+ base_timeouts[tn].start_time = start_time;
+ base_timeouts[tn].fin_time = fin_time;
+ }
+
+ static void
+ DestroyTimeout(TimeoutName tn, bool keep_indicator)
+ {
+ if (!keep_indicator)
+ base_timeouts[tn].indicator = false;
+ base_timeouts[tn].start_time = 0;
+ base_timeouts[tn].fin_time = 0;
+ }
+
+ /*
+ * Functions to manage deadlock
+ */
+
+ static void
+ InitDeadLock(TimestampTz start_time, TimestampTz fin_time)
+ {
+ InitTimeout(DEADLOCK_TIMEOUT, start_time, fin_time);
+ }
+
+ static void
+ DestroyDeadLock(bool keep_indicator)
+ {
+ DestroyTimeout(DEADLOCK_TIMEOUT, keep_indicator);
+ }
+
+ /*
+ * CheckDeadLock
+ *
+ * Look to see if there's a deadlock; if not, just return.
+ * (But signal ProcSleep to log a message, if log_lock_waits is true.)
+ * If we have a real deadlock, remove ourselves from the lock's wait queue
+ * and signal an error to ProcSleep.
+ */
+ static bool
+ CheckDeadLock(void)
+ {
+ TimestampTz now;
+ int i;
+
+ now = GetCurrentTimestamp();
+
+ /* If our time has not come yet, do nothing. */
+ if (now < base_timeouts[DEADLOCK_TIMEOUT].fin_time)
+ return false;
+
+ /*
+ * Acquire exclusive lock on the entire shared lock data structures. Must
+ * grab LWLocks in partition-number order to avoid LWLock deadlock.
+ *
+ * Note that the deadlock check interrupt had better not be enabled
+ * anywhere that this process itself holds lock partition locks, else this
+ * will wait forever. Also note that LWLockAcquire creates a critical
+ * section, so that this routine cannot be interrupted by cancel/die
+ * interrupts.
+ */
+ for (i = 0; i < NUM_LOCK_PARTITIONS; i++)
+ LWLockAcquire(FirstLockMgrLock + i, LW_EXCLUSIVE);
+
+ /*
+ * Check to see if we've been awoken by anyone in the interim.
+ *
+ * If we have, we can return and resume our transaction -- happy day.
+ * Before we are awoken the process releasing the lock grants it to us so
+ * we know that we don't have to wait anymore.
+ *
+ * We check by looking to see if we've been unlinked from the wait queue.
+ * This is quicker than checking our semaphore's state, since no kernel
+ * call is needed, and it is safe because we hold the lock partition lock.
+ */
+ if (MyProc->links.prev == NULL ||
+ MyProc->links.next == NULL)
+ goto check_done;
+
+ #ifdef LOCK_DEBUG
+ if (Debug_deadlocks)
+ DumpAllLocks();
+ #endif
+
+ /* Run the deadlock check, and set deadlock_state for use by ProcSleep */
+ deadlock_state = DeadLockCheck(MyProc);
+
+ if (deadlock_state == DS_HARD_DEADLOCK)
+ {
+ /*
+ * Oops. We have a deadlock.
+ *
+ * Get this process out of wait state. (Note: we could do this more
+ * efficiently by relying on lockAwaited, but use this coding to
+ * preserve the flexibility to kill some other transaction than the
+ * one detecting the deadlock.)
+ *
+ * RemoveFromWaitQueue sets MyProc->waitStatus to STATUS_ERROR, so
+ * ProcSleep will report an error after we return from the signal
+ * handler.
+ */
+ Assert(MyProc->waitLock != NULL);
+ RemoveFromWaitQueue(MyProc, LockTagHashCode(&(MyProc->waitLock->tag)));
+
+ /*
+ * Unlock my semaphore so that the interrupted ProcSleep() call can
+ * finish.
+ */
+ PGSemaphoreUnlock(&MyProc->sem);
+
+ /*
+ * We're done here. Transaction abort caused by the error that
+ * ProcSleep will raise will cause any other locks we hold to be
+ * released, thus allowing other processes to wake up; we don't need
+ * to do that here. NOTE: an exception is that releasing locks we
+ * hold doesn't consider the possibility of waiters that were blocked
+ * behind us on the lock we just failed to get, and might now be
+ * wakable because we're not in front of them anymore. However,
+ * RemoveFromWaitQueue took care of waking up any such processes.
+ */
+ }
+ else if (log_lock_waits || deadlock_state == DS_BLOCKED_BY_AUTOVACUUM)
+ {
+ /*
+ * Unlock my semaphore so that the interrupted ProcSleep() call can
+ * print the log message (we daren't do it here because we are inside
+ * a signal handler). It will then sleep again until someone releases
+ * the lock.
+ *
+ * If blocked by autovacuum, this wakeup will enable ProcSleep to send
+ * the canceling signal to the autovacuum worker.
+ */
+ PGSemaphoreUnlock(&MyProc->sem);
+ }
+
+ /*
+ * And release locks. We do this in reverse order for two reasons: (1)
+ * Anyone else who needs more than one of the locks will be trying to lock
+ * them in increasing order; we don't want to release the other process
+ * until it can get all the locks it needs. (2) This avoids O(N^2)
+ * behavior inside LWLockRelease.
+ */
+ check_done:
+ for (i = NUM_LOCK_PARTITIONS; --i >= 0;)
+ LWLockRelease(FirstLockMgrLock + i);
+
+ base_timeouts[DEADLOCK_TIMEOUT].indicator = true;
+
+ return true;
+ }
+
+ /*
+ * Functions to manage lock timeout
+ */
+
+ static void
+ InitLockTimeout(TimestampTz start_time, TimestampTz fin_time)
+ {
+ InitTimeout(LOCK_TIMEOUT, start_time, fin_time);
+ }
+
+ static void
+ DestroyLockTimeout(bool keep_indicator)
+ {
+ DestroyTimeout(LOCK_TIMEOUT, keep_indicator);
+ }
+
+ static bool
+ CheckLockTimeout(void)
+ {
+ TimestampTz now;
+
+ now = GetCurrentTimestamp();
+
+ if (now < base_timeouts[LOCK_TIMEOUT].fin_time)
+ return false;
+
+ base_timeouts[LOCK_TIMEOUT].indicator = true;
+ return true;
+ }
+
+ /*
+ * Functions to manage statement timeout
+ */
+
+ static void
+ InitStatementTimeout(TimestampTz start_time, TimestampTz fin_time)
+ {
+ InitTimeout(STATEMENT_TIMEOUT, start_time, fin_time);
+ }
+
+ static void
+ DestroyStatementTimeout(bool keep_indicator)
+ {
+ DestroyTimeout(STATEMENT_TIMEOUT, keep_indicator);
+ }
+
+ static bool
+ CheckStatementTimeout(void)
+ {
+ TimestampTz now;
+
+ now = GetCurrentTimestamp();
+
+ if (now < base_timeouts[STATEMENT_TIMEOUT].fin_time)
+ return false;
+
+ base_timeouts[STATEMENT_TIMEOUT].indicator = true;
+ #ifdef HAVE_SETSID
+ /* try to signal whole process group */
+ kill(-MyProcPid, SIGINT);
+ #endif
+ kill(MyProcPid, SIGINT);
+
+ return true;
+ }
+
+ /*
+ * Functions to manage standby deadlock functions
+ */
+
+ static void
+ InitStandbyDeadLock(TimestampTz start_time, TimestampTz fin_time)
+ {
+ InitTimeout(STANDBY_DEADLOCK_TIMEOUT, start_time, fin_time);
+ }
+
+ static void
+ DestroyStandbyDeadLock(bool keep_indicator)
+ {
+ DestroyTimeout(STANDBY_DEADLOCK_TIMEOUT, keep_indicator);
+ }
+
+ static bool
+ CheckStandbyDeadLock(void)
+ {
+ TimestampTz now;
+
+ now = GetCurrentTimestamp();
+
+ if (now < base_timeouts[STANDBY_DEADLOCK_TIMEOUT].fin_time)
+ return false;
+
+ base_timeouts[STANDBY_DEADLOCK_TIMEOUT].indicator = true;
+ SendRecoveryConflictWithBufferPin(PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK);
+ return true;
+ }
+
+ /*
+ * Functions to manage standby timeout
+ */
+
+ static void
+ InitStandbyTimeout(TimestampTz start_time, TimestampTz fin_time)
+ {
+ InitTimeout(STANDBY_TIMEOUT, start_time, fin_time);
+ }
+
+ static void
+ DestroyStandbyTimeout(bool keep_indicator)
+ {
+ DestroyTimeout(STANDBY_TIMEOUT, keep_indicator);
+ }
+
+ static bool
+ CheckStandbyTimeout(void)
+ {
+ TimestampTz now;
+
+ now = GetCurrentTimestamp();
+
+ if (now < base_timeouts[STANDBY_TIMEOUT].fin_time)
+ return false;
+
+ base_timeouts[STANDBY_TIMEOUT].indicator = true;
+ SendRecoveryConflictWithBufferPin(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN);
+ return true;
+ }
+
+ /*****************************************************************************
+ * Public API
+ *****************************************************************************/
+
+
+ /*
+ * 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 different timeout alarms.
+ *
+ * Returns TRUE if okay, FALSE on failure to set the timer.
+ */
+ bool
+ enable_timeout(TimeoutName tn, int delayms)
+ {
+ TimestampTz start_time;
+ TimestampTz fin_time;
+ int i;
+
+ Assert(!is_timeout_active(tn));
+
+ if (delayms <= 0)
+ return true;
+
+ start_time = base_timeouts[tn].timeout_start();
+ fin_time = TimestampTzPlusMilliseconds(start_time, delayms);
+
+ /* Find out the index where to insert the new timeout. */
+ for (i = 0; i < n_timeouts; i++)
+ {
+ /*
+ * The new timeout triggers earlier than
+ * a previously added one: insert here.
+ */
+ if (fin_time < timeouts[i]->fin_time)
+ break;
+ /*
+ * The new timeout triggers at the same time
+ * as the previously added one but has greater priority.
+ */
+ if (fin_time == timeouts[i]->fin_time && tn < timeouts[i]->index)
+ break;
+ }
+
+ /*
+ * Initialize the timeout parameters
+ */
+ base_timeouts[tn].timeout_init(start_time, fin_time);
+
+ insert_timeout(tn, i);
+
+ if (i > 0)
+ return true;
+
+ /* If we reach here, okay to set the timer interrupt */
+ if (!schedule_timeout(start_time))
+ return false;
+ return true;
+ }
+
+ /*
+ * Cancel the SIGALRM timer for the specific timeout.
+ * If a timeout is canceled, any other active timeout remains in force.
+ *
+ * Returns TRUE if okay, FALSE on failure to set the timer for
+ * the next timeout source.
+ */
+ bool
+ disable_timeout(TimeoutName tn, bool keep_indicator)
+ {
+ int i;
+
+ /*
+ * 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 ->check_timeout().
+ */
+ if (n_timeouts > 0)
+ {
+ struct itimerval timeval;
+
+ MemSet(&timeval, 0, sizeof(struct itimerval));
+ if (setitimer(ITIMER_REAL, &timeval, NULL))
+ {
+ n_timeouts = 0;
+ for (i = 0; i < TIMEOUT_MAX; i++)
+ base_timeouts[i].timeout_destroy(false);
+
+ return false;
+ }
+ }
+
+ /* Find the timeout and remove from the list. */
+ i = find_active_timeout(tn);
+ remove_timeout_index(i);
+
+ /* Do cleanup. */
+ base_timeouts[tn].timeout_destroy(keep_indicator);
+
+ /*
+ * If the first timeout was removed from the list and there is
+ * at least one active left, reschedule it.
+ */
+ if (i == 0 && n_timeouts > 0)
+ if (!schedule_timeout(GetCurrentTimestamp()))
+ return false;
+
+ return true;
+ }
+
+ /*
+ * Disable SIGALRM and remove all timeouts from the list and
+ * reset the timeout indicators.
+ */
+ bool
+ disable_all_timeouts(bool keep_indicators)
+ {
+ struct itimerval timeval;
+ int i;
+ bool ret;
+
+ MemSet(&timeval, 0, sizeof(struct itimerval));
+ ret = (setitimer(ITIMER_REAL, &timeval, NULL) == 0);
+
+ n_timeouts = 0;
+ for (i = 0; i < TIMEOUT_MAX; i++)
+ base_timeouts[i].timeout_destroy(keep_indicators);
+
+ return ret;
+ }
+
+ /*
+ * Return the timeout indicator
+ */
+ bool
+ get_timeout_indicator(TimeoutName tn)
+ {
+ return base_timeouts[tn].indicator;
+ }
+
+ /*
+ * Return the start of the timer for this timeout
+ */
+ TimestampTz
+ get_timeout_start(TimeoutName tn)
+ {
+ return base_timeouts[tn].start_time;
+ }
+
+ /*
+ * Signal handler for SIGALRM
+ *
+ * Process the check for the currently active timeout source and
+ * reschedule the next as needed. To avoid various edge cases,
+ * we must be careful to do nothing when there is nothing to be done.
+ */
+ void
+ handle_sig_alarm(SIGNAL_ARGS)
+ {
+ int save_errno = errno;
+
+ /*
+ * SIGALRM is cause for waking anything waiting on the process latch.
+ * Recovery (the startup process) doesn't have MyProc set so
+ * it can also use this signal handler.
+ */
+ if (MyProc)
+ SetLatch(&MyProc->procLatch);
+
+ if (n_timeouts > 0)
+ {
+ bool ret;
+ bool reschedule = true;
+
+ ret = timeouts[0]->timeout_check();
+
+ /* Shift timeouts if the timeout was triggered */
+ if (ret)
+ {
+ reschedule = timeouts[0]->resched_next;
+ /*
+ * Short circuit disable_timeout(..., true) for the
+ * timeout source that just triggered.
+ */
+ remove_timeout_index(0);
+ }
+
+ if (reschedule)
+ schedule_timeout(GetCurrentTimestamp());
+ else
+ disable_all_timeouts(true);
+ }
+
+ errno = save_errno;
+ }
diff -dcrpN postgresql.orig/src/backend/tcop/postgres.c postgresql/src/backend/tcop/postgres.c
*** postgresql.orig/src/backend/tcop/postgres.c 2012-03-28 10:54:25.195368447 +0200
--- postgresql/src/backend/tcop/postgres.c 2012-04-04 15:07:43.152700939 +0200
***************
*** 64,69 ****
--- 64,70 ----
#include "storage/proc.h"
#include "storage/procsignal.h"
#include "storage/sinval.h"
+ #include "storage/timeout.h"
#include "tcop/fastpath.h"
#include "tcop/pquery.h"
#include "tcop/tcopprot.h"
*************** start_xact_command(void)
*** 2368,2376 ****
/* 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);
else
! cancel_from_timeout = false;
xact_started = true;
}
--- 2369,2377 ----
/* Set statement timeout running, if any */
/* NB: this mustn't be enabled until we are within an xact */
if (StatementTimeout > 0)
! enable_timeout(STATEMENT_TIMEOUT, StatementTimeout);
else
! disable_timeout(STATEMENT_TIMEOUT, false);
xact_started = true;
}
*************** finish_xact_command(void)
*** 2382,2388 ****
if (xact_started)
{
/* Cancel any active statement timeout before committing */
! disable_sig_alarm(true);
/* Now commit the command */
ereport(DEBUG3,
--- 2383,2389 ----
if (xact_started)
{
/* Cancel any active statement timeout before committing */
! disable_all_timeouts(false);
/* Now commit the command */
ereport(DEBUG3,
*************** ProcessInterrupts(void)
*** 2863,2869 ****
(errcode(ERRCODE_QUERY_CANCELED),
errmsg("canceling authentication due to timeout")));
}
! if (cancel_from_timeout)
{
ImmediateInterruptOK = false; /* not idle anymore */
DisableNotifyInterrupt();
--- 2864,2870 ----
(errcode(ERRCODE_QUERY_CANCELED),
errmsg("canceling authentication due to timeout")));
}
! if (get_timeout_indicator(STATEMENT_TIMEOUT))
{
ImmediateInterruptOK = false; /* not idle anymore */
DisableNotifyInterrupt();
*************** PostgresMain(int argc, char *argv[], con
*** 3728,3737 ****
/*
* Forget any pending QueryCancel request, since we're returning to
! * the idle loop anyway, and cancel the statement timer if running.
*/
QueryCancelPending = false;
! disable_sig_alarm(true);
QueryCancelPending = false; /* again in case timeout occurred */
/*
--- 3729,3738 ----
/*
* Forget any pending QueryCancel request, since we're returning to
! * the idle loop anyway, and cancel the timer if running.
*/
QueryCancelPending = false;
! disable_all_timeouts(false);
QueryCancelPending = false; /* again in case timeout occurred */
/*
diff -dcrpN postgresql.orig/src/backend/utils/adt/lockfuncs.c postgresql/src/backend/utils/adt/lockfuncs.c
*** postgresql.orig/src/backend/utils/adt/lockfuncs.c 2012-01-02 12:35:11.533185089 +0100
--- postgresql/src/backend/utils/adt/lockfuncs.c 2012-04-04 11:24:59.796965393 +0200
*************** pg_advisory_lock_int8(PG_FUNCTION_ARGS)
*** 421,427 ****
SET_LOCKTAG_INT64(tag, key);
! (void) LockAcquire(&tag, ExclusiveLock, true, false);
PG_RETURN_VOID();
}
--- 421,431 ----
SET_LOCKTAG_INT64(tag, key);
! if (LockAcquire(&tag, ExclusiveLock, true, false) == LOCKACQUIRE_NOT_AVAIL)
! ereport(ERROR,
! (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
! errmsg("could not obtain exclusive advisory lock on value %llu",
! (long long)key)));
PG_RETURN_VOID();
}
*************** pg_advisory_xact_lock_int8(PG_FUNCTION_A
*** 438,444 ****
SET_LOCKTAG_INT64(tag, key);
! (void) LockAcquire(&tag, ExclusiveLock, false, false);
PG_RETURN_VOID();
}
--- 442,452 ----
SET_LOCKTAG_INT64(tag, key);
! if (LockAcquire(&tag, ExclusiveLock, false, false) == LOCKACQUIRE_NOT_AVAIL)
! ereport(ERROR,
! (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
! errmsg("could not obtain exclusive advisory lock on value %llu",
! (long long)key)));
PG_RETURN_VOID();
}
*************** pg_advisory_lock_shared_int8(PG_FUNCTION
*** 454,460 ****
SET_LOCKTAG_INT64(tag, key);
! (void) LockAcquire(&tag, ShareLock, true, false);
PG_RETURN_VOID();
}
--- 462,472 ----
SET_LOCKTAG_INT64(tag, key);
! if (LockAcquire(&tag, ShareLock, true, false) == LOCKACQUIRE_NOT_AVAIL)
! ereport(ERROR,
! (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
! errmsg("could not obtain shared advisory lock on value %llu",
! (long long)key)));
PG_RETURN_VOID();
}
*************** pg_advisory_xact_lock_shared_int8(PG_FUN
*** 471,477 ****
SET_LOCKTAG_INT64(tag, key);
! (void) LockAcquire(&tag, ShareLock, false, false);
PG_RETURN_VOID();
}
--- 483,493 ----
SET_LOCKTAG_INT64(tag, key);
! if (LockAcquire(&tag, ShareLock, false, false) == LOCKACQUIRE_NOT_AVAIL)
! ereport(ERROR,
! (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
! errmsg("could not obtain shared advisory lock on value %llu",
! (long long)key)));
PG_RETURN_VOID();
}
*************** pg_advisory_lock_int4(PG_FUNCTION_ARGS)
*** 604,610 ****
SET_LOCKTAG_INT32(tag, key1, key2);
! (void) LockAcquire(&tag, ExclusiveLock, true, false);
PG_RETURN_VOID();
}
--- 620,630 ----
SET_LOCKTAG_INT32(tag, key1, key2);
! if (LockAcquire(&tag, ExclusiveLock, true, false) == LOCKACQUIRE_NOT_AVAIL)
! ereport(ERROR,
! (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
! errmsg("could not obtain exclusive advisory lock on values %u:%u",
! key1, key2)));
PG_RETURN_VOID();
}
*************** pg_advisory_xact_lock_int4(PG_FUNCTION_A
*** 622,628 ****
SET_LOCKTAG_INT32(tag, key1, key2);
! (void) LockAcquire(&tag, ExclusiveLock, false, false);
PG_RETURN_VOID();
}
--- 642,652 ----
SET_LOCKTAG_INT32(tag, key1, key2);
! if (LockAcquire(&tag, ExclusiveLock, false, false) == LOCKACQUIRE_NOT_AVAIL)
! ereport(ERROR,
! (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
! errmsg("could not obtain exclusive advisory lock on values %u:%u",
! key1, key2)));
PG_RETURN_VOID();
}
*************** pg_advisory_lock_shared_int4(PG_FUNCTION
*** 639,645 ****
SET_LOCKTAG_INT32(tag, key1, key2);
! (void) LockAcquire(&tag, ShareLock, true, false);
PG_RETURN_VOID();
}
--- 663,673 ----
SET_LOCKTAG_INT32(tag, key1, key2);
! if (LockAcquire(&tag, ShareLock, true, false) == LOCKACQUIRE_NOT_AVAIL)
! ereport(ERROR,
! (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
! errmsg("could not obtain shared advisory lock on values %u:%u",
! key1, key2)));
PG_RETURN_VOID();
}
*************** pg_advisory_xact_lock_shared_int4(PG_FUN
*** 657,663 ****
SET_LOCKTAG_INT32(tag, key1, key2);
! (void) LockAcquire(&tag, ShareLock, false, false);
PG_RETURN_VOID();
}
--- 685,695 ----
SET_LOCKTAG_INT32(tag, key1, key2);
! if (LockAcquire(&tag, ShareLock, false, false) == LOCKACQUIRE_NOT_AVAIL)
! ereport(ERROR,
! (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
! errmsg("could not obtain shared advisory lock on values %u:%u",
! key1, key2)));
PG_RETURN_VOID();
}
diff -dcrpN postgresql.orig/src/backend/utils/init/postinit.c postgresql/src/backend/utils/init/postinit.c
*** postgresql.orig/src/backend/utils/init/postinit.c 2012-01-20 13:59:24.913240794 +0100
--- postgresql/src/backend/utils/init/postinit.c 2012-04-04 15:08:31.671944981 +0200
***************
*** 41,51 ****
#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/sinvaladt.h"
#include "storage/smgr.h"
#include "tcop/tcopprot.h"
#include "utils/acl.h"
#include "utils/fmgroids.h"
--- 41,51 ----
#include "storage/fd.h"
#include "storage/ipc.h"
#include "storage/lmgr.h"
#include "storage/procarray.h"
#include "storage/procsignal.h"
#include "storage/sinvaladt.h"
#include "storage/smgr.h"
+ #include "storage/timeout.h"
#include "tcop/tcopprot.h"
#include "utils/acl.h"
#include "utils/fmgroids.h"
*************** PerformAuthentication(Port *port)
*** 204,210 ****
* 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");
/*
--- 204,210 ----
* during authentication. Since we're inside a transaction and might do
* database access, we have to use the statement_timeout infrastructure.
*/
! if (!enable_timeout(STATEMENT_TIMEOUT, AuthenticationTimeout * 1000))
elog(FATAL, "could not set timer for authorization timeout");
/*
*************** PerformAuthentication(Port *port)
*** 215,221 ****
/*
* Done with authentication. Disable the timeout, and log if needed.
*/
! if (!disable_sig_alarm(true))
elog(FATAL, "could not disable timer for authorization timeout");
if (Log_connections)
--- 215,221 ----
/*
* Done with authentication. Disable the timeout, and log if needed.
*/
! if (!disable_timeout(STATEMENT_TIMEOUT, false))
elog(FATAL, "could not disable timer for authorization timeout");
if (Log_connections)
diff -dcrpN postgresql.orig/src/backend/utils/misc/guc.c postgresql/src/backend/utils/misc/guc.c
*** postgresql.orig/src/backend/utils/misc/guc.c 2012-03-28 10:54:25.196368452 +0200
--- postgresql/src/backend/utils/misc/guc.c 2012-04-04 15:09:07.395124656 +0200
***************
*** 63,68 ****
--- 63,69 ----
#include "storage/standby.h"
#include "storage/fd.h"
#include "storage/predicate.h"
+ #include "storage/timeout.h"
#include "tcop/tcopprot.h"
#include "tsearch/ts_cache.h"
#include "utils/builtins.h"
*************** static struct config_int ConfigureNamesI
*** 1858,1863 ****
--- 1859,1875 ----
0, 0, INT_MAX,
NULL, NULL, NULL
},
+
+ {
+ {"lock_timeout", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ gettext_noop("Sets the maximum allowed timeout for any lock taken by a statement."),
+ gettext_noop("A value of 0 turns off the timeout."),
+ GUC_UNIT_MS
+ },
+ &LockTimeout,
+ 0, 0, INT_MAX,
+ NULL, NULL, NULL
+ },
{
{"vacuum_freeze_min_age", PGC_USERSET, CLIENT_CONN_STATEMENT,
diff -dcrpN postgresql.orig/src/backend/utils/misc/postgresql.conf.sample postgresql/src/backend/utils/misc/postgresql.conf.sample
*** postgresql.orig/src/backend/utils/misc/postgresql.conf.sample 2012-03-28 10:54:25.197368458 +0200
--- postgresql/src/backend/utils/misc/postgresql.conf.sample 2012-04-03 15:29:03.725060295 +0200
***************
*** 527,532 ****
--- 527,535 ----
#------------------------------------------------------------------------------
#deadlock_timeout = 1s
+ #lock_timeout = 0 # timeout value for heavy-weight locks
+ # taken by statements. 0 disables timeout
+ # unit in milliseconds, default is 0
#max_locks_per_transaction = 64 # min 10
# (change requires restart)
# Note: Each lock table slot uses ~270 bytes of shared memory, and there are
diff -dcrpN postgresql.orig/src/include/storage/pg_sema.h postgresql/src/include/storage/pg_sema.h
*** postgresql.orig/src/include/storage/pg_sema.h 2012-01-02 12:35:11.640178818 +0100
--- postgresql/src/include/storage/pg_sema.h 2012-04-03 15:30:52.716621135 +0200
*************** extern void PGSemaphoreUnlock(PGSemaphor
*** 80,83 ****
--- 80,86 ----
/* Lock a semaphore only if able to do so without blocking */
extern bool PGSemaphoreTryLock(PGSemaphore sema);
+ /* Lock a semaphore (decrement count), blocking if count would be < 0 */
+ extern void PGSemaphoreTimedLock(PGSemaphore sema, bool interruptOK);
+
#endif /* PG_SEMA_H */
diff -dcrpN postgresql.orig/src/include/storage/proc.h postgresql/src/include/storage/proc.h
*** postgresql.orig/src/include/storage/proc.h 2012-01-31 09:43:43.936392561 +0100
--- postgresql/src/include/storage/proc.h 2012-04-04 15:40:27.595781818 +0200
*************** extern PGPROC *PreparedXactProcs;
*** 215,227 ****
/* configurable options */
- extern int DeadlockTimeout;
- extern int StatementTimeout;
extern bool log_lock_waits;
- extern volatile bool cancel_from_timeout;
-
-
/*
* Function Prototypes
*/
--- 215,222 ----
*************** extern void LockWaitCancel(void);
*** 249,261 ****
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 */
--- 244,247 ----
diff -dcrpN postgresql.orig/src/include/storage/timeout.h postgresql/src/include/storage/timeout.h
*** postgresql.orig/src/include/storage/timeout.h 1970-01-01 01:00:00.000000000 +0100
--- postgresql/src/include/storage/timeout.h 2012-04-04 15:02:21.255081715 +0200
***************
*** 0 ****
--- 1,40 ----
+ /*-------------------------------------------------------------------------
+ *
+ * timeout.h
+ * SIGALRM timeout API
+ *
+ *
+ * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/storage/timeout.h
+ *
+ *-------------------------------------------------------------------------
+ */
+ #ifndef _TIMEOUT_H_
+ #define _TIMEOUT_H_
+
+ #include "datatype/timestamp.h"
+
+ /* configurable options */
+ extern int DeadlockTimeout;
+ extern int StatementTimeout;
+ extern int LockTimeout;
+
+ typedef enum TimeoutName {
+ DEADLOCK_TIMEOUT,
+ LOCK_TIMEOUT,
+ STATEMENT_TIMEOUT,
+ STANDBY_DEADLOCK_TIMEOUT,
+ STANDBY_TIMEOUT,
+ TIMEOUT_MAX
+ } TimeoutName;
+
+ extern bool enable_timeout(TimeoutName tn, int delayms);
+ extern bool disable_timeout(TimeoutName tn, bool keep_indicator);
+ extern bool disable_all_timeouts(bool keep_indicators);
+ extern bool get_timeout_indicator(TimeoutName tn);
+ extern TimestampTz get_timeout_start(TimeoutName tn);
+ extern void handle_sig_alarm(SIGNAL_ARGS);
+
+ #endif /* _TIMEOUT_H_ */
diff -dcrpN postgresql.orig/src/test/regress/expected/prepared_xacts.out postgresql/src/test/regress/expected/prepared_xacts.out
*** postgresql.orig/src/test/regress/expected/prepared_xacts.out 2011-08-16 09:39:01.117194748 +0200
--- postgresql/src/test/regress/expected/prepared_xacts.out 2012-04-04 12:22:18.719786919 +0200
*************** set statement_timeout to 2000;
*** 198,203 ****
--- 198,210 ----
SELECT * FROM pxtest3;
ERROR: canceling statement due to statement timeout
reset statement_timeout;
+ -- pxtest3 should be locked because of the pending DROP
+ set lock_timeout to 2000;
+ SELECT * FROM pxtest3;
+ ERROR: could not obtain lock on relation "pxtest3"
+ LINE 1: SELECT * FROM pxtest3;
+ ^
+ reset lock_timeout;
-- Disconnect, we will continue testing in a different backend
\c -
-- There should still be two prepared transactions
*************** set statement_timeout to 2000;
*** 213,218 ****
--- 220,232 ----
SELECT * FROM pxtest3;
ERROR: canceling statement due to statement timeout
reset statement_timeout;
+ -- pxtest3 should be locked because of the pending DROP
+ set lock_timeout to 2000;
+ SELECT * FROM pxtest3;
+ ERROR: could not obtain lock on relation "pxtest3"
+ LINE 1: SELECT * FROM pxtest3;
+ ^
+ reset lock_timeout;
-- Commit table creation
COMMIT PREPARED 'regress-one';
\d pxtest2
diff -dcrpN postgresql.orig/src/test/regress/sql/prepared_xacts.sql postgresql/src/test/regress/sql/prepared_xacts.sql
*** postgresql.orig/src/test/regress/sql/prepared_xacts.sql 2011-07-18 15:42:00.117371861 +0200
--- postgresql/src/test/regress/sql/prepared_xacts.sql 2012-04-04 12:20:31.456241386 +0200
*************** set statement_timeout to 2000;
*** 126,131 ****
--- 126,136 ----
SELECT * FROM pxtest3;
reset statement_timeout;
+ -- pxtest3 should be locked because of the pending DROP
+ set lock_timeout to 2000;
+ SELECT * FROM pxtest3;
+ reset lock_timeout;
+
-- Disconnect, we will continue testing in a different backend
\c -
*************** set statement_timeout to 2000;
*** 137,142 ****
--- 142,152 ----
SELECT * FROM pxtest3;
reset statement_timeout;
+ -- pxtest3 should be locked because of the pending DROP
+ set lock_timeout to 2000;
+ SELECT * FROM pxtest3;
+ reset lock_timeout;
+
-- Commit table creation
COMMIT PREPARED 'regress-one';
\d pxtest2