/** * Copyright (C) Mellanox Technologies Ltd. 2001-2014. ALL RIGHTS RESERVED. * * See file LICENSE for terms. */ #ifndef UCS_QUEUE_H_ #define UCS_QUEUE_H_ #include "queue_types.h" #include #include /** * Initialize a queue. * * @param queue Queue to initialize. */ static inline void ucs_queue_head_init(ucs_queue_head_t *queue) { #ifdef __clang_analyzer__ queue->head = (ucs_queue_elem_t*)(void*)queue; #endif queue->ptail = &queue->head; } /** * @return Queue length. */ static inline size_t ucs_queue_length(ucs_queue_head_t *queue) { ucs_queue_elem_t **pelem; size_t length; length = 0; for (pelem = &queue->head; pelem != queue->ptail; pelem = &(*pelem)->next) { ++length; } return length; } /** * @return Whether the queue is empty. */ static inline int ucs_queue_is_empty(ucs_queue_head_t *queue) { return queue->ptail == &queue->head; } /** * Enqueue an element to the tail of the queue. * * @param queue Queue to add to. * @param elem Element to add. */ static inline void ucs_queue_push(ucs_queue_head_t *queue, ucs_queue_elem_t *elem) { *queue->ptail = elem; queue->ptail = &elem->next; #if UCS_ENABLE_ASSERT elem->next = NULL; /* For sanity check below */ #endif } /** * Add an element to the head of the queue. * * @param queue Queue to add to. * @param elem Element to add. */ static inline void ucs_queue_push_head(ucs_queue_head_t *queue, ucs_queue_elem_t *elem) { elem->next = queue->head; queue->head = elem; if (queue->ptail == &queue->head) { queue->ptail = &elem->next; } } /** * Dequeue an element from the head of the queue, assuming the queue is not empty. * * @param queue Non-empty queue to pull from. * @return Element from the head of the queue. */ static inline ucs_queue_elem_t *ucs_queue_pull_non_empty(ucs_queue_head_t *queue) { ucs_queue_elem_t *elem; elem = queue->head; queue->head = elem->next; if (queue->ptail == &elem->next) { queue->ptail = &queue->head; } return elem; } /** * Delete an element. * The element must be valid when deleting it. * After the call, iter points to the next element, and the element may be released. */ static inline void ucs_queue_del_iter(ucs_queue_head_t *queue, ucs_queue_iter_t iter) { ucs_assert((iter != NULL) && (*iter != NULL)); if (queue->ptail == &(*iter)->next) { queue->ptail = iter; /* deleting the last element */ *iter = NULL; /* make *ptail point to NULL */ } else { *iter = (*iter)->next; } /* Sanity check */ ucs_assertv((queue->head != NULL) || (queue->ptail == &queue->head), "head=%p ptail=%p &head=%p iter=%p", queue->head, queue->ptail, &queue->head, iter); /* If the queue is empty, head must point to null */ ucs_assertv((queue->ptail != &queue->head) || (queue->head == NULL), "head=%p ptail=%p &head=%p iter=%p", queue->head, queue->ptail, &queue->head, iter); } /** * Dequeue an element from the head of the queue. * * @param queue Queue to pull from. * @return Element from the head of the queue, or NULL if the queue is empty. */ static inline ucs_queue_elem_t *ucs_queue_pull(ucs_queue_head_t *queue) { if (ucs_queue_is_empty(queue)) return NULL; return ucs_queue_pull_non_empty(queue); } /** * Insert all elements from one queue to another queue, leaving the first queue * empty. * * @param queue Queue to push elements to. * @param new_elems Queue of elements to add. */ static inline void ucs_queue_splice(ucs_queue_head_t *queue, ucs_queue_head_t *new_elems) { if (!ucs_queue_is_empty(new_elems)) { *queue->ptail = new_elems->head; queue->ptail = new_elems->ptail; new_elems->ptail = &new_elems->head; } } /** * Convenience macro to pull from a non-empty queue and return the containing element. * * @param queue Non-empty queue to pull from. * @param type Container element type. * @param member Queue element member inside the container. * * @return Pulled element. */ #define ucs_queue_pull_elem_non_empty(queue, type, member) \ ucs_container_of(ucs_queue_pull_non_empty(queue), type, member) /** * Convenience macro to get the head element of a non-empty queue. * * @param queue Non-empty queue whose head element to get. * @param type Container element type. * @param member Queue element member inside the container. * * @return Head element. */ #define ucs_queue_head_elem_non_empty(queue, type, member) \ ucs_container_of((queue)->head, type, member) /** * Convenience macro to get the tail element of a non-empty queue. * * @param queue Non-empty queue whose head element to get. * @param type Container element type. * @param member Queue element member inside the container. * * @return Head element. */ #define ucs_queue_tail_elem_non_empty(queue, type, member) \ ucs_container_of((queue)->ptail, type, member) /** * Iterate over queue elements. The queue must not be modified during the iteration. * * @param elem Variable which will hold point to the element in the queue. * @param queue Queue to iterate on. * @param member Member inside 'elem' which is the queue link. */ #define ucs_queue_for_each(elem, queue, member) \ /* we set `ptail` field to queue address to not substract NULL pointer */ \ for (*(queue)->ptail = (ucs_queue_elem_t*)(void*)(queue), \ elem = ucs_container_of((queue)->head, typeof(*elem), member); \ (elem) != ucs_container_of((ucs_queue_elem_t*)(void*)(queue), \ typeof(*elem), member); \ elem = ucs_container_of(elem->member.next, typeof(*elem), member)) /** * Iterate over queue elements. The current element may be safely removed from * the queue using ucs_queue_del_iter(). * * @param elem Variable which will hold point to the element in the queue. * @param iter Iterator variable. May be passed to ucs_queue_del_iter(). * @param queue Queue to iterate on. * @param member Member inside 'elem' which is the queue link. */ #define ucs_queue_for_each_safe(elem, iter, queue, member) \ for (iter = &(queue)->head, \ elem = ucs_container_of(*iter, typeof(*elem), member); \ iter != (queue)->ptail; \ iter = (*iter == &elem->member) ? &(*iter)->next : iter, \ elem = ucs_container_of(*iter, typeof(*elem), member)) /** * Iterate and extract elements from the queue while a condition is true. * * @param elem Variable which will hold point to the element in the queue. * @param queue Queue to iterate on. * @param member Member inside 'elem' which is the queue link. * @param cond Condition to continue iterating. * * TODO optimize */ #define ucs_queue_for_each_extract(elem, queue, member, cond) \ for (elem = ucs_container_of((queue)->head, typeof(*elem), member); \ \ !ucs_queue_is_empty(queue) && (cond) && ucs_queue_pull_non_empty(queue); \ \ elem = ucs_container_of((queue)->head, typeof(*elem), member)) /* * Queue iteration */ static inline ucs_queue_iter_t ucs_queue_iter_begin(ucs_queue_head_t *q) { return &q->head; } static inline ucs_queue_iter_t ucs_queue_iter_next(ucs_queue_iter_t i) { return &(*i)->next; } static inline int ucs_queue_iter_end(ucs_queue_head_t *q, ucs_queue_iter_t i) { return i == q->ptail; } static inline void ucs_queue_remove(ucs_queue_head_t *queue, ucs_queue_elem_t *elem) { ucs_queue_iter_t iter = ucs_queue_iter_begin(queue); while (!ucs_queue_iter_end(queue, iter)) { if (*iter == elem) { ucs_queue_del_iter(queue, iter); return; } iter = ucs_queue_iter_next(iter); } } #define ucs_queue_iter_elem(elem, iter, member) \ ucs_container_of(*iter, typeof(*elem), member) #endif