diff --git a/contrib/pgrowlocks/pgrowlocks.c b/contrib/pgrowlocks/pgrowlocks.c
index 075d781..9ef90cd 100644
*** a/contrib/pgrowlocks/pgrowlocks.c
--- b/contrib/pgrowlocks/pgrowlocks.c
***************
*** 138,151 **** pgrowlocks(PG_FUNCTION_ARGS)
  		infomask = tuple->t_data->t_infomask;
  
  		/*
! 		 * a tuple is locked if HTSU returns BeingUpdated, and if it returns
! 		 * MayBeUpdated but the Xmax is valid and pointing at us.
  		 */
! 		if (htsu == HeapTupleBeingUpdated ||
! 			(htsu == HeapTupleMayBeUpdated &&
! 			 !(infomask & HEAP_XMAX_INVALID) &&
! 			 !(infomask & HEAP_XMAX_IS_MULTI) &&
! 			 (xmax == GetCurrentTransactionIdIfAny())))
  		{
  			char	  **values;
  
--- 138,146 ----
  		infomask = tuple->t_data->t_infomask;
  
  		/*
! 		 * A tuple is locked if HTSU returns BeingUpdated.
  		 */
! 		if (htsu == HeapTupleBeingUpdated)
  		{
  			char	  **values;
  
diff --git a/src/backend/access/heap/hindex b5977da..6eb1de2 100644
*** a/src/backend/access/heap/heapam.c
--- b/src/backend/access/heap/heapam.c
***************
*** 2596,2601 **** l1:
--- 2596,2621 ----
  		TransactionId xwait;
  		uint16		infomask;
  
+ 		/*
+ 		 * If any subtransaction of the current top transaction already holds
+ 		 * a lock on this tuple, (and no one else does,) we must skip sleeping
+ 		 * on the xwait; that would raise an assert about sleeping on our own
+ 		 * Xid (or sleep indefinitely in a non-assertion enabled build.)  Note
+ 		 * we don't need to do this when the Xmax is a multi, because that
+ 		 * code is prepared to deal with transactions of the same session.
+ 		 *
+ 		 * This must be done before acquiring our tuple lock, to avoid
+ 		 * deadlocks with other transaction that are already waiting on the
+ 		 * lock we hold.
+ 		 */
+ 		if (!(tp.t_data->t_infomask & HEAP_XMAX_IS_MULTI) &&
+ 			TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tp.t_data)))
+ 		{
+ 			Assert(HEAP_XMAX_IS_LOCKED_ONLY(tp.t_data->t_infomask));
+ 
+ 			goto continue_deletion;
+ 		}
+ 
  		/* must copy state data before unlocking buffer */
  		xwait = HeapTupleHeaderGetRawXmax(tp.t_data);
  		infomask = tp.t_data->t_infomask;
***************
*** 2669,2674 **** l1:
--- 2689,2695 ----
  			UpdateXmaxHintBits(tp.t_data, buffer, xwait);
  		}
  
+ continue_deletion:
  		/*
  		 * We may overwrite if previous xmax aborted, or if it committed but
  		 * only locked the tuple without updating it.
***************
*** 3089,3094 **** l2:
--- 3110,3137 ----
  		 * heap_update directly.
  		 */
  
+ 		/*
+ 		 * If any subtransaction of the current top transaction already holds
+ 		 * a lock on this tuple, (and no one else does,) we must skip sleeping
+ 		 * on the xwait; that would raise an assert about sleeping on our own
+ 		 * Xid (or sleep indefinitely in a non-assertion enabled build.)  Note
+ 		 * we don't need to do this when the Xmax is a multi, because that
+ 		 * code is prepared to deal with transactions of the same session.
+ 		 *
+ 		 * This must be done before acquiring our tuple lock, to avoid
+ 		 * deadlocks with other transaction that are already waiting on the
+ 		 * lock we hold.
+ 		 */
+ 		if (!(oldtup.t_data->t_infomask & HEAP_XMAX_IS_MULTI) &&
+ 			TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(oldtup.t_data)))
+ 		{
+ 			Assert(HEAP_XMAX_IS_LOCKED_ONLY(oldtup.t_data->t_infomask));
+ 
+ 			can_continue = true;
+ 			locker_remains = true;
+ 			goto continue_updating;
+ 		}
+ 
  		/* must copy state data before unlocking buffer */
  		xwait = HeapTupleHeaderGetRawXmax(oldtup.t_data);
  		infomask = oldtup.t_data->t_infomask;
***************
*** 3223,3228 **** l2:
--- 3266,3272 ----
  			}
  		}
  
+ continue_updating:
  		result = can_continue ? HeapTupleMayBeUpdated : HeapTupleUpdated;
  	}
  
***************
*** 3945,3951 **** l3:
  		TransactionId xwait;
  		uint16		infomask;
  		uint16		infomask2;
! 		bool		require_sleep;
  		ItemPointerData t_ctid;
  
  		/* must copy state data before unlocking buffer */
