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