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 <pab@pabigot.com>
This commit is contained in:
parent
692e1033e7
commit
d40b8ce1fb
2 changed files with 33 additions and 1 deletions
|
@ -181,7 +181,7 @@ typedef struct _dnode sys_dnode_t;
|
||||||
__cns = SYS_DLIST_PEEK_NEXT_CONTAINER(__dl, __cn, __n))
|
__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
|
* @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)} }
|
#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
|
* @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->prev->next = node->next;
|
||||||
node->next->prev = node->prev;
|
node->next->prev = node->prev;
|
||||||
|
sys_dnode_init(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -189,9 +189,13 @@ void test_dlist(void)
|
||||||
"test_list head/tail are wrong");
|
"test_list head/tail are wrong");
|
||||||
|
|
||||||
/* Finding and removing node 1 */
|
/* 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);
|
sys_dlist_remove(&test_node_1.node);
|
||||||
zassert_true((verify_emptyness(&test_list)),
|
zassert_true((verify_emptyness(&test_list)),
|
||||||
"test_list should be empty");
|
"test_list should be empty");
|
||||||
|
zassert_false(sys_dnode_is_linked(&test_node_1.node),
|
||||||
|
"node1 is still linked");
|
||||||
|
|
||||||
/* Prepending node 1 */
|
/* Prepending node 1 */
|
||||||
sys_dlist_prepend(&test_list, &test_node_1.node);
|
sys_dlist_prepend(&test_list, &test_node_1.node);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue