1
1
openmpi/opal/class/opal_interval_tree.c
Nathan Hjelm 1145abc0b7 opal: make interval tree resilient to similar intervals
There are cases where the same interval may be in the tree multiple
times. This generally isn't a problem when searching the tree but
may cause issues when attempting to delete a particular registration
from the tree. The issue is fixed by breaking a low value tie by
checking the high value then the interval data.

If the high, low, and data of a new insertion exactly matches an
existing interval then an assertion is raised.

Signed-off-by: Nathan Hjelm <hjelmn@google.com>
2020-01-07 21:43:58 -07:00

939 строки
30 KiB
C

/* -*- Mode: C; c-basic-offset:4 ; indent-tabs-mode:nil -*- */
/*
* Copyright (c) 2004-2005 The Trustees of Indiana University and Indiana
* University Research and Technology
* Corporation. All rights reserved.
* Copyright (c) 2004-2013 The University of Tennessee and The University
* of Tennessee Research Foundation. All rights
* reserved.
* Copyright (c) 2004-2005 High Performance Computing Center Stuttgart,
* University of Stuttgart. All rights reserved.
* Copyright (c) 2004-2005 The Regents of the University of California.
* All rights reserved.
* Copyright (c) 2015-2018 Los Alamos National Security, LLC. All rights
* reserved.
* Copyright (c) 2020 Google, LLC. All rights reserved.
* $COPYRIGHT$
*
* Additional copyrights may follow
*
* $HEADER$
*/
/*
* @file
*/
#include "opal_config.h"
#include "opal/class/opal_interval_tree.h"
#include <limits.h>
/* Private functions */
static void opal_interval_tree_insert_node (opal_interval_tree_t *tree, opal_interval_tree_node_t *node);
/* tree rebalancing functions */
static void opal_interval_tree_delete_fixup (opal_interval_tree_t *tree, opal_interval_tree_node_t *node,
opal_interval_tree_node_t *parent);
static void opal_interval_tree_insert_fixup (opal_interval_tree_t *tree, opal_interval_tree_node_t *x);
static opal_interval_tree_node_t *opal_interval_tree_next (opal_interval_tree_t *tree,
opal_interval_tree_node_t *node);
static opal_interval_tree_node_t * opal_interval_tree_find_node(opal_interval_tree_t *tree,
uint64_t low, uint64_t high,
void *data);
static opal_interval_tree_node_t *left_rotate (opal_interval_tree_t *tree, opal_interval_tree_node_t *x);
static opal_interval_tree_node_t *right_rotate (opal_interval_tree_t *tree, opal_interval_tree_node_t *x);
static void inorder_destroy(opal_interval_tree_t *tree, opal_interval_tree_node_t * node);
#define max(x,y) (((x) > (y)) ? (x) : (y))
/**
* the constructor function. creates the free list to get the nodes from
*
* @param object the tree that is to be used
*
* @retval NONE
*/
static void opal_interval_tree_construct (opal_interval_tree_t *tree)
{
OBJ_CONSTRUCT(&tree->root, opal_interval_tree_node_t);
OBJ_CONSTRUCT(&tree->nill, opal_interval_tree_node_t);
OBJ_CONSTRUCT(&tree->free_list, opal_free_list_t);
OBJ_CONSTRUCT(&tree->gc_list, opal_list_t);
/* initialize sentinel */
tree->nill.color = OPAL_INTERVAL_TREE_COLOR_BLACK;
tree->nill.left = tree->nill.right = tree->nill.parent = &tree->nill;
tree->nill.max = 0;
tree->nill.data = NULL;
/* initialize root sentinel */
tree->root.color = OPAL_INTERVAL_TREE_COLOR_BLACK;
tree->root.left = tree->root.right = tree->root.parent = &tree->nill;
/* this simplifies inserting at the root as we only have to check the
* low value. */
tree->root.low = (uint64_t) -1;
tree->root.data = NULL;
/* set the tree size to zero */
tree->tree_size = 0;
tree->lock = 0;
tree->reader_count = 0;
tree->epoch = 0;
/* set all reader epochs to UINT_MAX. this value is used to simplfy
* checks against the current epoch. */
for (int i = 0 ; i < OPAL_INTERVAL_TREE_MAX_READERS ; ++i) {
tree->reader_epochs[i] = UINT_MAX;
}
}
/**
* the destructor function. Free the tree and destroys the free list.
*
* @param object the tree object
*/
static void opal_interval_tree_destruct (opal_interval_tree_t *tree)
{
opal_interval_tree_destroy (tree);
OBJ_DESTRUCT(&tree->free_list);
OBJ_DESTRUCT(&tree->root);
OBJ_DESTRUCT(&tree->nill);
}
/* declare the instance of the classes */
OBJ_CLASS_INSTANCE(opal_interval_tree_node_t, opal_free_list_item_t, NULL, NULL);
OBJ_CLASS_INSTANCE(opal_interval_tree_t, opal_object_t, opal_interval_tree_construct,
opal_interval_tree_destruct);
typedef int32_t opal_interval_tree_token_t;
/**
* @brief pick and return a reader slot
*/
static opal_interval_tree_token_t opal_interval_tree_reader_get_token (opal_interval_tree_t *tree)
{
opal_interval_tree_token_t token = -1;
if (token < 0) {
int32_t reader_count = tree->reader_count;
/* NTH: could have used an atomic here but all we are after is some distribution of threads
* across the reader slots. with high thread counts i see no real performance difference
* using atomics. */
token = tree->reader_id++ % OPAL_INTERVAL_TREE_MAX_READERS;
while (OPAL_UNLIKELY(reader_count <= token)) {
if (opal_atomic_compare_exchange_strong_32 (&tree->reader_count, &reader_count, token + 1)) {
break;
}
}
}
while (!OPAL_ATOMIC_COMPARE_EXCHANGE_STRONG_32((opal_atomic_int32_t *) &tree->reader_epochs[token],
&(int32_t) {UINT_MAX}, tree->epoch));
return token;
}
static void opal_interval_tree_reader_return_token (opal_interval_tree_t *tree, opal_interval_tree_token_t token)
{
tree->reader_epochs[token] = UINT_MAX;
}
/* Create the tree */
int opal_interval_tree_init (opal_interval_tree_t *tree)
{
return opal_free_list_init (&tree->free_list, sizeof(opal_interval_tree_node_t),
opal_cache_line_size, OBJ_CLASS(opal_interval_tree_node_t),
0, opal_cache_line_size, 0, -1 , 128, NULL, 0, NULL, NULL, NULL);
}
static bool opal_interval_tree_write_trylock (opal_interval_tree_t *tree)
{
opal_atomic_rmb ();
return !(tree->lock || opal_atomic_swap_32 (&tree->lock, 1));
}
static void opal_interval_tree_write_lock (opal_interval_tree_t *tree)
{
while (!opal_interval_tree_write_trylock (tree));
}
static void opal_interval_tree_write_unlock (opal_interval_tree_t *tree)
{
opal_atomic_wmb ();
tree->lock = 0;
}
static void opal_interval_tree_insert_fixup_helper (opal_interval_tree_t *tree, opal_interval_tree_node_t *node) {
opal_interval_tree_node_t *y, *parent = node->parent;
bool rotate_right = false;
if (parent->color == OPAL_INTERVAL_TREE_COLOR_BLACK) {
return;
}
if (parent == parent->parent->left) {
y = parent->parent->right;
rotate_right = true;
} else {
y = parent->parent->left;
}
if (y->color == OPAL_INTERVAL_TREE_COLOR_RED) {
parent->color = OPAL_INTERVAL_TREE_COLOR_BLACK;
y->color = OPAL_INTERVAL_TREE_COLOR_BLACK;
parent->parent->color = OPAL_INTERVAL_TREE_COLOR_RED;
opal_interval_tree_insert_fixup_helper (tree, parent->parent);
return;
}
if (rotate_right) {
if (node == parent->right) {
node = left_rotate (tree, parent);
parent = node->parent;
}
parent->color = OPAL_INTERVAL_TREE_COLOR_BLACK;
parent->parent->color = OPAL_INTERVAL_TREE_COLOR_RED;
(void) right_rotate(tree, parent->parent);
} else {
if (node == parent->left) {
node = right_rotate(tree, parent);
parent = node->parent;
}
parent->color = OPAL_INTERVAL_TREE_COLOR_BLACK;
parent->parent->color = OPAL_INTERVAL_TREE_COLOR_RED;
(void) left_rotate(tree, parent->parent);
}
opal_interval_tree_insert_fixup_helper (tree, node);
}
static void opal_interval_tree_insert_fixup (opal_interval_tree_t *tree, opal_interval_tree_node_t *node) {
/* do the rotations */
/* usually one would have to check for NULL, but because of the sentinal,
* we don't have to */
opal_interval_tree_insert_fixup_helper (tree, node);
/* after the rotations the root is black */
tree->root.left->color = OPAL_INTERVAL_TREE_COLOR_BLACK;
}
/**
* @brief Guts of the delete fixup
*
* @param[in] tree opal interval tree
* @param[in] node node to fixup
* @param[in] left true if the node is a left child of its parent
*
* @returns the next node to fixup or root if done
*/
static inline opal_interval_tree_node_t *
opal_interval_tree_delete_fixup_helper (opal_interval_tree_t *tree, opal_interval_tree_node_t *node,
opal_interval_tree_node_t *parent, const bool left)
{
opal_interval_tree_node_t *w;
/* get sibling */
w = left ? parent->right : parent->left;
if (w->color == OPAL_INTERVAL_TREE_COLOR_RED) {
w->color = OPAL_INTERVAL_TREE_COLOR_BLACK;
parent->color = OPAL_INTERVAL_TREE_COLOR_RED;
if (left) {
(void) left_rotate(tree, parent);
w = parent->right;
} else {
(void) right_rotate(tree, parent);
w = parent->left;
}
}
if ((w->left->color == OPAL_INTERVAL_TREE_COLOR_BLACK) && (w->right->color == OPAL_INTERVAL_TREE_COLOR_BLACK)) {
w->color = OPAL_INTERVAL_TREE_COLOR_RED;
return parent;
}
if (left) {
if (w->right->color == OPAL_INTERVAL_TREE_COLOR_BLACK) {
w->left->color = OPAL_INTERVAL_TREE_COLOR_BLACK;
w->color = OPAL_INTERVAL_TREE_COLOR_RED;
(void) right_rotate(tree, w);
w = parent->right;
}
w->color = parent->color;
parent->color = OPAL_INTERVAL_TREE_COLOR_BLACK;
w->right->color = OPAL_INTERVAL_TREE_COLOR_BLACK;
(void) left_rotate(tree, parent);
} else {
if (w->left->color == OPAL_INTERVAL_TREE_COLOR_BLACK) {
w->right->color = OPAL_INTERVAL_TREE_COLOR_BLACK;
w->color = OPAL_INTERVAL_TREE_COLOR_RED;
(void) left_rotate(tree, w);
w = parent->left;
}
w->color = parent->color;
parent->color = OPAL_INTERVAL_TREE_COLOR_BLACK;
w->left->color = OPAL_INTERVAL_TREE_COLOR_BLACK;
(void) right_rotate(tree, parent);
}
/* return the root */
return tree->root.left;
}
/* Fixup the balance of the btree after deletion */
static void opal_interval_tree_delete_fixup (opal_interval_tree_t *tree, opal_interval_tree_node_t *node,
opal_interval_tree_node_t *parent)
{
while ((node != tree->root.left) && (node->color == OPAL_INTERVAL_TREE_COLOR_BLACK)) {
node = opal_interval_tree_delete_fixup_helper (tree, node, parent, node == parent->left);
parent = node->parent;
}
node->color = OPAL_INTERVAL_TREE_COLOR_BLACK;
tree->nill.color = OPAL_INTERVAL_TREE_COLOR_BLACK;
}
/* traverse the garbage-collection list and return any nodes that can not have any
* references. this function MUST be called with the writer lock held. */
static void opal_interval_tree_gc_clean (opal_interval_tree_t *tree)
{
opal_interval_tree_node_t *node, *next;
uint32_t oldest_epoch = UINT_MAX;
if (0 == opal_list_get_size (&tree->gc_list)) {
return;
}
for (int i = 0 ; i < tree->reader_count ; ++i) {
oldest_epoch = (oldest_epoch < tree->reader_epochs[i]) ? oldest_epoch : tree->reader_epochs[i];
}
OPAL_LIST_FOREACH_SAFE(node, next, &tree->gc_list, opal_interval_tree_node_t) {
if (node->epoch < oldest_epoch) {
opal_list_remove_item (&tree->gc_list, &node->super.super);
opal_free_list_return_st (&tree->free_list, &node->super);
}
}
}
/* This inserts a node into the tree based on the passed values. */
int opal_interval_tree_insert (opal_interval_tree_t *tree, void *value, uint64_t low, uint64_t high)
{
opal_interval_tree_node_t * node;
if (low > high) {
return OPAL_ERR_BAD_PARAM;
}
opal_interval_tree_write_lock (tree);
opal_interval_tree_gc_clean (tree);
/* get the memory for a node */
node = (opal_interval_tree_node_t *) opal_free_list_get (&tree->free_list);
if (OPAL_UNLIKELY(NULL == node)) {
opal_interval_tree_write_unlock (tree);
return OPAL_ERR_OUT_OF_RESOURCE;
}
/* insert the data into the node */
node->data = value;
node->low = low;
node->high = high;
node->max = high;
node->epoch = tree->epoch;
/* insert the node into the tree */
opal_interval_tree_insert_node (tree, node);
opal_interval_tree_insert_fixup (tree, node);
opal_interval_tree_write_unlock (tree);
return OPAL_SUCCESS;
}
static int opal_interval_tree_compare_node(opal_interval_tree_node_t *node, uint64_t low, uint64_t high, void *data) {
if ((data && node->low == low && node->high == high && node->data == data) ||
(!data && node->low <= low && node->high >= high)) {
return 0;
}
if (node->low > low) {
return -1;
}
if (node->low < low) {
return 1;
}
if (node->high < high) {
return -1;
}
if (node->high > high) {
return 1;
}
if (node->data > data) {
return -1;
}
return 1;
}
static opal_interval_tree_node_t *opal_interval_tree_find_interval(opal_interval_tree_t *tree, opal_interval_tree_node_t *node, uint64_t low,
uint64_t high, void *data)
{
if (node == &tree->nill) {
return NULL;
}
int check = opal_interval_tree_compare_node(node, low, high, data);
if (0 == check) {
return node;
}
if (-1 == check) {
return opal_interval_tree_find_interval (tree, node->left, low, high, data);
}
return opal_interval_tree_find_interval (tree, node->right, low, high, data);
}
/* Finds the node in the tree based on the key and returns a pointer
* to the node. This is a bit a code duplication, but this has to be fast
* so we go ahead with the duplication */
static opal_interval_tree_node_t *opal_interval_tree_find_node(opal_interval_tree_t *tree, uint64_t low, uint64_t high, void *data)
{
return opal_interval_tree_find_interval (tree, tree->root.left, low, high, data);
}
void *opal_interval_tree_find_overlapping (opal_interval_tree_t *tree, uint64_t low, uint64_t high)
{
opal_interval_tree_token_t token;
opal_interval_tree_node_t *node;
token = opal_interval_tree_reader_get_token (tree);
node = opal_interval_tree_find_node (tree, low, high, NULL);
opal_interval_tree_reader_return_token (tree, token);
return node ? node->data : NULL;
}
static size_t opal_interval_tree_depth_node (opal_interval_tree_t *tree, opal_interval_tree_node_t *node)
{
if (&tree->nill == node) {
return 0;
}
return 1 + max (opal_interval_tree_depth_node (tree, node->right), opal_interval_tree_depth_node (tree, node->left));
}
size_t opal_interval_tree_depth (opal_interval_tree_t *tree)
{
opal_interval_tree_token_t token;
size_t depth;
token = opal_interval_tree_reader_get_token (tree);
depth = opal_interval_tree_depth_node (tree, &tree->root);
opal_interval_tree_reader_return_token (tree, token);
return depth;
}
/* update the value of a tree pointer */
static inline void rp_publish (opal_interval_tree_node_t **ptr, opal_interval_tree_node_t *node)
{
/* ensure all writes complete before continuing */
opal_atomic_wmb ();
/* just set the value */
*ptr = node;
}
static inline void rp_wait_for_readers (opal_interval_tree_t *tree)
{
uint32_t epoch_id = ++tree->epoch;
/* wait for all readers to see the new tree version */
for (int i = 0 ; i < tree->reader_count ; ++i) {
while (tree->reader_epochs[i] < epoch_id);
}
}
/* waits for all writers to finish with the node then releases the last reference */
static inline void rp_free_wait (opal_interval_tree_t *tree, opal_interval_tree_node_t *node)
{
rp_wait_for_readers (tree);
/* no other threads are working on this node so go ahead and return it */
opal_free_list_return_st (&tree->free_list, &node->super);
}
/* schedules the node for releasing */
static inline void rp_free (opal_interval_tree_t *tree, opal_interval_tree_node_t *node)
{
opal_list_append (&tree->gc_list, &node->super.super);
}
static opal_interval_tree_node_t *opal_interval_tree_node_copy (opal_interval_tree_t *tree, opal_interval_tree_node_t *node)
{
opal_interval_tree_node_t *copy = (opal_interval_tree_node_t *) opal_free_list_wait_st (&tree->free_list);
size_t color_offset = offsetof(opal_interval_tree_node_t, color);
assert (NULL != copy);
memcpy ((unsigned char *) copy + color_offset, (unsigned char *) node + color_offset,
sizeof (*node) - color_offset);
return copy;
}
/* this function deletes a node that is either a left or right leaf (or both) */
static void opal_interval_tree_delete_leaf (opal_interval_tree_t *tree, opal_interval_tree_node_t *node)
{
const opal_interval_tree_node_t *nill = &tree->nill;
opal_interval_tree_node_t **parent_ptr, *next, *parent = node->parent;
opal_interval_tree_nodecolor_t color = node->color;
assert (node->left == nill || node->right == nill);
parent_ptr = (parent->right == node) ? &parent->right : &parent->left;
next = (node->right == nill) ? node->left : node->right;
next->parent = node->parent;
rp_publish (parent_ptr, next);
rp_free (tree, node);
if (OPAL_INTERVAL_TREE_COLOR_BLACK == color) {
if (OPAL_INTERVAL_TREE_COLOR_RED == next->color) {
next->color = OPAL_INTERVAL_TREE_COLOR_BLACK;
} else {
opal_interval_tree_delete_fixup (tree, next, parent);
}
}
}
static void opal_interval_tree_delete_interior (opal_interval_tree_t *tree, opal_interval_tree_node_t *node)
{
opal_interval_tree_node_t **parent_ptr, *next, *next_copy, *parent = node->parent;
opal_interval_tree_nodecolor_t color = node->color, next_color;
parent_ptr = (parent->right == node) ? &parent->right : &parent->left;
next = opal_interval_tree_next (tree, node);
next_color = next->color;
if (next != node->right) {
/* case 3 */
next_copy = opal_interval_tree_node_copy (tree, next);
next_copy->color = node->color;
next_copy->left = node->left;
next_copy->left->parent = next_copy;
next_copy->right = node->right;
next_copy->right->parent = next_copy;
next_copy->parent = node->parent;
rp_publish (parent_ptr, next_copy);
rp_free_wait (tree, node);
opal_interval_tree_delete_leaf (tree, next);
} else {
/* case 2. no copies are needed */
next->color = color;
next->left = node->left;
next->left->parent = next;
next->parent = node->parent;
rp_publish (parent_ptr, next);
rp_free (tree, node);
/* since we are actually "deleting" the next node the fixup needs to happen on the
* right child of next (by definition next was a left child) */
if (OPAL_INTERVAL_TREE_COLOR_BLACK == next_color) {
if (OPAL_INTERVAL_TREE_COLOR_RED == next->right->color) {
next->right->color = OPAL_INTERVAL_TREE_COLOR_BLACK;
} else {
opal_interval_tree_delete_fixup (tree, next->right, next);
}
}
}
}
/* Delete a node from the tree based on the key */
int opal_interval_tree_delete (opal_interval_tree_t *tree, uint64_t low, uint64_t high, void *data)
{
opal_interval_tree_node_t *node;
opal_interval_tree_write_lock (tree);
node = opal_interval_tree_find_node (tree, low, high, data);
if (NULL == node) {
opal_interval_tree_write_unlock (tree);
return OPAL_ERR_NOT_FOUND;
}
/* there are three cases that have to be handled:
* 1) the node p is a left leaf or a right left (one of p's children is nill)
* in this case we can delete p and we can replace it with one of it's children
* or nill (if both children are nill).
* 2) the right child of p is a left leaf (node->right->left == nill)
* in this case we can set node->right->left = node->left and replace node with node->right
* 3) p is a interior node
* we replace node with next(node)
*/
if ((node->left == &tree->nill) || (node->right == &tree->nill)) {
/* handle case 1 */
opal_interval_tree_delete_leaf (tree, node);
} else {
/* handle case 2 and 3 */
opal_interval_tree_delete_interior (tree, node);
}
--tree->tree_size;
opal_interval_tree_write_unlock (tree);
return OPAL_SUCCESS;
}
int opal_interval_tree_destroy (opal_interval_tree_t *tree)
{
/* Recursive inorder traversal for delete */
inorder_destroy(tree, &tree->root);
tree->tree_size = 0;
return OPAL_SUCCESS;
}
/* Find the next inorder successor of a node */
static opal_interval_tree_node_t *opal_interval_tree_next (opal_interval_tree_t *tree, opal_interval_tree_node_t *node)
{
opal_interval_tree_node_t *p = node->right;
if (p == &tree->nill) {
p = node->parent;
while (node == p->right) {
node = p;
p = p->parent;
}
if (p == &tree->root) {
return &tree->nill;
}
return p;
}
while (p->left != &tree->nill) {
p = p->left;
}
return p;
}
/* Insert an element in the normal binary search tree fashion */
/* this function goes through the tree and finds the leaf where
* the node will be inserted */
static void opal_interval_tree_insert_node (opal_interval_tree_t *tree, opal_interval_tree_node_t *node)
{
opal_interval_tree_node_t *parent = &tree->root;
opal_interval_tree_node_t *n = parent->left; /* the real root of the tree */
opal_interval_tree_node_t *nill = &tree->nill;
/* set up initial values for the node */
node->color = OPAL_INTERVAL_TREE_COLOR_RED;
node->parent = NULL;
node->left = nill;
node->right = nill;
/* find the leaf where we will insert the node */
int check = -1;
while (n != nill) {
check = opal_interval_tree_compare_node(n, node->low, node->high, node->data);
/* node already exists */
assert (0 != check);
if (n->max < node->high) {
n->max = node->high;
}
parent = n;
n = (-1 == check) ? n->left : n->right;
assert (nill == n || n->parent == parent);
}
/* place it on either the left or the right */
if (-1 == check) {
parent->left = node;
} else {
parent->right = node;
}
/* set its parent and children */
node->parent = parent;
++tree->tree_size;
}
static int inorder_traversal (opal_interval_tree_t *tree, uint64_t low, uint64_t high,
bool partial_ok, opal_interval_tree_action_fn_t action,
opal_interval_tree_node_t * node, void *ctx)
{
int rc;
if (node == &tree->nill) {
return OPAL_SUCCESS;
}
rc = inorder_traversal(tree, low, high, partial_ok, action, node->left, ctx);
if (OPAL_SUCCESS != rc) {
return rc;
}
if ((!partial_ok && (node->low <= low && node->high >= high)) ||
(partial_ok && ((low >= node->low && low <= node->high) ||
(high >= node->low && high <= node->high) ||
(node->low >= low && node->low <= high) ||
(node->high >= high && node->high <= high)))) {
rc = action (node->low, node->high, node->data, ctx);
if (OPAL_SUCCESS != rc) {
return rc;
}
}
return inorder_traversal(tree, low, high, partial_ok, action, node->right, ctx);
}
/* Free the nodes in inorder fashion */
static void inorder_destroy (opal_interval_tree_t *tree, opal_interval_tree_node_t *node)
{
if (node == &tree->nill) {
return;
}
inorder_destroy(tree, node->left);
inorder_destroy(tree, node->right);
if (node->left != &tree->nill) {
opal_free_list_return_st (&tree->free_list, &node->left->super);
}
if (node->right != &tree->nill) {
opal_free_list_return_st (&tree->free_list, &node->right->super);
}
}
/* Try to access all the elements of the hashmap conditionally */
int opal_interval_tree_traverse (opal_interval_tree_t *tree, uint64_t low, uint64_t high,
bool partial_ok, opal_interval_tree_action_fn_t action, void *ctx)
{
opal_interval_tree_token_t token;
int rc;
if (action == NULL) {
return OPAL_ERR_BAD_PARAM;
}
token = opal_interval_tree_reader_get_token (tree);
rc = inorder_traversal (tree, low, high, partial_ok, action, tree->root.left, ctx);
opal_interval_tree_reader_return_token (tree, token);
return rc;
}
/* Left rotate the tree */
/* basically what we want to do is to make x be the left child
* of its right child */
static opal_interval_tree_node_t *left_rotate (opal_interval_tree_t *tree, opal_interval_tree_node_t *x)
{
opal_interval_tree_node_t *x_copy = x;
opal_interval_tree_node_t *y = x->right;
opal_interval_tree_node_t *parent = x->parent;
/* make the left child of y's parent be x if it is not the sentinal node*/
if (y->left != &tree->nill) {
y->left->parent = x_copy;
}
/* x's parent is now y */
x_copy->parent = y;
x_copy->right = y->left;
x_copy->max = max (x_copy->high, max (x_copy->left->max, x_copy->left->max));
rp_publish (&y->left, x_copy);
/* normlly we would have to check to see if we are at the root.
* however, the root sentinal takes care of it for us */
if (x == parent->left) {
rp_publish (&parent->left, y);
} else {
rp_publish (&parent->right, y);
}
/* the old parent of x is now y's parent */
y->parent = parent;
return x_copy;
}
/* Right rotate the tree */
/* basically what we want to do is to make x be the right child
* of its left child */
static opal_interval_tree_node_t *right_rotate (opal_interval_tree_t *tree, opal_interval_tree_node_t *x)
{
opal_interval_tree_node_t *x_copy = x;
opal_interval_tree_node_t *y = x->left;
opal_interval_tree_node_t *parent = x->parent;
/* make the left child of y's parent be x if it is not the sentinal node*/
if (y->right != &tree->nill) {
y->right->parent = x_copy;
}
x_copy->left = y->right;
x_copy->parent = y;
rp_publish (&y->right, x_copy);
/* the maximum value in the subtree rooted at y is now the value it
* was at x */
y->max = x->max;
y->parent = parent;
if (parent->left == x) {
rp_publish (&parent->left, y);
} else {
rp_publish (&parent->right, y);
}
return x_copy;
}
/* returns the size of the tree */
size_t opal_interval_tree_size(opal_interval_tree_t *tree)
{
return tree->tree_size;
}
static bool opal_interval_tree_verify_node (opal_interval_tree_t *tree, opal_interval_tree_node_t *node, int black_depth,
int current_black_depth)
{
if (node == &tree->nill) {
return true;
}
if (OPAL_INTERVAL_TREE_COLOR_RED == node->color &&
(OPAL_INTERVAL_TREE_COLOR_BLACK != node->left->color ||
OPAL_INTERVAL_TREE_COLOR_BLACK != node->right->color)) {
fprintf (stderr, "Red node has a red child!\n");
return false;
}
if (OPAL_INTERVAL_TREE_COLOR_BLACK == node->color) {
current_black_depth++;
}
if (node->left == &tree->nill && node->right == &tree->nill) {
if (black_depth != current_black_depth) {
fprintf (stderr, "Found leaf with unexpected black depth: %d, expected: %d\n", current_black_depth, black_depth);
return false;
}
return true;
}
return opal_interval_tree_verify_node (tree, node->left, black_depth, current_black_depth) ||
opal_interval_tree_verify_node (tree, node->right, black_depth, current_black_depth);
}
static int opal_interval_tree_black_depth (opal_interval_tree_t *tree, opal_interval_tree_node_t *node, int depth)
{
if (node == &tree->nill) {
return depth;
}
/* suffices to always go left */
if (OPAL_INTERVAL_TREE_COLOR_BLACK == node->color) {
depth++;
}
return opal_interval_tree_black_depth (tree, node->left, depth);
}
bool opal_interval_tree_verify (opal_interval_tree_t *tree)
{
int black_depth;
if (OPAL_INTERVAL_TREE_COLOR_BLACK != tree->root.left->color) {
fprintf (stderr, "Root node of tree is NOT black!\n");
return false;
}
if (OPAL_INTERVAL_TREE_COLOR_BLACK != tree->nill.color) {
fprintf (stderr, "Leaf node color is NOT black!\n");
return false;
}
black_depth = opal_interval_tree_black_depth (tree, tree->root.left, 0);
return opal_interval_tree_verify_node (tree, tree->root.left, black_depth, 0);
}
static void opal_interval_tree_dump_node (opal_interval_tree_t *tree, opal_interval_tree_node_t *node, int black_rank, FILE *fh)
{
const char *color = (node->color == OPAL_INTERVAL_TREE_COLOR_BLACK) ? "black" : "red";
uintptr_t left = (uintptr_t) node->left, right = (uintptr_t) node->right;
opal_interval_tree_node_t *nill = &tree->nill;
if (node->color == OPAL_INTERVAL_TREE_COLOR_BLACK) {
++black_rank;
}
if (nill == node) {
return;
}
/* print out nill nodes if any */
if ((uintptr_t) nill == left) {
left = (uintptr_t) node | 0x1;
fprintf (fh, " Node%lx [color=black,label=nill];\n\n", left);
} else {
left = (uintptr_t) node->left;
}
if ((uintptr_t) nill == right) {
right = (uintptr_t) node | 0x2;
fprintf (fh, " Node%lx [color=black,label=nill];\n\n", right);
} else {
right = (uintptr_t) node->right;
}
/* print out this node and its edges */
fprintf (fh, " Node%lx [color=%s,shape=box,label=\"[0x%" PRIx64 ",0x%" PRIx64 "]\\nmax=0x%" PRIx64
"\\ndata=0x%lx\\nblack rank=%d\"];\n", (uintptr_t) node, color, node->low, node->high, node->max,
(uintptr_t) node->data, black_rank);
fprintf (fh, " Node%lx -> Node%lx;\n", (uintptr_t) node, left);
fprintf (fh, " Node%lx -> Node%lx;\n\n", (uintptr_t) node, right);
if (node != tree->root.left) {
fprintf (fh, " Node%lx -> Node%lx;\n\n", (uintptr_t) node, (uintptr_t) node->parent);
}
opal_interval_tree_dump_node (tree, node->left, black_rank, fh);
opal_interval_tree_dump_node (tree, node->right, black_rank, fh);
}
int opal_interval_tree_dump (opal_interval_tree_t *tree, const char *path)
{
FILE *fh;
fh = fopen (path, "w");
if (NULL == fh) {
return OPAL_ERR_BAD_PARAM;
}
fprintf (fh, "digraph {\n");
fprintf (fh, " graph [ordering=\"out\"];");
opal_interval_tree_dump_node (tree, tree->root.left, 0, fh);
fprintf (fh, "}\n");
fclose (fh);
return OPAL_SUCCESS;
}