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 10:36:01.982604984 +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 occurs 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-03 15:33:14.193385578 +0200
***************
*** 24,29 ****
--- 24,30 ----
  #include "miscadmin.h"
  #include "storage/ipc.h"
  #include "storage/pg_sema.h"
+ #include "storage/proc.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 && !lock_timeout_detected);
+ 
+ 	if (lock_timeout_detected)
+ 		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 09:16:29.002527208 +0200
***************
*** 30,35 ****
--- 30,36 ----
  #include "miscadmin.h"
  #include "storage/ipc.h"
  #include "storage/pg_sema.h"
+ #include "storage/proc.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 && !lock_timeout_detected);
+ 
+ 	if (lock_timeout_detected)
+ 		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-03 15:33:14.194385583 +0200
***************
*** 16,21 ****
--- 16,22 ----
  #include "miscadmin.h"
  #include "storage/ipc.h"
  #include "storage/pg_sema.h"
+ #include "storage/proc.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 && !lock_timeout_detected);
+ 
+ 	if (lock_timeout_detected)
+ 		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 09:03:39.597690176 +0200
*************** 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 09:04:04.022811571 +0200
*************** 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 06:42:53.812799815 +0200
*************** 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 10:06:07.282576364 +0200
*************** 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/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 11:56:38.481132982 +0200
***************
*** 54,59 ****
--- 54,60 ----
  /* GUC variables */
  int			DeadlockTimeout = 1000;
  int			StatementTimeout = 0;
+ int			LockTimeout = 0;
  bool		log_lock_waits = false;
  
  /* Pointer to this process's PGPROC struct, if any */
*************** static LOCALLOCK *lockAwaited = NULL;
*** 79,103 ****
  
  /* 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.
--- 80,184 ----
  
  /* Mark these volatile because they can be changed by signal handler */
  static volatile bool standby_timeout_active = false;
  static volatile DeadLockState deadlock_state = DS_NOT_YET_CHECKED;
  volatile bool cancel_from_timeout = false;
+ volatile bool lock_timeout_detected = false;
  
  /* timeout_start_time is set when log_lock_waits is true */
  static TimestampTz timeout_start_time;
  
  static void RemoveProcFromArray(int code, Datum arg);
  static void ProcKill(int code, Datum arg);
  static void AuxiliaryProcKill(int code, Datum arg);
+ 
+ /*
+  * 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;
+ 	timeout_init	timeout_init;
+ 	timeout_destroy	timeout_destroy;
+ 	timeout_check	timeout_check;
+ 	timeout_start	timeout_start;
+ 	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,
+ 		InitDeadLock, DestroyDeadLock,
+ 		CheckDeadLock, GetCurrentTimestamp,
+ 		0
+ 	},
+ 
+ 	{
+ 		LOCK_TIMEOUT, false,
+ 		InitLockTimeout, DestroyLockTimeout,
+ 		CheckLockTimeout, GetCurrentTimestamp,
+ 		0
+ 	},
+ 
+ 	{
+ 		STATEMENT_TIMEOUT, false,
+ 		InitStatementTimeout, DestroyStatementTimeout,
+ 		CheckStatementTimeout, GetCurrentStatementStartTimestamp,
+ 		0
+ 	},
+ 
+ 	{
+ 		STANDBY_DEADLOCK_TIMEOUT, true,
+ 		InitStandbyDeadLock, DestroyStandbyDeadLock,
+ 		CheckStandbyDeadLock, GetCurrentTimestamp,
+ 		0
+ 	},
+ 
+ 	{
+ 		STANDBY_TIMEOUT, 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];
+ 
  
  /*
   * 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);
--- 732,738 ----
  		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.
--- 966,975 ----
   * 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 ****
--- 991,997 ----
  	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).
--- 1129,1148 ----
  	 * 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
--- 1159,1168 ----
  	 */
  	do
  	{
! 		PGSemaphoreTimedLock(&MyProc->sem, true);
! 
! 		if (lock_timeout_detected == true)
! 			break;
  
  		/*
  		 * waitStatus could change from STATUS_WAITING to something else
*************** 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");
  
  	/*
--- 1290,1306 ----
  	} 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 = lock_timeout_detected;
! 	if (!disable_timeout(LOCK_TIMEOUT, false))
  		elog(FATAL, "could not disable timer for process wakeup");
  
  	/*
*************** ProcSleep(LOCALLOCK *locallock, LockMeth
*** 1207,1212 ****
--- 1311,1325 ----
  	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;
  }
  
  
--- 1333,1342 ----
  	/*
  	 * 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
*** 1340,1350 ****
   * 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.
--- 1455,1472 ----
   * NB: this is run inside a signal handler, so be very wary about what is done
   * here or in called routines.
   */
! 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.
*************** CheckDeadLock(void)
*** 1439,1444 ****
--- 1561,1568 ----
  check_done:
  	for (i = NUM_LOCK_PARTITIONS; --i >= 0;)
  		LWLockRelease(FirstLockMgrLock + i);
+ 
+ 	return true;
  }
  
  
