Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Mailing lists / lazarus / 2010-04

RFH: AV when deleting TAChart from the form

12 messages · 2 people ·

Reply by email Subscribe to this list Your subscription

Read-only archive. Use j and k to move between messages.

Martin
to Alexander Klenin
Alexander Klenin wrote 8 lines
Can anyone help me with http://bugs.freepascal.org/view.php?id=16035 ? This code is so complicated, I can not understand what is going on. It may be a bug in TAChart itself, but I do not see it either. Now that the user was burned by the issue (see http://bugs.freepascal.org/view.php?id=16254), I think the priority has risen somewhat.

Not sure if it helps.

but I found the following:

When it crashes, it reads the Property (and tries to perform an "is TCollection" on it)
TLineSeries.Marks

(in this case ChartTSummer)

However it Seems that Marks has a dangling pointer to a destroyed object.

I don't know where it may have been destroyed, nor if that should have removed the pointer.

Martin

Reply by email

Martin
to Martin

How to find this in the debuuger:

If you are on the crash, you have this stacktrace: #0 INHERITSFROM(0xf0f0f0f0, 0xb8f888) at ..\inc\objpas.inc:529 #1 GETOBJECTPROP(0xf0f0f0f0, 0xb8f888, 0xb8f888) at ..\objpas\typinfo.pp:1170
#2 ?? at :0
#3 ?? at :0
#4 GETCOLLECTIONS(0xf019308, 0xeb82c70, {Proc = {procedure (POINTER, TCOLLECTION)} 0xa20f02c, Self = 0xf019308}) at componenttreeview.pas:133 #5 WALK(0xf019308, 0xeb82c70) at componenttreeview.pas:174 #6 GETCHILDREN(0xeeba128, {Proc = {procedure (POINTER, TCOMPONENT)} 0xa20f0c4, Self = 0xf019308}, 0xee1fa98) at tagraph.pas:988 #7 WALK(0xf019308, 0xeeba128) at componenttreeview.pas:182 #26 REMOVECOMPONENT(0xf0f0f0f0, 0xb8f888) at
..\objpas\classes\compon.inc:618
....

** Select Frame 1:
----------
Function GetObjectProp(Instance: TObject; PropInfo : PPropInfo;
MinClass: TClass): TObject;
begin
.....
   If (MinClassNil) and (ResultNil) Then
     If Not Result.InheritsFrom(MinClass) then   // << current line
<<<<<<<<<
       Result:=Nil;
end;
----------

Looking at Result: TObject ($0DD5CD40) { _vptr$TOBJECT = $f0f0f0f0} => this object was destroyed before

So what was it?
** Look at frame 4:
----------
procedure TComponentWalker.GetCollections(AComponent: TComponent; AProc:
TGetCollectionProc);
....
begin
   PropCount := GetPropList(AComponent, PropList);
   try
     for i := 0 to PropCount - 1 do
       if (PropList^[i]^.PropType^.Kind = tkClass) then
       begin
         Obj := GetObjectProp(AComponent, PropList^[i], TCollection); <<
current line <<<<<<<<<<<<<<<
.....
-----------
We are looking at all published properties of AComponent

What is AComponent?
We may not have the Class, but we have the name: ChartTSummer => looking at the Form in the OI => it is a TLineSeries

using ctrl-F1 (evaluate/modify), we can evaluate:
TLineSeries(AComponent)^

We see all the variables, some properties =>all we need is to search wich one has a value of $0DD5CD40 (pointing to the destroyed object) => its FMarks

Martin wrote 32 lines
On 11/04/2010 13:57, Alexander Klenin wrote: > Can anyone help me with http://bugs.freepascal.org/view.php?id=16035 ? > This code is so complicated, I can not understand what is going on. > It may be a bug in TAChart itself, but I do not see it either. > Now that the user was burned by the issue > (see http://bugs.freepascal.org/view.php?id=16254), > I think the priority has risen somewhat. > Not sure if it helps. but I found the following: When it crashes, it reads the Property (and tries to perform an "is TCollection" on it) TLineSeries.Marks (in this case ChartTSummer) However it Seems that Marks has a dangling pointer to a destroyed object. I don't know where it may have been destroyed, nor if that should have removed the pointer. Martin -- _______________________________________________ Lazarus mailing list http://lists.lazarus.freepascal.org/mailman/listinfo/lazarus

Reply by email

Alexander Klenin
to Martin

On Mon, Apr 12, 2010 at 01:07, Martin wrote:

That is all true, but did you notice what I wrote in "Additional info":

Note how TChart.GetChildren is called after TChart.Destroy

Does your analysis confirm that?

Alexander S. Klenin

Reply by email

