View Issue Details

IDProjectCategoryView StatusLast Update
0016578FPCCompilerpublic2010-11-16 17:04
ReporterJaromir Będkowski Assigned ToJonas Maebe  
PrioritynormalSeveritymajorReproducibilityalways
Status closedResolutionno change required 
PlatformWindows, UnixOSWindows 7, FreeBsd 
Product Version2.4.0 
Summary0016578: Implicit create interface references
DescriptionIn sample code line:

  if lTestObject.FTestInterface.GetMyRefCount = 1 then
    Writeln('ok');

create interface reference.

Sample work properly in Delphi2010 and FPC 2.2.2.
TagsNo tags attached.
Fixed in Revision
FPCOldBugId
FPCTarget
Attached Files

Relationships

duplicate of 0006035 closedJonas Maebe AV by interface access 
related to 0014019 closedJonas Maebe Reference counting fails for result values 
related to 0011503 closedJonas Maebe Interfaces returned from functions are not properly released. 
related to 0009472 closedYuriy Sydorov "as" increase .RefCount in INTERFACE 

Activities

2010-05-27 11:25

 

pool.dpr (1,546 bytes)

Jonas Maebe

2010-05-27 11:48

manager   ~0038045

The behaviour you depended on caused the crash described in 0014019 and is implementation-wise the same as what was described in 0006035

In general: you cannot and must never depend on particular reference counting behaviour. Reference counting is an implementation-defined mechanism and its specific behaviour is completely undefined. If you want deterministic reference counting, by definition you have to do it manually. You cannot count on the compiler to perform the reference counting in a particular way, because you cannot control how many temporary variables it creates and when these are finalised. See 0009472 for a long discussion about this. This will not be changed.

Furthermore, your program is wrong (also for Delphi, even if it may seem to run correctly) and leaks memory. This is the correct version (all changes are at the end):


program pool;

{$ifdef FPC} {$mode DELPHI}
{$else} {$apptype console}
{$endif}

uses Classes, SysUtils;

type
  ITestInterface = interface
    function GetMyRefCount: Integer;
    property MyRefCount: Integer read GetMyRefCount;
  end;

  TTestInterfacedObject = class(TInterfacedObject, ITestInterface)
  private
    FMyRefCount: Integer;
  protected
    function _AddRef: Integer; stdcall;
    function _Release: Integer; stdcall;
  public
    function GetMyRefCount: Integer;
  end;

  TTestObject = class(TObject)
  private
    FTestInterface: ITestInterface;
  end;

var
  lTestObject: TTestObject;
  lTestInterfacedObject: TTestInterfacedObject;

{ TTestInterfacedObject }

function TTestInterfacedObject._AddRef: Integer;
begin
  Inc(FMyRefCount);
  Result := FMyRefCount;
  Writeln('After _AddRef, RefCount = ' + IntToStr(GetMyRefCount));
end;

function TTestInterfacedObject._Release: Integer;
begin
  Dec(FMyRefCount);
  Result := FMyRefCount;
  Writeln('After _Release, RefCount = ' + IntToStr(GetMyRefCount));
  //if not more references, call Destroy
  if Result = 0 then begin
    Destroy;
  end;
end;

function TTestInterfacedObject.GetMyRefCount: Integer;
begin
  Result := FMyRefCount;
end;

begin
  lTestInterfacedObject := TTestInterfacedObject.Create;

  lTestObject := TTestObject.Create;
  lTestObject.FTestInterface := lTestInterfacedObject;

  if lTestObject.FTestInterface.GetMyRefCount = 1 then
    Writeln('ok');
// do not free interfaced objects that have been assigned to an interface, because
// after the assignment they are reference counted
// lTestInterfacedObject.Free;

// this will decrease the reference count of FTestInterface and free the object
  lTestObject.Free;

end.

Jonas Maebe

2010-05-27 11:50

manager   ~0038046

Last edited: 2010-05-27 11:51

And there's also a very long archived discussion about this on the mailing list, starting at http://tinyurl.com/39cov9x