*************** ProcSendSignal(int pid)
*** 1503,1689 ****
   *****************************************************************************/
  
  /*
   * 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;
  }
  
  
--- 1627,1973 ----
   *****************************************************************************/
  
  /*
+  * 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;
+ }
+ 
+ /*
+  * Check whether the given kind of timeout is active.
+  */
+ #define is_timeout_active(tn)	(find_active_timeout(tn) >= 0)
+ 
+ /*
+  * Init and Destroy functions for different timeouts.
+  */
+ static void
+ InitDeadLock(TimestampTz start_time, TimestampTz fin_time)
+ {
+ 	if (log_lock_waits)
+ 		timeout_start_time = start_time;
+ 	base_timeouts[DEADLOCK_TIMEOUT].fin_time = fin_time;
+ }
+ 
+ static void
+ DestroyDeadLock(bool keep_indicator)
+ {
+ 	base_timeouts[DEADLOCK_TIMEOUT].fin_time = 0;
+ }
+ 
+ static void
+ InitLockTimeout(TimestampTz start_time, TimestampTz fin_time)
+ {
+ 	base_timeouts[LOCK_TIMEOUT].fin_time = fin_time;
+ }
+ 
+ static void
+ DestroyLockTimeout(bool keep_indicator)
+ {
+ 	if (!keep_indicator)
+ 		lock_timeout_detected = false;
+ 	base_timeouts[LOCK_TIMEOUT].fin_time = 0;
+ }
+ 
+ static void
+ InitStatementTimeout(TimestampTz start_time, TimestampTz fin_time)
+ {
+ 	cancel_from_timeout = false;
+ 	base_timeouts[STATEMENT_TIMEOUT].fin_time = fin_time;
+ }
+ 
+ static void
+ DestroyStatementTimeout(bool keep_indicator)
+ {
+ 	if (!keep_indicator)
+ 		cancel_from_timeout = false;
+ 	base_timeouts[STATEMENT_TIMEOUT].fin_time = 0;
+ }
+ 
+ static void
+ InitStandbyDeadLock(TimestampTz start_time, TimestampTz fin_time)
+ {
+ 	base_timeouts[STANDBY_DEADLOCK_TIMEOUT].fin_time = fin_time;
+ }
+ 
+ static void
+ DestroyStandbyDeadLock(bool keep_indicator)
+ {
+ 	base_timeouts[STANDBY_DEADLOCK_TIMEOUT].fin_time = 0;
+ }
+ 
+ static void
+ InitStandbyTimeout(TimestampTz start_time, TimestampTz fin_time)
+ {
+ 	base_timeouts[STANDBY_TIMEOUT].fin_time = fin_time;
+ }
+ 
+ static void
+ DestroyStandbyTimeout(bool keep_indicator)
+ {
+ 	base_timeouts[STANDBY_TIMEOUT].fin_time = 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;
+ }
+ 
+ 
+ /*
   * 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.
   */
  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.
   */
  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].fin_time = 0;
! 
  			cancel_from_timeout = 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;