--- 3989,3995 ----
  		TransactionId xwait;
  		uint16		infomask;
  		uint16		infomask2;
! 		bool		require_sleep = true;
  		ItemPointerData t_ctid;
  
  		/* must copy state data before unlocking buffer */
***************
*** 3997,4002 **** l3:
--- 4041,4064 ----
  
  			pfree(members);
  		}
+ 		else if (HEAP_XMAX_IS_LOCKED_ONLY(infomask) &&
+ 				 TransactionIdIsCurrentTransactionId(xwait))
+ 		{
+ 			/*
+ 			 * If we already hold a lock on this tuple, we can fall through
+ 			 * to perhaps create a new MultiXact including the previous lock
+ 			 * and ours, or perhaps just keep the old lock.
+ 			 */
+ 			result = HeapTupleMayBeUpdated;
+ 
+ 			LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
+ 			/* If xmax changed while we weren't looking, start over */
+ 			if ((tuple->t_data->t_infomask & HEAP_XMAX_IS_MULTI) ||
+ 				!TransactionIdEquals(HeapTupleHeaderGetRawXmax(tuple->t_data),
+ 									 xwait))
+ 				goto l3;
+ 			goto continue_locking;
+ 		}
  
  		/*
  		 * Acquire tuple lock to establish our priority for the tuple.
***************
*** 4330,4335 **** l3:
--- 4392,4399 ----
  
  		/* By here, we're certain that we hold buffer exclusive lock again */
  
+ continue_locking:
+ 
  		/*
  		 * We may lock if previous xmax aborted, or if it committed but only
  		 * locked the tuple without updating it; or if we didn't have to wait
***************
*** 4365,4401 **** failed:
  	old_infomask = tuple->t_data->t_infomask;
  
  	/*
- 	 * We might already hold the desired lock (or stronger), possibly under a
- 	 * different subtransaction of the current top transaction.  If so, there
- 	 * is no need to change state or issue a WAL record.  We already handled
- 	 * the case where this is true for xmax being a MultiXactId, so now check
- 	 * for cases where it is a plain TransactionId.
- 	 *
- 	 * Note in particular that this covers the case where we already hold
- 	 * exclusive lock on the tuple and the caller only wants key share or
- 	 * share lock. It would certainly not do to give up the exclusive lock.
- 	 */
- 	if (!(old_infomask & (HEAP_XMAX_INVALID |
- 						  HEAP_XMAX_COMMITTED |
- 						  HEAP_XMAX_IS_MULTI)) &&
- 		(mode == LockTupleKeyShare ?
- 		 (HEAP_XMAX_IS_KEYSHR_LOCKED(old_infomask) ||
- 		  HEAP_XMAX_IS_SHR_LOCKED(old_infomask) ||
- 		  HEAP_XMAX_IS_EXCL_LOCKED(old_infomask)) :
- 		 mode == LockTupleShare ?
- 		 (HEAP_XMAX_IS_SHR_LOCKED(old_infomask) ||
- 		  HEAP_XMAX_IS_EXCL_LOCKED(old_infomask)) :
- 		 (HEAP_XMAX_IS_EXCL_LOCKED(old_infomask))) &&
- 		TransactionIdIsCurrentTransactionId(xmax))
- 	{
- 		LockBuffer(*buffer, BUFFER_LOCK_UNLOCK);
- 		/* Probably can't hold tuple lock here, but may as well check */
- 		if (have_tuple_lock)
- 			UnlockTupleTuplock(relation, tid, mode);
- 		return HeapTupleMayBeUpdated;
- 	}
- 
- 	/*
  	 * If this is the first possibly-multixact-able operation in the current
  	 * transaction, set my per-backend OldestMemberMXactId setting. We can be
  	 * certain that the transaction will never become a member of any older
--- 4429,4434 ----
***************
*** 4585,4591 **** l5:
  	}
  	else if (old_infomask & HEAP_XMAX_IS_MULTI)
  	{
! 		MultiXactStatus new_status;
  
  		/*
  		 * Currently we don't allow XMAX_COMMITTED to be set for multis, so
--- 4618,4624 ----
  	}
  	else if (old_infomask & HEAP_XMAX_IS_MULTI)
  	{
! 		bool	keepmulti = false;
  
  		/*
  		 * Currently we don't allow XMAX_COMMITTED to be set for multis, so
***************
*** 4609,4630 **** l5:
  		}
  
  		/*
! 		 * If the XMAX is already a MultiXactId, then we need to expand it to
! 		 * include add_to_xmax; but if all the members were lockers and are
! 		 * all gone, we can do away with the IS_MULTI bit and just set
! 		 * add_to_xmax as the only locker/updater.	If all lockers are gone
! 		 * and we have an updater that aborted, we can also do without a
! 		 * multi.
! 		 *
! 		 * The cost of doing GetMultiXactIdMembers would be paid by
! 		 * MultiXactIdExpand if we weren't to do this, so this check is not
! 		 * incurring extra work anyhow.
  		 */
