[crossfire] safe/common item stack/inventory iteration?

Bernd Edler edler at heydernet.de
Mon Jun 25 11:52:40 CDT 2007



On Sun, 24 Jun 2007, Mark Wedel wrote:

>
>  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.
   ...
> 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. 
>

If we are sure tmp will not be destroyed in some uncontrollable recursive
calls we can avoid bailing out altogether:


      for (tmp = op->inv;tmp != NULL;;) {
          destroy_me = NULL;
          {
           do some work
           replace destroy_obj(tmp) with destroy_me = tmp;
           do more work
          }
          tmp = tmp->below;
          if { destroy_me } then destroy_obj(destroy_me);
      }

If we want the big and clean solution, we only really kill objects
afterwards. This costs us one extra pointer per
object. like ob->destroy_list,
plus two variables: Destroy_list_first,Destroy_list_last
As in:

void mark_for_destruction(object ob) {
   if ( ob->destroy_list != null ) then return;
     /* this one is dead already */
   if ( Destroy_list_first == NULL ) then {
      Destroy_list_first = ob;
      Destroy_list_last = ob;
    }
   Destroy_list_last->destroy_list = ob;
   ob->destroy_list = ob;
     /* self-reference marks end of list  */
   Destroy_list_last = ob;
    { remove the object from lists like active,below etc. }
}


void destroy_marked_objects() {
     for (tmp = Destroy_list_first; tmp != NULL; tmp = next) {
          next = tmp->destroy_list;
          free(tmp);
           /* the object should already be removed from all lists */
           /*   save this destruction list                        */
          if (next == tmp) then last;
           /* self-reference marks end of list  *
      }
    Destroy_list_first = NULL;
    Destroy_list_last = NULL;
}




More information about the crossfire mailing list