diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index 2ca1c14..0661b1c 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -1694,9 +1694,10 @@ StartTransaction(void)
 	vxid.localTransactionId = GetNextLocalTransactionId();
 
 	/*
-	 * Lock the virtual transaction id before we announce it in the proc array
+	 * Prior to 9.1 we used to create a lock table entry for the vxid, but
+	 * that was only needed by a few operations, so its cheaper overall
+	 * to avoid this and perform the waits another way.
 	 */
-	VirtualXactLockTableInsert(vxid);
 
 	/*
 	 * Advertise it in the proc array.	We assume assignment of
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index b91e4a4..b2531d1 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -481,7 +481,7 @@ DefineIndex(RangeVar *heapRelation,
 
 	while (VirtualTransactionIdIsValid(*old_lockholders))
 	{
-		VirtualXactLockTableWait(*old_lockholders);
+		VirtualXactWait(*old_lockholders);
 		old_lockholders++;
 	}
 
@@ -567,7 +567,7 @@ DefineIndex(RangeVar *heapRelation,
 
 	while (VirtualTransactionIdIsValid(*old_lockholders))
 	{
-		VirtualXactLockTableWait(*old_lockholders);
+		VirtualXactWait(*old_lockholders);
 		old_lockholders++;
 	}
 
@@ -664,7 +664,7 @@ DefineIndex(RangeVar *heapRelation,
 		}
 
 		if (VirtualTransactionIdIsValid(old_snapshots[i]))
-			VirtualXactLockTableWait(old_snapshots[i]);
+			VirtualXactWait(old_snapshots[i]);
 	}
 
 	/*
diff --git a/src/backend/storage/ipc/procarray.c b/src/backend/storage/ipc/procarray.c
index e7593fa..9fef532 100644
--- a/src/backend/storage/ipc/procarray.c
+++ b/src/backend/storage/ipc/procarray.c
@@ -51,10 +51,12 @@
 #include "access/xact.h"
 #include "access/twophase.h"
 #include "miscadmin.h"
+#include "pgstat.h"
 #include "storage/procarray.h"
 #include "storage/spin.h"
 #include "storage/standby.h"
 #include "utils/builtins.h"
+#include "utils/ps_status.h"
 #include "utils/snapmgr.h"
 
 
@@ -1862,6 +1864,121 @@ GetConflictingVirtualXIDs(TransactionId limitXmin, Oid dbOid)
 }
 
 /*
+ *		VirtualXactExists
+ *
+ * Does the VXID still exist? Could also be called ConditionalVirtualXactWait()
+ * but that doesn't describe this very well.
+ */
+bool
+VirtualXactExists(VirtualTransactionId vxid)
+{
+	ProcArrayStruct *arrayP = procArray;
+	int			index;
+	bool		found = false;
+
+	/* Fast exit, to avoid overhead if there's no work to be done. */
+	if (!VirtualTransactionIdIsValid(vxid))
+		return false;
+
+	LWLockAcquire(ProcArrayLock, LW_SHARED);
+
+	for (index = 0; index < arrayP->numProcs; index++)
+	{
+		VirtualTransactionId procvxid;
+		PGPROC	   *proc = arrayP->procs[index];
+
+		GET_VXID_FROM_PGPROC(procvxid, *proc);
+
+		if (procvxid.backendId == vxid.backendId &&
+			procvxid.localTransactionId == vxid.localTransactionId)
+		{
+			found = true;
+			break;
+		}
+	}
+
+	LWLockRelease(ProcArrayLock);
+
+	return found;
+}
+
+/*
+ *		VirtualXactWait
+ *
+ * Waits until the top-level transaction owning the VXID has ended.
+ * We don't use latches to allow us to have multiple waiters on a VXID.
+ */
+void
+VirtualXactWait(VirtualTransactionId vxid)
+{
+#define INITIAL_WAIT_US  1000
+	int		wait_us = INITIAL_WAIT_US;
+	TimestampTz waitStart;
+	char	   *new_status;
+	bool		reported = false;
+
+	/* Fast exit, to avoid a kernel call if there's no work to be done. */
+	if (!VirtualTransactionIdIsValid(vxid))
+		return;
+
+	waitStart = GetCurrentTimestamp();
+	new_status = NULL;			/* we haven't changed the ps display */
+
+	/* wait until the virtual xid is gone */
+	while (VirtualXactExists(vxid))
+	{
+		if (!reported)
+		{
+			pgstat_report_waiting(true);
+			reported = true;
+		}
+
+		/*
+		 * Report via ps if we have been waiting for more than 500 msec
+		 * (should that be configurable?)
+		 */
+		if (update_process_title && new_status == NULL &&
+			TimestampDifferenceExceeds(waitStart, GetCurrentTimestamp(),
+									   500))
+		{
+			const char *old_status;
+			int			len;
+
+			old_status = get_ps_display(&len);
+			new_status = (char *) palloc(len + 8 + 1);
+			memcpy(new_status, old_status, len);
+			strcpy(new_status + len, " waiting");
+			set_ps_display(new_status, false);
+			new_status[len] = '\0'; /* truncate off " waiting" */
+
+			pgstat_report_waiting(true);
+		}
+
+		CHECK_FOR_INTERRUPTS();
+
+		pg_usleep(wait_us);
+
+		/*
+		 * Progressively increase the sleep times, but not to more than 1s, since
+		 * pg_usleep isn't interruptable on some platforms.
+		 */
+		wait_us *= 2;
+		if (wait_us > 1000000)
+			wait_us = 1000000;
+	}
+
+	/* Reset ps display if we changed it */
+	if (new_status)
+	{
+		set_ps_display(new_status, false);
+		pfree(new_status);
+	}
+
+	if (reported)
+		pgstat_report_waiting(false);
+}
+
+/*
  * CancelVirtualTransaction - used in recovery conflict processing
  *
  * Returns pid of the process signaled, or 0 if not found.
diff --git a/src/backend/storage/ipc/standby.c b/src/backend/storage/ipc/standby.c
index 75b5ab4..52824f3 100644
--- a/src/backend/storage/ipc/standby.c
+++ b/src/backend/storage/ipc/standby.c
@@ -82,7 +82,7 @@ InitRecoveryTransactionEnvironment(void)
 	 */
 	vxid.backendId = MyBackendId;
 	vxid.localTransactionId = GetNextLocalTransactionId();
