Skip to content

Commit

Permalink
Add a batch deletion mode to rbtree_walk
Browse files Browse the repository at this point in the history
   Walks the tree calling callbacks as per InOrder
   Allows callback to ask for deletion of records after evaluating criteria
   All callbacks and deletions done while lock is held
  • Loading branch information
skids committed Jun 25, 2013
1 parent 863a4f7 commit c868559
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 1 deletion.
7 changes: 6 additions & 1 deletion src/include/libradius.h
Original file line number Diff line number Diff line change
Expand Up @@ -581,7 +581,7 @@ void *rbtree_min(rbtree_t *tree);
void *rbtree_node2data(rbtree_t *tree, rbnode_t *node);

/* callback order for walking */
typedef enum { PreOrder, InOrder, PostOrder } RBTREE_ORDER;
typedef enum { PreOrder, InOrder, PostOrder, DeleteOrder } RBTREE_ORDER;

/*
* The callback should be declared as:
Expand All @@ -593,6 +593,11 @@ typedef enum { PreOrder, InOrder, PostOrder } RBTREE_ORDER;
*
* It should return 0 if all is OK, and !0 for any error.
* The walking will stop on any error.
*
* Except with DeleteOrder, where the callback should return <0 for
* errors, and may return 1 to delete the current node and halt,
* or 2 to delete the current node and continue. This may be
* used to batch-delete select nodes from a locked rbtree.
*/
int rbtree_walk(rbtree_t *tree, RBTREE_ORDER order, int (*callback)(void *, void *), void *context);

Expand Down
59 changes: 59 additions & 0 deletions src/lib/rbtree.c
Original file line number Diff line number Diff line change
Expand Up @@ -672,6 +672,62 @@ static int WalkNodePostOrder(rbnode_t *X,
return 0; /* we know everything returned zero */
}


/*
* DeleteOrder
*
* This executes an InOrder-like walk that adapts to changes in the
* tree above it, which may occur because we allow the callback to
* tell us to delete the current node.
*/
static int WalkDeleteOrder(rbtree_t *tree, int (*callback)(void *, void *),
void *context)
{
rbnode_t *Solid, *X;
int rcode = 0;

/* Keep track of last node that refused deletion. */
Solid = NIL;
while (Solid == NIL) {
X = tree->Root;
if (X == NIL) break;
descend:
while (X->Left != NIL) {
X = X->Left;
}
visit:
rcode = callback(context, X->Data);
if (rcode < 0) {
return rcode;
}
if (rcode) {
rbtree_delete_internal(tree, X, 1);
if (rcode != 2) {
return rcode;
}
}
else {
Solid = X;
}
}
if (Solid != NIL) {
X = Solid;
if (X->Right != NIL) {
X = X->Right;
goto descend;
}
while (X->Parent) {
if (X->Parent->Left == X) {
X = X->Parent;
goto visit;
}
X = X->Parent;
}
}
return rcode;
}


/*
* Walk the entire tree. The callback function CANNOT modify
* the tree.
Expand All @@ -697,6 +753,9 @@ int rbtree_walk(rbtree_t *tree, RBTREE_ORDER order,
case PostOrder:
rcode = WalkNodePostOrder(tree->Root, callback, context);
break;
case DeleteOrder:
rcode = WalkDeleteOrder(tree, callback, context);
break;
default:
rcode = -1;
break;
Expand Down

0 comments on commit c868559

Please sign in to comment.