Jaromir Będkowski

2010-05-27 13:42

reporter   ~0038052

You are right about free at the end - this is my mistake in sample code.

But i don't understand this fpc behaviour. Reference couting in this sample is mine.

The problem is here: lTestObject.FTestInterface.GetMyRefCount = 1
In this line first is create interface object and next you have compare (with additional counted interfaced object) so in this line you have 2 counted references (in my opinion should be 1).

If this is as designed this is breaking change from 2.2.2 to 2.4.0 version !! You should inform users about this fact !!

Jonas Maebe

2010-05-27 13:55

manager   ~0038053

> But i don't understand this fpc behaviour. Reference couting in this sample is mine.

It is the compiler's reference counting, because it is the compiler that inserts the calls to _addref and _release methods. And the compiler is free to place them wherever it wants, as long as there are no memory leaks when the program terminates.

Manual reference counting means that you manually insert the incref/decref calls everywhere.

> The problem is here: lTestObject.FTestInterface.GetMyRefCount = 1
> In this line first is create interface object and next you have compare
> (with additional counted interfaced object) so in this line you have 2
> counted references (in my opinion should be 1).

Please read the entire referenced discussion, it explains at least 10 times how this can happen (in short: by invisible temporary variables that can be created by the compiler while evaluating expressions).

> If this is as designed this is breaking change from 2.2.2 to 2.4.0 version !!
> You should inform users about this fact !!

It is not a change (breaking or otherwise) for correctly written programs. E.g., the reference counting behaviour can also differ depending on the optimization level you use to compile the code and depending on whether you are working with local or global variables (even with 2.2.2).

It's like saying that we should warn with every new compiler version that uninitialised local variables may have a different value (which also differs depending on the optimization level), because the stack layout may be different. These are simply changes that can happen at any time and behaviour which you can never rely on in any way.

Jaromir Będkowski

2010-05-27 14:39

reporter   ~0038056

Thank you for clarification.

Jonas Maebe

2010-05-27 15:08

manager   ~0038057

You're welcome.

Issue History

Date Modified Username Field Change
2010-05-27 11:25 Jaromir Będkowski New Issue
2010-05-27 11:25 Jaromir Będkowski File Added: pool.dpr
2010-05-27 11:40 Jonas Maebe Relationship added related to 0014019
2010-05-27 11:40 Jonas Maebe Relationship added duplicate of 0006035
2010-05-27 11:41 Jonas Maebe Relationship added related to 0011503
2010-05-27 11:48 Jonas Maebe Status new => resolved
2010-05-27 11:48 Jonas Maebe Resolution open => no change required
2010-05-27 11:48 Jonas Maebe Assigned To => Jonas Maebe
2010-05-27 11:48 Jonas Maebe Note Added: 0038045
2010-05-27 11:49 Jonas Maebe Relationship added related to 0009472
2010-05-27 11:50 Jonas Maebe Note Added: 0038046
2010-05-27 11:51 Jonas Maebe Note Edited: 0038046
2010-05-27 13:42 Jaromir Będkowski Status resolved => feedback
2010-05-27 13:42 Jaromir Będkowski Resolution no change required => reopened
2010-05-27 13:42 Jaromir Będkowski Note Added: 0038052
2010-05-27 13:55 Jonas Maebe Status feedback => resolved
2010-05-27 13:55 Jonas Maebe Resolution reopened => no change required
2010-05-27 13:55 Jonas Maebe Note Added: 0038053
2010-05-27 14:39 Jaromir Będkowski Status resolved => feedback
2010-05-27 14:39 Jaromir Będkowski Resolution no change required => reopened
2010-05-27 14:39 Jaromir Będkowski Note Added: 0038056
2010-05-27 15:08 Jonas Maebe Status feedback => resolved
2010-05-27 15:08 Jonas Maebe Resolution reopened => no change required
2010-05-27 15:08 Jonas Maebe Note Added: 0038057
2010-11-16 17:04 Jonas Maebe Status resolved => closed