Martin
to Alexander Klenin
Alexander Klenin wrote 8 lines
On Mon, Apr 12, 2010 at 01:07, Martin wrote: That is all true, but did you notice what I wrote in "Additional info": > Note how TChart.GetChildren is called after TChart.Destroy > Does your analysis confirm that?

Yes and No

It is called during Destroy. Somewhere in the inherited classes.

---------------------
what needs to be done, is that any TComponent, that:
- has other TComponents
- and offers them through a property (published, public, private, any) (or even just stores it in a memebr-var)

must request a FreeNotification
=> so it can clear the variable/property if the component is destroyed

There may be exception, but that is the save way

Martin

Reply by email

Martin
to Martin
Martin wrote
what needs to be done, is that any TComponent, that: - has other TComponents

actually, skip that.

- and offers them through a property (published, public, private, any) (or even just stores it in a memebr-var)

any component to which a reference is stored in a variable.

Show quoted text 14 lines
must request a FreeNotification => so it can clear the variable/property if the component is destroyed There may be exception, but that is the save way Martin -- _______________________________________________ Lazarus mailing list http://lists.lazarus.freepascal.org/mailman/listinfo/lazarus

Reply by email

Alexander Klenin
to Martin
Martin wrote
> must request a FreeNotification > => so it can clear the variable/property if the component is destroyed

So, are you saying that I should add a call to FreeNotification to the TBasicChartSeries destructor?
BTW, another way to illustrate the issue is that attached patch fixes it, although obviously just by hiding the real problem.

Alexander S. Klenin -------------- next part -------------- A non-text attachment was scrubbed... Name: tachart_crash_workaround.patch Type: application/octet-stream Size: 468 bytes Desc: not available URL: <http://lists.lazarus.freepascal.org/pipermail/lazarus/attachments/20100412/b2e82360/attachment.obj>

Reply by email

Martin
to Alexander Klenin
Alexander Klenin wrote 10 lines
On Mon, Apr 12, 2010 at 01:28, Martin wrote: >> must request a FreeNotification >> => so it can clear the variable/property if the component is destroyed >> So, are you saying that I should add a call to FreeNotification to the TBasicChartSeries destructor? BTW, another way to illustrate the issue is that attached patch fixes it, although obviously just by hiding the real problem.

Initialize I thought FMarks was a tcomponent => for a TComponent, i would have done
FMarks.FreeNotification
and then added
TChartSeries.Notification(Acomponent...) override
to react to it.

But, it is only TPersistent.... => so that wasn't the cause. => hence forget about my comment about FreeNotification

-----------------
I think the patch is correct then. (When I found that FMarks was destroyed, I overlooked that the TChartSeries holding it, were also destroyed)

destructor TChart.Destroy;
begin
   FSeries.Free;
...
   inherited Destroy;
end;

inherited destroy calls getChildren, which accesses FSeries.

Only question is, if anything still needs to see those children. Maybe move inherited Destroy to the top, then you still can return the children

I don't know which one is better. I don't know why the children are queried, nor if anything important is done with them

--------
Btw, I would change all foo.free into FreeAndNil(Foo)

just in case there are more issues hiding

Martin

Reply by email

Alexander Klenin
to Martin
Martin wrote 11 lines
I think the patch is correct then. (When I found that FMarks was destroyed, I overlooked that the TChartSeries holding it, were also destroyed) destructor TChart.Destroy; begin ?FSeries.Free; ... ?inherited Destroy; end; inherited destroy calls getChildren, which accesses FSeries.

Even that I would call suspicious, but I think the problem is deeper. The crash still happens even if I comment out the call to inherited Destroy in TChart.Destroy.
Also, the callstacks I get after the crash do not contain TChart.Destroy This suggests that ComponentTreeView/ObjectInstector code does call TChart.GetChildern after TChart.Destroy, and the patch I posted works only because the freed memory is not reused yet during the call to GetChildren.
This is why I asked for help -- I suspect there is a bug in ComponentTreeView/ObjectInstector code, but there are only a few people capable of understanding it, and I am not one of them.

Btw, I would change all foo.free into FreeAndNil(Foo) just in case there are more issues hiding

Yes, it is the item number 15 in my TAChart todo list ;-)

Alexander S. Klenin

Reply by email

Martin
to Alexander Klenin
Alexander Klenin wrote 19 lines
On Mon, Apr 12, 2010 at 02:02, Martin wrote: > I think the patch is correct then. (When I found that FMarks was destroyed, > I overlooked that the TChartSeries holding it, were also destroyed) > > destructor TChart.Destroy; > begin > FSeries.Free; > ... > inherited Destroy; > end; > > inherited destroy calls getChildren, which accesses FSeries. > Even that I would call suspicious, but I think the problem is deeper. The crash still happens even if I comment out the call to inherited Destroy in TChart.Destroy.

