diff --git a/src/backend/storage/smgr/smgr.c b/src/backend/storage/smgr/smgr.c index 0ca095c..b47374d 100644 --- a/src/backend/storage/smgr/smgr.c +++ b/src/backend/storage/smgr/smgr.c @@ -76,18 +76,25 @@ static const f_smgr smgrsw[] = { static const int NSmgr = lengthof(smgrsw); +typedef struct SMgrRelationHashEntry +{ + /* rnode is the hashtable lookup key, so it must be first! */ + RelFileNodeBackend smgr_rnode; /* relation physical identifier */ + + SMgrRelation reln; +} SMgrRelationHashEntry; + + /* - * Each backend has a hashtable that stores all extant SMgrRelation objects. - * In addition, "unowned" SMgrRelation objects are chained together in a list. + * Each backend has a hashtable that stores all extant SMgrRelation objects + * and a hashtable that stores "unowned" SMgrRelation objects. */ static HTAB *SMgrRelationHash = NULL; -static SMgrRelation first_unowned_reln = NULL; +static HTAB *UnownedSMgrRelationHash = NULL; /* local function prototypes */ static void smgrshutdown(int code, Datum arg); -static void add_to_unowned_list(SMgrRelation reln); -static void remove_from_unowned_list(SMgrRelation reln); /* @@ -143,14 +150,19 @@ smgropen(RelFileNode rnode, BackendId backend) if (SMgrRelationHash == NULL) { /* First time through: initialize the hash table */ - HASHCTL ctl; + HASHCTL ctl, unownctl; MemSet(&ctl, 0, sizeof(ctl)); ctl.keysize = sizeof(RelFileNodeBackend); ctl.entrysize = sizeof(SMgrRelationData); SMgrRelationHash = hash_create("smgr relation table", 400, &ctl, HASH_ELEM | HASH_BLOBS); - first_unowned_reln = NULL; + + MemSet(&unownctl, 0, sizeof(unownctl)); + unownctl.keysize = sizeof(RelFileNodeBackend); + unownctl.entrysize = sizeof(SMgrRelationHashEntry); + UnownedSMgrRelationHash = hash_create("unowned smgr relation table", + 400, &unownctl, HASH_ELEM | HASH_BLOBS); } /* Look up or create an entry */ @@ -164,6 +176,7 @@ smgropen(RelFileNode rnode, BackendId backend) if (!found) { int forknum; + SMgrRelationHashEntry *unownreln; /* hash_search already filled in the lookup key */ reln->smgr_owner = NULL; @@ -177,7 +190,13 @@ smgropen(RelFileNode rnode, BackendId backend) reln->md_num_open_segs[forknum] = 0; /* it has no owner yet */ - add_to_unowned_list(reln); + unownreln = (SMgrRelationHashEntry *) hash_search(UnownedSMgrRelationHash, + (void *) &(reln->smgr_rnode), + HASH_ENTER, &found); + + Assert(!found); + /* hash_search already filled in the lookup key */ + unownreln->reln = reln; } return reln; @@ -207,7 +226,10 @@ smgrsetowner(SMgrRelation *owner, SMgrRelation reln) if (reln->smgr_owner) *(reln->smgr_owner) = NULL; else - remove_from_unowned_list(reln); + if (hash_search(UnownedSMgrRelationHash, + (void *) &(reln->smgr_rnode), + HASH_REMOVE, NULL) == NULL) + elog(ERROR, "Unowned SMgrRelation hashtable corrupted"); /* Now establish the ownership relationship. */ reln->smgr_owner = owner; @@ -221,6 +243,9 @@ smgrsetowner(SMgrRelation *owner, SMgrRelation reln) void smgrclearowner(SMgrRelation *owner, SMgrRelation reln) { + bool found; + SMgrRelationHashEntry *unownreln; + /* Do nothing if the SMgrRelation object is not owned by the owner */ if (reln->smgr_owner != owner) return; @@ -231,53 +256,12 @@ smgrclearowner(SMgrRelation *owner, SMgrRelation reln) /* unset our reference to the owner */ reln->smgr_owner = NULL; - add_to_unowned_list(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) */ - reln->next_unowned_reln = first_unowned_reln; - first_unowned_reln = reln; -} - -/* - * remove_from_unowned_list -- unlink an SMgrRelation from the unowned list - * - * 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; - - for (link = &first_unowned_reln, cur = *link; - cur != NULL; - link = &cur->next_unowned_reln, cur = *link) - { - if (cur == reln) - { - *link = cur->next_unowned_reln; - cur->next_unowned_reln = NULL; - break; - } - } + unownreln = (SMgrRelationHashEntry *) hash_search(UnownedSMgrRelationHash, + (void *) &(reln->smgr_rnode), + HASH_ENTER, &found); + Assert(!found); + /* hash_search already filled in the lookup key */ + unownreln->reln = reln; } /* @@ -304,7 +288,10 @@ smgrclose(SMgrRelation reln) owner = reln->smgr_owner; if (!owner) - remove_from_unowned_list(reln); + if (hash_search(UnownedSMgrRelationHash, + (void *) &(reln->smgr_rnode), + HASH_REMOVE, NULL) == NULL) + elog(ERROR, "Unowned SMgrRelation hashtable corrupted"); if (hash_search(SMgrRelationHash, (void *) &(reln->smgr_rnode), @@ -797,13 +784,17 @@ smgrpostckpt(void) void AtEOXact_SMgr(void) { + HASH_SEQ_STATUS status; + SMgrRelationHashEntry *unownreln; + + if (UnownedSMgrRelationHash == NULL) + return; + /* * Zap all unowned SMgrRelations. We rely on smgrclose() to remove each - * one from the list. + * one from the hash. */ - while (first_unowned_reln != NULL) - { - Assert(first_unowned_reln->smgr_owner == NULL); - smgrclose(first_unowned_reln); - } + hash_seq_init(&status, UnownedSMgrRelationHash); + while ((unownreln = (SMgrRelationHashEntry *) hash_seq_search(&status)) != NULL) + smgrclose(unownreln->reln); }