Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

list: add macro LIST_FOREACH_SAFE #1197

Closed
wants to merge 2 commits into from

Conversation

jobo-zt
Copy link
Contributor

@jobo-zt jobo-zt commented Oct 17, 2024

support modification during linked list traversal.
referring to the implementation of the Linux kernel "list_for_each_safe(pos, n, head)"

@jobo-zt jobo-zt changed the title list: add the macro LIST_FOREACH_SAFE to support modification during … list: add macro LIST_FOREACH_SAFE Oct 17, 2024
@sreimers
Copy link
Member

sreimers commented Oct 17, 2024

The preferred solution for deleting list elements while iterating is currently:

struct le *le = list_head(list);
while (le)
{
     struct object *obj = le->data;
     le = le->next;
     
     list_unlink(&obj->le);
}

@sreimers
Copy link
Member

Your solution is:

struct le *le;
struct le *n;
LIST_FOREACH_SAFE(list, le, n)
{
     struct object *obj = le->data;
     
     list_unlink(&obj->le);
}

This needs a variable more and is less efficient, since it double checks NULL (next and current).
I think we should avoid this and keep the simple while approach, since it's safe, efficient and more readable.

@sreimers sreimers closed this Oct 18, 2024
@jobo-zt
Copy link
Contributor Author

jobo-zt commented Oct 18, 2024

Thank you for your reply.

  1. The first approach directly modifies the le pointer during the list traversal. If an exception occurs or the list structure is modified in list_unlink or mem_deref, it may cause the traversal to be interrupted or access already freed memory.
  2. The second approach uses the LIST_FOREACH_SAFE macro, which safely traverses the list using two pointers, le and n. Even if the list structure is modified in list_unlink or mem_deref, it will not affect the safety of the traversal.

@jobo-zt
Copy link
Contributor Author

jobo-zt commented Oct 18, 2024

I tried to use it, but it didn't work properly.

#if 0	
	LIST_FOREACH_SAFE(&turndp()->rm_map, le, n)
	{
		struct udp_socks *uks = list_ledata(le);
		if (thrd_id == uks->thrd_id) {

			udp_thread_detach(uks->rel_us);
			udp_thread_detach(uks->rsv_us);

			list_unlink(&uks->le);
			mem_deref(uks);
		}
	}
#endif
#if 1
	le = list_head(&turndp()->rm_map);
	while (le)
	{
		struct udp_socks *uks = list_ledata(le);
		if (thrd_id == uks->thrd_id) {
			le = le->next;

			udp_thread_detach(uks->rel_us);
			udp_thread_detach(uks->rsv_us);
			
			list_unlink(&uks->le);
			mem_deref(uks);
		}
	}
#endif

@sreimers
Copy link
Member

Try this one:

	le = list_head(&turndp()->rm_map);
	while (le)
	{
		struct udp_socks *uks = list_ledata(le);
                le = le->next;
		if (thrd_id == uks->thrd_id) {
			udp_thread_detach(uks->rel_us);
			udp_thread_detach(uks->rsv_us);
			
			list_unlink(&uks->le);
			mem_deref(uks);
		}
	}
  1. The first approach directly modifies the le pointer during the list traversal. If an exception occurs or the list structure is modified in list_unlink or mem_deref, it may cause the traversal to be interrupted or access already freed memory.

The original pointer is not modified, only the reference pointer is set to the next element and should not be used after this (before next round).

@jobo-zt
Copy link
Contributor Author

jobo-zt commented Oct 18, 2024

Thank you for your reply. I tried to use it. it is ok. tks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants