Sign in Join

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

Mailing lists / fpc-devel / 2010-11

Re: Interface scope incompatibility with Delphi

10 messages · 6 people · –

Reply by email Subscribe to this list Your subscription

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

Dimitri Smits

ok, answering my own mail after a small test below

----- "Dimitri Smits" wrote 5 lines
what I DON'T do is use it like in your example, Graeme. I always assign the result to a local variable, which goes out of scope in an implicit finally 't tried if this does (not) work in fpc though.

tested with following program and 2.4.0 (can someone verify on trunk)

it gives somewhat the desired result, but not completely. This IS a bug, I believe.

the expected behaviour is the next output:
Entering: DoSomething 1
Entering: DoSomething 2
random message 1
random message 2
Exiting: DoSomething 2
Exiting: DoSomething 1

but I got this output:
Entering: DoSomething 1
Entering: DoSomething 2
random message 1
random message 2
Exiting: DoSomething 1
Exiting: DoSomething 2

it seems that the stackvariables are NOT unloaded in the correct order (ie: reverse order of declaration). It shouldn't matter in what order you fill them, but in the order they are declared. (like good practice in constructor vs destructor, but then in stack variables setup/cleanup)

---8<-----------8<-----------8<-----------8<-----------8<-----------
program project1;
{$mode objfpc}{$H+}

uses

  {$IFDEF UNIX}{$IFDEF UseCThreads}
  cthreads,
  {$ENDIF}{$ENDIF}
  Classes,
  SysUtils
  { you can add units after this };
{$R *.res}
type
  { TWriteLog }
  TWriteLog=class(TInterfacedObject)
  private
    FLogMessage: String;
  public
    constructor Create(str:string); overload;
    destructor Destroy; override;
  end;

{ TWriteLog }

constructor TWriteLog.Create(str: string);
begin
  inherited Create;
  FLogMessage := str;
  writeln(Format('Entering: %s', [ FLogMessage ]));
end;
destructor TWriteLog.Destroy;
begin
  writeln(Format('Exiting: %s', [ FLogMessage ]));
  inherited Destroy;
end;
procedure DoSomething;
var logMsg1, logMsg2: IInterface;
begin
  logMsg1 := TWriteLog.Create('DoSomething 1');
  logMsg2 := TWriteLog.Create('DoSomething 2');
  writeln('random message 1');
  writeln('random message 2');
end;
begin
  DoSomething;
  readln;
end.
---8<-----------8<-----------8<-----------8<-----------8<-----------

mind you, you could explicitly nillify one of the logMsg variables to force order as well. Usually this is being used by our company for things like Screen.Cursor replacements and making sure that any unforseen errors in additional call(stacks) are handled without cluttering everything up with additional try/finally blocks.

other stuff we used this trick for includes 'DataSet.DisableControls', profiling/timing and transactionmanagment to name a few.

oh, and by the way: I get 2 warnings that the logMsg variables are assigned but not being used. This is too strict for tag-interfaces, as they actually are used due to assignment and ref-counting.

should I file (2/a) bug report(s)? or is there no point in doing so since they won't be fixed either?

kind regards,
Dimitri Smits

Reply by email

Marc Weustink
to Dimitri Smits
Dimitri Smits wrote 32 lines
ok, answering my own mail after a small test below ----- "Dimitri Smits" schreef: > what I DON'T do is use it like in your example, Graeme. I always > assign the result to a local variable, which goes out of scope in an > implicit finally 't tried if this does > (not) work in fpc though. > tested with following program and 2.4.0 (can someone verify on trunk) it gives somewhat the desired result, but not completely. This IS a bug, I believe. the expected behaviour is the next output: Entering: DoSomething 1 Entering: DoSomething 2 random message 1 random message 2 Exiting: DoSomething 2 Exiting: DoSomething 1 but I got this output: Entering: DoSomething 1 Entering: DoSomething 2 random message 1 random message 2 Exiting: DoSomething 1 Exiting: DoSomething 2 it seems that the stackvariables are NOT unloaded in the correct order (ie: reverse order of declaration). It shouldn't matter in what order you fill them, but in the order they are declared. (like good practice in constructor vs destructor, but then in stack variables setup/cleanup)