eh, removing that inherited call will get a lot of other trouble...

Also, the callstacks I get after the crash do not contain TChart.Destroy

you mean "not inherited destroy"?

Show quoted text 5 lines
This suggests that ComponentTreeView/ObjectInstector code does call TChart.GetChildern after TChart.Destroy, and the patch I posted works only because the freed memory is not reused yet during the call to GetChildren.

If that was true, then it would suggest that some other component (which is known to OI) must hold a pointer to TChart; and (on request) hand that pointer to OI....

On the other hand: if you disabled the call to inherited destroy => then
the dead object would most certainly get a lot of calls.

in that inherited call your TChart notifies a lot of other components (Owner, everyone who requested FreeComponent (which probably includes OI)) that it is destroyed => if you prevent that, they will believe it is still there....

This is why I asked for help -- I suspect there is a bug in ComponentTreeView/ObjectInstector code, but there are only a few people capable of understanding it, and I am not one of them.

Well neither am I.

But I am still reasonable convincent, that it is to do with destruction order, and dangling references...

Martin

Reply by email

Martin
to Martin
Martin wrote 14 lines
On 11/04/2010 18:37, Alexander Klenin wrote: > On Mon, Apr 12, 2010 at 02:02, Martin wrote: >> destructor TChart.Destroy; >> begin >> FSeries.Free; >> ... >> inherited Destroy; >> end; >> >> inherited destroy calls getChildren, which accesses FSeries. > Even that I would call suspicious, but I think the problem is deeper. > The crash still happens even if I comment out the call to inherited > Destroy > in TChart.Destroy.

I applied your other patch [ FreeAndNil(Fseries); // if FSeries = nil then exit ]
And I no longer get the crash, so that works.

--------------------

As for calls to GetChildren

the first happens when
FreeAndNil(FSeries)
is called.

I looked through the Callstack, and it seems more than reasonable:

FSerieas.Free, calls TChartSeriesList.Clear;
This destorys some TCharts that are hold by FSeries [ Items[i].FChart := nil; ] => TCharts are components, so they notify there owner, and everyone who requested it, about their death. => The owner is the Form, and the form tells OI => and OI makes sure that all of its own structures are good (RefreshSelection;
TComponentTreeView.RebuildComponentNodes; and the likes)

IMHO that is reasonable.

I wont do it, but if you dig deeper, you will find that the other calls are of similar nature.

Bottomline is, that any Component must expect all kinds of calls during it's destruction, and must at least return them without crash.

And hey, the best I have seen was a WM_SETFOCUS to a Component in destruction. I couldn't believe it.....

Martin

Reply by email

Martin
to Martin
Martin wrote 32 lines
On 11/04/2010 19:39, Martin wrote: > On 11/04/2010 18:37, Alexander Klenin wrote: >> On Mon, Apr 12, 2010 at 02:02, Martin wrote: >>> destructor TChart.Destroy; >>> begin >>> FSeries.Free; >>> ... >>> inherited Destroy; >>> end; >>> >>> inherited destroy calls getChildren, which accesses FSeries. >> Even that I would call suspicious, but I think the problem is deeper. >> The crash still happens even if I comment out the call to inherited >> Destroy >> in TChart.Destroy. I applied your other patch [ FreeAndNil(Fseries); // if FSeries = nil then exit ] And I no longer get the crash, so that works. -------------------- As for calls to GetChildren the first happens when FreeAndNil(FSeries) is called. I looked through the Callstack, and it seems more than reasonable: FSerieas.Free, calls TChartSeriesList.Clear; This destorys some TCharts that are hold by FSeries [ Items[i].FChart := nil; ] => TCharts are components,
Sorry one line off, next line
     Items[i].Free;
in
procedure TChartSeriesList.Clear;
var
   i: Integer;
begin
   for i := 0 to FList.Count - 1 do begin
     Items[i].FChart := nil;
     Items[i].Free;
   end;
   FList.Clear;
end;

Items are TLineSeries and they are TComponents

Show quoted text 33 lines
so they notify there owner, and everyone who requested it, about their death. => The owner is the Form, and the form tells OI => and OI makes sure that all of its own structures are good (RefreshSelection; TComponentTreeView.RebuildComponentNodes; and the likes) IMHO that is reasonable. I wont do it, but if you dig deeper, you will find that the other calls are of similar nature. Bottomline is, that any Component must expect all kinds of calls during it's destruction, and must at least return them without crash. And hey, the best I have seen was a WM_SETFOCUS to a Component in destruction. I couldn't believe it..... Martin -- _______________________________________________ Lazarus mailing list http://lists.lazarus.freepascal.org/mailman/listinfo/lazarus

Reply by email