1
1
openmpi/opal/class/opal_tree.h
2015-06-23 20:59:57 -07:00

531 строка
17 KiB
C

/*
* Copyright (c) 2011 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2011 Oak Ridge National Labs. All rights reserved.
*
* $COPYRIGHT$
*
* Additional copyrights may follow
*
* $HEADER$
*/
/**
* @file
*
* The opal_tree_t interface is used to provide a generic
* tree list container for Open MPI. It was inspired by the opal_list_t
* interface but instead of organizing items in a doubly-linked list
* fashion, we order them in a finite tree structure.
*
* The general idea is a user creates an class instance that has two
* components. A tree structure component as defined by opal_tree_item_t
* that links all the items together to form the tree. Then there is
* a user specific data component which the user defines what is stored at
* each item. When a user create a type to be used for a OBJ_CLASS_INSTANCE
* it will contain the opal_tree_item_t followed by any user specific
* data. Then the opal_tree_item_t objects can be put in an
* opal_tree_t. Hence, you create a new type that derives from
* opal_tree_item_t; this new type can then be used with opal_tree_t
* containers.
*
* NOTE: opal_tree_item_t instances can only be on \em one tree at a
* time. Specifically, if you add an opal_tree_item_t to one tree,
* and then add it to another tree (without first removing it from the
* first tree), you will effectively be hosing the first tree. You
* have been warned.
*
* If OPAL_ENABLE_DEBUG is true, a bunch of checks occur, including
* some spot checks for a debugging reference count in an attempt to
* ensure that an opal_tree_item_t is only one *one* tree at a time.
* Given the highly concurrent nature of this class, these spot checks
* cannot guarantee that an item is only one tree at a time.
* Specifically, since it is a desirable attribute of this class to
* not use locks for normal operations, it is possible that two
* threads may [erroneously] modify an opal_tree_item_t concurrently.
*
* The only way to guarantee that a debugging reference count is valid
* for the duration of an operation is to lock the item_t during the
* operation. But this fundamentally changes the desirable attribute
* of this class (i.e., no locks). So all we can do is spot-check the
* reference count in a bunch of places and check that it is still the
* value that we think it should be. But this doesn't mean that you
* can run into "unlucky" cases where two threads are concurrently
* modifying an item_t, but all the spot checks still return the
* "right" values. All we can do is hope that we have enough spot
* checks to statistically drive down the possibility of the unlucky
* cases happening.
*/
#ifndef OPAL_TREE_H
#define OPAL_TREE_H
#include "opal_config.h"
#include <stdio.h>
#include <stdlib.h>
#include "opal/class/opal_object.h"
#include "opal/dss/dss.h"
#if OPAL_ENABLE_DEBUG
/* Need atomics for debugging (reference counting) */
#include "opal/sys/atomic.h"
#include "opal/threads/mutex.h"
#endif
BEGIN_C_DECLS
/**
* \internal
*
* The class for the tree container.
*/
OPAL_DECLSPEC OBJ_CLASS_DECLARATION(opal_tree_t);
/**
* \internal
*
* Base class for items that are put in tree containers.
*/
OPAL_DECLSPEC OBJ_CLASS_DECLARATION(opal_tree_item_t);
/**
* \internal
*
* Struct of an opal_tree_item_t
*/
typedef struct opal_tree_item_t
{
/** Generic parent class for all Open MPI objects */
opal_object_t super;
/** Pointer to the tree this item belongs to */
struct opal_tree_t *opal_tree_container;
/* Parent info */
/** Pointer to parent tree item */
struct opal_tree_item_t *opal_tree_parent;
/** Depth from the root item in tree */
unsigned opal_tree_num_ancestors;
/* Logical rank we are compared to other siblings */
unsigned opal_tree_sibling_rank;
/** Pointer to next item below same parent (or NULL if this is the
leftmost sibling) */
struct opal_tree_item_t *opal_tree_next_sibling;
/** Pointer to previous item below same parent (or NULL if this is
the rightmost sibling) */
struct opal_tree_item_t *opal_tree_prev_sibling;
/* Children info */
/** Number of children */
unsigned opal_tree_num_children;
/** Pointer to first child item, or NULL if there are no children */
struct opal_tree_item_t *opal_tree_first_child;
/** Pointer to last child item, or NULL if there are no children */
struct opal_tree_item_t *opal_tree_last_child;
#if OPAL_ENABLE_DEBUG
/** Atomic reference count for debugging */
int32_t opal_tree_item_refcount;
/** The tree this item belong to */
struct opal_tree_t* opal_tree_item_belong_to;
#endif
} opal_tree_item_t;
/**
* Check to see if item's user specific data matches key
*
* @param item - The item we want to check to see if it matches key
* @param key - The opaque key that we want the match function to check
* in the item's user specific data.
*
* @returns 0 - If item's user specific data matches key
* @returns non-zero - If item's user specific data does not match key
*
* This function is implemented by the code that constructs the tree
* and initialized the pointer by the call to opal_tree_init.
*
*/
typedef int (*opal_tree_comp_fn_t)(opal_tree_item_t *item, void *key);
/**
* The serialize function typedef. This function is called by the
* opal tree serialize code to serialize a tree item's user specific
* data of a class type.
*
* @params item - item to serialize the user specific data from
* @params buffer - the opal_buffer_t to store the serialized data in.
*
* @returns OPAL_SUCCESS - when successfully serialized item
*/
typedef int (*opal_tree_item_serialize_fn_t)(opal_tree_item_t *item,
opal_buffer_t *buffer);
/**
* The deserialize function typedef. This function is called by the
* opal tree deserialize code to deserialize a tree item's user
* specific data.
*
* @params buffer - the opal_buffer_t to deserialized data.
* @params item - item to store the deserialized data into the user
* specific data
*
* @returns OPAL_SUCCESS - when successfully deserialized item
*/
typedef int (*opal_tree_item_deserialize_fn_t)(opal_buffer_t *buffer,
opal_tree_item_t **item);
/**
* Get the 'key' associated with this item
*/
typedef void *(*opal_tree_get_key_fn_t)(opal_tree_item_t *item);
/**
* \internal
*
* Struct of an opal_tree_t
*
*/
typedef struct opal_tree_t
{
/** Generic parent class for all Open MPI objects */
opal_object_t super;
/** Guard item of the tree that child points to root */
opal_tree_item_t opal_tree_sentinel;
/** Quick reference to the number of items in the tree */
volatile size_t opal_tree_num_items;
/** Function to compare two items in tree */
opal_tree_comp_fn_t comp;
/** Function to serialize tree item data */
opal_tree_item_serialize_fn_t serialize;
/** Function to deserialize tree item data */
opal_tree_item_deserialize_fn_t deserialize;
/**< Function to deserialize tree item data */
opal_tree_get_key_fn_t get_key;
} opal_tree_t;
/** Macros to access items in the tree */
/**
* Get the parent of item in the tree.
*
* @param item A tree item.
*
* @returns The parent item in the tree
*
* This function is safe to be called with a null item pointer.
*/
static inline opal_tree_item_t *opal_tree_get_parent(opal_tree_item_t *item)
{
return ((item) ? item->opal_tree_parent : NULL);
}
/**
* Get the next sibling item in the tree.
*
* @param item A tree item.
*
* @returns The next sibling item in the tree
*
* This function is safe to be called with a null item pointer.
*/
static inline opal_tree_item_t *opal_tree_get_next_sibling(opal_tree_item_t
*item)
{
return ((item) ? item->opal_tree_next_sibling : NULL);
}
/**
* Get the previous sibling item in the tree.
*
* @param item A tree item.
*
* @returns The previous sibling item in the tree
*
* This function is safe to be called with a null item pointer.
*/
static inline opal_tree_item_t *opal_tree_get_prev_sibling(opal_tree_item_t
*item)
{
return ((item) ? item->opal_tree_prev_sibling : NULL);
}
/**
* Get the first child item in the tree.
*
* @param item A tree item.
*
* @returns The first child item in the tree
*
* This function is safe to be called with a null item pointer.
*
*/
static inline opal_tree_item_t *opal_tree_get_first_child(opal_tree_item_t
*item)
{
return ((item) ? item->opal_tree_first_child : NULL);
}
/**
* Get the last child item in the tree.
*
* @param item A tree item.
*
* @returns The last child item in the tree
*
* This function is safe to be called with a null item pointer.
*
*/
static inline opal_tree_item_t *opal_tree_get_last_child(opal_tree_item_t
*item)
{
return ((item) ? item->opal_tree_last_child : NULL);
}
/**
* Check for empty tree
*
* @param tree The tree container
*
* @returns true if tree's size is 0, false otherwise
*
* This is an O(1) operation.
*
* This is an inlined function in compilers that support inlining,
* so it's usually a cheap operation.
*
*/
static inline bool opal_tree_is_empty(opal_tree_t* tree)
{
#if OPAL_ENABLE_DEBUG
/* Spot check that the tree is a non-null pointer */
assert(NULL != tree);
#endif
return (tree->opal_tree_sentinel.opal_tree_first_child ==
&(tree->opal_tree_sentinel) ? true : false);
}
/**
* Return the root item on the tree (does not remove it).
*
* @param tree The tree container
*
* @returns A pointer to the first item in the tree
*
* This is an O(1) operation to return the first item in the tree.
*
* This is an inlined function in compilers that support inlining, so
* it's usually a cheap operation.
*
*/
static inline opal_tree_item_t* opal_tree_get_root(opal_tree_t* tree)
{
opal_tree_item_t* item;
#if OPAL_ENABLE_DEBUG
assert(NULL != tree);
#endif
item = tree->opal_tree_sentinel.opal_tree_first_child;
#if OPAL_ENABLE_DEBUG
/* Spot check: ensure that the first item is only on one list */
assert(1 == item->opal_tree_item_refcount);
assert(tree == item->opal_tree_item_belong_to );
#endif
return item;
}
/**
* Return the number of items in a tree
*
* @param tree The tree container
*
* @returns The size of the tree (size_t)
*
* This is an O(1) (in non-debug mode) lookup to return the
* size of the list.
*/
OPAL_DECLSPEC size_t opal_tree_get_size(opal_tree_t* tree);
/* Functions to manage the tree */
/**
* Initialize tree container; must be called before using
* the tree.
*
* @param tree The tree to initialize
* @param comp Comparison function to attach to tree.
* @param serialize Serialization function to attach to tree.
* @param deserialize De-serialization function to attach to tree.
*
*/
OPAL_DECLSPEC void opal_tree_init(opal_tree_t *tree,
opal_tree_comp_fn_t comp,
opal_tree_item_serialize_fn_t serialize,
opal_tree_item_deserialize_fn_t deserialize,
opal_tree_get_key_fn_t get_key);
/**
* Add new item as child to its parent item
*
* @param parent_item pointer to what parent the new item belongs to
* @param new_item the item to be added as a child to parent_item
*
* The new_item is added at the end of the child list of the parent_item.
*/
OPAL_DECLSPEC void opal_tree_add_child(opal_tree_item_t *parent_item,
opal_tree_item_t *new_item);
/**
* Remove an item and everything below from a tree.
*
* @param item The item at the top of subtree to remove
*
* @returns A pointer to the item on the list previous to the one
* that was removed.
*
* This is an O(1) operation to remove an item from the tree. The
* item and all children below it will be removed from the tree. This
* means the item's siblings pointers and potentially the parents first
* and last pointers will be updated to skip over the item. The tree container
* will also have its num_items adjusted to reflect the number of items
* that were removed. The tree item (and all children below it) that is
* returned is now "owned" by the caller -- they are responsible for
* OBJ_RELEASE()'ing it.
*
* With ENABLE_DEBUG on this routine will validate whether the item is actually
* in the tree before doing pointer manipulation.
*/
OPAL_DECLSPEC opal_tree_item_t *opal_tree_remove_subtree(opal_tree_item_t *item);
/**
* Remove an item, everything below inherited by parent.
*
* @param tree Tree from which to remove
* @param item The item to remove
*
* @returns Success/Failure
*/
OPAL_DECLSPEC int opal_tree_remove_item(opal_tree_t *tree,
opal_tree_item_t *item);
/**
* Serialize tree data
*
* @param start_item The item of a tree to start serializing data
* @param buffer The opal buffer that contains the serialized
* data stream of the tree
*
* @returns OPAL_SUCCESS if data has been successfully converted.
*
* This routine walks the tree starting at start_item until it has serialized
* all children items of start_item and creates a bytestream of data,
* using the opal_dss.pack routine, that can be sent over a network.
* The format of the bytestream represents the tree parent/child relationship
* of each item in the tree plus the data inside the tree. This routine calls
* the tree's serialization method to serialize the user specific data for
* each item.
*
*/
OPAL_DECLSPEC int opal_tree_serialize(opal_tree_item_t *start_item,
opal_buffer_t *buffer);
/**
* De-serialize tree data
*
* @param buffer The opal buffer that is to be deserialized
* @param start_item The item in the tree the data should be
* deserialized into
*
* @returns Status of call OPAL_SUCCESS if everything worked
*
* This routine takes a bytestream that was created by the
* opal_tree_serialize() function and deserializes it into the
* tree given. If the tree already has data in it, this routine
* will start adding the new data as a new child of the root
* item. This routine calls the tree's de-serialization
* method to deserialize the user specific data for each item.
*
*/
OPAL_DECLSPEC int opal_tree_deserialize(opal_buffer_t *buffer,
opal_tree_item_t *start_item);
/**
* Access the 'key' associated with the item
*
* @param tree Source Tree
* @param item Item to access key of
*
* @returns Success/Failure
*/
OPAL_DECLSPEC void * opal_tree_get_key(opal_tree_t *tree, opal_tree_item_t *item);
/**
* Copy/Duplicate a tree (requires serialize/deserialize)
*
* @param from Source tree to copy 'from'
* @param to Destination tree to copy 'to'
*
* @returns Success/Failure
*/
OPAL_DECLSPEC int opal_tree_dup(opal_tree_t *from, opal_tree_t *to);
/**
* Copy/Duplicate a subtree (requires serialize/deserialize)
*
* @param base Base tree
* @param from Source tree item to copy 'from'
*
* @returns Tree item copy
*/
OPAL_DECLSPEC int opal_tree_copy_subtree(opal_tree_t *from_tree, opal_tree_item_t *from_item,
opal_tree_t *to_tree, opal_tree_item_t *to_parent);
/**
* Copy/Duplicate a tree item (requires serialize/deserialize)
*
* @param base Base tree
* @param from Source tree item to copy 'from'
*
* @returns Tree item copy
*/
OPAL_DECLSPEC opal_tree_item_t *opal_tree_dup_item(opal_tree_t *base, opal_tree_item_t *from);
/**
* Count the number of children of this parent
*
* @param parent A parent node in the tree
*
* @returns Number of children of this parent
*/
OPAL_DECLSPEC int opal_tree_num_children(opal_tree_item_t *parent);
/**
* Compare two trees
*
* @param left Tree
* @param right Tree
*
* @returns 0 if identical, ow returns non-zero
*/
OPAL_DECLSPEC int opal_tree_compare(opal_tree_t *left, opal_tree_t *right);
/* Functions to search for items on tree */
/**
* Return the next tree item that matches key provided
*
* @param item The item to start the find from
* @param key the key we are wanting to match with
*
* @returns A pointer to the next item that in the tree (starting from item)
* that matches the key based on a depth first search of the tree. A null
* pointer is returned if we've reached the end of the tree and have not
* matched the key.
*
* This routine uses the tree container's comp function to determine the
* whether there is a match between the key and each item we search in the
* tree. This means the actual tree type constructed determines how the
* compare is done with the key. In the case no compare routine is given
* and NULL pointer is always returned for this function.
*
*/
OPAL_DECLSPEC opal_tree_item_t *opal_tree_find_with(opal_tree_item_t *item,
void *key);
END_C_DECLS
#endif /* OPAL_TREE_H */