8

I'm studying sys/queue.h from FreeBSD and I have one question:

In sys/queue.h, LIST_ENTRY is defined as follows:

#define LIST_ENTRY(type)                        \
struct {                                \
    struct type *le_next;   /* next element */          \
    struct type **le_prev;  /* address of previous next element */  \
}

Why does it maintain the address of previous next element (struct type **le_prev) rather than simply previous elment like struct type *le_prev?

cnst
  • 25,870
  • 6
  • 90
  • 122
Yanzhe Chen
  • 427
  • 3
  • 9

2 Answers2

8

If you would have read the queue.h file from the beginning, you may have got following comment:

 * A list is headed by a single forward pointer (or an array of forward
 * pointers for a hash table header). The elements are doubly linked
 * so that an arbitrary element can be removed without a need to
 * traverse the list. New elements can be added to the list before
 * or after an existing element or at the head of the list. A list
 * may only be traversed in the forward direction.

so list, which provides O(1) insertion and deletion, but only forward traversal. To achieve this, you only need the reference to the previously next pointer, which is exactly what is implemented.

Dayal rai
  • 6,548
  • 22
  • 29
  • Do you mean that this implementation is to prevent backward traversal as well as gain O(1) insertion and deletion? – Yanzhe Chen May 08 '13 at 12:52
  • @YanzheChen complexity of (linear) list operations will not change when you use a single pointer and and the double pointer does not prevent backward traversal. I think, the important part is "or an array of forward pointers"; when having such a (hash) table, removal is cheaper when address of previous pointer is stored. – ensc Dec 31 '13 at 12:27
  • @ensc I read this [post](http://stackoverflow.com/questions/9362896/deleting-any-node-from-a-single-linked-list-when-only-pointer-to-that-node-is-gi) and understand that the deletion in single-linked list can be achieved in O(1) if it is not the tail node. But why the double pointer does **not** prevent backward traversal? How to perform the backward traversal? And I don't understand **the important part** you mentioned either. Could you explain this in more details? – Yanzhe Chen Jan 01 '14 at 06:34
  • 1
    @YanzheChen backward traversal would be `n = container_of(*n->le_prev, type, le_next)`. **The important part** applies to cases where the first element (the root) of the list is something special; a typical example is the root of a hash tree which consists of an array of links pointing to lists. Here, you can not deref the anchor (you do not know the index within the array) and have to use a double pointer hence. – ensc Jan 01 '14 at 11:58
  • 1
    I think this answer better explains it: http://stackoverflow.com/questions/3058592/use-of-double-pointer-in-linux-kernel-hash-list-implementation – wonder.mice Dec 18 '14 at 08:15
0

Let me try to explain. Actually the **le_prev* affords ablity to list defined by sys/queue.h to insert_before that forward-list can not. Compared with insert_before, the insert_after can both be implemented well in forward-list or list. So list is more functional.

insert_before(entry* list_elem, entry* elem, type val)
{
    elem->next = list_elem;
    *(list->prev) = elem;
    elem->prev = *(list->prev);
    list_elem->prev = elem->next;
}
insert_after(entry* list_elem, entry* elem, type val)
{
    if( ((elem)->next= (list_elem)->next) != NULL ) {
        (elem_list)->next->prev = &(elem)->next;
    }
    (list_elem)->next =  elem;
    elem->prev =  &(list_elem)->next;
}
Ding
  • 1
  • 2