From d40b8ce1fb5abea8268ffb4bfe4a6e29c110bd42 Mon Sep 17 00:00:00 2001 From: "Peter A. Bigot" Date: Sun, 30 Dec 2018 05:46:41 -0600 Subject: [PATCH] sys: dlist: Add sys_dnode_is_linked The original implementation allows a list to be corrupted by list operations on the removed node. Existing code attempts to avoid this by using external state to determine whether a node is in a list, but this is fragile and fails when the state that holds the flag value is changed after the node is removed, e.g. in preparation for re-using the node. Follow Linux in invalidating the link pointers in a removed node. Add API so that detection of particpation in a list is available at the node abstraction. This solution relies on the following steady-state invariants: * A node (as opposed to a list) will never be adjacent to itself in a list; * The next and prev pointers of a node are always either both null or both non-null. Signed-off-by: Peter A. Bigot --- include/misc/dlist.h | 30 +++++++++++++++++++++++++++++- tests/kernel/common/src/dlist.c | 4 ++++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/include/misc/dlist.h b/include/misc/dlist.h index 937a55c28f1..ba0795233ed 100644 --- a/include/misc/dlist.h +++ b/include/misc/dlist.h @@ -181,7 +181,7 @@ typedef struct _dnode sys_dnode_t; __cns = SYS_DLIST_PEEK_NEXT_CONTAINER(__dl, __cn, __n)) /** - * @brief initialize list + * @brief initialize list to its empty state * * @param list the doubly-linked list * @@ -196,6 +196,33 @@ static inline void sys_dlist_init(sys_dlist_t *list) #define SYS_DLIST_STATIC_INIT(ptr_to_list) { {(ptr_to_list)}, {(ptr_to_list)} } +/** + * @brief initialize node to its state when not in a list + * + * @param node the node + * + * @return N/A + */ + +static inline void sys_dnode_init(sys_dnode_t *node) +{ + node->next = NULL; + node->prev = NULL; +} + +/** + * @brief check if a node is a member of any list + * + * @param node the node + * + * @return true if node is linked into a list, false if it is not + */ + +static inline bool sys_dnode_is_linked(const sys_dnode_t *node) +{ + return node->next != NULL; +} + /** * @brief check if a node is the list's head * @@ -500,6 +527,7 @@ static inline void sys_dlist_remove(sys_dnode_t *node) { node->prev->next = node->next; node->next->prev = node->prev; + sys_dnode_init(node); } /** diff --git a/tests/kernel/common/src/dlist.c b/tests/kernel/common/src/dlist.c index 686f4396833..95ba0eaa98c 100644 --- a/tests/kernel/common/src/dlist.c +++ b/tests/kernel/common/src/dlist.c @@ -189,9 +189,13 @@ void test_dlist(void) "test_list head/tail are wrong"); /* Finding and removing node 1 */ + zassert_true(sys_dnode_is_linked(&test_node_1.node), + "node1 is not linked"); sys_dlist_remove(&test_node_1.node); zassert_true((verify_emptyness(&test_list)), "test_list should be empty"); + zassert_false(sys_dnode_is_linked(&test_node_1.node), + "node1 is still linked"); /* Prepending node 1 */ sys_dlist_prepend(&test_list, &test_node_1.node);