IMO not.
Why should the order of declaration matter on how your code works ? It's just an declaration.
If your code requires a specific unload order, code it that way by setting the variables to nil in the order you need. That is the only way you can make sure that interfaces will be unloaded in the correct order. In Delphi I had a issue when working with DirectSound interfaces. The order how interfaces are unloaded was important, the Delphi default way lead to a crash. Explicitly nilling the variables in the correct order solved this.

Marc

Reply by email

Thomas Schatzl
to Dimitri Smits: ok, answering my own mail after a small test below tested with following program and…

Hi,

Dimitri Smits wrote
it seems that the stackvariables are NOT unloaded in the correct order (ie: reverse order of declaration). It shouldn't matter in what order you fill them, but in the order they

are

declared. (like good practice in constructor vs destructor, but then in stack variables setup/cleanup)

Imho the compiler should generally be free to reorder/remove/reuse/add temporary variables on the stack as it sees fit. What you experience is only an artifact of the current implementation.
Making assumptions about exact stack layout (in particular of local variables) beyond what is specified by the calling convention is generally not a good idea.

Show quoted text 5 lines
---8<-----------8<-----------8<-----------8<-----------8<----------- [... snipped use of interface hacks to implement scoped memory...] should I file (2/a) bug report(s)? or is there no point in doing so

since

they won't be fixed either?

