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:
Peter A. Bigot 2018-12-30 05:46:41 -06:00 committed by Carles Cufí
commit d40b8ce1fb
2 changed files with 33 additions and 1 deletions

View file

@ -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);
}
/**

View file

@ -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);