-	VirtualXactLockTableInsert(vxid);
+	MyProc->lxid = vxid.localTransactionId;
 
 	standbyState = STANDBY_INITIALIZED;
 }
@@ -201,7 +201,7 @@ ResolveRecoveryConflictWithVirtualXIDs(VirtualTransactionId *waitlist,
 		standbyWait_us = STANDBY_INITIAL_WAIT_US;
 
 		/* wait until the virtual xid is gone */
-		while (!ConditionalVirtualXactLockTableWait(*waitlist))
+		while (VirtualXactExists(*waitlist))
 		{
 			/*
 			 * Report via ps if we have been waiting for more than 500 msec
diff --git a/src/backend/storage/lmgr/lmgr.c b/src/backend/storage/lmgr/lmgr.c
index 859b385..9d0994e 100644
--- a/src/backend/storage/lmgr/lmgr.c
+++ b/src/backend/storage/lmgr/lmgr.c
@@ -514,70 +514,6 @@ ConditionalXactLockTableWait(TransactionId xid)
 	return true;
 }
 
-
-/*
- *		VirtualXactLockTableInsert
- *
- * Insert a lock showing that the given virtual transaction ID is running ---
- * this is done at main transaction start when its VXID is assigned.
- * The lock can then be used to wait for the transaction to finish.
- */
-void
-VirtualXactLockTableInsert(VirtualTransactionId vxid)
-{
-	LOCKTAG		tag;
-
-	Assert(VirtualTransactionIdIsValid(vxid));
-
-	SET_LOCKTAG_VIRTUALTRANSACTION(tag, vxid);
-
-	(void) LockAcquire(&tag, ExclusiveLock, false, false);
-}
-
-/*
- *		VirtualXactLockTableWait
- *
- * Waits until the lock on the given VXID is released, which shows that
- * the top-level transaction owning the VXID has ended.
- */
-void
-VirtualXactLockTableWait(VirtualTransactionId vxid)
-{
-	LOCKTAG		tag;
-
-	Assert(VirtualTransactionIdIsValid(vxid));
-
-	SET_LOCKTAG_VIRTUALTRANSACTION(tag, vxid);
-
-	(void) LockAcquire(&tag, ShareLock, false, false);
-
-	LockRelease(&tag, ShareLock, false);
-}
-
-/*
- *		ConditionalVirtualXactLockTableWait
- *
- * As above, but only lock if we can get the lock without blocking.
- * Returns TRUE if the lock was acquired.
- */
-bool
-ConditionalVirtualXactLockTableWait(VirtualTransactionId vxid)
-{
-	LOCKTAG		tag;
-
-	Assert(VirtualTransactionIdIsValid(vxid));
-
-	SET_LOCKTAG_VIRTUALTRANSACTION(tag, vxid);
-
-	if (LockAcquire(&tag, ShareLock, false, true) == LOCKACQUIRE_NOT_AVAIL)
-		return false;
-
-	LockRelease(&tag, ShareLock, false);
-
-	return true;
-}
-
-
 /*
  *		LockDatabaseObject
  *
diff --git a/src/include/storage/lmgr.h b/src/include/storage/lmgr.h
index bd44d92..340f6a3 100644
--- a/src/include/storage/lmgr.h
+++ b/src/include/storage/lmgr.h
@@ -56,11 +56,6 @@ extern void XactLockTableDelete(TransactionId xid);
 extern void XactLockTableWait(TransactionId xid);
 extern bool ConditionalXactLockTableWait(TransactionId xid);
 
-/* Lock a VXID (used to wait for a transaction to finish) */
-extern void VirtualXactLockTableInsert(VirtualTransactionId vxid);
-extern void VirtualXactLockTableWait(VirtualTransactionId vxid);
-extern bool ConditionalVirtualXactLockTableWait(VirtualTransactionId vxid);
-
 /* Lock a general object (other than a relation) of the current database */
 extern void LockDatabaseObject(Oid classid, Oid objid, uint16 objsubid,
 				   LOCKMODE lockmode);
diff --git a/src/include/storage/procarray.h b/src/include/storage/procarray.h
index 3c20fc4..ce3b562 100644
--- a/src/include/storage/procarray.h
+++ b/src/include/storage/procarray.h
@@ -58,6 +58,8 @@ extern VirtualTransactionId *GetCurrentVirtualXIDs(TransactionId limitXmin,
 					  bool excludeXmin0, bool allDbs, int excludeVacuum,
 					  int *nvxids);
 extern VirtualTransactionId *GetConflictingVirtualXIDs(TransactionId limitXmin, Oid dbOid);
+extern bool VirtualXactExists(VirtualTransactionId vxid);
+extern void VirtualXactWait(VirtualTransactionId vxid);
 extern pid_t CancelVirtualTransaction(VirtualTransactionId vxid, ProcSignalReason sigmode);
 
 extern bool MinimumActiveBackends(int min);
