Consider these two standalone functions
Consider these two standalone functions
function CaptureList: TArray;
procedure LogList(const aList: TArray);
and the two following use cases:
This one works,
procedure TSomeClass.Case1;
var
List: TArray;
begin
List := CaptureList;
LogList(CaptureList);
end;
but this one fails.
procedure TSomeClass.Case2;
begin
LogList(CaptureList);
end; <- EInvalidPointer in _DynArrayClear on exit
Does anyone see any potential reason why the second fails?
Berlin 10.1 Update 2 Enterprise
function CaptureList: TArray
procedure LogList(const aList: TArray
and the two following use cases:
This one works,
procedure TSomeClass.Case1;
var
List: TArray
begin
List := CaptureList;
LogList(CaptureList);
end;
but this one fails.
procedure TSomeClass.Case2;
begin
LogList(CaptureList);
end; <- EInvalidPointer in _DynArrayClear on exit
Does anyone see any potential reason why the second fails?
Berlin 10.1 Update 2 Enterprise
I have no 10.1 here, but will it work if you declare the method this way?
ReplyDeleteprocedure LogList(aList: TArray); // removed the const
Not sure, haven't tried. It would defeat the purpose of not duplicating the dynamic array while passing it down the line, though.
ReplyDeleteDo you have a complete failing example in a short unit or so?
ReplyDeleteNeed the code from these two routines. Without that it is impossible to identify the defect.
ReplyDeleteAs always - it is complicated. Multiple threads, multiple units, and the values of each element of the list is copied line by line into elements of a separate type (record with a string field) at the innermost point. The record will exist for a longer period of time after the calls are completed.
ReplyDeleteI just find it odd that it works with a local list, but not without it.
Works fine for me. Does this work for you? If it does, then it's something else and you've provided not enough information.
ReplyDeletetype
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
protected
{ Protected declarations }
public
{ Public declarations }
procedure Case1;
procedure Case2;
end;
implementation
{$R *.dfm}
function CaptureList: TArray;
begin
Result := TArray.Create('One', 'Two', 'Three');
end;
procedure LogList(const aList: TArray);
begin
Showmessage(aList[1]);
end;
procedure TForm1.Case1;
var
List: TArray;
begin
List := CaptureList;
LogList(CaptureList);
end;
procedure TForm1.Case2;
begin
LogList(CaptureList);
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
Case1;
Case2;
end;
Lars Fosdal If it happens in multithreaded environment then I guess is is because the const is causing the refcount to not be increased for the duration of that routine to run which means that while some other code runs it might reach zero too early and the array gets finalized which causes the invalid pointer exception.
ReplyDeleteStefan Glienke Sounds plausible, but why does it work when using an explicit local variable?
ReplyDeleteLars Fosdal Because no other threads are using the same local variable? The second code block you posted in OP suggests it's shared if not local.
ReplyDeleteLars Fosdallocal variable increments refcounter and prevents array from destroying during Case1 execution
ReplyDeleteIf I am not mistaken, dynamic arrays are like interfaces, as they both have refcounts (interfaces refcounts are visible, which dynamic arrays aren't). Here is a discussion about interfaces and refcounts - plus.google.com - Please vote for http://qc.embarcadero.com/wc/qcmain.aspx?d=90482 You need to... - just apply it dynamic arrays.
ReplyDeleteprocedure LogList(aList: TArray);
ReplyDeletewould indeed solve the problem. It would not lead to any duplication. This is a dynamic array rather than an open array. What would be copied by value is the reference to the array. And that copy requires reference counting which is what keeps your array alive.
I don't much care for it though. I'd much rather that the compiler would hold a reference for duration of the function call, from the outside. That would allow the use of const and not force all consumers of the function to increment reference counts needlessly.