DO NOT REPLY TO THIS MESSAGE.  INSTEAD, POST ANY RESPONSES TO THE LINK BELOW.

[STR New]

Link: http://www.fltk.org/str.php?L1894
Version: 1.1-current


This is a long standing bug. Maybe since version 1.0.

The docs of Fl_Group state: "The destructor also deletes all the children.
This allows a whole tree to be deleted at once, without having to keep a
pointer to all the children in the user code. A kludge has been done so
the Fl_Group and all of it's children can be automatic (local) variables,
but you must declare the Fl_Group first, so that it is destroyed last."

Obviously, the kludge is this statement in Fl_Group::clear() at line #357:

  if (o->parent() == this) delete o;

together with the Fl_Widget destructor setting its parent_ to 0 (without
removing itself from the parent group).

Most of the time, this works...

But sometimes, it can happen, that the memory of a deleted child widget
has already been "returned to the system" and thus isn't accessible any
more. Then, the program segfaults in o->parent().

I will attach a test program (crash.cxx) that demonstrates this behavior,
together with comments, how to make it "work" or "crash". The values given
in the head of "crash.cxx" are from my test systems: Windows XP (SP2) and
Linux (debian/Knoppix based, kernel 2.6.17). They may vary on different
systems, or the program may not crash at all, dependent on the memory
allocation implementation.

The test program adds two widgets (My_Input) to a group, and each My_Input
adds two other box widgets (green and red) to the same group.

There are two buttons to remove (1) My_Input #1 from the group and (2) the
complete group from the window.

IMHO, this is all perfectly correct code WRT FLTK docs and implementation
details, but if I'm wrong, please correct me.

I'll attach another, slightly modified source file with a documented
debugging session in the next post.

Albrecht


Link: http://www.fltk.org/str.php?L1894
Version: 1.1-current
/*
===============================================================
  modify "KBS" below to allocate a different amount of memory
  
        KBS = 1 ... 127         may work (linux)
        KBS = 128 or more       may crash (linux)
  
        KBS = 1 ... 15          may work (windows)
        KBS = 16 or more        may crash (windows)

  test for yourself ...

  compile and link with: fltk-config --compile crash.cxx
  
  run the program, click Button 1, then click Button 2, then
  close the window.
  
  This is, what should happen (comments after '#'):

# program started:

>>> My_Box(1[1]): this=003D4080, parent()=003D26D8, mem=15 KB
>>> My_Box(1[2]): this=003D7CD8, parent()=003D26D8, mem=15 KB
>>> My_Box(2[1]): this=003DB9A0, parent()=003D26D8, mem=15 KB
>>> My_Box(2[2]): this=00DE0048, parent()=003D26D8, mem=15 KB

# Button 1 clicked:

button1_cb - delete input1 ...
>>> ~My_Input (1): parent()=00000000
>>> ~My_Box(1[2]): this=003D7CD8, parent()=00000000
--- ~My_Box(1[2]): this=003D7CD8, parent()=00000000
--- ~My_Input (1): parent()=00000000
>>> ~My_Box(1[1]): this=003D4080, parent()=00000000
--- ~My_Box(1[1]): this=003D4080, parent()=00000000

# Button 2 clicked:

button2_cb - delete group ...
>>> ~My_Input (2): parent()=003D26D8
>>> ~My_Box(2[2]): this=00DE0048, parent()=003D26D8
--- ~My_Box(2[2]): this=00DE0048, parent()=003D26D8
--- ~My_Input (2): parent()=003D26D8
>>> ~My_Box(2[1]): this=003DB9A0, parent()=003D26D8
--- ~My_Box(2[1]): this=003DB9A0, parent()=003D26D8

# Window close button clicked:

exit_cb() - exiting normally :-)

===============================================================
*/

#define KBS 15  // modify: different values see above

#include <FL/Fl.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Input.H>
#include <FL/Fl_Box.H>
#include <FL/Fl_Button.H>
#include <FL/Fl_Widget.H>

#include <FL/fl_message.H>

#include <stdio.h>
#include <stdlib.h>

class My_Input;

Fl_Window *window;
Fl_Group *group;
Fl_Button *button1;
Fl_Button *button2;
My_Input *input1;
My_Input *input2;

class My_Box : public Fl_Box {

  int   idx;                            // box index
  char  dummy_array[1024*KBS];          // allocate <KBS> KB memory

public:

  My_Box (int, int, int, int, char *, int);
  ~My_Box();

};

My_Box::My_Box (int X, int Y, int W, int H, char *L, int I)
: Fl_Box (X,Y,W,H,L) {

  align (FL_ALIGN_INSIDE);
  idx   = I; // box index
  printf (">>> My_Box(%s[%d]): this=%p, parent()=%p, mem=%d KB\n",
    label(),idx,this,parent(),KBS);
}

My_Box::~My_Box () {

  printf (">>> ~My_Box(%s[%d]): this=%p, 
parent()=%p\n",label(),idx,this,parent());
  if (parent()) parent()->remove(this);
  printf ("--- ~My_Box(%s[%d]): this=%p, 
parent()=%p\n",label(),idx,this,parent());
}

class My_Input : public Fl_Input {

  My_Box        box1;
  My_Box        *box2;

public:

  My_Input (int, int, int, int, char *);
  ~My_Input();
};


My_Input::My_Input (int X, int Y, int W, int H, char *L)
: Fl_Input (X,Y,W,H,L), box1(X+W+1,Y,H,H,L,1) {

  box1.box(FL_THIN_DOWN_BOX);           // 1st box
  box1.color(FL_RED);

  box2 = new My_Box(X+W+H+2,Y,H,H,L,2); // 2nd box
  box2->box(FL_THIN_UP_BOX);
  box2->color(FL_GREEN);
}

My_Input::~My_Input () {

  printf (">>> ~My_Input (%s): parent()=%p\n",label(),parent());

  if (box1.parent()) box1.parent()->remove(box1);

  if (box2->parent()) box2->parent()->remove(box2);
  delete box2;

  printf ("--- ~My_Input (%s): parent()=%p\n",label(),parent());
}

void exit_cb(Fl_Widget *,void *) {
  printf ("exit_cb() - exiting normally :-)\n");
  exit(0);
}

void button1_cb(Fl_Widget *,void *) {

  button1->deactivate();

  group->remove(input1);        // remove from group

  printf ("button1_cb - delete input1 ...\n");
  delete input1;

  window->redraw();
}

void button2_cb(Fl_Widget *,void *) {

  button1->deactivate();
  button2->deactivate();
  window->remove(group);

  printf ("button2_cb - delete group ...\n");
  delete group;

  window->redraw();
}

int main(int argc, char **argv) {
  setvbuf (stdout,NULL,_IONBF,0);       // unbuffered for testing
  window = new Fl_Window(600,400);
    group = new Fl_Group(10,10,580,180);
      input1 = new My_Input( 50,50,100,20,"1");
      input2 = new My_Input(350,50,100,20,"2");
    group->end();
    group->box(FL_FRAME_BOX);

    button1 = new Fl_Button( 50,250,200,100,"Delete Input 1");
    button1->callback(button1_cb,0);

    button2 = new Fl_Button(350,250,200,100,"Delete Group");
    button2->callback(button2_cb,0);

  window->end();
  window->callback(exit_cb);
  window->resizable(group);
  window->size_range(600,400,0,0);
  window->show(argc, argv);
  return Fl::run();
}
_______________________________________________
fltk-bugs mailing list
[email protected]
http://lists.easysw.com/mailman/listinfo/fltk-bugs

Reply via email to