[crossfire] safe/common item stack/inventory iteration?
Mark Wedel
mwedel at sonic.net
Mon Jun 25 00:17:47 CDT 2007
Looking at a crash on metalforge, and it is in a loop like:
for (tmp = op->inv; tmp != NULL; tmp = next) {
next = tmp->below;
{ do some work }
}
Now this construct is used to cover the case where the current object (tmp)
goes away - we have a pointer to the next object. Such operations are not that
uncommon, as the {do some work} block is more likely to destroy tmp.
However, in this case, it appears that next was destroyed, so after {do some
work} completed, tmp became next, and next (tmp->below) pointed to garbage, so
the iteration after that crashed. In fact, in that second iteration, all of tmp
was probably garbage, but perhaps didn't crash on that because the checks done
(in this case, in the plugin.c file) probably didn't match - tmp->type and
tmp->subtype did not match what was being looked for.
At some level, handling this becomes difficult - it doesn't make sense to have
a next_next pointer, as there could probably be cases where the next two objects
get destoryed. The easiest thing in this case is to probably store something
like next_count, and see if that differs (suggesting the item was destroyed),
and if so, just bail out of the loop. This could perhaps cause some small
inconsistencies (loop didn't do all the processing it should), but that is
probably better than a crash.
However, I know there are loops like that all throughout the code, so I wonder
if it would be better to macroize that in some way. The correct code block is
something like:
next_count=op->inv?op->inv->count:0
for (tmp = op->inv; tmp != NULL; tmp = next) {
/* May want to set tmp to null before the break, so that if
* anything after the for loop tries to use it, it won't be
* able to
*/
if (tmp->count != next_count) break;
next = tmp->below;
/* if next is NULL, this won't process again in any case */
if (next) next_count=next->count;
{ do some work }
}
I wonder if that could be put in a macro. Well, I know it could, but the
problem is really that { at the end of the for line - ideally you want to be
able to do something like:
FOR_BELOW_OB(op, tmp) {
{do some work }
}
With the macro taking care of declaring declaring the variables to handle the
checking of destroyed status, etc. But you really want that opening brace to be
in the code segment with the loop, simply because most editors key on that for
indentation and other factors. A possibility is to have two macros, so
something like:
FOR_BELOW_OB(op, tmp, next, tmp_tag) {
CHECK_BELOW_OB_STATUS(op, tmp, next, tmp_tag);
{do some work }
}
So that at least the braces line up. Thoughts? It would seem good to try to
make this standard, since it seems this is fairly common, and a crash seen one
place could occur almost anyplace.
More information about the crossfire
mailing list