1 /*
2  * Copyright (c) 2011      Oracle and/or its affiliates.  All rights reserved.
3  * Copyright (c) 2011      Oak Ridge National Labs.  All rights reserved.
4  *
5  * $COPYRIGHT$
6  *
7  * Additional copyrights may follow
8  *
9  * $HEADER$
10  */
11 /**
12  * @file
13  *
14  * The opal_tree_t interface is used to provide a generic
15  * tree list container for Open MPI.  It was inspired by the opal_list_t
16  * interface but instead of organizing items in a doubly-linked list
17  * fashion, we order them in a finite tree structure.
18  *
19  * The general idea is a user creates an class instance that has two
20  * components.  A tree structure component as defined by opal_tree_item_t
21  * that links all the items together to form the tree.  Then there is
22  * a user specific data component which the user defines what is stored at
23  * each item.  When a user create a type to be used for a OBJ_CLASS_INSTANCE
24  * it will contain the opal_tree_item_t followed by any user specific
25  * data.  Then the opal_tree_item_t objects can be put in an
26  * opal_tree_t.  Hence, you create a new type that derives from
27  * opal_tree_item_t; this new type can then be used with opal_tree_t
28  * containers.
29  *
30  * NOTE: opal_tree_item_t instances can only be on \em one tree at a
31  * time.  Specifically, if you add an opal_tree_item_t to one tree,
32  * and then add it to another tree (without first removing it from the
33  * first tree), you will effectively be hosing the first tree.  You
34  * have been warned.
35  *
36  * If OPAL_ENABLE_DEBUG is true, a bunch of checks occur, including
37  * some spot checks for a debugging reference count in an attempt to
38  * ensure that an opal_tree_item_t is only one *one* tree at a time.
39  * Given the highly concurrent nature of this class, these spot checks
40  * cannot guarantee that an item is only one tree at a time.
41  * Specifically, since it is a desirable attribute of this class to
42  * not use locks for normal operations, it is possible that two
43  * threads may [erroneously] modify an opal_tree_item_t concurrently.
44  *
45  * The only way to guarantee that a debugging reference count is valid
46  * for the duration of an operation is to lock the item_t during the
47  * operation.  But this fundamentally changes the desirable attribute
48  * of this class (i.e., no locks).  So all we can do is spot-check the
49  * reference count in a bunch of places and check that it is still the
50  * value that we think it should be.  But this doesn't mean that you
51  * can run into "unlucky" cases where two threads are concurrently
52  * modifying an item_t, but all the spot checks still return the
53  * "right" values.  All we can do is hope that we have enough spot
54  * checks to statistically drive down the possibility of the unlucky
55  * cases happening.
56  */
57 
58 #ifndef OPAL_TREE_H
59 #define OPAL_TREE_H
60 
61 #include "opal_config.h"
62 #include <stdio.h>
63 #include <stdlib.h>
64 #include "opal/class/opal_object.h"
65 #include "opal/dss/dss.h"
66 
67 #if OPAL_ENABLE_DEBUG
68 /* Need atomics for debugging (reference counting) */
69 #include "opal/sys/atomic.h"
70 #include "opal/threads/mutex.h"
71 #endif
72 
73 BEGIN_C_DECLS
74 
75 /**
76  * \internal
77  *
78  * The class for the tree container.
79  */
80 OPAL_DECLSPEC OBJ_CLASS_DECLARATION(opal_tree_t);
81 /**
82  * \internal
83  *
84  * Base class for items that are put in tree containers.
85  */
86 OPAL_DECLSPEC OBJ_CLASS_DECLARATION(opal_tree_item_t);
87 
88 /**
89  * \internal
90  *
91  * Struct of an opal_tree_item_t
92  */
93 typedef struct opal_tree_item_t
94 {
95     /** Generic parent class for all Open MPI objects */
96     opal_object_t super;
97     /** Pointer to the tree this item belongs to */
98     struct opal_tree_t *opal_tree_container;
99     /* Parent info */
100     /** Pointer to parent tree item */
101     struct opal_tree_item_t *opal_tree_parent;
102     /** Depth from the root item in tree */
103     unsigned opal_tree_num_ancestors;
104     /* Logical rank we are compared to other siblings */
105     unsigned opal_tree_sibling_rank;
106     /** Pointer to next item below same parent (or NULL if this is the
107         leftmost sibling) */
108     struct opal_tree_item_t *opal_tree_next_sibling;
109     /** Pointer to previous item below same parent (or NULL if this is
110         the rightmost sibling) */
111     struct opal_tree_item_t *opal_tree_prev_sibling;
112 
113     /* Children info */
114     /** Number of children */
115     unsigned opal_tree_num_children;
116     /** Pointer to first child item, or NULL if there are no children */
117     struct opal_tree_item_t *opal_tree_first_child;
118     /** Pointer to last child item, or NULL if there are no children */
119     struct opal_tree_item_t *opal_tree_last_child;
120 
121 #if OPAL_ENABLE_DEBUG
122     /** Atomic reference count for debugging */
123     int32_t opal_tree_item_refcount;
124     /** The tree this item belong to */
125     struct opal_tree_t* opal_tree_item_belong_to;
126 #endif
127 } opal_tree_item_t;
128 
129 /**
130  * Check to see if item's user specific data matches key
131  *
132  * @param item - The item we want to check to see if it matches key
133  * @param key - The opaque key that we want the match function to check
134  *              in the item's user specific data.
135  *
136  * @returns 0 - If item's user specific data matches key
137  * @returns non-zero - If item's user specific data does not match key
138  *
139  * This function is implemented by the code that constructs the tree
140  * and initialized the pointer by the call to opal_tree_init.
141  *
142  */
143 typedef int (*opal_tree_comp_fn_t)(opal_tree_item_t *item, void *key);
144 
145 /**
146   * The serialize function typedef. This function is called by the
147   * opal tree serialize code to serialize a tree item's user specific
148   * data of a class type.
149   *
150   * @params item - item to serialize the user specific data from
151   * @params buffer - the opal_buffer_t to store the serialized data in.
152   *
153   * @returns OPAL_SUCCESS - when successfully serialized item
154   */
155 typedef int (*opal_tree_item_serialize_fn_t)(opal_tree_item_t *item,
156 					     opal_buffer_t *buffer);
157 
158 /**
159   * The deserialize function typedef. This function is called by the
160   * opal tree deserialize code to deserialize a tree item's user
161   * specific data.
162   *
163   * @params buffer - the opal_buffer_t to deserialized data.
164   * @params item - item to store the deserialized data into the user
165   *                specific data
166   *
167   * @returns OPAL_SUCCESS - when successfully deserialized item
168   */
169 typedef int (*opal_tree_item_deserialize_fn_t)(opal_buffer_t *buffer,
170 					       opal_tree_item_t **item);
171 
172 /**
173   * Get the 'key' associated with this item
174   */
175 typedef void *(*opal_tree_get_key_fn_t)(opal_tree_item_t *item);
176 
177 /**
178  * \internal
179  *
180  * Struct of an opal_tree_t
181  *
182  */
183 typedef struct opal_tree_t
184 {
185     /** Generic parent class for all Open MPI objects */
186     opal_object_t       super;
187     /** Guard item of the tree that child points to root */
188     opal_tree_item_t    opal_tree_sentinel;
189     /** Quick reference to the number of items in the tree */
190     volatile size_t     opal_tree_num_items;
191     /** Function to compare two items in tree */
192     opal_tree_comp_fn_t comp;
193     /** Function to serialize tree item data */
194     opal_tree_item_serialize_fn_t serialize;
195     /** Function to deserialize tree item data */
196     opal_tree_item_deserialize_fn_t deserialize;
197     /**< Function to deserialize tree item data */
198     opal_tree_get_key_fn_t get_key;
199 } opal_tree_t;
200 
201 /** Macros to access items in the tree */
202 
203 /**
204  * Get the parent of item in the tree.
205  *
206  * @param item A tree item.
207  *
208  * @returns The parent item in the tree
209  *
210  * This function is safe to be called with a null item pointer.
211  */
opal_tree_get_parent(opal_tree_item_t * item)212 static inline opal_tree_item_t *opal_tree_get_parent(opal_tree_item_t *item)
213 {
214     return ((item) ? item->opal_tree_parent : NULL);
215 }
216 
217 /**
218  * Get the next sibling item in the tree.
219  *
220  * @param item A tree item.
221  *
222  * @returns The next sibling item in the tree
223  *
224  * This function is safe to be called with a null item pointer.
225  */
opal_tree_get_next_sibling(opal_tree_item_t * item)226 static inline opal_tree_item_t *opal_tree_get_next_sibling(opal_tree_item_t
227                                                            *item)
228 {
229     return ((item) ? item->opal_tree_next_sibling : NULL);
230 }
231 
232 
233 /**
234  * Get the previous sibling item in the tree.
235  *
236  * @param item A tree item.
237  *
238  * @returns The previous sibling item in the tree
239  *
240  * This function is safe to be called with a null item pointer.
241  */
opal_tree_get_prev_sibling(opal_tree_item_t * item)242 static inline opal_tree_item_t *opal_tree_get_prev_sibling(opal_tree_item_t
243                                                            *item)
244 {
245     return ((item) ? item->opal_tree_prev_sibling : NULL);
246 }
247 
248 /**
249  * Get the first child item in the tree.
250  *
251  * @param item A tree item.
252  *
253  * @returns The first child item in the tree
254  *
255  * This function is safe to be called with a null item pointer.
256  *
257  */
opal_tree_get_first_child(opal_tree_item_t * item)258 static inline opal_tree_item_t *opal_tree_get_first_child(opal_tree_item_t
259                                                           *item)
260 {
261     return ((item) ? item->opal_tree_first_child : NULL);
262 }
263 
264 /**
265  * Get the last child item in the tree.
266  *
267  * @param item A tree item.
268  *
269  * @returns The last child item in the tree
270  *
271  * This function is safe to be called with a null item pointer.
272  *
273  */
opal_tree_get_last_child(opal_tree_item_t * item)274 static inline opal_tree_item_t *opal_tree_get_last_child(opal_tree_item_t
275                                                          *item)
276 {
277     return ((item) ? item->opal_tree_last_child : NULL);
278 }
279 
280 /**
281  * Check for empty tree
282  *
283  * @param tree The tree container
284  *
285  * @returns true if tree's size is 0, false otherwise
286  *
287  * This is an O(1) operation.
288  *
289  * This is an inlined function in compilers that support inlining,
290  * so it's usually a cheap operation.
291  *
292  */
opal_tree_is_empty(opal_tree_t * tree)293 static inline bool opal_tree_is_empty(opal_tree_t* tree)
294 {
295 #if OPAL_ENABLE_DEBUG
296     /* Spot check that the tree is a non-null pointer */
297     assert(NULL != tree);
298 #endif
299     return (tree->opal_tree_sentinel.opal_tree_first_child ==
300             &(tree->opal_tree_sentinel) ? true : false);
301 }
302 
303 
304 /**
305  * Return the root item on the tree (does not remove it).
306  *
307  * @param tree The tree container
308  *
309  * @returns A pointer to the first item in the tree
310  *
311  * This is an O(1) operation to return the first item in the tree.
312  *
313  * This is an inlined function in compilers that support inlining, so
314  * it's usually a cheap operation.
315  *
316  */
opal_tree_get_root(opal_tree_t * tree)317 static inline opal_tree_item_t* opal_tree_get_root(opal_tree_t* tree)
318 {
319     opal_tree_item_t* item;
320 #if OPAL_ENABLE_DEBUG
321     assert(NULL != tree);
322 #endif
323     item = tree->opal_tree_sentinel.opal_tree_first_child;
324 #if OPAL_ENABLE_DEBUG
325     /* Spot check: ensure that the first item is only on one list */
326     assert(1 == item->opal_tree_item_refcount);
327     assert(tree == item->opal_tree_item_belong_to );
328 #endif
329     return item;
330 }
331 
332 /**
333  * Return the number of items in a tree
334  *
335  * @param tree The tree container
336  *
337  * @returns The size of the tree (size_t)
338  *
339  * This is an O(1) (in non-debug mode) lookup to return the
340  * size of the list.
341  */
342 OPAL_DECLSPEC size_t opal_tree_get_size(opal_tree_t* tree);
343 
344 
345 /* Functions to manage the tree */
346 /**
347  * Initialize tree container; must be called before using
348  * the tree.
349  *
350  * @param tree        The tree to initialize
351  * @param comp        Comparison function to attach to tree.
352  * @param serialize   Serialization function to attach to tree.
353  * @param deserialize De-serialization function to attach to tree.
354  *
355  */
356 OPAL_DECLSPEC void opal_tree_init(opal_tree_t *tree,
357                                   opal_tree_comp_fn_t comp,
358                                   opal_tree_item_serialize_fn_t serialize,
359                                   opal_tree_item_deserialize_fn_t deserialize,
360                                   opal_tree_get_key_fn_t get_key);
361 
362 /**
363  * Add new item as child to its parent item
364  *
365  * @param parent_item pointer to what parent the new item belongs to
366  * @param new_item the item to be added as a child to parent_item
367  *
368  * The new_item is added at the end of the child list of the parent_item.
369  */
370 OPAL_DECLSPEC void opal_tree_add_child(opal_tree_item_t *parent_item,
371 				       opal_tree_item_t *new_item);
372 
373 /**
374  * Remove an item and everything below from a tree.
375  *
376  * @param item The item at the top of subtree to remove
377  *
378  * @returns A pointer to the item on the list previous to the one
379  * that was removed.
380  *
381  * This is an O(1) operation to remove an item from the tree.  The
382  * item and all children below it will be removed from the tree.  This
383  * means the item's siblings pointers and potentially the parents first
384  * and last pointers will be updated to skip over the item.  The tree container
385  * will also have its num_items adjusted to reflect the number of items
386  * that were removed.  The tree item (and all children below it) that is
387  * returned is now "owned" by the caller -- they are responsible for
388  * OBJ_RELEASE()'ing it.
389  *
390  * With ENABLE_DEBUG on this routine will validate whether the item is actually
391  * in the tree before doing pointer manipulation.
392  */
393 OPAL_DECLSPEC opal_tree_item_t *opal_tree_remove_subtree(opal_tree_item_t *item);
394 
395 /**
396  * Remove an item, everything below inherited by parent.
397  *
398  * @param tree Tree from which to remove
399  * @param item The item to remove
400  *
401  * @returns Success/Failure
402  */
403 OPAL_DECLSPEC int opal_tree_remove_item(opal_tree_t *tree,
404                                         opal_tree_item_t *item);
405 
406 /**
407  * Serialize tree data
408  *
409  * @param start_item The item of a tree to start serializing data
410  * @param buffer The opal buffer that contains the serialized
411  *  data stream of the tree
412  *
413  * @returns OPAL_SUCCESS if data has been successfully converted.
414  *
415  * This routine walks the tree starting at start_item until it has serialized
416  * all children items of start_item and creates a bytestream of data,
417  * using the opal_dss.pack routine, that can be sent over a network.
418  * The format of the bytestream represents the tree parent/child relationship
419  * of each item in the tree plus the data inside the tree.  This routine calls
420  * the tree's serialization method to serialize the user specific data for
421  * each item.
422  *
423  */
424 OPAL_DECLSPEC int opal_tree_serialize(opal_tree_item_t *start_item,
425 				      opal_buffer_t *buffer);
426 
427 /**
428  * De-serialize tree data
429  *
430  * @param buffer The opal buffer that is to be deserialized
431  * @param start_item The item in the tree the data should be
432  * deserialized into
433  *
434  * @returns Status of call OPAL_SUCCESS if everything worked
435  *
436  * This routine takes a bytestream that was created by the
437  * opal_tree_serialize() function and deserializes it into the
438  * tree given.  If the tree already has data in it, this routine
439  * will start adding the new data as a new child of the root
440  * item.  This routine calls the tree's de-serialization
441  * method to deserialize the user specific data for each item.
442  *
443  */
444 OPAL_DECLSPEC int opal_tree_deserialize(opal_buffer_t *buffer,
445                                         opal_tree_item_t *start_item);
446 
447 /**
448  * Access the 'key' associated with the item
449  *
450  * @param tree Source Tree
451  * @param item Item to access key of
452  *
453  * @returns Success/Failure
454  */
455 OPAL_DECLSPEC void * opal_tree_get_key(opal_tree_t *tree, opal_tree_item_t *item);
456 
457 /**
458  * Copy/Duplicate a tree (requires serialize/deserialize)
459  *
460  * @param from Source tree to copy 'from'
461  * @param to   Destination tree to copy 'to'
462  *
463  * @returns Success/Failure
464  */
465 OPAL_DECLSPEC int opal_tree_dup(opal_tree_t *from, opal_tree_t *to);
466 
467 /**
468  * Copy/Duplicate a subtree (requires serialize/deserialize)
469  *
470  * @param base Base tree
471  * @param from Source tree item to copy 'from'
472  *
473  * @returns Tree item copy
474  */
475 OPAL_DECLSPEC int opal_tree_copy_subtree(opal_tree_t *from_tree, opal_tree_item_t *from_item,
476                                          opal_tree_t *to_tree,   opal_tree_item_t *to_parent);
477 
478 /**
479  * Copy/Duplicate a tree item (requires serialize/deserialize)
480  *
481  * @param base Base tree
482  * @param from Source tree item to copy 'from'
483  *
484  * @returns Tree item copy
485  */
486 OPAL_DECLSPEC opal_tree_item_t *opal_tree_dup_item(opal_tree_t *base, opal_tree_item_t *from);
487 
488 /**
489  * Count the number of children of this parent
490  *
491  * @param parent A parent node in the tree
492  *
493  * @returns Number of children of this parent
494  */
495 OPAL_DECLSPEC int opal_tree_num_children(opal_tree_item_t *parent);
496 
497 /**
498  * Compare two trees
499  *
500  * @param left Tree
501  * @param right Tree
502  *
503  * @returns 0 if identical, ow returns non-zero
504  */
505 OPAL_DECLSPEC int opal_tree_compare(opal_tree_t *left, opal_tree_t *right);
506 
507 /* Functions to search for items on tree */
508 /**
509  * Return the next tree item that matches key provided
510  *
511  * @param item The item to start the find from
512  * @param key the key we are wanting to match with
513  *
514  * @returns A pointer to the next item that in the tree (starting from item)
515  * that matches the key based on a depth first search of the tree.  A null
516  * pointer is returned if we've reached the end of the tree and have not
517  * matched the key.
518  *
519  * This routine uses the tree container's comp function to determine the
520  * whether there is a match between the key and each item we search in the
521  * tree.  This means the actual tree type constructed determines how the
522  * compare is done with the key.  In the case no compare routine is given
523  * and NULL pointer is always returned for this function.
524  *
525  */
526 OPAL_DECLSPEC opal_tree_item_t *opal_tree_find_with(opal_tree_item_t *item,
527                                                     void *key);
528 END_C_DECLS
529 
530 #endif /* OPAL_TREE_H */
531