Improve some of the comments in fsmpage.c.

This commit is contained in:
Tom Lane 2008-10-07 21:10:11 +00:00
parent 0d115dde82
commit dd4c165bc3

View File

@ -8,15 +8,15 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/storage/freespace/fsmpage.c,v 1.1 2008/09/30 10:52:13 heikki Exp $ * $PostgreSQL: pgsql/src/backend/storage/freespace/fsmpage.c,v 1.2 2008/10/07 21:10:11 tgl Exp $
* *
* NOTES: * NOTES:
* *
* The public functions in this file form an API that hides the internal * The public functions in this file form an API that hides the internal
* structure of a FSM page. This allows freespace.c to treat each FSM page * structure of a FSM page. This allows freespace.c to treat each FSM page
* as a black box with SlotsPerPage "slots". fsm_set_avail() and * as a black box with SlotsPerPage "slots". fsm_set_avail() and
* fsm_get_avail() let's you get/set the value of a slot, and * fsm_get_avail() let you get/set the value of a slot, and
* fsm_search_avail() let's you search for a slot with value >= X. * fsm_search_avail() lets you search for a slot with value >= X.
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -25,14 +25,16 @@
#include "storage/bufmgr.h" #include "storage/bufmgr.h"
#include "storage/fsm_internals.h" #include "storage/fsm_internals.h"
/* macros to navigate the tree within a page. */ /* Macros to navigate the tree within a page. Root has index zero. */
#define leftchild(x) (2 * (x) + 1) #define leftchild(x) (2 * (x) + 1)
#define rightchild(x) (2 * (x) + 2) #define rightchild(x) (2 * (x) + 2)
#define parentof(x) (((x) - 1) / 2) #define parentof(x) (((x) - 1) / 2)
/* returns right sibling of x, wrapping around within the level */ /*
* Find right neighbor of x, wrapping around within the level
*/
static int static int
rightsibling(int x) rightneighbor(int x)
{ {
/* /*
* Move right. This might wrap around, stepping to the leftmost node at * Move right. This might wrap around, stepping to the leftmost node at
@ -42,8 +44,9 @@ rightsibling(int x)
/* /*
* Check if we stepped to the leftmost node at next level, and correct * Check if we stepped to the leftmost node at next level, and correct
* if so. The leftmost nodes at each level are of form x = 2^level - 1, so * if so. The leftmost nodes at each level are numbered x = 2^level - 1,
* check if (x + 1) is a power of two. * so check if (x + 1) is a power of two, using a standard
* twos-complement-arithmetic trick.
*/ */
if (((x + 1) & x) == 0) if (((x + 1) & x) == 0)
x = parentof(x); x = parentof(x);
@ -52,8 +55,7 @@ rightsibling(int x)
} }
/* /*
* Sets the value of a slot on page. Returns true if the page was * Sets the value of a slot on page. Returns true if the page was modified.
* modified.
* *
* The caller must hold an exclusive lock on the page. * The caller must hold an exclusive lock on the page.
*/ */
@ -101,8 +103,8 @@ fsm_set_avail(Page page, int slot, uint8 value)
} while (nodeno > 0); } while (nodeno > 0);
/* /*
* sanity check: if the new value value is higher than the value * sanity check: if the new value is (still) higher than the value
* at the top, the tree is corrupt. * at the top, the tree is corrupt. If so, rebuild.
*/ */
if (value > fsmpage->fp_nodes[0]) if (value > fsmpage->fp_nodes[0])
fsm_rebuild_page(page); fsm_rebuild_page(page);
@ -121,11 +123,14 @@ fsm_get_avail(Page page, int slot)
{ {
FSMPage fsmpage = (FSMPage) PageGetContents(page); FSMPage fsmpage = (FSMPage) PageGetContents(page);
Assert(slot < LeafNodesPerPage);
return fsmpage->fp_nodes[NonLeafNodesPerPage + slot]; return fsmpage->fp_nodes[NonLeafNodesPerPage + slot];
} }
/* /*
* Returns the value at the root of a page. * Returns the value at the root of a page.
*
* Since this is just a read-only access of a single byte, the page doesn't * Since this is just a read-only access of a single byte, the page doesn't
* need to be locked. * need to be locked.
*/ */
@ -133,12 +138,13 @@ uint8
fsm_get_max_avail(Page page) fsm_get_max_avail(Page page)
{ {
FSMPage fsmpage = (FSMPage) PageGetContents(page); FSMPage fsmpage = (FSMPage) PageGetContents(page);
return fsmpage->fp_nodes[0]; return fsmpage->fp_nodes[0];
} }
/* /*
* Searches for a slot with min. category. Returns slot number, or -1 if * Searches for a slot with category at least minvalue.
* none found. * Returns slot number, or -1 if none found.
* *
* The caller must hold at least a shared lock on the page, and this * The caller must hold at least a shared lock on the page, and this
* function can unlock and lock the page again in exclusive mode if it * function can unlock and lock the page again in exclusive mode if it
@ -146,7 +152,7 @@ fsm_get_max_avail(Page page)
* caller is already holding an exclusive lock, to avoid extra work. * caller is already holding an exclusive lock, to avoid extra work.
* *
* If advancenext is false, fp_next_slot is set to point to the returned * If advancenext is false, fp_next_slot is set to point to the returned
* slot, and if it's true, to the slot next to the returned slot. * slot, and if it's true, to the slot after the returned slot.
*/ */
int int
fsm_search_avail(Buffer buf, uint8 minvalue, bool advancenext, fsm_search_avail(Buffer buf, uint8 minvalue, bool advancenext,
@ -160,33 +166,44 @@ fsm_search_avail(Buffer buf, uint8 minvalue, bool advancenext,
restart: restart:
/* /*
* Check the root first, and exit quickly if there's no page with * Check the root first, and exit quickly if there's no leaf with
* enough free space * enough free space
*/ */
if (fsmpage->fp_nodes[0] < minvalue) if (fsmpage->fp_nodes[0] < minvalue)
return -1; return -1;
/*
/* fp_next_slot is just a hint, so check that it's sane */ * Start search using fp_next_slot. It's just a hint, so check that it's
* sane. (This also handles wrapping around when the prior call returned
* the last slot on the page.)
*/
target = fsmpage->fp_next_slot; target = fsmpage->fp_next_slot;
if (target < 0 || target >= LeafNodesPerPage) if (target < 0 || target >= LeafNodesPerPage)
target = 0; target = 0;
target += NonLeafNodesPerPage; target += NonLeafNodesPerPage;
/* /*----------
* Start the search from the target slot. At every step, move one * Start the search from the target slot. At every step, move one
* node to the right, and climb up to the parent. Stop when we reach a * node to the right, then climb up to the parent. Stop when we reach
* node with enough free space. (note that moving to the right only * a node with enough free space (as we must, since the root has enough
* makes a difference if we're on the right child of the parent) * space).
* *
* The idea is to graduall expand our "search triangle", that is, all * The idea is to gradually expand our "search triangle", that is, all
* nodes covered by the current node. In the beginning, just the target * nodes covered by the current node, and to be sure we search to the
* node is included, and more nodes to the right of the target node, * right from the start point. At the first step, only the target slot
* taking wrap-around into account, is included at each step. Nodes are * is examined. When we move up from a left child to its parent, we are
* added to the search triangle in left-to-right order, starting from * adding the right-hand subtree of that parent to the search triangle.
* the target node. This ensures that we'll find the first suitable node * When we move right then up from a right child, we are dropping the
* to the right of the target node, and not some other node with enough * current search triangle (which we know doesn't contain any suitable
* free space. * page) and instead looking at the next-larger-size triangle to its
* right. So we never look left from our original start point, and at
* each step the size of the search triangle doubles, ensuring it takes
* only log2(N) work to search N pages.
*
* The "move right" operation will wrap around if it hits the right edge
* of the tree, so the behavior is still good if we start near the right.
* Note also that the move-and-climb behavior ensures that we can't end
* up on one of the missing nodes at the right of the leaf level.
* *
* For example, consider this tree: * For example, consider this tree:
* *
@ -196,25 +213,27 @@ fsm_search_avail(Buffer buf, uint8 minvalue, bool advancenext,
* 4 5 5 7 2 6 5 2 * 4 5 5 7 2 6 5 2
* T * T
* *
* Imagine that target node is the node indicated by the letter T, and * Assume that the target node is the node indicated by the letter T,
* we're searching for a node with value of 6 or higher. The search * and we're searching for a node with value of 6 or higher. The search
* begins at T. At first iteration, we move to the right, and to the * begins at T. At the first iteration, we move to the right, then to the
* parent, arriving the rightmost 5. At the 2nd iteration, we move to the * parent, arriving at the rightmost 5. At the second iteration, we move
* right, wrapping around, and climb up, arriving at the 7 at the 2nd * to the right, wrapping around, then climb up, arriving at the 7 on the
* level. 7 satisfies our search, so we descend down to the bottom, * third level. 7 satisfies our search, so we descend down to the bottom,
* following the path of sevens. * following the path of sevens. This is in fact the first suitable page
* to the right of (allowing for wraparound) our start point.
*----------
*/ */
nodeno = target; nodeno = target;
while (nodeno > 0) while (nodeno > 0)
{ {
if (fsmpage->fp_nodes[nodeno] >= minvalue) if (fsmpage->fp_nodes[nodeno] >= minvalue)
break; break;
/* /*
* Move to the right, wrapping around at the level if necessary, and * Move to the right, wrapping around on same level if necessary,
* climb up. * then climb up.
*/ */
nodeno = parentof(rightsibling(nodeno)); nodeno = parentof(rightneighbor(nodeno));
} }
/* /*
@ -271,10 +290,10 @@ fsm_search_avail(Buffer buf, uint8 minvalue, bool advancenext,
slot = nodeno - NonLeafNodesPerPage; slot = nodeno - NonLeafNodesPerPage;
/* /*
* Update the next slot pointer. Note that we do this even if we're only * Update the next-target pointer. Note that we do this even if we're only
* holding a shared lock, on the grounds that it's better to use a shared * holding a shared lock, on the grounds that it's better to use a shared
* lock and get a garbled next pointer every now and then, than take the * lock and get a garbled next pointer every now and then, than take the
* concurrency hit of an exlusive lock. * concurrency hit of an exclusive lock.
* *
* Wrap-around is handled at the beginning of this function. * Wrap-around is handled at the beginning of this function.
*/ */
@ -324,7 +343,7 @@ fsm_rebuild_page(Page page)
int nodeno; int nodeno;
/* /*
* Start from the lowest non-leaflevel, at last node, working our way * Start from the lowest non-leaf level, at last node, working our way
* backwards, through all non-leaf nodes at all levels, up to the root. * backwards, through all non-leaf nodes at all levels, up to the root.
*/ */
for (nodeno = NonLeafNodesPerPage - 1; nodeno >= 0; nodeno--) for (nodeno = NonLeafNodesPerPage - 1; nodeno >= 0; nodeno--)
@ -333,6 +352,7 @@ fsm_rebuild_page(Page page)
int rchild = lchild + 1; int rchild = lchild + 1;
uint8 newvalue = 0; uint8 newvalue = 0;
/* The first few nodes we examine might have zero or one child. */
if (lchild < NodesPerPage) if (lchild < NodesPerPage)
newvalue = fsmpage->fp_nodes[lchild]; newvalue = fsmpage->fp_nodes[lchild];
@ -349,4 +369,3 @@ fsm_rebuild_page(Page page)
return changed; return changed;
} }