+ }
+ 
+ 
+ /*
   * Check for statement timeout.  If the timeout time has come,
!  * trigger a query-cancel interrupt.
   *
!  * Returns true if the time has come.
   */
  static bool
  CheckStatementTimeout(void)
  {
  	TimestampTz now;
  
  	now = GetCurrentTimestamp();
  
! 	if (now >= base_timeouts[STATEMENT_TIMEOUT].fin_time)
  	{
  		/* Time to die */
  		cancel_from_timeout = true;
  #ifdef HAVE_SETSID
  		/* try to signal whole process group */
  		kill(-MyProcPid, SIGINT);
  #endif
  		kill(MyProcPid, SIGINT);
+ 
+ 		return true;
  	}
  
! 	return false;
! }
  
! /*
!  * Check for lock timeout. If the timeout time has come,
!  * indicate that our timeout was detected.
!  */
! static bool
! CheckLockTimeout(void)
! {
! 	TimestampTz now;
! 
! 	now = GetCurrentTimestamp();
! 
! 	if (now >= base_timeouts[LOCK_TIMEOUT].fin_time)
! 	{
! 		lock_timeout_detected = true;
! 		return true;
  	}
  
! 	return false;
  }
  
  
*************** handle_sig_alarm(SIGNAL_ARGS)
*** 1704,1894 ****
  	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;
  }
--- 1988,2049 ----
  	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;
! 			remove_timeout_index(0);
  		}
  
! 		if (reschedule)
! 			schedule_timeout(GetCurrentTimestamp());
! 		else
! 			disable_all_timeouts(true);
! 	}
  
! 	errno = save_errno;
  }
  
  /*
!  * CheckStandby*() 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
! CheckStandbyDeadLock(void)
  {
  	TimestampTz now;
  
  	now = GetCurrentTimestamp();
  
! 	if (now >= base_timeouts[STANDBY_DEADLOCK_TIMEOUT].fin_time)
  	{
! 		SendRecoveryConflictWithBufferPin(PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK);
! 		return true;
  	}
  
! 	return false;
  }
  
! static bool
! CheckStandbyTimeout(void)
  {
! 	TimestampTz now;
  
! 	now = GetCurrentTimestamp();
  
! 	if (now >= base_timeouts[STANDBY_TIMEOUT].fin_time)
! 	{
! 		SendRecoveryConflictWithBufferPin(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN);
! 		return true;
! 	}
! 
! 	return false;
  }
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 09:08:41.215191200 +0200
*************** start_xact_command(void)
*** 2368,2374 ****
  		/* 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;
  
--- 2368,2374 ----
  		/* 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
  			cancel_from_timeout = false;
  
*************** 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,
--- 2382,2388 ----
  	if (xact_started)
  	{
  		/* Cancel any active statement timeout before committing */
! 		disable_all_timeouts(false);
  
  		/* Now commit the command */
  		ereport(DEBUG3,
*************** 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 */
  
  		/*
--- 3728,3737 ----
  
  		/*
  		 * 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 09:09:16.103365096 +0200
*************** 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-03 15:27:36.964637553 +0200
*************** static struct config_int ConfigureNamesI
*** 1860,1865 ****
--- 1860,1876 ----
  	},
  
  	{
+ 		{"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,
  			gettext_noop("Minimum age at which VACUUM should freeze a table row."),
  			NULL
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 11:41:55.978800729 +0200
*************** extern PGPROC *PreparedXactProcs;
*** 217,225 ****
--- 217,236 ----
  /* configurable options */
  extern int	DeadlockTimeout;
  extern int	StatementTimeout;
+ extern int	LockTimeout;
  extern bool log_lock_waits;
  
+ typedef enum TimeoutName {
+ 	DEADLOCK_TIMEOUT,
+ 	LOCK_TIMEOUT,
+ 	STATEMENT_TIMEOUT,
+ 	STANDBY_DEADLOCK_TIMEOUT,
+ 	STANDBY_TIMEOUT,
+ 	TIMEOUT_MAX
+ } TimeoutName;
+ 
  extern volatile bool cancel_from_timeout;
+ extern volatile bool lock_timeout_detected;
  
  
  /*
*************** 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 */
--- 260,268 ----
  extern void ProcWaitForSignal(void);
  extern void ProcSendSignal(int pid);
  
! 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 void handle_sig_alarm(SIGNAL_ARGS);
  
  #endif   /* PROC_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
