Deletion from a specific list.
#ifndef CCAN_LIST_H
#define CCAN_LIST_H
#include <stdbool.h>
#ifndef CCAN_LIST_H
#define CCAN_LIST_H
#include <stdbool.h>
#include <ccan/container_of/container_of.h>
/**
#include <ccan/container_of/container_of.h>
/**
- * list_del - delete an entry from a linked list.
+ * list_empty - is a list empty?
+ * @h: the list_head
+ *
+ * If the list is empty, returns true.
+ *
+ * Example:
+ * assert(list_empty(&parent->children) == (parent->num_children == 0));
+ */
+static inline bool list_empty(const struct list_head *h)
+{
+ (void)list_debug(h);
+ return h->n.next == &h->n;
+}
+
+/**
+ * list_del - delete an entry from an (unknown) linked list.
* @n: the list_node to delete from the list.
*
* @n: the list_node to delete from the list.
*
+ * Note that this leaves @n in an undefined state; it can be added to
+ * another list, but not deleted again.
+ *
+ * See also:
+ * list_del_from()
+ *
* Example:
* list_del(&child->list);
* parent->num_children--;
* Example:
* list_del(&child->list);
* parent->num_children--;
- * list_empty - is a list empty?
- * @h: the list_head
+ * list_del_from - delete an entry from a known linked list.
+ * @h: the list_head the node is in.
+ * @n: the list_node to delete from the list.
- * If the list is empty, returns true.
+ * This explicitly indicates which list a node is expected to be in,
+ * which is better documentation and can catch more bugs.
+ *
+ * See also: list_del()
- * assert(list_empty(&parent->children) == (parent->num_children == 0));
+ * list_del_from(&parent->children, &child->list);
+ * parent->num_children--;
-static inline bool list_empty(const struct list_head *h)
+static inline void list_del_from(struct list_head *h, struct list_node *n)
- (void)list_debug(h);
- return h->n.next == &h->n;
+#ifdef CCAN_LIST_DEBUG
+ {
+ /* Thorough check: make sure it was in list! */
+ struct list_node *i;
+ for (i = h->n.next; i != n; i = i->next)
+ assert(i != &h->n);
+ }
+#endif /* CCAN_LIST_DEBUG */
+
+ /* Quick test that catches a surprising number of bugs. */
+ assert(!list_empty(h));
+ list_del(n);
- /* Test list_for_each_safe and list_del. */
+ /* Test list_for_each_safe, list_del and list_del_from. */
i = 0;
list_for_each_safe(&parent.children, c, n, list) {
switch (i++) {
case 0:
i = 0;
list_for_each_safe(&parent.children, c, n, list) {
switch (i++) {
case 0:
+ ok1(c == &c1);
+ list_del(&c->list);
break;
case 1:
ok1(c == &c2);
break;
case 1:
ok1(c == &c2);
+ list_del_from(&parent.children, &c->list);
break;
case 2:
ok1(c == &c3);
break;
case 2:
ok1(c == &c3);
+ list_del_from(&parent.children, &c->list);
ok1(list_check(&parent.children, NULL));
if (i > 2)
break;
ok1(list_check(&parent.children, NULL));
if (i > 2)
break;
- /* Test list_for_each_safe and list_del. */
+ /* Test list_for_each_safe, list_del and list_del_from. */
i = 0;
list_for_each_safe(&parent.children, c, n, list) {
switch (i++) {
case 0:
i = 0;
list_for_each_safe(&parent.children, c, n, list) {
switch (i++) {
case 0:
+ ok1(c == &c1);
+ list_del(&c->list);
break;
case 1:
ok1(c == &c2);
break;
case 1:
ok1(c == &c2);
+ list_del_from(&parent.children, &c->list);
break;
case 2:
ok1(c == &c3);
break;
case 2:
ok1(c == &c3);
+ list_del_from(&parent.children, &c->list);
ok1(list_check(&parent.children, NULL));
if (i > 2)
break;
ok1(list_check(&parent.children, NULL));
if (i > 2)
break;