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))
|
||||
|
||||
/**
|
||||
* @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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue