mirror of
http://git.haproxy.org/git/haproxy.git/
synced 2024-12-23 13:17:16 +00:00
196 lines
8.8 KiB
Plaintext
196 lines
8.8 KiB
Plaintext
2021-11-09 - List API
|
|
|
|
|
|
1. Background
|
|
-------------
|
|
|
|
HAProxy's lists are almost all doubly-linked and circular so that it is always
|
|
possible to insert at the beginning, append at the end, scan them in any order
|
|
and delete any element without having to scan to search the predecessor nor the
|
|
successor.
|
|
|
|
A list's head is just a regular list element, and an element always points to
|
|
another list element. Such elements only have two pointers, the next and the
|
|
previous elements. The object being pointed to is retrieved by subtracting the
|
|
list element's offset in its structure from the list element's pointer. This
|
|
way there is no need for any separate allocation for the list element, for a
|
|
pointer to the object in the list, nor for a pointer to the list element from
|
|
the object, as the list is embedded into the object.
|
|
|
|
All basic operations are provided, as well as some iterators. Some iterators
|
|
are safe for removal of the current element within the loop, others not. In any
|
|
case a list cannot be freely modified while iterating over it (e.g. the current
|
|
element's successor cannot not be freed if it's saved as the restart point).
|
|
|
|
Extreme care is taken nowadays in HAProxy to make sure that no dangling
|
|
pointers are left in elements, so it is important to always initialize list
|
|
heads and list elements, as well as elements that are removed from a list if
|
|
they are not immediately freed, so that their deletion is idempotent. A rule of
|
|
thumb is that a list pointer's validity never has to be checked, it is always
|
|
valid to dereference it. A lot of complex bugs have been caused in the past by
|
|
incorrect list manipulation, such as an element being deleted twice, resulting
|
|
in damaging previously adjacent elements' neighbours. This usually has serious
|
|
consequences at locations that are totally different from the one of the bug,
|
|
and that are only detected much later, so it is required to be particularly
|
|
strict on using lists safely.
|
|
|
|
The lists are not thread-safe, but mt_lists may be used instead.
|
|
|
|
|
|
2. API description
|
|
------------------
|
|
|
|
A list is defined like this, both for the list's head, and for any other
|
|
element:
|
|
|
|
struct list {
|
|
struct list *n; /* next */
|
|
struct list *p; /* prev */
|
|
};
|
|
|
|
An empty list points to itself for both pointers. I.e. a list's head is both
|
|
its own successor and its own predecessor. This guarantees that insertions
|
|
and deletions can be done without any check and that deletion is idempotent.
|
|
For this reason and by convention, a detached element ought to be represented
|
|
like an empty head.
|
|
|
|
Lists are manipulated using a set of macros which are used to initialize, add,
|
|
remove, or iterate over elements. Most of these macros are extremely simple and
|
|
are not even protected against multiple evaluation, so it is fundamentally
|
|
important that the expressions used in the arguments are idempotent and that
|
|
the result does not depend on the evaluation order of the arguments.
|
|
|
|
Macro Description
|
|
|
|
ILH
|
|
Initialized List Head : this is a non-NULL, non-empty list element used
|
|
to prevent the compiler from moving an empty list head declaration to
|
|
BSS, typically when it appears in an array of keywords Without this,
|
|
some older versions of gcc tend to trim all the array and cause
|
|
corruption.
|
|
|
|
LIST_INIT(l)
|
|
Initialize the list as an empty list head
|
|
|
|
LIST_HEAD_INIT(l)
|
|
Return a valid initialized empty list head pointing to this
|
|
element. Essentially used with assignments in declarations.
|
|
|
|
LIST_INSERT(l, e)
|
|
Add an element at the beginning of a list and return it
|
|
|
|
LIST_APPEND(l, e)
|
|
Add an element at the end of a list and return it
|
|
|
|
LIST_SPLICE(n, o)
|
|
Add the contents of a list <o> at the beginning of another list <n>.
|
|
The old list head remains untouched.
|
|
|
|
LIST_SPLICE_END_DETACHED(n, o)
|
|
Add the contents of a list whose first element is is <o> and last one
|
|
is <o->p> at the end of another list <n>. The old list DOES NOT have
|
|
any head here.
|
|
|
|
LIST_DELETE(e)
|
|
Remove an element from a list and return it. Safe to call on
|
|
initialized elements, but will not change the element itself so it is
|
|
not idempotent. Consider using LIST_DEL_INIT() instead unless called
|
|
immediately after a free().
|
|
|
|
LIST_DEL_INIT(e)
|
|
Remove an element from a list, initialize it and return it so that a
|
|
subsequent LIST_DELETE() is safe. This is faster than performing a
|
|
LIST_DELETE() followed by a LIST_INIT() as pointers are not reloaded.
|
|
|
|
LIST_ELEM(l, t, m)
|
|
Return a pointer of type <t> to a structure containing a list head
|
|
member called <m> at address <l>. Note that <l> can be the result of a
|
|
function or macro since it's used only once.
|
|
|
|
LIST_ISEMPTY(l)
|
|
Check if the list head <l> is empty (=initialized) or not, and return
|
|
non-zero only if so.
|
|
|
|
LIST_INLIST(e)
|
|
Check if the list element <e> was added to a list or not, thus return
|
|
true unless the element was initialized.
|
|
|
|
LIST_INLIST_ATOMIC(e)
|
|
Atomically check if the list element's next pointer points to anything
|
|
different from itself, implying the element should be part of a
|
|
list. This usually is similar to LIST_INLIST() except that while that
|
|
one might be instrumented using debugging code to perform further
|
|
consistency checks, the macro below guarantees to always perform a
|
|
single atomic test and is safe to use with barriers.
|
|
|
|
LIST_NEXT(l, t, m)
|
|
Return a pointer of type <t> to a structure following the element which
|
|
contains list head <l>, which is known as member <m> in struct <t>.
|
|
|
|
LIST_PREV(l, t, m)
|
|
Return a pointer of type <t> to a structure preceding the element which
|
|
contains list head <l>, which is known as member <m> in struct <t>.
|
|
Note that this macro is first undefined as it happened to already exist
|
|
on some old OSes.
|
|
|
|
list_for_each_entry(i, l, m)
|
|
Iterate local variable <i> through a list of items of type "typeof(*i)"
|
|
which are linked via a "struct list" member named <m>. A pointer to the
|
|
head of the list is passed in <l>. No temporary variable is needed.
|
|
Note that <i> must not be modified during the loop.
|
|
|
|
list_for_each_entry_from(i, l, m)
|
|
Same as list_for_each_entry() but starting from current value of <i>
|
|
instead of the list's head.
|
|
|
|
list_for_each_entry_from_rev(i, l, m)
|
|
Same as list_for_each_entry_rev() but starting from current value of <i>
|
|
instead of the list's head.
|
|
|
|
list_for_each_entry_rev(i, l, m)
|
|
Iterate backwards local variable <i> through a list of items of type
|
|
"typeof(*i)" which are linked via a "struct list" member named <m>. A
|
|
pointer to the head of the list is passed in <l>. No temporary variable
|
|
is needed. Note that <i> must not be modified during the loop.
|
|
|
|
list_for_each_entry_safe(i, b, l, m)
|
|
Iterate variable <i> through a list of items of type "typeof(*i)" which
|
|
are linked via a "struct list" member named <m>. A pointer to the head
|
|
of the list is passed in <l>. A temporary backup variable <b> of same
|
|
type as <i> is needed so that <i> may safely be deleted if needed. Note
|
|
that it is only permitted to delete <i> and no other element during
|
|
this operation!
|
|
|
|
list_for_each_entry_safe_from(i, b, l, m)
|
|
Same as list_for_each_entry_safe() but starting from current value of
|
|
<i> instead of the list's head.
|
|
|
|
list_for_each_entry_safe_from_rev(i, b, l, m)
|
|
Same as list_for_each_entry_safe_rev() but starting from current value
|
|
of <i> instead of the list's head.
|
|
|
|
list_for_each_entry_safe_rev(i, b, l, m)
|
|
Iterate backwards local variable <i> through a list of items of type
|
|
"typeof(*i)" which are linked via a "struct list" member named <m>. A
|
|
pointer to the head of the list is passed in <l>. A temporary variable
|
|
<b> of same type as <i> is needed so that <i> may safely be deleted if
|
|
needed. Note that it is only permitted to delete <i> and no other
|
|
element during this operation!
|
|
|
|
3. Notes
|
|
--------
|
|
|
|
- This API is quite old and some macros are missing. For example there's still
|
|
no list_first() so it's common to use LIST_ELEM(head->n, ...) instead. Some
|
|
older parts of the code also used to rely on list_for_each() followed by a
|
|
break to stop on the first element.
|
|
|
|
- Some parts were recently renamed because LIST_ADD() used to do what
|
|
LIST_INSERT() currently does and was often mistaken with LIST_ADDQ() which is
|
|
what LIST_APPEND() now is. As such it is not totally impossible that some
|
|
places use a LIST_INSERT() where a LIST_APPEND() would be desired.
|
|
|
|
- The structure must not be modified at all (even to add debug info). Some
|
|
parts of the code assume that its layout is exactly this one, particularly
|
|
the parts ensuring the casting between MT lists and lists.
|