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.
Mailing lists / lazarus / 2010-04
RFH: AV when deleting TAChart from the form
12 messages · 2 people ·
- MMartin 8
- AAlexander Klenin 4
Reply by email Subscribe to this list Your subscription
Read-only archive. Use j and k to move between messages.
Alexander Klenin wrote 8 lines
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
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 AComponentWhat 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 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 Klenin wrote 8 lines
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
Martin wrotewhat 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
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 Klenin wrote 10 lines
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
Martin wrote 11 lines
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 Klenin wrote 19 lines
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
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
Martin wrote 14 lines
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
Martin wrote 32 lines
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
