queue(3bsd)
β¨ queue(3bsd) β¨
okay bestie, sit DOWN. This is the manual page for queue(3bsd) β the BSD queue macros, living in your friendly neighborhood LOCAL section. It's implementations of singly-linked lists, singly-linked tail queues, lists and tail queues, and no cap, it's the blueprint for like half of every kernel data structure you've ever touched. π
π NAME π
Here's the whole girl squad, grouped by family so it actually makes sense:
The SLIST girlies (singly-linked lists):
SLIST_CLASS_ENTRYSLIST_CLASS_HEADSLIST_CONCATSLIST_EMPTYSLIST_ENTRYSLIST_FIRSTSLIST_FOREACHSLIST_FOREACH_FROMSLIST_FOREACH_FROM_SAFESLIST_FOREACH_SAFESLIST_HEADSLIST_HEAD_INITIALIZERSLIST_INITSLIST_INSERT_AFTERSLIST_INSERT_HEADSLIST_NEXTSLIST_REMOVESLIST_REMOVE_AFTERSLIST_REMOVE_HEADSLIST_SWAP
The STAILQ girlies (singly-linked tail queues):
STAILQ_CLASS_ENTRYSTAILQ_CLASS_HEADSTAILQ_CONCATSTAILQ_EMPTYSTAILQ_ENTRYSTAILQ_FIRSTSTAILQ_FOREACHSTAILQ_FOREACH_FROMSTAILQ_FOREACH_FROM_SAFESTAILQ_FOREACH_SAFESTAILQ_HEADSTAILQ_HEAD_INITIALIZERSTAILQ_INITSTAILQ_INSERT_AFTERSTAILQ_INSERT_HEADSTAILQ_INSERT_TAILSTAILQ_LASTSTAILQ_NEXTSTAILQ_REMOVESTAILQ_REMOVE_AFTERSTAILQ_REMOVE_HEADSTAILQ_SWAP
The LIST girlies (doubly linked lists):
LIST_CLASS_ENTRYLIST_CLASS_HEADLIST_CONCATLIST_EMPTYLIST_ENTRYLIST_FIRSTLIST_FOREACHLIST_FOREACH_FROMLIST_FOREACH_FROM_SAFELIST_FOREACH_SAFELIST_HEADLIST_HEAD_INITIALIZERLIST_INITLIST_INSERT_AFTERLIST_INSERT_BEFORELIST_INSERT_HEADLIST_NEXTLIST_PREVLIST_REMOVELIST_SWAP
The TAILQ girlies (tail queues, the main characters honestly):
TAILQ_CLASS_ENTRYTAILQ_CLASS_HEADTAILQ_CONCATTAILQ_EMPTYTAILQ_ENTRYTAILQ_FIRSTTAILQ_FOREACHTAILQ_FOREACH_FROMTAILQ_FOREACH_FROM_SAFETAILQ_FOREACH_REVERSETAILQ_FOREACH_REVERSE_FROMTAILQ_FOREACH_REVERSE_FROM_SAFETAILQ_FOREACH_REVERSE_SAFETAILQ_FOREACH_SAFETAILQ_HEADTAILQ_HEAD_INITIALIZERTAILQ_INITTAILQ_INSERT_AFTERTAILQ_INSERT_BEFORETAILQ_INSERT_HEADTAILQ_INSERT_TAILTAILQ_LASTTAILQ_NEXTTAILQ_PREVTAILQ_REMOVETAILQ_SWAP
...and that's implementations of singly-linked lists, singly-linked tail queues, lists and tail queues. π«Ά
π LIBRARY π
Utility functions from BSD systems (libbsd, -lbsd). That's the whole vibe. π¦
π« SYNOPSIS π«
#include <sys/queue.h>
(See libbsd(7) for include usage.) iykyk, go read that one too.
SLIST_CLASS_ENTRY(CLASSTYPE);
SLIST_CLASS_HEAD(HEADNAME, CLASSTYPE);
SLIST_CONCAT(SLIST_HEAD *head1, SLIST_HEAD *head2, TYPE,
SLIST_ENTRY NAME);
SLIST_EMPTY(SLIST_HEAD *head);
SLIST_ENTRY(TYPE);
SLIST_FIRST(SLIST_HEAD *head);
SLIST_FOREACH(TYPE *var, SLIST_HEAD *head, SLIST_ENTRY NAME);
SLIST_FOREACH_FROM(TYPE *var, SLIST_HEAD *head, SLIST_ENTRY NAME);
SLIST_FOREACH_FROM_SAFE(TYPE *var, SLIST_HEAD *head, SLIST_ENTRY NAME,
TYPE *temp_var);
SLIST_FOREACH_SAFE(TYPE *var, SLIST_HEAD *head, SLIST_ENTRY NAME,
TYPE *temp_var);
SLIST_HEAD(HEADNAME, TYPE);
SLIST_HEAD_INITIALIZER(SLIST_HEAD head);
SLIST_INIT(SLIST_HEAD *head);
SLIST_INSERT_AFTER(TYPE *listelm, TYPE *elm, SLIST_ENTRY NAME);
SLIST_INSERT_HEAD(SLIST_HEAD *head, TYPE *elm, SLIST_ENTRY NAME);
SLIST_NEXT(TYPE *elm, SLIST_ENTRY NAME);
SLIST_REMOVE(SLIST_HEAD *head, TYPE *elm, TYPE, SLIST_ENTRY NAME);
SLIST_REMOVE_AFTER(TYPE *elm, SLIST_ENTRY NAME);
SLIST_REMOVE_HEAD(SLIST_HEAD *head, SLIST_ENTRY NAME);
SLIST_SWAP(SLIST_HEAD *head1, SLIST_HEAD *head2, TYPE);
STAILQ_CLASS_ENTRY(CLASSTYPE);
STAILQ_CLASS_HEAD(HEADNAME, CLASSTYPE);
STAILQ_CONCAT(STAILQ_HEAD *head1, STAILQ_HEAD *head2);
STAILQ_EMPTY(STAILQ_HEAD *head);
STAILQ_ENTRY(TYPE);
STAILQ_FIRST(STAILQ_HEAD *head);
STAILQ_FOREACH(TYPE *var, STAILQ_HEAD *head, STAILQ_ENTRY NAME);
STAILQ_FOREACH_FROM(TYPE *var, STAILQ_HEAD *head, STAILQ_ENTRY NAME);
STAILQ_FOREACH_FROM_SAFE(TYPE *var, STAILQ_HEAD *head,
STAILQ_ENTRY NAME, TYPE *temp_var);
STAILQ_FOREACH_SAFE(TYPE *var, STAILQ_HEAD *head, STAILQ_ENTRY NAME,
TYPE *temp_var);
STAILQ_HEAD(HEADNAME, TYPE);
STAILQ_HEAD_INITIALIZER(STAILQ_HEAD head);
STAILQ_INIT(STAILQ_HEAD *head);
STAILQ_INSERT_AFTER(STAILQ_HEAD *head, TYPE *listelm, TYPE *elm,
STAILQ_ENTRY NAME);
STAILQ_INSERT_HEAD(STAILQ_HEAD *head, TYPE *elm, STAILQ_ENTRY NAME);
STAILQ_INSERT_TAIL(STAILQ_HEAD *head, TYPE *elm, STAILQ_ENTRY NAME);
STAILQ_LAST(STAILQ_HEAD *head, TYPE *elm, STAILQ_ENTRY NAME);
STAILQ_NEXT(TYPE *elm, STAILQ_ENTRY NAME);
STAILQ_REMOVE(STAILQ_HEAD *head, TYPE *elm, TYPE, STAILQ_ENTRY NAME);
STAILQ_REMOVE_AFTER(STAILQ_HEAD *head, TYPE *elm, STAILQ_ENTRY NAME);
STAILQ_REMOVE_HEAD(STAILQ_HEAD *head, STAILQ_ENTRY NAME);
STAILQ_SWAP(STAILQ_HEAD *head1, STAILQ_HEAD *head2, TYPE);
LIST_CLASS_ENTRY(CLASSTYPE);
LIST_CLASS_HEAD(HEADNAME, CLASSTYPE);
LIST_CONCAT(LIST_HEAD *head1, LIST_HEAD *head2, TYPE, LIST_ENTRY NAME);
LIST_EMPTY(LIST_HEAD *head);
LIST_ENTRY(TYPE);
LIST_FIRST(LIST_HEAD *head);
LIST_FOREACH(TYPE *var, LIST_HEAD *head, LIST_ENTRY NAME);
LIST_FOREACH_FROM(TYPE *var, LIST_HEAD *head, LIST_ENTRY NAME);
LIST_FOREACH_FROM_SAFE(TYPE *var, LIST_HEAD *head, LIST_ENTRY NAME,
TYPE *temp_var);
LIST_FOREACH_SAFE(TYPE *var, LIST_HEAD *head, LIST_ENTRY NAME,
TYPE *temp_var);
LIST_HEAD(HEADNAME, TYPE);
LIST_HEAD_INITIALIZER(LIST_HEAD head);
LIST_INIT(LIST_HEAD *head);
LIST_INSERT_AFTER(TYPE *listelm, TYPE *elm, LIST_ENTRY NAME);
LIST_INSERT_BEFORE(TYPE *listelm, TYPE *elm, LIST_ENTRY NAME);
LIST_INSERT_HEAD(LIST_HEAD *head, TYPE *elm, LIST_ENTRY NAME);
LIST_NEXT(TYPE *elm, LIST_ENTRY NAME);
LIST_PREV(TYPE *elm, LIST_HEAD *head, TYPE, LIST_ENTRY NAME);
LIST_REMOVE(TYPE *elm, LIST_ENTRY NAME);
LIST_SWAP(LIST_HEAD *head1, LIST_HEAD *head2, TYPE, LIST_ENTRY NAME);
TAILQ_CLASS_ENTRY(CLASSTYPE);
TAILQ_CLASS_HEAD(HEADNAME, CLASSTYPE);
TAILQ_CONCAT(TAILQ_HEAD *head1, TAILQ_HEAD *head2, TAILQ_ENTRY NAME);
TAILQ_EMPTY(TAILQ_HEAD *head);
TAILQ_ENTRY(TYPE);
TAILQ_FIRST(TAILQ_HEAD *head);
TAILQ_FOREACH(TYPE *var, TAILQ_HEAD *head, TAILQ_ENTRY NAME);
TAILQ_FOREACH_FROM(TYPE *var, TAILQ_HEAD *head, TAILQ_ENTRY NAME);
TAILQ_FOREACH_FROM_SAFE(TYPE *var, TAILQ_HEAD *head, TAILQ_ENTRY NAME,
TYPE *temp_var);
TAILQ_FOREACH_REVERSE(TYPE *var, TAILQ_HEAD *head, HEADNAME,
TAILQ_ENTRY NAME);
TAILQ_FOREACH_REVERSE_FROM(TYPE *var, TAILQ_HEAD *head, HEADNAME,
TAILQ_ENTRY NAME);
TAILQ_FOREACH_REVERSE_FROM_SAFE(TYPE *var, TAILQ_HEAD *head, HEADNAME,
TAILQ_ENTRY NAME, TYPE *temp_var);
TAILQ_FOREACH_REVERSE_SAFE(TYPE *var, TAILQ_HEAD *head, HEADNAME,
TAILQ_ENTRY NAME, TYPE *temp_var);
TAILQ_FOREACH_SAFE(TYPE *var, TAILQ_HEAD *head, TAILQ_ENTRY NAME,
TYPE *temp_var);
TAILQ_HEAD(HEADNAME, TYPE);
TAILQ_HEAD_INITIALIZER(TAILQ_HEAD head);
TAILQ_INIT(TAILQ_HEAD *head);
TAILQ_INSERT_AFTER(TAILQ_HEAD *head, TYPE *listelm, TYPE *elm,
TAILQ_ENTRY NAME);
TAILQ_INSERT_BEFORE(TYPE *listelm, TYPE *elm, TAILQ_ENTRY NAME);
TAILQ_INSERT_HEAD(TAILQ_HEAD *head, TYPE *elm, TAILQ_ENTRY NAME);
TAILQ_INSERT_TAIL(TAILQ_HEAD *head, TYPE *elm, TAILQ_ENTRY NAME);
TAILQ_LAST(TAILQ_HEAD *head, HEADNAME);
TAILQ_NEXT(TYPE *elm, TAILQ_ENTRY NAME);
TAILQ_PREV(TYPE *elm, HEADNAME, TAILQ_ENTRY NAME);
TAILQ_REMOVE(TAILQ_HEAD *head, TYPE *elm, TAILQ_ENTRY NAME);
TAILQ_SWAP(TAILQ_HEAD *head1, TAILQ_HEAD *head2, TYPE,
TAILQ_ENTRY NAME);
π DESCRIPTION π
So these macros define and operate on four types of data structures, and the best part? They work in both C and C++ source code. Versatile icon behavior. π
- Lists
- Singly-linked lists
- Singly-linked tail queues
- Tail queues
The shared starter pack
All four structures support this core functionality:
- Insertion of a new entry at the head of the list.
- Insertion of a new entry after any element in the list.
O(1)removal of an entry from the head of the list.- Forward traversal through the list.
- Swapping the contents of two lists.
Singly-linked lists (SLIST) π
Singly-linked lists are the simplest of the four, lowkey the baby of the group β they support only that core functionality above and nothing more. They're ideal for applications with large datasets and few or no removals, or for implementing a LIFO queue. And they add this:
O(n)removal of any entry in the list.O(n)concatenation of two lists.
Singly-linked tail queues (STAILQ) β
Singly-linked tail queues add the following functionality:
- Entries can be added at the end of a list.
O(n)removal of any entry in the list.- They may be concatenated.
However β listen bestie, the trade-offs are real:
- All list insertions must specify the head of the list.
- Each head entry requires two pointers rather than one.
- Code size is about 15% greater and operations run about 20% slower than singly-linked lists.
Singly-linked tail queues are ideal for applications with large datasets and few or no removals, or for implementing a FIFO queue.
The doubly linked girlies π
All doubly linked types of data structures (lists and tail queues) additionally allow:
- Insertion of a new entry before any element in the list.
O(1)removal of any entry in the list.
However:
- Each element requires two pointers rather than one.
- Code size and execution time of operations (except for removal) is about twice that of the singly-linked data-structures.
Lists (LIST) π§
Linked lists are the simplest of the doubly linked data structures. They add the following functionality over the above:
O(n)concatenation of two lists.- They may be traversed backwards.
However:
- To traverse backwards, an entry to begin the traversal and the list in which it is contained must be specified.
Tail queues (TAILQ) π
Tail queues add the following functionality:
- Entries can be added at the end of a list.
- They may be traversed backwards, from tail to head.
- They may be concatenated.
However:
- All list insertions and removals must specify the head of the list.
- Each head entry requires two pointers rather than one.
- Code size is about 15% greater and operations run about 20% slower than singly-linked lists.
How the macro arguments actually work π₯
In the macro definitions, TYPE is the name of a user defined structure. The structure must contain a field called NAME which is of type SLIST_ENTRY, STAILQ_ENTRY, LIST_ENTRY, or TAILQ_ENTRY.
In the macro definitions, CLASSTYPE is the name of a user defined class. The class must contain a field called NAME which is of type SLIST_CLASS_ENTRY, STAILQ_CLASS_ENTRY, LIST_CLASS_ENTRY, or TAILQ_CLASS_ENTRY.
The argument HEADNAME is the name of a user defined structure that must be declared using the macros SLIST_HEAD, SLIST_CLASS_HEAD, STAILQ_HEAD, STAILQ_CLASS_HEAD, LIST_HEAD, LIST_CLASS_HEAD, TAILQ_HEAD, or TAILQ_CLASS_HEAD. See the examples below for further explanation of how these macros are used.
fr fr, read the examples when we get there β they make the wholeHEADNAME/TYPE/NAMEsituation click. π
π SINGLY-LINKED LISTS π
okay bestie so a singly-linked list is headed by a structure that the SLIST_HEAD macro defines for you. This structure contains a single pointer to the first element on the list. The elements are singly linked for minimum space and pointer manipulation overhead, at the expense of O(n) removal for arbitrary elements. New elements can be added to the list after an existing element or at the head of the list. An SLIST_HEAD structure is declared like this:
SLIST_HEAD(HEADNAME, TYPE) head;
where HEADNAME is the name of the structure to be defined, and TYPE is the type of the elements to be linked into the list. A pointer to the head of the list can later be declared as:
struct HEADNAME *headp;
(The names head and headp are user selectable, so name them whatever your heart desires π
.)
SLIST_HEAD_INITIALIZERβ evaluates to an initializer for the list head.SLIST_CONCATβ concatenates the list headed byhead2onto the end of the one headed byhead1, removing all entries from the former. > bestie pls avoid this one β it traverses the entirety of thehead1list. A singly-linked tail queue should be used if this macro is needed in high-usage code paths or to operate on long lists. iykyk.SLIST_EMPTYβ evaluates to true if there are no elements in the list.SLIST_ENTRYβ declares a structure that connects the elements in the list.SLIST_FIRSTβ returns the first element in the list, orNULLif the list is empty.SLIST_FOREACHβ traverses the list referenced by head in the forward direction, assigning each element in turn to var.SLIST_FOREACH_FROMβ behaves identically toSLIST_FOREACHwhen var isNULL, else it treats var as a previously found SLIST element and begins the loop at var instead of the first element in the SLIST referenced by head.SLIST_FOREACH_SAFEβ traverses the list referenced by head in the forward direction, assigning each element in turn to var. However, unlikeSLIST_FOREACH()here it is permitted to both remove var as well as free it from within the loop safely without interfering with the traversal. iconic behaviour honestly π«ΆSLIST_FOREACH_FROM_SAFEβ behaves identically toSLIST_FOREACH_SAFEwhen var isNULL, else it treats var as a previously found SLIST element and begins the loop at var instead of the first element in the SLIST referenced by head.SLIST_INITβ initializes the list referenced by head.SLIST_INSERT_HEADβ inserts the new element elm at the head of the list.SLIST_INSERT_AFTERβ inserts the new element elm after the element listelm.SLIST_NEXTβ returns the next element in the list.SLIST_REMOVE_AFTERβ removes the element after elm from the list. UnlikeSLIST_REMOVE, this macro does not traverse the entire list. slay.SLIST_REMOVE_HEADβ removes the element elm from the head of the list. For optimum efficiency, elements being removed from the head of the list should explicitly use this macro instead of the genericSLIST_REMOVEmacro.SLIST_REMOVEβ removes the element elm from the list. > Use of this macro should be avoided as it traverses the entire list. A doubly-linked list should be used if this macro is needed in high-usage code paths or to operate on long lists. no cap, it's givingO(n)πSLIST_SWAPβ swaps the contents ofhead1andhead2.
β¨ SINGLY-LINKED LIST EXAMPLE β¨
SLIST_HEAD(slisthead, entry) head =
SLIST_HEAD_INITIALIZER(head);
struct slisthead *headp; /* Singly-linked List head. */
struct entry {
...
SLIST_ENTRY(entry) entries; /* Singly-linked List. */
...
} *n1, *n2, *n3, *np;
SLIST_INIT(&head); /* Initialize the list. */
n1 = malloc(sizeof(struct entry)); /* Insert at the head. */
SLIST_INSERT_HEAD(&head, n1, entries);
n2 = malloc(sizeof(struct entry)); /* Insert after. */
SLIST_INSERT_AFTER(n1, n2, entries);
SLIST_REMOVE(&head, n2, entry, entries);/* Deletion. */
free(n2);
n3 = SLIST_FIRST(&head);
SLIST_REMOVE_HEAD(&head, entries); /* Deletion from the head. */
free(n3);
/* Forward traversal. */
SLIST_FOREACH(np, &head, entries)
np-> ...
/* Safe forward traversal. */
SLIST_FOREACH_SAFE(np, &head, entries, np_temp) {
np->do_stuff();
...
SLIST_REMOVE(&head, np, entry, entries);
free(np);
}
while (!SLIST_EMPTY(&head)) { /* List Deletion. */
n1 = SLIST_FIRST(&head);
SLIST_REMOVE_HEAD(&head, entries);
free(n1);
}
π¦ SINGLY-LINKED TAIL QUEUES π¦
A singly-linked tail queue is headed by a structure defined by the STAILQ_HEAD macro. This structure contains a pair of pointers, one to the first element in the tail queue and the other to the last element in the tail queue. The elements are singly linked for minimum space and pointer manipulation overhead, at the expense of O(n) removal for arbitrary elements. New elements can be added to the tail queue after an existing element, at the head of the tail queue, or at the end of the tail queue. A STAILQ_HEAD structure is declared as follows:
STAILQ_HEAD(HEADNAME, TYPE) head;
where HEADNAME is the name of the structure to be defined, and TYPE is the type of the elements to be linked into the tail queue. A pointer to the head of the tail queue can later be declared as:
struct HEADNAME *headp;
(The names head and headp are user selectable.)
STAILQ_HEAD_INITIALIZERβ evaluates to an initializer for the tail queue head.STAILQ_CONCATβ concatenates the tail queue headed byhead2onto the end of the one headed byhead1, removing all entries from the former. (and this one is actually fine, no traversal pain, we love to see it β¨)STAILQ_EMPTYβ evaluates to true if there are no items on the tail queue.STAILQ_ENTRYβ declares a structure that connects the elements in the tail queue.STAILQ_FIRSTβ returns the first item on the tail queue, orNULLif the tail queue is empty.STAILQ_FOREACHβ traverses the tail queue referenced by head in the forward direction, assigning each element in turn to var.STAILQ_FOREACH_FROMβ behaves identically toSTAILQ_FOREACHwhen var isNULL, else it treats var as a previously found STAILQ element and begins the loop at var instead of the first element in the STAILQ referenced by head.STAILQ_FOREACH_SAFEβ traverses the tail queue referenced by head in the forward direction, assigning each element in turn to var. However, unlikeSTAILQ_FOREACH()here it is permitted to both remove var as well as free it from within the loop safely without interfering with the traversal.STAILQ_FOREACH_FROM_SAFEβ behaves identically toSTAILQ_FOREACH_SAFEwhen var isNULL, else it treats var as a previously found STAILQ element and begins the loop at var instead of the first element in the STAILQ referenced by head.STAILQ_INITβ initializes the tail queue referenced by head.STAILQ_INSERT_HEADβ inserts the new element elm at the head of the tail queue.STAILQ_INSERT_TAILβ inserts the new element elm at the end of the tail queue. the moment of all moments πSTAILQ_INSERT_AFTERβ inserts the new element elm after the element listelm.STAILQ_LASTβ returns the last item on the tail queue. If the tail queue is empty the return value isNULL.STAILQ_NEXTβ returns the next item on the tail queue, orNULLthis item is the last.STAILQ_REMOVE_AFTERβ removes the element after elm from the tail queue. UnlikeSTAILQ_REMOVE, this macro does not traverse the entire tail queue.STAILQ_REMOVE_HEADβ removes the element at the head of the tail queue. For optimum efficiency, elements being removed from the head of the tail queue should use this macro explicitly rather than the genericSTAILQ_REMOVEmacro.STAILQ_REMOVEβ removes the element elm from the tail queue. > Use of this macro should be avoided as it traverses the entire list. A doubly-linked tail queue should be used if this macro is needed in high-usage code paths or to operate on long tail queues. literally walking the whole thing, not the vibe for hot paths π₯²STAILQ_SWAPβ swaps the contents ofhead1andhead2.
π SINGLY-LINKED TAIL QUEUE EXAMPLE π
STAILQ_HEAD(stailhead, entry) head =
STAILQ_HEAD_INITIALIZER(head);
struct stailhead *headp; /* Singly-linked tail queue head. */
struct entry {
...
STAILQ_ENTRY(entry) entries; /* Tail queue. */
...
} *n1, *n2, *n3, *np;
STAILQ_INIT(&head); /* Initialize the queue. */
n1 = malloc(sizeof(struct entry)); /* Insert at the head. */
STAILQ_INSERT_HEAD(&head, n1, entries);
n1 = malloc(sizeof(struct entry)); /* Insert at the tail. */
STAILQ_INSERT_TAIL(&head, n1, entries);
n2 = malloc(sizeof(struct entry)); /* Insert after. */
STAILQ_INSERT_AFTER(&head, n1, n2, entries);
/* Deletion. */
STAILQ_REMOVE(&head, n2, entry, entries);
free(n2);
/* Deletion from the head. */
n3 = STAILQ_FIRST(&head);
STAILQ_REMOVE_HEAD(&head, entries);
free(n3);
/* Forward traversal. */
STAILQ_FOREACH(np, &head, entries)
np-> ...
/* Safe forward traversal. */
STAILQ_FOREACH_SAFE(np, &head, entries, np_temp) {
np->do_stuff();
...
STAILQ_REMOVE(&head, np, entry, entries);
free(np);
}
/* TailQ Deletion. */
while (!STAILQ_EMPTY(&head)) {
n1 = STAILQ_FIRST(&head);
STAILQ_REMOVE_HEAD(&head, entries);
free(n1);
}
/* Faster TailQ Deletion. */
n1 = STAILQ_FIRST(&head);
while (n1 != NULL) {
n2 = STAILQ_NEXT(n1, entries);
free(n1);
n1 = n2;
}
STAILQ_INIT(&head);
look at that last one bestie β walk the links, free as you go, then STAILQ_INIT it back to life. main character energy fr fr π
π LISTS π
okay bestie, a list is headed by a structure that the LIST_HEAD macro defines for you. this head structure has literally one pointer β to the first element on the list. the elements themselves are doubly linked, which is the slay part: you can yank out any element you want without walking the whole list first. you can add new elements after an existing element, before an existing element, or right at the head. iconic.
you declare a LIST_HEAD structure like this:
LIST_HEAD(HEADNAME, TYPE) head;
where HEADNAME is the name of the structure being defined (you pick it, queen), and TYPE is the type of the elements getting linked in. later you can declare a pointer to the head of the list like:
struct HEADNAME *headp;
(the names head and headp are user selectable, so name them whatever your heart desires π«Ά)
LIST_HEAD_INITIALIZERβ evaluates to an initializer for the list head. chef's kiss, static init energy β¨LIST_CONCATβ concatenates the list headed byhead2onto the end of the one headed byhead1, removing all entries from the former. β οΈ but bestie, avoid this macro β it traverses the entirety of thehead1list. if you need this in high-usage code paths, or you're operating on long lists, use a tail queue instead. that's the move. no cap.LIST_EMPTYβ evaluates to true if there are no elements in the list. empty list vibe check πLIST_ENTRYβ declares a structure that connects the elements in the list. the little link-up that makes it all work πLIST_FIRSTβ returns the first element in the list, orNULLif the list is empty.LIST_FOREACHβ traverses the list referenced byheadin the forward direction, assigning each element in turn tovar. going through it front to back, periodt.LIST_FOREACH_FROMβ behaves identically toLIST_FOREACHwhenvarisNULL; otherwise it treatsvaras a previously foundLISTelement and begins the loop atvarinstead of the first element in theLISTreferenced byhead. starting mid-way, main character energy πLIST_FOREACH_SAFEβ traverses the list referenced byheadin the forward direction, assigning each element in turn tovar. however, unlikeLIST_FOREACH(), here it is permitted to both removevaras well as free it from within the loop safely without interfering with the traversal. the safe version ate and left no crumbs π₯LIST_FOREACH_FROM_SAFEβ behaves identically toLIST_FOREACH_SAFEwhenvarisNULL; otherwise it treatsvaras a previously foundLISTelement and begins the loop atvarinstead of the first element in theLISTreferenced byhead.LIST_INITβ initializes the list referenced byhead. fresh start, clean slate π§LIST_INSERT_HEADβ inserts the new elementelmat the head of the list.LIST_INSERT_AFTERβ inserts the new elementelmafter the elementlistelm.LIST_INSERT_BEFOREβ inserts the new elementelmbefore the elementlistelm.LIST_NEXTβ returns the next element in the list, orNULLif this is the last.LIST_PREVβ returns the previous element in the list, orNULLif this is the first. note: the list head must contain elementelm. don't get cute.LIST_REMOVEβ removes the elementelmfrom the list. bye bestie πLIST_SWAPβ swaps the contents ofhead1andhead2. trading places, iconic.
π LIST EXAMPLE π
here's the whole thing in action, bestie β full code, verbatim, go off:
LIST_HEAD(listhead, entry) head =
LIST_HEAD_INITIALIZER(head);
struct listhead *headp; /* List head. */
struct entry {
...
LIST_ENTRY(entry) entries; /* List. */
...
} *n1, *n2, *n3, *np, *np_temp;
LIST_INIT(&head); /* Initialize the list. */
n1 = malloc(sizeof(struct entry)); /* Insert at the head. */
LIST_INSERT_HEAD(&head, n1, entries);
n2 = malloc(sizeof(struct entry)); /* Insert after. */
LIST_INSERT_AFTER(n1, n2, entries);
n3 = malloc(sizeof(struct entry)); /* Insert before. */
LIST_INSERT_BEFORE(n2, n3, entries);
LIST_REMOVE(n2, entries); /* Deletion. */
free(n2);
/* Forward traversal. */
LIST_FOREACH(np, &head, entries)
np-> ...
/* Safe forward traversal. */
LIST_FOREACH_SAFE(np, &head, entries, np_temp) {
np->do_stuff();
...
LIST_REMOVE(np, entries);
free(np);
}
while (!LIST_EMPTY(&head)) { /* List Deletion. */
n1 = LIST_FIRST(&head);
LIST_REMOVE(n1, entries);
free(n1);
}
n1 = LIST_FIRST(&head); /* Faster List Deletion. */
while (n1 != NULL) {
n2 = LIST_NEXT(n1, entries);
free(n1);
n1 = n2;
}
LIST_INIT(&head);
π¦ TAIL QUEUES π¦
a tail queue is headed by a structure defined by the TAILQ_HEAD macro. this head structure holds a pair of pointers β one to the first element, one to the last element. yes bestie, it knows both ends. the elements are doubly linked so an arbitrary element can be removed without traversing the tail queue. new elements can be added after an existing element, before an existing element, at the head of the tail queue, or at the end of the tail queue. four whole options, we love flexibility π
you declare a TAILQ_HEAD structure like:
TAILQ_HEAD(HEADNAME, TYPE) head;
where HEADNAME is the name of the structure to be defined, and TYPE is the type of the elements to be linked into the tail queue. a pointer to the head of the tail queue can later be declared as:
struct HEADNAME *headp;
(the names head and headp are user selectable, as always π)
TAILQ_HEAD_INITIALIZERβ evaluates to an initializer for the tail queue head.TAILQ_CONCATβ concatenates the tail queue headed byhead2onto the end of the one headed byhead1, removing all entries from the former. (no scary warning here, unlike its LIST cousin β this one's chill β¨)TAILQ_EMPTYβ evaluates to true if there are no items on the tail queue.TAILQ_ENTRYβ declares a structure that connects the elements in the tail queue.TAILQ_FIRSTβ returns the first item on the tail queue, orNULLif the tail queue is empty.TAILQ_FOREACHβ traverses the tail queue referenced byheadin the forward direction, assigning each element in turn tovar. heads up:varis set toNULLif the loop completes normally, or if there were no elements. sneaky but useful.TAILQ_FOREACH_FROMβ behaves identically toTAILQ_FOREACHwhenvarisNULL; otherwise it treatsvaras a previously foundTAILQelement and begins the loop atvarinstead of the first element in theTAILQreferenced byhead.TAILQ_FOREACH_REVERSEβ traverses the tail queue referenced byheadin the reverse direction, assigning each element in turn tovar. going backwards, we love a plot twist πTAILQ_FOREACH_REVERSE_FROMβ behaves identically toTAILQ_FOREACH_REVERSEwhenvarisNULL; otherwise it treatsvaras a previously foundTAILQelement and begins the reverse loop atvarinstead of the last element in theTAILQreferenced byhead.TAILQ_FOREACH_SAFEandTAILQ_FOREACH_REVERSE_SAFEβ traverse the list referenced byheadin the forward or reverse direction respectively, assigning each element in turn tovar. however, unlike their unsafe counterparts,TAILQ_FOREACHandTAILQ_FOREACH_REVERSEmake it possible to both removevaras well as free it from within the loop safely without interfering with the traversal. (yes, that's what the page says β the safe ones are the ones that let you do the removing-and-freeing π )TAILQ_FOREACH_FROM_SAFEβ behaves identically toTAILQ_FOREACH_SAFEwhenvarisNULL; otherwise it treatsvaras a previously foundTAILQelement and begins the loop atvarinstead of the first element in theTAILQreferenced byhead.TAILQ_FOREACH_REVERSE_FROM_SAFEβ behaves identically toTAILQ_FOREACH_REVERSE_SAFEwhenvarisNULL; otherwise it treatsvaras a previously foundTAILQelement and begins the reverse loop atvarinstead of the last element in theTAILQreferenced byhead.TAILQ_INITβ initializes the tail queue referenced byhead.TAILQ_INSERT_HEADβ inserts the new elementelmat the head of the tail queue.TAILQ_INSERT_TAILβ inserts the new elementelmat the end of the tail queue. the whole point of a tail queue, honestly.TAILQ_INSERT_AFTERβ inserts the new elementelmafter the elementlistelm.TAILQ_INSERT_BEFOREβ inserts the new elementelmbefore the elementlistelm.TAILQ_LASTβ returns the last item on the tail queue. if the tail queue is empty, the return value isNULL.TAILQ_NEXTβ returns the next item on the tail queue, orNULLif this item is the last.TAILQ_PREVβ returns the previous item on the tail queue, orNULLif this item is the first.TAILQ_REMOVEβ removes the elementelmfrom the tail queue.TAILQ_SWAPβ swaps the contents ofhead1andhead2.
πΈ TAIL QUEUE EXAMPLE πΈ
and here's the whole tail queue glow-up in code, verbatim, enjoy:
TAILQ_HEAD(tailhead, entry) head =
TAILQ_HEAD_INITIALIZER(head);
struct tailhead *headp; /* Tail queue head. */
struct entry {
...
TAILQ_ENTRY(entry) entries; /* Tail queue. */
...
} *n1, *n2, *n3, *np;
TAILQ_INIT(&head); /* Initialize the queue. */
n1 = malloc(sizeof(struct entry)); /* Insert at the head. */
TAILQ_INSERT_HEAD(&head, n1, entries);
n1 = malloc(sizeof(struct entry)); /* Insert at the tail. */
TAILQ_INSERT_TAIL(&head, n1, entries);
n2 = malloc(sizeof(struct entry)); /* Insert after. */
TAILQ_INSERT_AFTER(&head, n1, n2, entries);
n3 = malloc(sizeof(struct entry)); /* Insert before. */
TAILQ_INSERT_BEFORE(n2, n3, entries);
TAILQ_REMOVE(&head, n2, entries); /* Deletion. */
free(n2);
/* Forward traversal. */
TAILQ_FOREACH(np, &head, entries)
np-> ...
/* Safe forward traversal. */
TAILQ_FOREACH_SAFE(np, &head, entries, np_temp) {
np->do_stuff();
...
TAILQ_REMOVE(&head, np, entries);
free(np);
}
/* Reverse traversal. */
TAILQ_FOREACH_REVERSE(np, &head, tailhead, entries)
np-> ...
/* TailQ Deletion. */
while (!TAILQ_EMPTY(&head)) {
n1 = TAILQ_FIRST(&head);
TAILQ_REMOVE(&head, n1, entries);
free(n1);
}
/* Faster TailQ Deletion. */
n1 = TAILQ_FIRST(&head);
while (n1 != NULL) {
n2 = TAILQ_NEXT(n1, entries);
free(n1);
n1 = n2;
}
TAILQ_INIT(&head);
π©Ί DIAGNOSTICS π©Ί
when you're debugging queue(3), it can be useful to trace queue changes. to enable tracing, define the macro QUEUE_MACRO_DEBUG_TRACE at compile time. now you can see everything happening, and honestly? it's giving detective π
it can also be useful to trash pointers that have been unlinked from a queue, so you can detect use after removal. to enable pointer trashing, define the macro QUEUE_MACRO_DEBUG_TRASH at compile time. the macro QMD_IS_TRASHED(void *ptr) returns true if ptr has been trashed by the QUEUE_MACRO_DEBUG_TRASH option. use-after-free who? never heard of her πβ¨
π SEE ALSO π
β¨ HISTORY β¨
okay so storytime bestie πβ¨ the queue functions are not new. they did not just drop. they first appeared in 4.4BSD β like, ancient-history iconic ππ¦ β and they've been serving cunt in C codebases ever since. no cap, they've been carrying linked-list girlies for decades π
so yeah, that's the whole lore. your faves are old, they're proven, they're literally in the bloodline of Unix π©Έπ
and that's a wrap on queue(3bsd) π you made it to the end, bestie, i'm so proud of you fr fr π«Ά this lil masterpiece is a Debian joint, dated September 8, 2016 β go forth and link some lists, you absolute legend ππΈ
π¦ where this page comes from
- alpine 3.22: libbsd-doc 0.12.2-r0 Β· BSD-3-Clause
- alpine 3.23: libbsd-doc 0.12.2-r0 Β· BSD-3-Clause
- alpine 3.24: libbsd-doc 0.12.2-r0 Β· BSD-3-Clause
- arch rolling: libbsd 0.12.2-2 Β· custom
- debian bookworm: libbsd-dev 0.11.7-2
- debian trixie: libbsd-dev 0.12.2-2
- epel 10: libbsd-devel 0.12.2-4.el10_0 Β· Beerware AND BSD-2-Clause AND BSD-3-Clause AND ISC AND libutil-David-Nugent AND MIT AND LicenseRef-Fedora-Public-Domain
- epel 9: libbsd-devel 0.12.2-1.el9 Β· Beerware AND BSD-2-Clause AND BSD-3-Clause AND ISC AND libutil-David-Nugent AND MIT AND LicenseRef-Fedora-Public-Domain
- fedora 43: libbsd-devel 0.12.2-6.fc43 Β· Beerware AND BSD-2-Clause AND BSD-3-Clause AND ISC AND libutil-David-Nugent AND MIT AND LicenseRef-Fedora-Public-Domain
- fedora 43: libbsd-devel 0.12.2-6.fc43 Β· Beerware AND BSD-2-Clause AND BSD-3-Clause AND ISC AND libutil-David-Nugent AND MIT AND LicenseRef-Fedora-Public-Domain
- fedora 44: libbsd-devel 0.12.2-7.fc44 Β· Beerware AND BSD-2-Clause AND BSD-3-Clause AND ISC AND libutil-David-Nugent AND MIT AND LicenseRef-Fedora-Public-Domain
- fedora 44: libbsd-devel 0.12.2-7.fc44 Β· Beerware AND BSD-2-Clause AND BSD-3-Clause AND ISC AND libutil-David-Nugent AND MIT AND LicenseRef-Fedora-Public-Domain
- nixos 26.05: libbsd 0.12.2
- nixpkgs unstable: libbsd 0.12.2
- opensuse-leap 16.0: libbsd-devel 0.11.7-160000.3.2 Β· BSD-3-Clause
- opensuse-leap 16.0: libbsd-devel 0.11.7-160000.3.2 Β· BSD-3-Clause
- opensuse-leap 16.0: libbsd-devel 0.11.7-160000.3.2 Β· BSD-3-Clause
- opensuse-leap 16.0: libbsd-devel 0.11.7-160000.3.2 Β· BSD-3-Clause
- ubuntu noble: libbsd-dev 0.12.1-1build1
- ubuntu noble: libbsd-dev 0.12.1-1build1.1
- ubuntu questing: libbsd-dev 0.12.2-2build1
- ubuntu resolute: libbsd-dev 0.12.2-2build2
- ubuntu stonking: libbsd-dev 0.12.2-3
- Copyright (c) 1993 The Regents of the University of California. All rights reserved
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms