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