! 		if (!MultiXactIdIsRunning(xmax))
  		{
! 			if (HEAP_XMAX_IS_LOCKED_ONLY(old_infomask) ||
! 				TransactionIdDidAbort(MultiXactIdGetUpdateXid(xmax,
! 															  old_infomask)))
  			{
  				/*
  				 * Reset these bits and restart; otherwise fall through to
--- 4642,4706 ----
  		}
  
  		/*
! 		 * Examine the members of the existing MultiXact.  If any of them is
! 		 * the current transaction and holds a lock at least as strong as we
! 		 * are requesting, then the lock request can be satisfied with the
! 		 * existing MultiXact; there's no need to create a new one.  Note we
! 		 * only try this for locks, not updates, for three reasons: 1. if the
! 		 * savepoint with the update later tried to abort, we'd be screwed; 2.
! 		 * there's no way to "upgrade" a lock to an update within a multixact
! 		 * anyway; 3. there is no point in trying because if the multixact had
! 		 * a valid (non-aborted) update, the tuple would be invisible and we
! 		 * wouldn't be trying to lock it.
  		 */
! 		if (!is_update)
! 		{
! 			MultiXactMember *members;
! 			int		nmembers;
! 
! 			nmembers = GetMultiXactIdMembers(xmax, &members, true);
! 			if (nmembers > 0)
! 			{
! 				int		i;
! 				MultiXactStatus new_status;
! 
! 				new_status = get_mxact_status_for_lock(mode, false);
! 
! 				for (i = 0; i < nmembers; i++)
! 				{
! 					if (TransactionIdIsCurrentTransactionId(members[i].xid) &&
! 						(members[i].status >= new_status))
! 					{
! 						keepmulti = true;
! 						break;
! 					}
! 				}
! 				pfree(members);
! 			}
! 		}
! 
! 		if (keepmulti)
! 			new_xmax = xmax;
! 		else
  		{
! 			MultiXactStatus new_status;
! 
! 			/*
! 			 * If the XMAX is already a MultiXactId, then we need to expand it
! 			 * to include add_to_xmax; but if all the members were lockers and
! 			 * are all gone, we can do away with the IS_MULTI bit and just set
! 			 * add_to_xmax as the only locker/updater.	If all lockers are
! 			 * gone and we have an updater that aborted, we can also do
! 			 * without a multi.
! 			 *
! 			 * The cost of doing GetMultiXactIdMembers would be paid by
! 			 * MultiXactIdExpand if we weren't to do this, so this check is
! 			 * not incurring extra work anyhow.
! 			 */
! 			if (!MultiXactIdIsRunning(xmax) &&
! 				(HEAP_XMAX_IS_LOCKED_ONLY(old_infomask) ||
! 				 !TransactionIdDidCommit(MultiXactIdGetUpdateXid(xmax,
! 																 old_infomask))))
  			{
  				/*
  				 * Reset these bits and restart; otherwise fall through to
***************
*** 4634,4645 **** l5:
  				old_infomask |= HEAP_XMAX_INVALID;
  				goto l5;
  			}
- 		}
  
! 		new_status = get_mxact_status_for_lock(mode, is_update);
  
- 		new_xmax = MultiXactIdExpand((MultiXactId) xmax, add_to_xmax,
- 									 new_status);
  		GetMultiXactIdHintBits(new_xmax, &new_infomask, &new_infomask2);
  	}
  	else if (old_infomask & HEAP_XMAX_COMMITTED)
--- 4710,4722 ----
  				old_infomask |= HEAP_XMAX_INVALID;
  				goto l5;
  			}
  
! 			new_status = get_mxact_status_for_lock(mode, is_update);
! 
! 			new_xmax = MultiXactIdExpand((MultiXactId) xmax, add_to_xmax,
! 										 new_status);
! 		}
  
  		GetMultiXactIdHintBits(new_xmax, &new_infomask, &new_infomask2);
  	}
  	else if (old_infomask & HEAP_XMAX_COMMITTED)
diff --git a/src/backend/utils/time/tquindex 44b4ddc..0d82523 100644
*** a/src/backend/utils/time/tqual.c
--- b/src/backend/utils/time/tqual.c
***************
*** 846,852 **** HeapTupleSatisfiesUpdate(HeapTupleHeader tuple, CommandId curcid,
  	if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
  	{
  		if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
! 			return HeapTupleMayBeUpdated;
  		if (HeapTupleHeaderGetCmax(tuple) >= curcid)
  			return HeapTupleSelfUpdated;		/* updated after scan started */
  		else
--- 846,852 ----
  	if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
  	{
  		if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
! 			return HeapTupleBeingUpdated;
  		if (HeapTupleHeaderGetCmax(tuple) >= curcid)
  			return HeapTupleSelfUpdated;		/* updated after scan started */
  		else
diff --git a/src/include/access/xlog.index 83e5832..16a4c3b 100644
diff --git a/src/test/isolation/index f625650..2dfd872 100644
