From 72a25761300d02af1aea4f21cc05170531da0b08 Mon Sep 17 00:00:00 2001
From: Thomas Munro <thomas.munro@enterprisedb.com>
Date: Wed, 27 Jun 2018 11:18:01 +1200
Subject: [PATCH] Use a doubly-linked list for unowned relations.

Previously we used a singly-linked list, which required
remove_from_unowned_list() to perform an O(n) search.  Make it O(1).

Author: Thomas Munro
Discussion: https://postgr.es/m/CAHGQGwHVQkdfDqtvGVkty+19cQakAydXn1etGND3X0PHbZ3+6w@mail.gmail.com
---
 src/backend/storage/smgr/smgr.c | 43 ++++++++++++++++-----------------
 src/include/storage/smgr.h      |  1 +
 2 files changed, 22 insertions(+), 22 deletions(-)

diff --git a/src/backend/storage/smgr/smgr.c b/src/backend/storage/smgr/smgr.c
index 08f06bade25..ae6da4928fc 100644
--- a/src/backend/storage/smgr/smgr.c
+++ b/src/backend/storage/smgr/smgr.c
@@ -236,15 +236,15 @@ smgrclearowner(SMgrRelation *owner, SMgrRelation reln)
 
 /*
  * add_to_unowned_list -- link an SMgrRelation onto the unowned list
- *
- * Check remove_from_unowned_list()'s comments for performance
- * considerations.
  */
 static void
 add_to_unowned_list(SMgrRelation reln)
 {
-	/* place it at head of the list (to make smgrsetowner cheap) */
+	/* place it at head of the list */
+	reln->prev_unowned_reln = NULL;
 	reln->next_unowned_reln = first_unowned_reln;
+	if (first_unowned_reln)
+		first_unowned_reln->prev_unowned_reln = reln;
 	first_unowned_reln = reln;
 }
 
@@ -253,30 +253,29 @@ add_to_unowned_list(SMgrRelation reln)
  *
  * If the reln is not present in the list, nothing happens.  Typically this
  * would be caller error, but there seems no reason to throw an error.
- *
- * In the worst case this could be rather slow; but in all the cases that seem
- * likely to be performance-critical, the reln being sought will actually be
- * first in the list.  Furthermore, the number of unowned relns touched in any
- * one transaction shouldn't be all that high typically.  So it doesn't seem
- * worth expending the additional space and management logic needed for a
- * doubly-linked list.
  */
 static void
 remove_from_unowned_list(SMgrRelation reln)
 {
-	SMgrRelation *link;
-	SMgrRelation cur;
+	SMgrRelation prev = reln->prev_unowned_reln;
+	SMgrRelation next = reln->next_unowned_reln;
 
-	for (link = &first_unowned_reln, cur = *link;
-		 cur != NULL;
-		 link = &cur->next_unowned_reln, cur = *link)
+	if (first_unowned_reln == reln)
+	{
+		/* It is at the head of the list. */
+		Assert(prev == NULL);
+		first_unowned_reln = next;
+		if (next)
+			next->prev_unowned_reln = NULL;
+		reln->next_unowned_reln = NULL;
+	}
+	else if (prev != NULL)
 	{
-		if (cur == reln)
-		{
-			*link = cur->next_unowned_reln;
-			cur->next_unowned_reln = NULL;
-			break;
-		}
+		/* It is in the list, but not at the head. */
+		prev->next_unowned_reln = next;
+		if (next)
+			next->prev_unowned_reln = prev;
+		reln->next_unowned_reln = reln->prev_unowned_reln = NULL;
 	}
 }
 
diff --git a/src/include/storage/smgr.h b/src/include/storage/smgr.h
index 558e4d8518b..1bd978a7351 100644
--- a/src/include/storage/smgr.h
+++ b/src/include/storage/smgr.h
@@ -72,6 +72,7 @@ typedef struct SMgrRelationData
 	struct _MdfdVec *md_seg_fds[MAX_FORKNUM + 1];
 
 	/* if unowned, list link in list of all unowned SMgrRelations */
+	struct SMgrRelationData *prev_unowned_reln;
 	struct SMgrRelationData *next_unowned_reln;
 } SMgrRelationData;
 
-- 
2.17.0