Imo: Jonas already stated, the time of the release for interfaces is not guaranteed - afaik the docs/specs only state that they "will be released if the reference count gets zero" (I have not read the docs for the exact wording).
So what if the runtime system internally keeps a reference for en masse garbage collection using a background thread? (I.e. the refcount starting at one. This is a totally valid use case for interfaces, and which btw, also makes the "stack ordering" issue a moot point too.

Or, this interface memory management may be implemented using tracing garbage collection in some future target (LLVM, JVM, CLR, ...) the same way.

What would be interesting would be how garbage-collected Delphis handle this, can anyone help? Other than that, there is imo nothing else to say than that Pascal/Delphi(*) does not have explicitly scoped lifetimes with automatic destructor call for classes/objects at the moment.

(*) maybe newer Delphis have the C# "using" keyword or something similar
(maybe "with" recycling?). I would be interested to hear about that.

Thomas

Reply by email

Marco van de Voort
to Thomas Schatzl

In our previous episode, Thomas Schatzl said:

What would be interesting would be how garbage-collected Delphis handle this, can anyone help? Other than that, there is imo nothing else to say than that Pascal/Delphi(*) does not have explicitly scoped lifetimes with automatic destructor call for classes/objects at the moment.

The .NET versions are simply different, and don't have refcounted interfaces. You also need to implement a special interface to make sure a destructor is called when it is destructed. IIRC the Delphi.NET hierachy used this everywhere. (which was more compatible but slower)

Attempts to add Boehm collectors to native compilers ignore refcounts, since they work with scanning for roots, and keeps counting references. I assume simply removing the deallocation from the interfaces would be enough (since when the last reference disappears, the GC will notice eventually).

However while the Boehm GC route has been referenced many times for Delphi and FPC ("if it can for C++, it can for Delphi"), I never saw a rapport of somebody doing this with details (describing the setup etc).

An occasional claim has been made but afaik without any details (like the size of the tested app)

If sb has info on this, please add it to

http://wiki.freepascal.org/garbage_collection

since the question comes up occasionally.

Reply by email

Dimitri Smits
to Thomas Schatzl: On Fri, 12 Nov 2010 00:46:43 +0100 (CET), Dimitri Smits are Imho the compiler should…
----- "Thomas Schatzl" wrote 24 lines
Hi, On Fri, 12 Nov 2010 00:46:43 +0100 (CET), Dimitri Smits wrote: > > it seems that the stackvariables are NOT unloaded in the correct order > (ie: reverse order of declaration). > It shouldn't matter in what order you fill them, but in the order they are > declared. (like good practice in constructor vs destructor, but then in > stack variables setup/cleanup) Imho the compiler should generally be free to reorder/remove/reuse/add temporary variables on the stack as it sees fit. What you experience is only an artifact of the current implementation. Making assumptions about exact stack layout (in particular of local variables) beyond what is specified by the calling convention is generally not a good idea.

[snip]

Show quoted text 7 lines
Imo: Jonas already stated, the time of the release for interfaces is not guaranteed - afaik the docs/specs only state that they "will be released if the reference count gets zero" (I have not read the docs for the exact wording).

I was looking in the online docs for a mention of this behaviour regarding "managed" types and stack unwinding. cannot find it.

anyway, I never mix dependent variables like that in the same method, so it would not matter a lot to me. Was just stating that this is "how delphi does it" and that some code may depend on it.

tagging interfaces and their use in local variables are supposed to be in scope until exit of procedure (be it by reaching 'end;', explicit exit or exception). Then they are to be nilled (and _release'd), which may result in refcount=0 => Free.

Show quoted text 12 lines
So what if the runtime system internally keeps a reference for en masse garbage collection using a background thread? (I.e. the refcount starting at one. This is a totally valid use case for interfaces, and which btw, also makes the "stack ordering" issue a moot point too. Or, this interface memory management may be implemented using tracing garbage collection in some future target (LLVM, JVM, CLR, ...) the same way.

What most of you do not understand is that you CAN override the AddRef and Release methods to NOT do ref_counted free's. Also, Interface use automatic garbage collection, not even when you use a refcounted-with-free-on-0 interface.

COM interfaces are semi-unmanaged objects either way. (interface vars are managed, but the implementations have explicitly coded provisions for assistence)

The argument for an external garbage collector can be made for object-references, not for interfaces (although variables of interface type can be the reason why an instance of the implementation is not garbage collected)!!!!

that some people use it as autoptr implementations does not mean that interface-use = garbage-collector.

Show quoted text 9 lines
What would be interesting would be how garbage-collected Delphis handle this, can anyone help? Other than that, there is imo nothing else to say than that Pascal/Delphi(*) does not have explicitly scoped lifetimes with automatic destructor call for classes/objects at the moment.

argument is moot. .Net has provisions for object-instance assignment and internal "ref-counting" in the CLI and/or VM/interpreter. Java the same with its bytecode & jvm.

(*) maybe newer Delphis have the C# "using" keyword or something similar (maybe "with" recycling?). I would be interested to hear about that.

newer delphi's .net => both are different beasts entirely. win32 is NOT garbage collected.

kind regards,
Dimitri Smits

Reply by email

Marco van de Voort
to Thaddy

In our previous episode, Thaddy said:

Marco, there has been a Bhoem GC for delphi on my website for many years....

Please add it to the wiki, together with whatever experiences you have with it.

Reply by email

Thomas Schatzl
to Dimitri Smits: [snip] I was looking in the online docs for a mention of this behaviour regarding…

Hi,

Dimitri Smits wrote 10 lines
----- "Thomas Schatzl" schreef: > Imo: Jonas already stated, the time of the release for interfaces is > not > guaranteed - afaik the docs/specs only state that they "will be > released if > the reference count gets zero" (I have not read the docs for the > exact > wording). I was looking in the online docs for a mention of this behaviour

regarding

"managed" types and stack unwinding. cannot find it.

I'm quite sure that the Delphi/fpc docs do not specify anywhere that the managed types (interfaces, strings, dynamic arrays) are required to use refcounting at all and how and when their memory is given back to the system - these are implementation details as well. Except maybe for the guarantee that the memory they use will be given back eventually (and the Free() method of an object behind an interface will be called at some time if it has been detected as unreferenced).

tagging interfaces and their use in local variables are supposed to be

in

scope until exit of procedure (be it by reaching 'end;', explicit exit

or

exception). Then they are to be nilled (and _release'd), which may

result

in refcount=0 => Free.

You mention it: "supposed", "may result": I totally agree that there is no specified behavior for when memory is reclaimed (or the instance behind an interface finalized). Additionally you forgot one point: what if the runtime system keeps an extra reference to the interface for internal purposes? Then the program code cannot set the refcount to zero, so it will never be released immediately whatever you do. That behavior also conforms to the docs.

In effect, you (as a programmer) should be aware that you rely on implementation details if you do that, not something guaranteed by some document written by anyone.

Show quoted text 7 lines
> So what if the runtime system internally keeps a reference for en > masse garbage collection using a background thread? (I.e. the > refcount is a totally valid use case for > interfaces, and which btw, also makes the "stack ordering" issue > a moot point too. What most of you do not understand is that you CAN override the AddRef

and

Release methods to NOT do ref_counted free's. Also, Interface use automatic garbage collection, not even when you use a refcounted-with-free-on-0 interface.

Totally agree - this has only been an example about an implementation that conforms to the spec - that it still uses reference counts is only to demonstrate the point: even with refcounting, you can have a conformant implementation that has completely different runtime properties.

Btw, FPC has non-refcounted interfaces in the language (to keep being on-topic :). Only use COM/XPCOM interfaces if you need.

The argument for an external garbage collector can be made for object-references, not for interfaces (although variables of interface
type
can be the reason why an instance of the implementation is not garbage collected)!!!!

Well, the discussion was about interfaces alone (afaik), and what the runtime system does, or tries to do, is (imo) automatic dynamic memory management (generally called "garbage collection") for interfaces.

Just like the Java compiler and VM do with all objects, the FPC compiler and its runtime system does with interfaces.

that some people use it as autoptr implementations does not mean that interface-use = garbage-collector.

That has actually been another point of my previous post. You cannot/should not safely use this for autoptrs because it simply does not work (as in: is not guaranteed by the language, but maybe by the implementation. If that isn't the case either, bad luck).

Let's agree on that FPC has "limited garbage collection", okay? :)

Show quoted text 5 lines
> (*) maybe newer Delphis have the C# "using" keyword or something > similar > (maybe "with" recycling?). I would be interested to hear about that. newer delphi's .net => both are different beasts entirely. win32 is

NOT

garbage collected.

Let me rephrase the question: afaik the latest Delphi 2009(?) does have a native win32 compiler again, so did they add anything in that respect?

Thomas

Reply by email

DaWorm
to Marco van de Voort: In our previous episode, Thaddy said: Please add it to the wiki, together with whatever…

Rather than filling up the list with why this particular issue (something I've never done, and don't care about) behaves differently than Delphi, could someone instead focus on how to perform the needed task(s) with FPC?

FPC doesn't work the way Delphi does. Take that as a given. Then figure out how FPC can do the same task. If it is an easy compiler fix, fine, do that. If it isn't, don't gripe about it, instead figure out another way to do the job with what you have available. Wrap the code, IFDEF it, whatever it takes, get the job done, and move on. Pretty? Not particularly. Elegant? Certainly not. But it is far more productive than spending a week arguing back and forth on the mailing list.

Arguing about why FPC and Delphi are different, rather than implementing solutions within the framework of what is available, is a waste of everyone's time, including those of us who just want to read our mail.

Jeff.

Reply by email

Thaddy
to Marco van de Voort: In our previous episode, Thaddy said: Please add it to the wiki, together with whatever…
Marco van de Voort wrote 14 lines
In our previous episode, Thaddy said: > Marco, there has been a Bhoem GC for delphi on my website for many years.... Please add it to the wiki, together with whatever experiences you have with it. _______________________________________________ fpc-devel maillist - http://lists.freepascal.org/mailman/listinfo/fpc-devel No virus found in this incoming message. Checked by AVG - www.avg.com Version: 9.0.869 / Virus Database: 271.1.1/3251 - Release Date: 11/11/10 20:34:00

This GC interface unit was written by the delphi compiler engineering team, i.e. Barry Kelly and Barry Kelly :)
Will add it this weekend..... Experience: when your objects or classes are coded as explained, even things in need of finalization work as expected.
It is absolutely not useable for nubees though.

Reply by email