diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 68811f1..b185c1b 100644
*** a/src/backend/tcop/postgres.c
--- b/src/backend/tcop/postgres.c
*************** ProcessInterrupts(void)
*** 2909,2914 ****
--- 2909,2917 ----
  
  	if (QueryCancelPending)
  	{
+ 		bool		lock_timeout_occurred;
+ 		bool		stmt_timeout_occurred;
+ 
  		/*
  		 * Don't allow query cancel interrupts while reading input from the
  		 * client, because we might lose sync in the FE/BE protocol.  (Die
*************** ProcessInterrupts(void)
*** 2929,2945 ****
  
  		/*
  		 * If LOCK_TIMEOUT and STATEMENT_TIMEOUT indicators are both set, we
! 		 * prefer to report the former; but be sure to clear both.
  		 */
! 		if (get_timeout_indicator(LOCK_TIMEOUT, true))
  		{
- 			(void) get_timeout_indicator(STATEMENT_TIMEOUT, true);
  			LockErrorCleanup();
  			ereport(ERROR,
  					(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
  					 errmsg("canceling statement due to lock timeout")));
  		}
! 		if (get_timeout_indicator(STATEMENT_TIMEOUT, true))
  		{
  			LockErrorCleanup();
  			ereport(ERROR,
--- 2932,2960 ----
  
  		/*
  		 * If LOCK_TIMEOUT and STATEMENT_TIMEOUT indicators are both set, we
! 		 * need to clear both, so always fetch both.
  		 */
! 		lock_timeout_occurred = get_timeout_indicator(LOCK_TIMEOUT, true);
! 		stmt_timeout_occurred = get_timeout_indicator(STATEMENT_TIMEOUT, true);
! 
! 		/*
! 		 * If both were set, we want to report whichever timeout completed
! 		 * earlier; this ensures consistent behavior if the machine is slow
! 		 * enough that the second timeout triggers before we get here.  A tie
! 		 * is arbitrarily broken in favor of reporting a lock timeout.
! 		 */
! 		if (lock_timeout_occurred && stmt_timeout_occurred &&
! 			get_timeout_finish_time(STATEMENT_TIMEOUT) < get_timeout_finish_time(LOCK_TIMEOUT))
! 			lock_timeout_occurred = false;		/* report stmt timeout */
! 
! 		if (lock_timeout_occurred)
  		{
  			LockErrorCleanup();
  			ereport(ERROR,
  					(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
  					 errmsg("canceling statement due to lock timeout")));
  		}
! 		if (stmt_timeout_occurred)
  		{
  			LockErrorCleanup();
  			ereport(ERROR,
diff --git a/src/backend/utils/misc/timeout.c b/src/backend/utils/misc/timeout.c
index 3b3f220..7171a7c 100644
*** a/src/backend/utils/misc/timeout.c
--- b/src/backend/utils/misc/timeout.c
*************** typedef struct timeout_params
*** 34,40 ****
  	timeout_handler_proc timeout_handler;
  
  	TimestampTz start_time;		/* time that timeout was last activated */
! 	TimestampTz fin_time;		/* if active, time it is due to fire */
  } timeout_params;
  
  /*
--- 34,40 ----
  	timeout_handler_proc timeout_handler;
  
  	TimestampTz start_time;		/* time that timeout was last activated */
! 	TimestampTz fin_time;		/* time it is, or was last, due to fire */
  } timeout_params;
  
  /*
*************** get_timeout_start_time(TimeoutId id)
*** 654,656 ****
--- 654,670 ----
  {
  	return all_timeouts[id].start_time;
  }
+ 
+ /*
+  * Return the time when the timeout is, or most recently was, due to fire
+  *
+  * Note: will return 0 if timeout has never been activated in this process.
+  * However, we do *not* reset the fin_time when a timeout occurs, so as
+  * not to create a race condition if SIGALRM fires just as some code is
+  * about to fetch the value.
+  */
+ TimestampTz
+ get_timeout_finish_time(TimeoutId id)
+ {
+ 	return all_timeouts[id].fin_time;
+ }
diff --git a/src/include/utils/timeout.h b/src/include/utils/timeout.h
index f64921e..260c013 100644
*** a/src/include/utils/timeout.h
--- b/src/include/utils/timeout.h
*************** extern void disable_all_timeouts(bool ke
*** 82,86 ****
--- 82,87 ----
  /* accessors */
  extern bool get_timeout_indicator(TimeoutId id, bool reset_indicator);
  extern TimestampTz get_timeout_start_time(TimeoutId id);
+ extern TimestampTz get_timeout_finish_time(TimeoutId id);
  
  #endif   /* TIMEOUT_H */
diff --git a/src/test/isolation/expected/timeouts.out b/src/test/isolation/expected/timeouts.out
index ff64627..dfba324 100644
*** a/src/test/isolation/expected/timeouts.out
--- b/src/test/isolation/expected/timeouts.out
*************** accountid      balance        
*** 28,34 ****
  
  checking       600            
  savings        600            
! step lsto: SET lock_timeout = 5000; SET statement_timeout = 6000;
  step locktbl: LOCK TABLE accounts; <waiting ...>
  step locktbl: <... completed>
  ERROR:  canceling statement due to lock timeout
--- 28,34 ----
  
  checking       600            
  savings        600            
! step lsto: SET lock_timeout = 5000; SET statement_timeout = 5010;
  step locktbl: LOCK TABLE accounts; <waiting ...>
  step locktbl: <... completed>
  ERROR:  canceling statement due to lock timeout
*************** accountid      balance        
*** 39,45 ****
  
  checking       600            
  savings        600            
! step slto: SET lock_timeout = 6000; SET statement_timeout = 5000;
  step locktbl: LOCK TABLE accounts; <waiting ...>
  step locktbl: <... completed>
  ERROR:  canceling statement due to statement timeout
--- 39,45 ----
  
  checking       600            
  savings        600            
! step slto: SET statement_timeout = 5000; SET lock_timeout = 5010;
  step locktbl: LOCK TABLE accounts; <waiting ...>
  step locktbl: <... completed>
  ERROR:  canceling statement due to statement timeout
*************** ERROR:  canceling statement due to lock 
*** 60,73 ****
  
  starting permutation: wrtbl lsto update
  step wrtbl: UPDATE accounts SET balance = balance + 100;
! step lsto: SET lock_timeout = 5000; SET statement_timeout = 6000;
  step update: DELETE FROM accounts WHERE accountid = 'checking'; <waiting ...>
  step update: <... completed>
  ERROR:  canceling statement due to lock timeout
  
  starting permutation: wrtbl slto update
  step wrtbl: UPDATE accounts SET balance = balance + 100;
! step slto: SET lock_timeout = 6000; SET statement_timeout = 5000;
  step update: DELETE FROM accounts WHERE accountid = 'checking'; <waiting ...>
  step update: <... completed>
  ERROR:  canceling statement due to statement timeout
--- 60,73 ----
  
  starting permutation: wrtbl lsto update
  step wrtbl: UPDATE accounts SET balance = balance + 100;
! step lsto: SET lock_timeout = 5000; SET statement_timeout = 5010;
  step update: DELETE FROM accounts WHERE accountid = 'checking'; <waiting ...>
  step update: <... completed>
  ERROR:  canceling statement due to lock timeout
  
  starting permutation: wrtbl slto update
  step wrtbl: UPDATE accounts SET balance = balance + 100;
! step slto: SET statement_timeout = 5000; SET lock_timeout = 5010;
  step update: DELETE FROM accounts WHERE accountid = 'checking'; <waiting ...>
  step update: <... completed>
  ERROR:  canceling statement due to statement timeout
diff --git a/src/test/isolation/specs/timeouts.spec b/src/test/isolation/specs/timeouts.spec
index 7f821d4..7d157da 100644
*** a/src/test/isolation/specs/timeouts.spec
--- b/src/test/isolation/specs/timeouts.spec
*************** session "s2"
*** 21,28 ****
  setup		{ BEGIN ISOLATION LEVEL READ COMMITTED; }
  step "sto"	{ SET statement_timeout = 5000; }
  step "lto"	{ SET lock_timeout = 5000; }
! step "lsto"	{ SET lock_timeout = 5000; SET statement_timeout = 6000; }
! step "slto"	{ SET lock_timeout = 6000; SET statement_timeout = 5000; }
  step "locktbl"	{ LOCK TABLE accounts; }
  step "update"	{ DELETE FROM accounts WHERE accountid = 'checking'; }
  teardown	{ ABORT; }
--- 21,28 ----
  setup		{ BEGIN ISOLATION LEVEL READ COMMITTED; }
  step "sto"	{ SET statement_timeout = 5000; }
  step "lto"	{ SET lock_timeout = 5000; }
! step "lsto"	{ SET lock_timeout = 5000; SET statement_timeout = 5010; }
! step "slto"	{ SET statement_timeout = 5000; SET lock_timeout = 5010; }
  step "locktbl"	{ LOCK TABLE accounts; }
  step "update"	{ DELETE FROM accounts WHERE accountid = 'checking'; }
  teardown	{ ABORT; }
