From 87384ed062223039bb3975e064b510c68474051e Mon Sep 17 00:00:00 2001 From: terencehill Date: Thu, 16 Mar 2023 16:54:35 +0100 Subject: [PATCH] Implement safe removal of elements of an intrusive list while looping over them. It fixes #2790 "Hitting a player with crylink primary causes `setorigin: can not modify free entity`" --- qcsrc/lib/intrusivelist.qh | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/qcsrc/lib/intrusivelist.qh b/qcsrc/lib/intrusivelist.qh index 63a215656..7c4728a32 100644 --- a/qcsrc/lib/intrusivelist.qh +++ b/qcsrc/lib/intrusivelist.qh @@ -27,6 +27,7 @@ CLASS(IntrusiveList, Object) ATTRIB(IntrusiveList, il_tail, entity); ATTRIB(IntrusiveList, il_nextfld, .entity, nil); ATTRIB(IntrusiveList, il_prevfld, .entity, nil); + ATTRIB(IntrusiveList, il_loop_item, entity, nil); INIT(IntrusiveList) { IL_INIT(this); } DESTRUCTOR(IntrusiveList) { IL_DTOR(this); } ENDCLASS(IntrusiveList) @@ -142,6 +143,8 @@ void IL_REMOVE(IntrusiveList this, entity it) next ? next.(il_prev) = prev : this.il_tail = prev; prev ? prev.(il_next) = next : this.il_head = next; LOG_DEBUGF("remove %i (%i :: %i), head: %i -> %i, tail: %i -> %i", it, it.(il_prev), it.(il_next), ohead, this.il_head, otail, this.il_tail); + if (this.il_loop_item == it) + this.il_loop_item = it.(il_next); it.(il_next) = it.(il_prev) = NULL; } @@ -175,9 +178,15 @@ void IL_REMOVE(IntrusiveList this, entity it) for (entity _next, _it = _il.il_head; _it; (_it = _next, ++i)) \ { \ const noref entity it = _it; \ + this.il_loop_item = it; \ _next = it.(il_next); \ if (cond) { LAMBDA(body) } \ + if (this.il_loop_item != it) /* current item removed? */ \ + _next = this.il_loop_item; \ + else \ + _next = it.(il_next); /* in case next item has changed */ \ } \ + this.il_loop_item = nil; \ MACRO_END .int il_id; -- 2.39.2