![]() |
|
|||
File indexing completed on 2025-02-22 10:47:20
0001 /* -*- Mode: C; c-basic-offset:4 ; -*- */ 0002 /* 0003 * Copyright (c) 2004-2005 The Trustees of Indiana University and Indiana 0004 * University Research and Technology 0005 * Corporation. All rights reserved. 0006 * Copyright (c) 2004-2006 The University of Tennessee and The University 0007 * of Tennessee Research Foundation. All rights 0008 * reserved. 0009 * Copyright (c) 2004-2005 High Performance Computing Center Stuttgart, 0010 * University of Stuttgart. All rights reserved. 0011 * Copyright (c) 2004-2005 The Regents of the University of California. 0012 * All rights reserved. 0013 * Copyright (c) 2007 Voltaire All rights reserved. 0014 * Copyright (c) 2013 Los Alamos National Security, LLC. All rights 0015 * reserved. 0016 * Copyright (c) 2013-2020 Intel, Inc. All rights reserved. 0017 * Copyright (c) 2021-2022 Nanook Consulting All rights reserved. 0018 * $COPYRIGHT$ 0019 * 0020 * Additional copyrights may follow 0021 * 0022 * $HEADER$ 0023 */ 0024 /** 0025 * @file 0026 * 0027 * The pmix_list_t interface is used to provide a generic 0028 * doubly-linked list container for PMIx. It was inspired by (but 0029 * is slightly different than) the Standard Template Library (STL) 0030 * std::list class. One notable difference from std::list is that 0031 * when an pmix_list_t is destroyed, all of the pmix_list_item_t 0032 * objects that it contains are orphaned -- they are \em not 0033 * destroyed. 0034 * 0035 * The general idea is that pmix_list_item_t objects can be put on an 0036 * pmix_list_t. Hence, you create a new type that derives from 0037 * pmix_list_item_t; this new type can then be used with pmix_list_t 0038 * containers. 0039 * 0040 * NOTE: pmix_list_item_t instances can only be on \em one list at a 0041 * time. Specifically, if you add an pmix_list_item_t to one list, 0042 * and then add it to another list (without first removing it from the 0043 * first list), you will effectively be hosing the first list. You 0044 * have been warned. 0045 * 0046 * If PMIX_ENABLE_DEBUG is true, a bunch of checks occur, including 0047 * some spot checks for a debugging reference count in an attempt to 0048 * ensure that an pmix_list_item_t is only one *one* list at a time. 0049 * Given the highly concurrent nature of this class, these spot checks 0050 * cannot guarantee that an item is only one list at a time. 0051 * Specifically, since it is a desirable attribute of this class to 0052 * not use locks for normal operations, it is possible that two 0053 * threads may [erroneously] modify an pmix_list_item_t concurrently. 0054 * 0055 * The only way to guarantee that a debugging reference count is valid 0056 * for the duration of an operation is to lock the item_t during the 0057 * operation. But this fundamentally changes the desirable attribute 0058 * of this class (i.e., no locks). So all we can do is spot-check the 0059 * reference count in a bunch of places and check that it is still the 0060 * value that we think it should be. But this doesn't mean that you 0061 * can run into "unlucky" cases where two threads are concurrently 0062 * modifying an item_t, but all the spot checks still return the 0063 * "right" values. All we can do is hope that we have enough spot 0064 * checks to statistically drive down the possibility of the unlucky 0065 * cases happening. 0066 */ 0067 0068 #ifndef PMIX_LIST_H 0069 #define PMIX_LIST_H 0070 0071 #include "src/include/pmix_config.h" 0072 #include <stdio.h> 0073 #include <stdlib.h> 0074 #if HAVE_STDBOOL_H 0075 # include <stdbool.h> 0076 #endif 0077 0078 #include "pmix_common.h" 0079 #include "src/class/pmix_object.h" 0080 0081 BEGIN_C_DECLS 0082 0083 /** 0084 * \internal 0085 * 0086 * The class for the list container. 0087 */ 0088 PMIX_EXPORT PMIX_CLASS_DECLARATION(pmix_list_t); 0089 /** 0090 * \internal 0091 * 0092 * Base class for items that are put in list (pmix_list_t) containers. 0093 */ 0094 PMIX_EXPORT PMIX_CLASS_DECLARATION(pmix_list_item_t); 0095 0096 /** 0097 * \internal 0098 * 0099 * Struct of an pmix_list_item_t 0100 */ 0101 struct pmix_list_item_t { 0102 pmix_object_t super; 0103 /**< Generic parent class for all PMIx objects */ 0104 volatile struct pmix_list_item_t *pmix_list_next; 0105 /**< Pointer to next list item */ 0106 volatile struct pmix_list_item_t *pmix_list_prev; 0107 /**< Pointer to previous list item */ 0108 int32_t item_free; 0109 0110 #if PMIX_ENABLE_DEBUG 0111 /** Atomic reference count for debugging */ 0112 int32_t pmix_list_item_refcount; 0113 /** The list this item belong to */ 0114 volatile struct pmix_list_t *pmix_list_item_belong_to; 0115 #endif 0116 }; 0117 /** 0118 * Base type for items that are put in a list (pmix_list_t) containers. 0119 */ 0120 typedef struct pmix_list_item_t pmix_list_item_t; 0121 0122 /* static initializer for pmix_list_t */ 0123 #define PMIX_LIST_ITEM_STATIC_INIT \ 0124 { \ 0125 .super = PMIX_OBJ_STATIC_INIT(pmix_object_t), \ 0126 .pmix_list_next = NULL, \ 0127 .pmix_list_prev = NULL, \ 0128 .item_free = 0 \ 0129 } 0130 0131 /** 0132 * Get the next item in a list. 0133 * 0134 * @param item A list item. 0135 * 0136 * @returns The next item in the list 0137 */ 0138 #define pmix_list_get_next(item) \ 0139 ((item) ? ((pmix_list_item_t *) ((pmix_list_item_t *) (item))->pmix_list_next) : NULL) 0140 0141 /** 0142 * Get the next item in a list. 0143 * 0144 * @param item A list item. 0145 * 0146 * @returns The next item in the list 0147 */ 0148 #define pmix_list_get_prev(item) \ 0149 ((item) ? ((pmix_list_item_t *) ((pmix_list_item_t *) (item))->pmix_list_prev) : NULL) 0150 0151 /** 0152 * \internal 0153 * 0154 * Struct of an pmix_list_t 0155 */ 0156 struct pmix_list_t { 0157 pmix_object_t super; 0158 /**< Generic parent class for all PMIx objects */ 0159 pmix_list_item_t pmix_list_sentinel; 0160 /**< Head and tail item of the list */ 0161 volatile size_t pmix_list_length; 0162 /**< Quick reference to the number of items in the list */ 0163 }; 0164 /** 0165 * List container type. 0166 */ 0167 typedef struct pmix_list_t pmix_list_t; 0168 0169 /* static initializer for pmix_list_t */ 0170 #define PMIX_LIST_STATIC_INIT \ 0171 { \ 0172 .super = PMIX_OBJ_STATIC_INIT(pmix_object_t), \ 0173 .pmix_list_sentinel = PMIX_LIST_ITEM_STATIC_INIT, \ 0174 .pmix_list_length = 0 \ 0175 } 0176 0177 /** Cleanly destruct a list 0178 * 0179 * The pmix_list_t destructor doesn't release the items on the 0180 * list - so provide two convenience macros that do so and then 0181 * destruct/release the list object itself 0182 * 0183 * @param[in] list List to destruct or release 0184 */ 0185 #define PMIX_LIST_DESTRUCT(list) \ 0186 do { \ 0187 pmix_list_item_t *it; \ 0188 while (NULL != (it = pmix_list_remove_first(list))) { \ 0189 PMIX_RELEASE(it); \ 0190 } \ 0191 PMIX_DESTRUCT(list); \ 0192 } while (0) 0193 0194 #define PMIX_LIST_RELEASE(list) \ 0195 do { \ 0196 pmix_list_item_t *it; \ 0197 while (NULL != (it = pmix_list_remove_first(list))) { \ 0198 PMIX_RELEASE(it); \ 0199 } \ 0200 PMIX_RELEASE(list); \ 0201 } while (0) 0202 0203 /** 0204 * Loop over a list. 0205 * 0206 * @param[in] item Storage for each item 0207 * @param[in] list List to iterate over 0208 * @param[in] type Type of each list item 0209 * 0210 * This macro provides a simple way to loop over the items in an pmix_list_t. It 0211 * is not safe to call pmix_list_remove_item from within the loop. 0212 * 0213 * Example Usage: 0214 * 0215 * class_foo_t *foo; 0216 * pmix_list_foreach(foo, list, class_foo_t) { 0217 * do something; 0218 * } 0219 */ 0220 #define PMIX_LIST_FOREACH(item, list, type) \ 0221 for (item = (type *) (list)->pmix_list_sentinel.pmix_list_next; \ 0222 item != (type *) &(list)->pmix_list_sentinel; \ 0223 item = (type *) ((pmix_list_item_t *) (item))->pmix_list_next) 0224 0225 #define PMIX_LIST_FOREACH_DECL(item, list, type) \ 0226 for (type *item = (type *) (list)->pmix_list_sentinel.pmix_list_next; \ 0227 item != (type *) &(list)->pmix_list_sentinel; \ 0228 item = (type *) ((pmix_list_item_t *) (item))->pmix_list_next) 0229 /** 0230 * Loop over a list in reverse. 0231 * 0232 * @param[in] item Storage for each item 0233 * @param[in] list List to iterate over 0234 * @param[in] type Type of each list item 0235 * 0236 * This macro provides a simple way to loop over the items in an pmix_list_t. It 0237 * is not safe to call pmix_list_remove_item from within the loop. 0238 * 0239 * Example Usage: 0240 * 0241 * class_foo_t *foo; 0242 * pmix_list_foreach(foo, list, class_foo_t) { 0243 * do something; 0244 * } 0245 */ 0246 #define PMIX_LIST_FOREACH_REV(item, list, type) \ 0247 for (item = (type *) (list)->pmix_list_sentinel.pmix_list_prev; \ 0248 item != (type *) &(list)->pmix_list_sentinel; \ 0249 item = (type *) ((pmix_list_item_t *) (item))->pmix_list_prev) 0250 0251 /** 0252 * Loop over a list in a *safe* way 0253 * 0254 * @param[in] item Storage for each item 0255 * @param[in] next Storage for next item 0256 * @param[in] list List to iterate over 0257 * @param[in] type Type of each list item 0258 * 0259 * This macro provides a simple way to loop over the items in an pmix_list_t. It 0260 * is safe to call pmix_list_remove_item(list, item) from within the loop. 0261 * 0262 * Example Usage: 0263 * 0264 * class_foo_t *foo, *next; 0265 * pmix_list_foreach_safe(foo, next, list, class_foo_t) { 0266 * do something; 0267 * pmix_list_remove_item (list, (pmix_list_item_t *) foo); 0268 * } 0269 */ 0270 #define PMIX_LIST_FOREACH_SAFE(item, next, list, type) \ 0271 for (item = (type *) (list)->pmix_list_sentinel.pmix_list_next, \ 0272 next = (type *) ((pmix_list_item_t *) (item))->pmix_list_next; \ 0273 item != (type *) &(list)->pmix_list_sentinel; \ 0274 item = next, next = (type *) ((pmix_list_item_t *) (item))->pmix_list_next) 0275 0276 /** 0277 * Loop over a list in a *safe* way 0278 * 0279 * @param[in] item Storage for each item 0280 * @param[in] next Storage for next item 0281 * @param[in] list List to iterate over 0282 * @param[in] type Type of each list item 0283 * 0284 * This macro provides a simple way to loop over the items in an pmix_list_t. If 0285 * is safe to call pmix_list_remove_item(list, item) from within the loop. 0286 * 0287 * Example Usage: 0288 * 0289 * class_foo_t *foo, *next; 0290 * pmix_list_foreach_safe(foo, next, list, class_foo_t) { 0291 * do something; 0292 * pmix_list_remove_item (list, (pmix_list_item_t *) foo); 0293 * } 0294 */ 0295 #define PMIX_LIST_FOREACH_SAFE_REV(item, prev, list, type) \ 0296 for (item = (type *) (list)->pmix_list_sentinel.pmix_list_prev, \ 0297 prev = (type *) ((pmix_list_item_t *) (item))->pmix_list_prev; \ 0298 item != (type *) &(list)->pmix_list_sentinel; \ 0299 item = prev, prev = (type *) ((pmix_list_item_t *) (item))->pmix_list_prev) 0300 0301 /** 0302 * Check for empty list 0303 * 0304 * @param list The list container 0305 * 0306 * @returns true if list's size is 0, false otherwise 0307 * 0308 * This is an O(1) operation. 0309 * 0310 * This is an inlined function in compilers that support inlining, 0311 * so it's usually a cheap operation. 0312 */ 0313 static inline bool pmix_list_is_empty(pmix_list_t *list) 0314 { 0315 return (list->pmix_list_sentinel.pmix_list_next == &(list->pmix_list_sentinel) ? true : false); 0316 } 0317 0318 /** 0319 * Return the first item on the list (does not remove it). 0320 * 0321 * @param list The list container 0322 * 0323 * @returns A pointer to the first item on the list 0324 * 0325 * This is an O(1) operation to return the first item on the list. It 0326 * should be compared against the returned value from 0327 * pmix_list_get_end() to ensure that the list is not empty. 0328 * 0329 * This is an inlined function in compilers that support inlining, so 0330 * it's usually a cheap operation. 0331 */ 0332 static inline pmix_list_item_t *pmix_list_get_first(pmix_list_t *list) 0333 { 0334 pmix_list_item_t *item = (pmix_list_item_t *) list->pmix_list_sentinel.pmix_list_next; 0335 #if PMIX_ENABLE_DEBUG 0336 /* Spot check: ensure that the first item is only on one list */ 0337 0338 assert(1 == item->pmix_list_item_refcount); 0339 assert(list == item->pmix_list_item_belong_to); 0340 #endif 0341 0342 return item; 0343 } 0344 0345 /** 0346 * Return the last item on the list (does not remove it). 0347 * 0348 * @param list The list container 0349 * 0350 * @returns A pointer to the last item on the list 0351 * 0352 * This is an O(1) operation to return the last item on the list. It 0353 * should be compared against the returned value from 0354 * pmix_list_get_begin() to ensure that the list is not empty. 0355 * 0356 * This is an inlined function in compilers that support inlining, so 0357 * it's usually a cheap operation. 0358 */ 0359 static inline pmix_list_item_t *pmix_list_get_last(pmix_list_t *list) 0360 { 0361 pmix_list_item_t *item = (pmix_list_item_t *) list->pmix_list_sentinel.pmix_list_prev; 0362 #if PMIX_ENABLE_DEBUG 0363 /* Spot check: ensure that the last item is only on one list */ 0364 0365 assert(1 == item->pmix_list_item_refcount); 0366 assert(list == item->pmix_list_item_belong_to); 0367 #endif 0368 0369 return item; 0370 } 0371 0372 /** 0373 * Return the beginning of the list; an invalid list entry suitable 0374 * for comparison only. 0375 * 0376 * @param list The list container 0377 * 0378 * @returns A pointer to the beginning of the list. 0379 * 0380 * This is an O(1) operation to return the beginning of the list. 0381 * Similar to the STL, this is a special invalid list item -- it 0382 * should \em not be used for storage. It is only suitable for 0383 * comparison to other items in the list to see if they are valid or 0384 * not; it's usually used when iterating through the items in a list. 0385 * 0386 * This is an inlined function in compilers that support inlining, so 0387 * it's usually a cheap operation. 0388 */ 0389 static inline pmix_list_item_t *pmix_list_get_begin(pmix_list_t *list) 0390 { 0391 return &(list->pmix_list_sentinel); 0392 } 0393 0394 /** 0395 * Return the end of the list; an invalid list entry suitable for 0396 * comparison only. 0397 * 0398 * @param list The list container 0399 * 0400 * @returns A pointer to the end of the list. 0401 * 0402 * This is an O(1) operation to return the end of the list. 0403 * Similar to the STL, this is a special invalid list item -- it 0404 * should \em not be used for storage. It is only suitable for 0405 * comparison to other items in the list to see if they are valid or 0406 * not; it's usually used when iterating through the items in a list. 0407 * 0408 * This is an inlined function in compilers that support inlining, so 0409 * it's usually a cheap operation. 0410 */ 0411 static inline pmix_list_item_t *pmix_list_get_end(pmix_list_t *list) 0412 { 0413 return &(list->pmix_list_sentinel); 0414 } 0415 0416 /** 0417 * Return the number of items in a list 0418 * 0419 * @param list The list container 0420 * 0421 * @returns The size of the list (size_t) 0422 * 0423 * This is an O(1) lookup to return the size of the list. 0424 * 0425 * This is an inlined function in compilers that support inlining, so 0426 * it's usually a cheap operation. 0427 * 0428 * \warning The size of the list is cached as part of the list. In 0429 * the future, calling \c pmix_list_splice or \c pmix_list_join may 0430 * result in this function recomputing the list size, which would be 0431 * an O(N) operation. If \c pmix_list_splice or \c pmix_list_join is 0432 * never called on the specified list, this function will always be 0433 * O(1). 0434 */ 0435 static inline size_t pmix_list_get_size(pmix_list_t *list) 0436 { 0437 #if PMIX_ENABLE_DEBUG && 0 0438 /* not sure if we really want this running in devel, as it does 0439 * slow things down. Wanted for development of splice / join to 0440 * make sure length was reset properly 0441 */ 0442 size_t check_len = 0; 0443 pmix_list_item_t *item; 0444 0445 for (item = pmix_list_get_first(list); item != pmix_list_get_end(list); 0446 item = pmix_list_get_next(item)) { 0447 check_len++; 0448 } 0449 0450 if (check_len != list->pmix_list_length) { 0451 fprintf( 0452 stderr, 0453 " Error :: pmix_list_get_size - pmix_list_length does not match actual list length\n"); 0454 fflush(stderr); 0455 abort(); 0456 } 0457 #endif 0458 0459 return list->pmix_list_length; 0460 } 0461 0462 /** 0463 * Remove an item from a list. 0464 * 0465 * @param list The list container 0466 * @param item The item to remove 0467 * 0468 * @returns A pointer to the item on the list previous to the one 0469 * that was removed. 0470 * 0471 * This is an O(1) operation to remove an item from the list. The 0472 * forward / reverse pointers in the list are updated and the item is 0473 * removed. The list item that is returned is now "owned" by the 0474 * caller -- they are responsible for PMIX_RELEASE()'ing it. 0475 * 0476 * If debugging is enabled (specifically, if --enable-debug was used 0477 * to configure PMIx), this is an O(N) operation because it checks 0478 * to see if the item is actually in the list first. 0479 * 0480 * This is an inlined function in compilers that support inlining, so 0481 * it's usually a cheap operation. 0482 */ 0483 static inline pmix_list_item_t *pmix_list_remove_item(pmix_list_t *list, pmix_list_item_t *item) 0484 { 0485 #if PMIX_ENABLE_DEBUG 0486 pmix_list_item_t *item_ptr; 0487 bool found = false; 0488 0489 /* check to see that the item is in the list */ 0490 for (item_ptr = pmix_list_get_first(list); item_ptr != pmix_list_get_end(list); 0491 item_ptr = (pmix_list_item_t *) (item_ptr->pmix_list_next)) { 0492 if (item_ptr == (pmix_list_item_t *) item) { 0493 found = true; 0494 break; 0495 } 0496 } 0497 if (!found) { 0498 fprintf(stderr, " Warning :: pmix_list_remove_item - the item %p is not on the list %p \n", 0499 (void *) item, (void *) list); 0500 fflush(stderr); 0501 return (pmix_list_item_t *) NULL; 0502 } 0503 0504 assert(list == item->pmix_list_item_belong_to); 0505 #endif 0506 0507 /* reset next pointer of previous element */ 0508 item->pmix_list_prev->pmix_list_next = item->pmix_list_next; 0509 0510 /* reset previous pointer of next element */ 0511 item->pmix_list_next->pmix_list_prev = item->pmix_list_prev; 0512 0513 list->pmix_list_length--; 0514 0515 #if PMIX_ENABLE_DEBUG 0516 /* Spot check: ensure that this item is still only on one list */ 0517 0518 item->pmix_list_item_refcount -= 1; 0519 assert(0 == item->pmix_list_item_refcount); 0520 item->pmix_list_item_belong_to = NULL; 0521 #endif 0522 0523 return (pmix_list_item_t *) item->pmix_list_prev; 0524 } 0525 0526 /** 0527 * Append an item to the end of the list. 0528 * 0529 * @param list The list container 0530 * @param item The item to append 0531 * 0532 * This is an O(1) operation to append an item to the end of a list. 0533 * The pmix_list_item_t is not PMIX_RETAIN()'ed; it is assumed that 0534 * "ownership" of the item is passed from the caller to the list. 0535 * 0536 * This is an inlined function in compilers that support inlining, so 0537 * it's usually a cheap operation. 0538 */ 0539 0540 #if PMIX_ENABLE_DEBUG 0541 # define pmix_list_append(l, i) _pmix_list_append(l, i, __FILE__, __LINE__) 0542 #else 0543 # define pmix_list_append(l, i) _pmix_list_append(l, i) 0544 #endif /* PMIX_ENABLE_DEBUG */ 0545 0546 static inline void _pmix_list_append(pmix_list_t *list, pmix_list_item_t *item 0547 #if PMIX_ENABLE_DEBUG 0548 , 0549 const char *FILE_NAME, int LINENO 0550 #endif /* PMIX_ENABLE_DEBUG */ 0551 ) 0552 { 0553 pmix_list_item_t *sentinel = &(list->pmix_list_sentinel); 0554 #if PMIX_ENABLE_DEBUG 0555 /* Spot check: ensure that this item is previously on no lists */ 0556 0557 assert(0 == item->pmix_list_item_refcount); 0558 assert(NULL == item->pmix_list_item_belong_to); 0559 item->super.cls_init_file_name = FILE_NAME; 0560 item->super.cls_init_lineno = LINENO; 0561 #endif 0562 0563 /* set new element's previous pointer */ 0564 item->pmix_list_prev = sentinel->pmix_list_prev; 0565 0566 /* reset previous pointer on current last element */ 0567 sentinel->pmix_list_prev->pmix_list_next = item; 0568 0569 /* reset new element's next pointer */ 0570 item->pmix_list_next = sentinel; 0571 0572 /* reset the list's tail element previous pointer */ 0573 sentinel->pmix_list_prev = item; 0574 0575 /* increment list element counter */ 0576 list->pmix_list_length++; 0577 0578 #if PMIX_ENABLE_DEBUG 0579 /* Spot check: ensure this item is only on the list that we just 0580 appended it to */ 0581 0582 item->pmix_list_item_refcount += 1; 0583 assert(1 == item->pmix_list_item_refcount); 0584 item->pmix_list_item_belong_to = list; 0585 #endif 0586 } 0587 0588 /** 0589 * Prepend an item to the beginning of the list. 0590 * 0591 * @param list The list container 0592 * @param item The item to prepend 0593 * 0594 * This is an O(1) operation to prepend an item to the beginning of a 0595 * list. The pmix_list_item_t is not PMIX_RETAIN()'ed; it is assumed 0596 * that "ownership" of the item is passed from the caller to the list. 0597 * 0598 * This is an inlined function in compilers that support inlining, so 0599 * it's usually a cheap operation. 0600 */ 0601 static inline void pmix_list_prepend(pmix_list_t *list, pmix_list_item_t *item) 0602 { 0603 pmix_list_item_t *sentinel = &(list->pmix_list_sentinel); 0604 #if PMIX_ENABLE_DEBUG 0605 /* Spot check: ensure that this item is previously on no lists */ 0606 0607 assert(0 == item->pmix_list_item_refcount); 0608 assert(NULL == item->pmix_list_item_belong_to); 0609 #endif 0610 0611 /* reset item's next pointer */ 0612 item->pmix_list_next = sentinel->pmix_list_next; 0613 0614 /* reset item's previous pointer */ 0615 item->pmix_list_prev = sentinel; 0616 0617 /* reset previous first element's previous pointer */ 0618 sentinel->pmix_list_next->pmix_list_prev = item; 0619 0620 /* reset head's next pointer */ 0621 sentinel->pmix_list_next = item; 0622 0623 /* increment list element counter */ 0624 list->pmix_list_length++; 0625 0626 #if PMIX_ENABLE_DEBUG 0627 /* Spot check: ensure this item is only on the list that we just 0628 prepended it to */ 0629 0630 item->pmix_list_item_refcount += 1; 0631 assert(1 == item->pmix_list_item_refcount); 0632 item->pmix_list_item_belong_to = list; 0633 #endif 0634 } 0635 0636 /** 0637 * Remove the first item from the list and return it. 0638 * 0639 * @param list The list container 0640 * 0641 * @returns The first item on the list. If the list is empty, 0642 * NULL will be returned 0643 * 0644 * This is an O(1) operation to return the first item on the list. If 0645 * the list is not empty, a pointer to the first item in the list will 0646 * be returned. Ownership of the item is transferred from the list to 0647 * the caller; no calls to PMIX_RETAIN() or PMIX_RELEASE() are invoked. 0648 * 0649 * This is an inlined function in compilers that support inlining, so 0650 * it's usually a cheap operation. 0651 */ 0652 static inline pmix_list_item_t *pmix_list_remove_first(pmix_list_t *list) 0653 { 0654 /* Removes and returns first item on list. 0655 Caller now owns the item and should release the item 0656 when caller is done with it. 0657 */ 0658 volatile pmix_list_item_t *item; 0659 if (0 == list->pmix_list_length) { 0660 return (pmix_list_item_t *) NULL; 0661 } 0662 0663 #if PMIX_ENABLE_DEBUG 0664 /* Spot check: ensure that the first item is only on this list */ 0665 0666 assert(1 == list->pmix_list_sentinel.pmix_list_next->pmix_list_item_refcount); 0667 #endif 0668 0669 /* reset list length counter */ 0670 list->pmix_list_length--; 0671 0672 /* get pointer to first element on the list */ 0673 item = (pmix_list_item_t*)list->pmix_list_sentinel.pmix_list_next; 0674 0675 /* reset previous pointer of next item on the list */ 0676 item->pmix_list_next->pmix_list_prev = item->pmix_list_prev; 0677 0678 /* reset the head next pointer */ 0679 list->pmix_list_sentinel.pmix_list_next = item->pmix_list_next; 0680 0681 #if PMIX_ENABLE_DEBUG 0682 assert(list == item->pmix_list_item_belong_to); 0683 item->pmix_list_item_belong_to = NULL; 0684 item->pmix_list_prev = (pmix_list_item_t *) NULL; 0685 item->pmix_list_next = (pmix_list_item_t *) NULL; 0686 0687 /* Spot check: ensure that the item we're returning is now on no 0688 lists */ 0689 0690 item->pmix_list_item_refcount -= 1; 0691 assert(0 == item->pmix_list_item_refcount); 0692 #endif 0693 0694 return (pmix_list_item_t *) item; 0695 } 0696 0697 /** 0698 * Remove the last item from the list and return it. 0699 * 0700 * @param list The list container 0701 * 0702 * @returns The last item on the list. If the list is empty, 0703 * NULL will be returned 0704 * 0705 * This is an O(1) operation to return the last item on the list. If 0706 * the list is not empty, a pointer to the last item in the list will 0707 * be returned. Ownership of the item is transferred from the list to 0708 * the caller; no calls to PMIX_RETAIN() or PMIX_RELEASE() are invoked. 0709 * 0710 * This is an inlined function in compilers that support inlining, so 0711 * it's usually a cheap operation. 0712 */ 0713 static inline pmix_list_item_t *pmix_list_remove_last(pmix_list_t *list) 0714 { 0715 /* Removes, releases and returns last item on list. 0716 Caller now owns the item and should release the item 0717 when caller is done with it. 0718 */ 0719 volatile pmix_list_item_t *item; 0720 if (0 == list->pmix_list_length) { 0721 return (pmix_list_item_t *) NULL; 0722 } 0723 0724 #if PMIX_ENABLE_DEBUG 0725 /* Spot check: ensure that the first item is only on this list */ 0726 0727 assert(1 == list->pmix_list_sentinel.pmix_list_prev->pmix_list_item_refcount); 0728 #endif 0729 0730 /* reset list length counter */ 0731 list->pmix_list_length--; 0732 0733 /* get item */ 0734 item = (pmix_list_item_t*)list->pmix_list_sentinel.pmix_list_prev; 0735 0736 /* reset previous pointer on next to last pointer */ 0737 item->pmix_list_prev->pmix_list_next = item->pmix_list_next; 0738 0739 /* reset tail's previous pointer */ 0740 list->pmix_list_sentinel.pmix_list_prev = item->pmix_list_prev; 0741 0742 #if PMIX_ENABLE_DEBUG 0743 assert(list == item->pmix_list_item_belong_to); 0744 item->pmix_list_next = item->pmix_list_prev = (pmix_list_item_t *) NULL; 0745 0746 /* Spot check: ensure that the item we're returning is now on no 0747 lists */ 0748 0749 item->pmix_list_item_refcount -= 1; 0750 assert(0 == item->pmix_list_item_refcount); 0751 item->pmix_list_item_belong_to = NULL; 0752 #endif 0753 0754 return (pmix_list_item_t *) item; 0755 } 0756 0757 /** 0758 * Add an item to the list before a given element 0759 * 0760 * @param list The list container 0761 * @param pos List element to insert \c item before 0762 * @param item The item to insert 0763 * 0764 * Inserts \c item before \c pos. This is an O(1) operation. 0765 */ 0766 static inline void pmix_list_insert_pos(pmix_list_t *list, pmix_list_item_t *pos, 0767 pmix_list_item_t *item) 0768 { 0769 #if PMIX_ENABLE_DEBUG 0770 /* Spot check: ensure that the item we're insertting is currently 0771 not on any list */ 0772 0773 assert(0 == item->pmix_list_item_refcount); 0774 assert(NULL == item->pmix_list_item_belong_to); 0775 #endif 0776 0777 /* point item at the existing elements */ 0778 item->pmix_list_next = pos; 0779 item->pmix_list_prev = pos->pmix_list_prev; 0780 0781 /* splice into the list */ 0782 pos->pmix_list_prev->pmix_list_next = item; 0783 pos->pmix_list_prev = item; 0784 0785 /* reset list length counter */ 0786 list->pmix_list_length++; 0787 0788 #if PMIX_ENABLE_DEBUG 0789 /* Spot check: double check that this item is only on the list 0790 that we just added it to */ 0791 0792 item->pmix_list_item_refcount += 1; 0793 assert(1 == item->pmix_list_item_refcount); 0794 item->pmix_list_item_belong_to = list; 0795 #endif 0796 } 0797 0798 /** 0799 * Add an item to the list at a specific index location in the list. 0800 * 0801 * @param list The list container 0802 * @param item The item to insert 0803 * @param index Location to add the item 0804 * 0805 * @returns true if insertion succeeded; otherwise false 0806 * 0807 * This is potentially an O(N) operation to traverse down to the 0808 * correct location in the list and add an item. 0809 * 0810 * Example: if idx = 2 and list = item1->item2->item3->item4, then 0811 * after insert, list = item1->item2->item->item3->item4. 0812 * 0813 * If index is greater than the length of the list, no action is 0814 * performed and false is returned. 0815 */ 0816 PMIX_EXPORT bool pmix_list_insert(pmix_list_t *list, 0817 pmix_list_item_t *item, 0818 long long idx); 0819 0820 /** 0821 * Join a list into another list 0822 * 0823 * @param thislist List container for list being operated on 0824 * @param pos List item in \c thislist marking the position before 0825 * which items are inserted 0826 * @param xlist List container for list being spliced from 0827 * 0828 * Join a list into another list. All of the elements of \c xlist 0829 * are inserted before \c pos and removed from \c xlist. 0830 * 0831 * This operation is an O(1) operation. Both \c thislist and \c 0832 * xlist must be valid list containsers. \c xlist will be empty 0833 * but valid after the call. All pointers to \c pmix_list_item_t 0834 * containers remain valid, including those that point to elements 0835 * in \c xlist. 0836 */ 0837 PMIX_EXPORT void pmix_list_join(pmix_list_t *thislist, 0838 pmix_list_item_t *pos, 0839 pmix_list_t *xlist); 0840 0841 /** 0842 * Splice a list into another list 0843 * 0844 * @param thislist List container for list being operated on 0845 * @param pos List item in \c thislist marking the position before 0846 * which items are inserted 0847 * @param xlist List container for list being spliced from 0848 * @param first List item in \c xlist marking the start of elements 0849 * to be copied into \c thislist 0850 * @param last List item in \c xlist marking the end of elements 0851 * to be copied into \c thislist 0852 * 0853 * Splice a subset of a list into another list. The \c [first, 0854 * last) elements of \c xlist are moved into \c thislist, 0855 * inserting them before \c pos. \c pos must be a valid iterator 0856 * in \c thislist and \c [first, last) must be a valid range in \c 0857 * xlist. \c position must not be in the range \c [first, last). 0858 * It is, however, valid for \c xlist and \c thislist to be the 0859 * same list. 0860 * 0861 * This is an O(N) operation because the length of both lists must 0862 * be recomputed. 0863 */ 0864 PMIX_EXPORT void pmix_list_splice(pmix_list_t *thislist, 0865 pmix_list_item_t *pos, 0866 pmix_list_t *xlist, 0867 pmix_list_item_t *first, 0868 pmix_list_item_t *last); 0869 0870 /** 0871 * Comparison function for pmix_list_sort(), below. 0872 * 0873 * @param a Pointer to a pointer to an pmix_list_item_t. 0874 * Explanation below. 0875 * @param b Pointer to a pointer to an pmix_list_item_t. 0876 * Explanation below. 0877 * @retval 1 if \em a is greater than \em b 0878 * @retval 0 if \em a is equal to \em b 0879 * @retval 11 if \em a is less than \em b 0880 * 0881 * This function is invoked by qsort(3) from within 0882 * pmix_list_sort(). It is important to understand what 0883 * pmix_list_sort() does before invoking qsort, so go read that 0884 * documentation first. 0885 * 0886 * The important thing to realize here is that a and b will be \em 0887 * double pointers to the items that you need to compare. Here's 0888 * a sample compare function to illustrate this point: 0889 */ 0890 typedef int (*pmix_list_item_compare_fn_t)(pmix_list_item_t **a, 0891 pmix_list_item_t **b); 0892 0893 /** 0894 * Sort a list with a provided compare function. 0895 * 0896 * @param list The list to sort 0897 * @param compare Compare function 0898 * 0899 * Put crassly, this function's complexity is O(N) + O(log(N)). 0900 * Its algorithm is: 0901 * 0902 * - remove every item from the list and put the corresponding 0903 * (pmix_list_item_t*)'s in an array 0904 * - call qsort(3) with that array and your compare function 0905 * - re-add every element of the now-sorted array to the list 0906 * 0907 * The resulting list is now ordered. Note, however, that since 0908 * an array of pointers is sorted, the comparison function must do 0909 * a double de-reference to get to the actual pmix_list_item_t (or 0910 * whatever the underlying type is). See the documentation of 0911 * pmix_list_item_compare_fn_t for an example). 0912 */ 0913 PMIX_EXPORT int pmix_list_sort(pmix_list_t *list, 0914 pmix_list_item_compare_fn_t compare); 0915 0916 END_C_DECLS 0917 0918 #endif /* PMIX_LIST_H */
[ Source navigation ] | [ Diff markup ] | [ Identifier search ] | [ general search ] |
This page was automatically generated by the 2.3.7 LXR engine. The LXR team |
![]() ![]() |