1 /* -*- Mode: C; c-basic-offset:4 ; -*- */
2  /*
3  * Copyright (c) 2004-2005 The Trustees of Indiana University and Indiana
4  *                         University Research and Technology
5  *                         Corporation.  All rights reserved.
6  * Copyright (c) 2004-2006 The University of Tennessee and The University
7  *                         of Tennessee Research Foundation.  All rights
8  *                         reserved.
9  * Copyright (c) 2004-2005 High Performance Computing Center Stuttgart,
10  *                         University of Stuttgart.  All rights reserved.
11  * Copyright (c) 2004-2005 The Regents of the University of California.
12  *                         All rights reserved.
13  * Copyright (c) 2007      Voltaire All rights reserved.
14  * Copyright (c) 2013      Los Alamos National Security, LLC. All rights
15  *                         reserved.
16  * Copyright (c) 2013-2019 Intel, Inc.  All rights reserved.
17  * $COPYRIGHT$
18  *
19  * Additional copyrights may follow
20  *
21  * $HEADER$
22  */
23 /**
24  * @file
25  *
26  * The pmix_list_t interface is used to provide a generic
27  * doubly-linked list container for PMIx.  It was inspired by (but
28  * is slightly different than) the Standard Template Library (STL)
29  * std::list class.  One notable difference from std::list is that
30  * when an pmix_list_t is destroyed, all of the pmix_list_item_t
31  * objects that it contains are orphaned -- they are \em not
32  * destroyed.
33  *
34  * The general idea is that pmix_list_item_t objects can be put on an
35  * pmix_list_t.  Hence, you create a new type that derives from
36  * pmix_list_item_t; this new type can then be used with pmix_list_t
37  * containers.
38  *
39  * NOTE: pmix_list_item_t instances can only be on \em one list at a
40  * time.  Specifically, if you add an pmix_list_item_t to one list,
41  * and then add it to another list (without first removing it from the
42  * first list), you will effectively be hosing the first list.  You
43  * have been warned.
44  *
45  * If PMIX_ENABLE_DEBUG is true, a bunch of checks occur, including
46  * some spot checks for a debugging reference count in an attempt to
47  * ensure that an pmix_list_item_t is only one *one* list at a time.
48  * Given the highly concurrent nature of this class, these spot checks
49  * cannot guarantee that an item is only one list at a time.
50  * Specifically, since it is a desirable attribute of this class to
51  * not use locks for normal operations, it is possible that two
52  * threads may [erroneously] modify an pmix_list_item_t concurrently.
53  *
54  * The only way to guarantee that a debugging reference count is valid
55  * for the duration of an operation is to lock the item_t during the
56  * operation.  But this fundamentally changes the desirable attribute
57  * of this class (i.e., no locks).  So all we can do is spot-check the
58  * reference count in a bunch of places and check that it is still the
59  * value that we think it should be.  But this doesn't mean that you
60  * can run into "unlucky" cases where two threads are concurrently
61  * modifying an item_t, but all the spot checks still return the
62  * "right" values.  All we can do is hope that we have enough spot
63  * checks to statistically drive down the possibility of the unlucky
64  * cases happening.
65  */
66 
67 #ifndef PMIX_LIST_H
68 #define PMIX_LIST_H
69 
70 #include <src/include/pmix_config.h>
71 #include <stdio.h>
72 #include <stdlib.h>
73 #if HAVE_STDBOOL_H
74 #include <stdbool.h>
75 #endif
76 
77 #include "src/class/pmix_object.h"
78 
79 BEGIN_C_DECLS
80 
81 /**
82  * \internal
83  *
84  * The class for the list container.
85  */
86 PMIX_EXPORT PMIX_CLASS_DECLARATION(pmix_list_t);
87 /**
88  * \internal
89  *
90  * Base class for items that are put in list (pmix_list_t) containers.
91  */
92 PMIX_EXPORT PMIX_CLASS_DECLARATION(pmix_list_item_t);
93 
94 
95 /**
96  * \internal
97  *
98  * Struct of an pmix_list_item_t
99  */
100 struct pmix_list_item_t
101 {
102     pmix_object_t super;
103     /**< Generic parent class for all PMIx objects */
104     volatile struct pmix_list_item_t *pmix_list_next;
105     /**< Pointer to next list item */
106     volatile struct pmix_list_item_t *pmix_list_prev;
107     /**< Pointer to previous list item */
108     int32_t item_free;
109 
110 #if PMIX_ENABLE_DEBUG
111     /** Atomic reference count for debugging */
112     pmix_atomic_int32_t pmix_list_item_refcount;
113     /** The list this item belong to */
114     volatile struct pmix_list_t* pmix_list_item_belong_to;
115 #endif
116 };
117 /**
118  * Base type for items that are put in a list (pmix_list_t) containers.
119  */
120 typedef struct pmix_list_item_t pmix_list_item_t;
121 
122 
123 /**
124  * Get the next item in a list.
125  *
126  * @param item A list item.
127  *
128  * @returns The next item in the list
129  */
130 #define pmix_list_get_next(item) \
131     ((item) ? ((pmix_list_item_t*) ((pmix_list_item_t*)(item))->pmix_list_next) : NULL)
132 
133 /**
134  * Get the next item in a list.
135  *
136  * @param item A list item.
137  *
138  * @returns The next item in the list
139  */
140 #define pmix_list_get_prev(item) \
141     ((item) ? ((pmix_list_item_t*) ((pmix_list_item_t*)(item))->pmix_list_prev) : NULL)
142 
143 
144 /**
145  * \internal
146  *
147  * Struct of an pmix_list_t
148  */
149 struct pmix_list_t
150 {
151     pmix_object_t       super;
152     /**< Generic parent class for all PMIx objects */
153     pmix_list_item_t    pmix_list_sentinel;
154     /**< Head and tail item of the list */
155     volatile size_t     pmix_list_length;
156     /**< Quick reference to the number of items in the list */
157 };
158 /**
159  * List container type.
160  */
161 typedef struct pmix_list_t pmix_list_t;
162 
163 /** Cleanly destruct a list
164  *
165  * The pmix_list_t destructor doesn't release the items on the
166  * list - so provide two convenience macros that do so and then
167  * destruct/release the list object itself
168  *
169  * @param[in] list List to destruct or release
170  */
171 #define PMIX_LIST_DESTRUCT(list)                                \
172     do {                                                        \
173         pmix_list_item_t *it;                                   \
174         while (NULL != (it = pmix_list_remove_first(list))) {   \
175             PMIX_RELEASE(it);                                    \
176         }                                                       \
177         PMIX_DESTRUCT(list);                                     \
178     } while (0)
179 
180 #define PMIX_LIST_RELEASE(list)                                 \
181     do {                                                        \
182         pmix_list_item_t *it;                                   \
183         while (NULL != (it = pmix_list_remove_first(list))) {   \
184             PMIX_RELEASE(it);                                    \
185         }                                                       \
186         PMIX_RELEASE(list);                                      \
187     } while (0)
188 
189 
190 /**
191  * Loop over a list.
192  *
193  * @param[in] item Storage for each item
194  * @param[in] list List to iterate over
195  * @param[in] type Type of each list item
196  *
197  * This macro provides a simple way to loop over the items in an pmix_list_t. It
198  * is not safe to call pmix_list_remove_item from within the loop.
199  *
200  * Example Usage:
201  *
202  * class_foo_t *foo;
203  * pmix_list_foreach(foo, list, class_foo_t) {
204  *    do something;
205  * }
206  */
207 #define PMIX_LIST_FOREACH(item, list, type)                             \
208   for (item = (type *) (list)->pmix_list_sentinel.pmix_list_next ;      \
209        item != (type *) &(list)->pmix_list_sentinel ;                   \
210        item = (type *) ((pmix_list_item_t *) (item))->pmix_list_next)
211 
212 /**
213  * Loop over a list in reverse.
214  *
215  * @param[in] item Storage for each item
216  * @param[in] list List to iterate over
217  * @param[in] type Type of each list item
218  *
219  * This macro provides a simple way to loop over the items in an pmix_list_t. It
220  * is not safe to call pmix_list_remove_item from within the loop.
221  *
222  * Example Usage:
223  *
224  * class_foo_t *foo;
225  * pmix_list_foreach(foo, list, class_foo_t) {
226  *    do something;
227  * }
228  */
229 #define PMIX_LIST_FOREACH_REV(item, list, type)                         \
230   for (item = (type *) (list)->pmix_list_sentinel.pmix_list_prev ;      \
231        item != (type *) &(list)->pmix_list_sentinel ;                   \
232        item = (type *) ((pmix_list_item_t *) (item))->pmix_list_prev)
233 
234 /**
235  * Loop over a list in a *safe* way
236  *
237  * @param[in] item Storage for each item
238  * @param[in] next Storage for next item
239  * @param[in] list List to iterate over
240  * @param[in] type Type of each list item
241  *
242  * This macro provides a simple way to loop over the items in an pmix_list_t. It
243  * is safe to call pmix_list_remove_item(list, item) from within the loop.
244  *
245  * Example Usage:
246  *
247  * class_foo_t *foo, *next;
248  * pmix_list_foreach_safe(foo, next, list, class_foo_t) {
249  *    do something;
250  *    pmix_list_remove_item (list, (pmix_list_item_t *) foo);
251  * }
252  */
253 #define PMIX_LIST_FOREACH_SAFE(item, next, list, type)                  \
254   for (item = (type *) (list)->pmix_list_sentinel.pmix_list_next,       \
255          next = (type *) ((pmix_list_item_t *) (item))->pmix_list_next ;\
256        item != (type *) &(list)->pmix_list_sentinel ;                   \
257        item = next, next = (type *) ((pmix_list_item_t *) (item))->pmix_list_next)
258 
259 /**
260  * Loop over a list in a *safe* way
261  *
262  * @param[in] item Storage for each item
263  * @param[in] next Storage for next item
264  * @param[in] list List to iterate over
265  * @param[in] type Type of each list item
266  *
267  * This macro provides a simple way to loop over the items in an pmix_list_t. If
268  * is safe to call pmix_list_remove_item(list, item) from within the loop.
269  *
270  * Example Usage:
271  *
272  * class_foo_t *foo, *next;
273  * pmix_list_foreach_safe(foo, next, list, class_foo_t) {
274  *    do something;
275  *    pmix_list_remove_item (list, (pmix_list_item_t *) foo);
276  * }
277  */
278 #define PMIX_LIST_FOREACH_SAFE_REV(item, prev, list, type)              \
279   for (item = (type *) (list)->pmix_list_sentinel.pmix_list_prev,       \
280          prev = (type *) ((pmix_list_item_t *) (item))->pmix_list_prev ;\
281        item != (type *) &(list)->pmix_list_sentinel ;                   \
282        item = prev, prev = (type *) ((pmix_list_item_t *) (item))->pmix_list_prev)
283 
284 
285 /**
286  * Check for empty list
287  *
288  * @param list The list container
289  *
290  * @returns true if list's size is 0, false otherwise
291  *
292  * This is an O(1) operation.
293  *
294  * This is an inlined function in compilers that support inlining,
295  * so it's usually a cheap operation.
296  */
pmix_list_is_empty(pmix_list_t * list)297 static inline bool pmix_list_is_empty(pmix_list_t* list)
298 {
299     return (list->pmix_list_sentinel.pmix_list_next ==
300         &(list->pmix_list_sentinel) ? true : false);
301 }
302 
303 
304 /**
305  * Return the first item on the list (does not remove it).
306  *
307  * @param list The list container
308  *
309  * @returns A pointer to the first item on the list
310  *
311  * This is an O(1) operation to return the first item on the list.  It
312  * should be compared against the returned value from
313  * pmix_list_get_end() to ensure that the list is not empty.
314  *
315  * This is an inlined function in compilers that support inlining, so
316  * it's usually a cheap operation.
317  */
pmix_list_get_first(pmix_list_t * list)318 static inline pmix_list_item_t* pmix_list_get_first(pmix_list_t* list)
319 {
320     pmix_list_item_t* item = (pmix_list_item_t*)list->pmix_list_sentinel.pmix_list_next;
321 #if PMIX_ENABLE_DEBUG
322     /* Spot check: ensure that the first item is only on one list */
323 
324     assert(1 == item->pmix_list_item_refcount);
325     assert( list == item->pmix_list_item_belong_to );
326 #endif
327 
328     return item;
329 }
330 
331 /**
332  * Return the last item on the list (does not remove it).
333  *
334  * @param list The list container
335  *
336  * @returns A pointer to the last item on the list
337  *
338  * This is an O(1) operation to return the last item on the list.  It
339  * should be compared against the returned value from
340  * pmix_list_get_begin() to ensure that the list is not empty.
341  *
342  * This is an inlined function in compilers that support inlining, so
343  * it's usually a cheap operation.
344  */
pmix_list_get_last(pmix_list_t * list)345 static inline pmix_list_item_t* pmix_list_get_last(pmix_list_t* list)
346 {
347     pmix_list_item_t* item = (pmix_list_item_t *)list->pmix_list_sentinel.pmix_list_prev;
348 #if PMIX_ENABLE_DEBUG
349     /* Spot check: ensure that the last item is only on one list */
350 
351     assert( 1 == item->pmix_list_item_refcount );
352     assert( list == item->pmix_list_item_belong_to );
353 #endif
354 
355     return item;
356 }
357 
358 /**
359  * Return the beginning of the list; an invalid list entry suitable
360  * for comparison only.
361  *
362  * @param list The list container
363  *
364  * @returns A pointer to the beginning of the list.
365  *
366  * This is an O(1) operation to return the beginning of the list.
367  * Similar to the STL, this is a special invalid list item -- it
368  * should \em not be used for storage.  It is only suitable for
369  * comparison to other items in the list to see if they are valid or
370  * not; it's ususally used when iterating through the items in a list.
371  *
372  * This is an inlined function in compilers that support inlining, so
373  * it's usually a cheap operation.
374  */
pmix_list_get_begin(pmix_list_t * list)375 static inline pmix_list_item_t* pmix_list_get_begin(pmix_list_t* list)
376 {
377     return &(list->pmix_list_sentinel);
378 }
379 
380 /**
381  * Return the end of the list; an invalid list entry suitable for
382  * comparison only.
383  *
384  * @param list The list container
385  *
386  * @returns A pointer to the end of the list.
387  *
388  * This is an O(1) operation to return the end of the list.
389  * Similar to the STL, this is a special invalid list item -- it
390  * should \em not be used for storage.  It is only suitable for
391  * comparison to other items in the list to see if they are valid or
392  * not; it's ususally used when iterating through the items in a list.
393  *
394  * This is an inlined function in compilers that support inlining, so
395  * it's usually a cheap operation.
396  */
pmix_list_get_end(pmix_list_t * list)397 static inline pmix_list_item_t* pmix_list_get_end(pmix_list_t* list)
398 {
399     return &(list->pmix_list_sentinel);
400 }
401 
402 
403 /**
404  * Return the number of items in a list
405  *
406  * @param list The list container
407  *
408  * @returns The size of the list (size_t)
409  *
410  * This is an O(1) lookup to return the size of the list.
411  *
412  * This is an inlined function in compilers that support inlining, so
413  * it's usually a cheap operation.
414  *
415  * \warning The size of the list is cached as part of the list.  In
416  * the future, calling \c pmix_list_splice or \c pmix_list_join may
417  * result in this function recomputing the list size, which would be
418  * an O(N) operation.  If \c pmix_list_splice or \c pmix_list_join is
419  * never called on the specified list, this function will always be
420  * O(1).
421  */
pmix_list_get_size(pmix_list_t * list)422 static inline size_t pmix_list_get_size(pmix_list_t* list)
423 {
424 #if PMIX_ENABLE_DEBUG && 0
425     /* not sure if we really want this running in devel, as it does
426      * slow things down.  Wanted for development of splice / join to
427      * make sure length was reset properly
428      */
429     size_t check_len = 0;
430     pmix_list_item_t *item;
431 
432     for (item = pmix_list_get_first(list) ;
433          item != pmix_list_get_end(list) ;
434          item = pmix_list_get_next(item)) {
435         check_len++;
436     }
437 
438     if (check_len != list->pmix_list_length) {
439         fprintf(stderr," Error :: pmix_list_get_size - pmix_list_length does not match actual list length\n");
440         fflush(stderr);
441         abort();
442     }
443 #endif
444 
445     return list->pmix_list_length;
446 }
447 
448 
449 /**
450  * Remove an item from a list.
451  *
452  * @param list The list container
453  * @param item The item to remove
454  *
455  * @returns A pointer to the item on the list previous to the one
456  * that was removed.
457  *
458  * This is an O(1) operation to remove an item from the list.  The
459  * forward / reverse pointers in the list are updated and the item is
460  * removed.  The list item that is returned is now "owned" by the
461  * caller -- they are responsible for PMIX_RELEASE()'ing it.
462  *
463  * If debugging is enabled (specifically, if --enable-debug was used
464  * to configure PMIx), this is an O(N) operation because it checks
465  * to see if the item is actually in the list first.
466  *
467  * This is an inlined function in compilers that support inlining, so
468  * it's usually a cheap operation.
469  */
pmix_list_remove_item(pmix_list_t * list,pmix_list_item_t * item)470 static inline pmix_list_item_t *pmix_list_remove_item
471   (pmix_list_t *list, pmix_list_item_t *item)
472 {
473 #if PMIX_ENABLE_DEBUG
474     pmix_list_item_t *item_ptr;
475     bool found = false;
476 
477     /* check to see that the item is in the list */
478     for (item_ptr = pmix_list_get_first(list);
479             item_ptr != pmix_list_get_end(list);
480             item_ptr = (pmix_list_item_t *)(item_ptr->pmix_list_next)) {
481         if (item_ptr == (pmix_list_item_t *) item) {
482             found = true;
483             break;
484         }
485     }
486     if (!found) {
487         fprintf(stderr," Warning :: pmix_list_remove_item - the item %p is not on the list %p \n",(void*) item, (void*) list);
488         fflush(stderr);
489         return (pmix_list_item_t *)NULL;
490     }
491 
492     assert( list == item->pmix_list_item_belong_to );
493 #endif
494 
495     /* reset next pointer of previous element */
496     item->pmix_list_prev->pmix_list_next=item->pmix_list_next;
497 
498     /* reset previous pointer of next element */
499     item->pmix_list_next->pmix_list_prev=item->pmix_list_prev;
500 
501     list->pmix_list_length--;
502 
503 #if PMIX_ENABLE_DEBUG
504     /* Spot check: ensure that this item is still only on one list */
505 
506     item->pmix_list_item_refcount -= 1;
507     assert(0 == item->pmix_list_item_refcount);
508     item->pmix_list_item_belong_to = NULL;
509 #endif
510 
511     return (pmix_list_item_t *)item->pmix_list_prev;
512 }
513 
514 
515 /**
516  * Append an item to the end of the list.
517  *
518  * @param list The list container
519  * @param item The item to append
520  *
521  * This is an O(1) operation to append an item to the end of a list.
522  * The pmix_list_item_t is not PMIX_RETAIN()'ed; it is assumed that
523  * "ownership" of the item is passed from the caller to the list.
524  *
525  * This is an inlined function in compilers that support inlining, so
526  * it's usually a cheap operation.
527  */
528 
529 #if PMIX_ENABLE_DEBUG
530 #define pmix_list_append(l,i) \
531 _pmix_list_append(l,i,__FILE__,__LINE__)
532 #else
533 #define pmix_list_append(l,i) \
534 _pmix_list_append(l,i)
535 #endif  /* PMIX_ENABLE_DEBUG */
536 
_pmix_list_append(pmix_list_t * list,pmix_list_item_t * item,const char * FILE_NAME,int LINENO)537 static inline void _pmix_list_append(pmix_list_t *list, pmix_list_item_t *item
538 #if PMIX_ENABLE_DEBUG
539                                      , const char* FILE_NAME, int LINENO
540 #endif  /* PMIX_ENABLE_DEBUG */
541                                      )
542 {
543     pmix_list_item_t* sentinel = &(list->pmix_list_sentinel);
544 #if PMIX_ENABLE_DEBUG
545   /* Spot check: ensure that this item is previously on no lists */
546 
547   assert(0 == item->pmix_list_item_refcount);
548   assert( NULL == item->pmix_list_item_belong_to );
549   item->super.cls_init_file_name = FILE_NAME;
550   item->super.cls_init_lineno    = LINENO;
551 #endif
552 
553   /* set new element's previous pointer */
554   item->pmix_list_prev = sentinel->pmix_list_prev;
555 
556   /* reset previous pointer on current last element */
557   sentinel->pmix_list_prev->pmix_list_next = item;
558 
559   /* reset new element's next pointer */
560   item->pmix_list_next = sentinel;
561 
562   /* reset the list's tail element previous pointer */
563   sentinel->pmix_list_prev = item;
564 
565   /* increment list element counter */
566   list->pmix_list_length++;
567 
568 #if PMIX_ENABLE_DEBUG
569   /* Spot check: ensure this item is only on the list that we just
570      appended it to */
571 
572   item->pmix_list_item_refcount += 1;
573   assert(1 == item->pmix_list_item_refcount);
574   item->pmix_list_item_belong_to = list;
575 #endif
576 }
577 
578 
579 /**
580  * Prepend an item to the beginning of the list.
581  *
582  * @param list The list container
583  * @param item The item to prepend
584  *
585  * This is an O(1) operation to prepend an item to the beginning of a
586  * list.  The pmix_list_item_t is not PMIX_RETAIN()'ed; it is assumed
587  * that "ownership" of the item is passed from the caller to the list.
588  *
589  * This is an inlined function in compilers that support inlining, so
590  * it's usually a cheap operation.
591  */
pmix_list_prepend(pmix_list_t * list,pmix_list_item_t * item)592 static inline void pmix_list_prepend(pmix_list_t *list,
593                                      pmix_list_item_t *item)
594 {
595     pmix_list_item_t* sentinel = &(list->pmix_list_sentinel);
596 #if PMIX_ENABLE_DEBUG
597   /* Spot check: ensure that this item is previously on no lists */
598 
599   assert(0 == item->pmix_list_item_refcount);
600   assert( NULL == item->pmix_list_item_belong_to );
601 #endif
602 
603   /* reset item's next pointer */
604   item->pmix_list_next = sentinel->pmix_list_next;
605 
606   /* reset item's previous pointer */
607   item->pmix_list_prev = sentinel;
608 
609   /* reset previous first element's previous poiner */
610   sentinel->pmix_list_next->pmix_list_prev = item;
611 
612   /* reset head's next pointer */
613   sentinel->pmix_list_next = item;
614 
615   /* increment list element counter */
616   list->pmix_list_length++;
617 
618 #if PMIX_ENABLE_DEBUG
619   /* Spot check: ensure this item is only on the list that we just
620      prepended it to */
621 
622   item->pmix_list_item_refcount += 1;
623   assert(1 == item->pmix_list_item_refcount);
624   item->pmix_list_item_belong_to = list;
625 #endif
626 }
627 
628 
629 /**
630  * Remove the first item from the list and return it.
631  *
632  * @param list The list container
633  *
634  * @returns The first item on the list.  If the list is empty,
635  * NULL will be returned
636  *
637  * This is an O(1) operation to return the first item on the list.  If
638  * the list is not empty, a pointer to the first item in the list will
639  * be returned.  Ownership of the item is transferred from the list to
640  * the caller; no calls to PMIX_RETAIN() or PMIX_RELEASE() are invoked.
641  *
642  * This is an inlined function in compilers that support inlining, so
643  * it's usually a cheap operation.
644  */
pmix_list_remove_first(pmix_list_t * list)645 static inline pmix_list_item_t *pmix_list_remove_first(pmix_list_t *list)
646 {
647   /*  Removes and returns first item on list.
648       Caller now owns the item and should release the item
649       when caller is done with it.
650   */
651   volatile pmix_list_item_t *item;
652   if ( 0 == list->pmix_list_length ) {
653     return (pmix_list_item_t *)NULL;
654   }
655 
656 #if PMIX_ENABLE_DEBUG
657   /* Spot check: ensure that the first item is only on this list */
658 
659   assert(1 == list->pmix_list_sentinel.pmix_list_next->pmix_list_item_refcount);
660 #endif
661 
662   /* reset list length counter */
663   list->pmix_list_length--;
664 
665   /* get pointer to first element on the list */
666   item = list->pmix_list_sentinel.pmix_list_next;
667 
668   /* reset previous pointer of next item on the list */
669   item->pmix_list_next->pmix_list_prev = item->pmix_list_prev;
670 
671   /* reset the head next pointer */
672   list->pmix_list_sentinel.pmix_list_next = item->pmix_list_next;
673 
674 #if PMIX_ENABLE_DEBUG
675   assert( list == item->pmix_list_item_belong_to );
676   item->pmix_list_item_belong_to = NULL;
677   item->pmix_list_prev=(pmix_list_item_t *)NULL;
678   item->pmix_list_next=(pmix_list_item_t *)NULL;
679 
680   /* Spot check: ensure that the item we're returning is now on no
681      lists */
682 
683   item->pmix_list_item_refcount -= 1;
684   assert(0 == item->pmix_list_item_refcount);
685 #endif
686 
687   return (pmix_list_item_t *) item;
688 }
689 
690 
691 /**
692  * Remove the last item from the list and return it.
693  *
694  * @param list The list container
695  *
696  * @returns The last item on the list.  If the list is empty,
697  * NULL will be returned
698  *
699  * This is an O(1) operation to return the last item on the list.  If
700  * the list is not empty, a pointer to the last item in the list will
701  * be returned.  Ownership of the item is transferred from the list to
702  * the caller; no calls to PMIX_RETAIN() or PMIX_RELEASE() are invoked.
703  *
704  * This is an inlined function in compilers that support inlining, so
705  * it's usually a cheap operation.
706  */
pmix_list_remove_last(pmix_list_t * list)707 static inline pmix_list_item_t *pmix_list_remove_last(pmix_list_t *list)
708 {
709   /*  Removes, releases and returns last item on list.
710       Caller now owns the item and should release the item
711       when caller is done with it.
712   */
713   volatile pmix_list_item_t  *item;
714   if ( 0 == list->pmix_list_length ) {
715       return (pmix_list_item_t *)NULL;
716   }
717 
718 #if PMIX_ENABLE_DEBUG
719   /* Spot check: ensure that the first item is only on this list */
720 
721   assert(1 == list->pmix_list_sentinel.pmix_list_prev->pmix_list_item_refcount);
722 #endif
723 
724   /* reset list length counter */
725   list->pmix_list_length--;
726 
727   /* get item */
728   item = list->pmix_list_sentinel.pmix_list_prev;
729 
730   /* reset previous pointer on next to last pointer */
731   item->pmix_list_prev->pmix_list_next = item->pmix_list_next;
732 
733   /* reset tail's previous pointer */
734   list->pmix_list_sentinel.pmix_list_prev = item->pmix_list_prev;
735 
736 #if PMIX_ENABLE_DEBUG
737   assert( list == item->pmix_list_item_belong_to );
738   item->pmix_list_next = item->pmix_list_prev = (pmix_list_item_t *)NULL;
739 
740   /* Spot check: ensure that the item we're returning is now on no
741      lists */
742 
743   item->pmix_list_item_refcount -= 1;
744   assert(0 == item->pmix_list_item_refcount);
745   item->pmix_list_item_belong_to = NULL;
746 #endif
747 
748   return (pmix_list_item_t *) item;
749 }
750 
751   /**
752    * Add an item to the list before a given element
753    *
754    * @param list The list container
755    * @param pos List element to insert \c item before
756    * @param item The item to insert
757    *
758    * Inserts \c item before \c pos.  This is an O(1) operation.
759    */
pmix_list_insert_pos(pmix_list_t * list,pmix_list_item_t * pos,pmix_list_item_t * item)760 static inline void pmix_list_insert_pos(pmix_list_t *list, pmix_list_item_t *pos,
761                                         pmix_list_item_t *item)
762 {
763 #if PMIX_ENABLE_DEBUG
764     /* Spot check: ensure that the item we're insertting is currently
765        not on any list */
766 
767     assert(0 == item->pmix_list_item_refcount);
768     assert( NULL == item->pmix_list_item_belong_to );
769 #endif
770 
771     /* point item at the existing elements */
772     item->pmix_list_next = pos;
773     item->pmix_list_prev = pos->pmix_list_prev;
774 
775     /* splice into the list */
776     pos->pmix_list_prev->pmix_list_next = item;
777     pos->pmix_list_prev = item;
778 
779     /* reset list length counter */
780     list->pmix_list_length++;
781 
782 #if PMIX_ENABLE_DEBUG
783     /* Spot check: double check that this item is only on the list
784        that we just added it to */
785 
786     item->pmix_list_item_refcount += 1;
787     assert(1 == item->pmix_list_item_refcount);
788     item->pmix_list_item_belong_to = list;
789 #endif
790 }
791 
792   /**
793    * Add an item to the list at a specific index location in the list.
794    *
795    * @param list The list container
796    * @param item The item to insert
797    * @param index Location to add the item
798    *
799    * @returns true if insertion succeeded; otherwise false
800    *
801    * This is potentially an O(N) operation to traverse down to the
802    * correct location in the list and add an item.
803    *
804    * Example: if idx = 2 and list = item1->item2->item3->item4, then
805    * after insert, list = item1->item2->item->item3->item4.
806    *
807    * If index is greater than the length of the list, no action is
808    * performed and false is returned.
809    */
810   bool pmix_list_insert(pmix_list_t *list, pmix_list_item_t *item,
811                                       long long idx);
812 
813 
814     /**
815      * Join a list into another list
816      *
817      * @param thislist List container for list being operated on
818      * @param pos List item in \c thislist marking the position before
819      *              which items are inserted
820      * @param xlist List container for list being spliced from
821      *
822      * Join a list into another list.  All of the elements of \c xlist
823      * are inserted before \c pos and removed from \c xlist.
824      *
825      * This operation is an O(1) operation.  Both \c thislist and \c
826      * xlist must be valid list containsers.  \c xlist will be empty
827      * but valid after the call.  All pointers to \c pmix_list_item_t
828      * containers remain valid, including those that point to elements
829      * in \c xlist.
830      */
831     void pmix_list_join(pmix_list_t *thislist, pmix_list_item_t *pos,
832                                       pmix_list_t *xlist);
833 
834 
835     /**
836      * Splice a list into another list
837      *
838      * @param thislist List container for list being operated on
839      * @param pos List item in \c thislist marking the position before
840      *             which items are inserted
841      * @param xlist List container for list being spliced from
842      * @param first List item in \c xlist marking the start of elements
843      *             to be copied into \c thislist
844      * @param last List item in \c xlist marking the end of elements
845      * to be copied into \c thislist
846      *
847      * Splice a subset of a list into another list.  The \c [first,
848      * last) elements of \c xlist are moved into \c thislist,
849      * inserting them before \c pos.  \c pos must be a valid iterator
850      * in \c thislist and \c [first, last) must be a valid range in \c
851      * xlist.  \c postition must not be in the range \c [first, last).
852      * It is, however, valid for \c xlist and \c thislist to be the
853      * same list.
854      *
855      * This is an O(N) operation because the length of both lists must
856      * be recomputed.
857      */
858     void pmix_list_splice(pmix_list_t *thislist, pmix_list_item_t *pos,
859                                         pmix_list_t *xlist, pmix_list_item_t *first,
860                                         pmix_list_item_t *last);
861 
862     /**
863      * Comparison function for pmix_list_sort(), below.
864      *
865      * @param a Pointer to a pointer to an pmix_list_item_t.
866      * Explanation below.
867      * @param b Pointer to a pointer to an pmix_list_item_t.
868      * Explanation below.
869      * @retval 1 if \em a is greater than \em b
870      * @retval 0 if \em a is equal to \em b
871      * @retval 11 if \em a is less than \em b
872      *
873      * This function is invoked by qsort(3) from within
874      * pmix_list_sort().  It is important to understand what
875      * pmix_list_sort() does before invoking qsort, so go read that
876      * documentation first.
877      *
878      * The important thing to realize here is that a and b will be \em
879      * double pointers to the items that you need to compare.  Here's
880      * a sample compare function to illustrate this point:
881      */
882     typedef int (*pmix_list_item_compare_fn_t)(pmix_list_item_t **a,
883                                                pmix_list_item_t **b);
884 
885     /**
886      * Sort a list with a provided compare function.
887      *
888      * @param list The list to sort
889      * @param compare Compare function
890      *
891      * Put crassly, this function's complexity is O(N) + O(log(N)).
892      * Its algorithm is:
893      *
894      * - remove every item from the list and put the corresponding
895      *    (pmix_list_item_t*)'s in an array
896      * - call qsort(3) with that array and your compare function
897      * - re-add every element of the now-sorted array to the list
898      *
899      * The resulting list is now ordered.  Note, however, that since
900      * an array of pointers is sorted, the comparison function must do
901      * a double de-reference to get to the actual pmix_list_item_t (or
902      * whatever the underlying type is).  See the documentation of
903      * pmix_list_item_compare_fn_t for an example).
904      */
905     int pmix_list_sort(pmix_list_t* list, pmix_list_item_compare_fn_t compare);
906 
907 END_C_DECLS
908 
909 #endif /* PMIX_LIST_H */
910