Watch, Follow, &
Connect with Us

For forums, blogs and more please visit our
Developer Tools Community.


Welcome, Guest
Guest Settings
Help

Thread: Brewaking a cycle with Weak in Win32



Permlink Replies: 11 - Last Post: Jul 13, 2017 8:43 AM Last Post By: Markus Humm
Markus Humm

Posts: 5,113
Registered: 11/9/03
Brewaking a cycle with Weak in Win32
Click to report abuse...   Click to reply to this thread Reply
  Posted: Jul 12, 2017 1:08 PM
Hello,

given the following case would it be possible to break the described
cycle by using weak?

Case:

A class "ClassA" inherits from TInterfacedObject and implements some
interface "IntfA" we defined. This class creates some object from
another class "ClassB" and holds the object reference to this created
instance.

This instance needs some of the methods of the class which had created
the instance. So the creating class passes "self" to the created class
which provides a parameter for this which has the type of "IntfA".

Now if we free any "ClassA" instance OnBeforeDestroy of it is being
called and in that it is being found out that the reference count of it
is still 1, as the instance of "ClassB" it holds and which is freed in
the not yet run destructor still havs a valid interface reference to
"IntfA".

If we would try to free the "ClassB" object before that one would nil
the interface reference, the reference count would go to 0 and the
object would be freed, which would be bad as well.

Currently we deactivated refcounting in "ClassA" by overwriting _AddRef
and _Release, but the question now is, could that be avoided if the
interface reference field in "ClassB" is being declared [weak]?
Passing self as a const parameter doesn't help.

Or is there any other way to break the cy<cle?
(ours does work, but it feels a little bit unclean, it really would be
nice to have interfaces without reference counting in this scenario, but
that's something EMBT didn't want a few years ago already)

Greetings

Markus
Dalija Prasnikar

Posts: 2,325
Registered: 11/9/99
Re: Brewaking a cycle with Weak in Win32
Click to report abuse...   Click to reply to this thread Reply
  Posted: Jul 12, 2017 1:22 PM   in response to: Markus Humm in response to: Markus Humm
Markus Humm wrote:
Hello,

given the following case would it be possible to break the described
cycle by using weak?

Case:

A class "ClassA" inherits from TInterfacedObject and implements some
interface "IntfA" we defined. This class creates some object from
another class "ClassB" and holds the object reference to this created
instance.

This instance needs some of the methods of the class which had created
the instance. So the creating class passes "self" to the created class
which provides a parameter for this which has the type of "IntfA".

Now if we free any "ClassA" instance OnBeforeDestroy of it is being
called and in that it is being found out that the reference count of it
is still 1, as the instance of "ClassB" it holds and which is freed in
the not yet run destructor still havs a valid interface reference to
"IntfA".

If we would try to free the "ClassB" object before that one would nil
the interface reference, the reference count would go to 0 and the
object would be freed, which would be bad as well.

Currently we deactivated refcounting in "ClassA" by overwriting _AddRef
and _Release, but the question now is, could that be avoided if the
interface reference field in "ClassB" is being declared [weak]?
Passing self as a const parameter doesn't help.

Or is there any other way to break the cy<cle?
(ours does work, but it feels a little bit unclean, it really would be
nice to have interfaces without reference counting in this scenario, but
that's something EMBT didn't want a few years ago already)

If ClassA instance is sole owner of ClassB instance then using [weak] would
solve cycle issue, but you must have reference counting enabled for ClassB.

--
Dalija Prasnikar
https://twitter.com/dalijap
https://plus.google.com/+DalijaPrasnikar
Markus Humm

Posts: 5,113
Registered: 11/9/03
Re: Brewaking a cycle with Weak in Win32
Click to report abuse...   Click to reply to this thread Reply
  Posted: Jul 12, 2017 1:25 PM   in response to: Dalija Prasnikar in response to: Dalija Prasnikar
Am 12.07.2017 um 22:22 schrieb Dalija Prasnikar:
Markus Humm wrote:
Hello,

given the following case would it be possible to break the described
cycle by using weak?

Case:

A class "ClassA" inherits from TInterfacedObject and implements some
interface "IntfA" we defined. This class creates some object from
another class "ClassB" and holds the object reference to this created
instance.

This instance needs some of the methods of the class which had created
the instance. So the creating class passes "self" to the created class
which provides a parameter for this which has the type of "IntfA".

Now if we free any "ClassA" instance OnBeforeDestroy of it is being
called and in that it is being found out that the reference count of it
is still 1, as the instance of "ClassB" it holds and which is freed in
the not yet run destructor still havs a valid interface reference to
"IntfA".

If we would try to free the "ClassB" object before that one would nil
the interface reference, the reference count would go to 0 and the
object would be freed, which would be bad as well.

Currently we deactivated refcounting in "ClassA" by overwriting _AddRef
and _Release, but the question now is, could that be avoided if the
interface reference field in "ClassB" is being declared [weak]?
Passing self as a const parameter doesn't help.

Or is there any other way to break the cy<cle?
(ours does work, but it feels a little bit unclean, it really would be
nice to have interfaces without reference counting in this scenario, but
that's something EMBT didn't want a few years ago already)

If ClassA instance is sole owner of ClassB instance then using [weak] would
solve cycle issue, but you must have reference counting enabled for ClassB.

Hello,

you're a bit quick! ;-)
I just reposted this to fix the mistyped subject line... ;-)
Ok, what does this mean? Call B currently is a normal class inheriting
from TObject. Would that need to change?
Or would ClassA simply not override the reference counting?
Would my case be described in your upcoming book?
(hint at my student: no, the book would be too late for citation by you
;-) )

Greetings

Markus
Dalija Prasnikar

Posts: 2,325
Registered: 11/9/99
Re: Brewaking a cycle with Weak in Win32
Click to report abuse...   Click to reply to this thread Reply
  Posted: Jul 12, 2017 1:31 PM   in response to: Markus Humm in response to: Markus Humm
Markus Humm wrote:
Am 12.07.2017 um 22:22 schrieb Dalija Prasnikar:
Markus Humm wrote:
Hello,

given the following case would it be possible to break the described
cycle by using weak?

Case:

A class "ClassA" inherits from TInterfacedObject and implements some
interface "IntfA" we defined. This class creates some object from
another class "ClassB" and holds the object reference to this created
instance.

This instance needs some of the methods of the class which had created
the instance. So the creating class passes "self" to the created class
which provides a parameter for this which has the type of "IntfA".

Now if we free any "ClassA" instance OnBeforeDestroy of it is being
called and in that it is being found out that the reference count of it
is still 1, as the instance of "ClassB" it holds and which is freed in
the not yet run destructor still havs a valid interface reference to
"IntfA".

If we would try to free the "ClassB" object before that one would nil
the interface reference, the reference count would go to 0 and the
object would be freed, which would be bad as well.

Currently we deactivated refcounting in "ClassA" by overwriting _AddRef
and _Release, but the question now is, could that be avoided if the
interface reference field in "ClassB" is being declared [weak]?
Passing self as a const parameter doesn't help.

Or is there any other way to break the cy<cle?
(ours does work, but it feels a little bit unclean, it really would be
nice to have interfaces without reference counting in this scenario, but
that's something EMBT didn't want a few years ago already)

If ClassA instance is sole owner of ClassB instance then using [weak] would
solve cycle issue, but you must have reference counting enabled for ClassB.

Hello,

you're a bit quick! ;-)
I just reposted this to fix the mistyped subject line... ;-)
Ok, what does this mean? Call B currently is a normal class inheriting
from TObject. Would that need to change?
Or would ClassA simply not override the reference counting?
Would my case be described in your upcoming book?
(hint at my student: no, the book would be too late for citation by you
;-) )

No, Class B does not have to be reference counted - it has been a long day
so I misread :)

Appropriate example:

type
  IParent = interface
  end;
 
  TParent = class(TInterfacedObject, IParent)
  public
    var Child: TChild;
    constructor Create;
    destructor Destroy; override;
  end;
 
  TChild = class(TObject)
  public
    [weak] var Parent: IParent;
    constructor Create(const AParent: IParent);
    destructor Destroy; override;
  end;
 
constructor TParent.Create;
begin
  inherited;
  Child := TChild.Create(Self);
end;
 
destructor TParent.Destroy;
begin
  Child.Free;
  inherited;
end;
 
constructor TChild.Create(const AParent: IParent);
begin
  inherited Create;
  Parent := AParent;
end;
 
destructor TChild.Destroy;
begin
  inherited;
end;


Yes, such scenarios among others will be covered in the book :)

--
Dalija Prasnikar
https://twitter.com/dalijap
https://plus.google.com/+DalijaPrasnikar
Remy Lebeau (Te...


Posts: 9,447
Registered: 12/23/01
Re: Brewaking a cycle with Weak in Win32
Click to report abuse...   Click to reply to this thread Reply
  Posted: Jul 12, 2017 1:53 PM   in response to: Markus Humm in response to: Markus Humm
Markus Humm wrote:

Ok, what does this mean? Call B currently is a normal class inheriting
from TObject. Would that need to change?

No. ClassA can Create and Free it like any other object.

Or would ClassA simply not override the reference counting?

If ClassB has a weak reference to ClassA, then no, do not override it
anymore.

--
Remy Lebeau (TeamB)
Dalija Prasnikar

Posts: 2,325
Registered: 11/9/99
Re: Brewaking a cycle with Weak in Win32
Click to report abuse...   Click to reply to this thread Reply
  Posted: Jul 12, 2017 1:26 PM   in response to: Dalija Prasnikar in response to: Dalija Prasnikar
Example code:

type
  IParent = interface
  end;
 
  IChild = interface
  end;
 
  TParent = class(TInterfacedObject, IParent)
  public
    var Child: IChild;
    constructor Create;
    destructor Destroy; override;
  end;
 
  TChild = class(TInterfacedObject, IChild)
  public
    [weak] var Parent: IParent;
    constructor Create(const AParent: IParent);
    destructor Destroy; override;
  end;
 
constructor TParent.Create;
begin
  inherited;
  Child := TChild.Create(Self);
end;
 
destructor TParent.Destroy;
begin
  inherited;
end;
 
constructor TChild.Create(const AParent: IParent);
begin
  inherited Create;
  Parent := AParent;
end;
 
destructor TChild.Destroy;
begin
  inherited;
end;


If there is a need that child is destroyed before its parent, this can be
done by setting its reference to nil in Parent Destroy or OnBeforeDestruction.

--
Dalija Prasnikar
https://twitter.com/dalijap
https://plus.google.com/+DalijaPrasnikar
Markus Humm

Posts: 5,113
Registered: 11/9/03
Re: Brewaking a cycle with Weak in Win32
Click to report abuse...   Click to reply to this thread Reply
  Posted: Jul 12, 2017 1:30 PM   in response to: Dalija Prasnikar in response to: Dalija Prasnikar
Am 12.07.2017 um 22:26 schrieb Dalija Prasnikar:
Example code:

type
  IParent = interface
  end;
 
  IChild = interface
  end;
 
  TParent = class(TInterfacedObject, IParent)
  public
    var Child: IChild;
    constructor Create;
    destructor Destroy; override;
  end;
 
  TChild = class(TInterfacedObject, IChild)
  public
    [weak] var Parent: IParent;
    constructor Create(const AParent: IParent);
    destructor Destroy; override;
  end;
 
constructor TParent.Create;
begin
  inherited;
  Child := TChild.Create(Self);
end;
 
destructor TParent.Destroy;
begin
  inherited;
end;
 
constructor TChild.Create(const AParent: IParent);
begin
  inherited Create;
  Parent := AParent;
end;
 
destructor TChild.Destroy;
begin
  inherited;
end;


If there is a need that child is destroyed before its parent, this can be
done by setting its reference to nil in Parent Destroy or OnBeforeDestruction.

Hello,

thanks for this thorough explanation.
Now I eagerly wait until the book's available in order to check if I
found the code above in that book ;-)

I'll got to bed now... ;-)

Greetings

Markus
Dalija Prasnikar

Posts: 2,325
Registered: 11/9/99
Re: Brewaking a cycle with Weak in Win32
Click to report abuse...   Click to reply to this thread Reply
  Posted: Jul 12, 2017 1:54 PM   in response to: Markus Humm in response to: Markus Humm
Markus Humm wrote:

I'll got to bed now... ;-)

Same here :)

I hope my second example is appropriate for your case...
if I misunderstood anything please ask...

--
Dalija Prasnikar
https://twitter.com/dalijap
https://plus.google.com/+DalijaPrasnikar
Remy Lebeau (Te...


Posts: 9,447
Registered: 12/23/01
Re: Brewaking a cycle with Weak in Win32
Click to report abuse...   Click to reply to this thread Reply
  Posted: Jul 12, 2017 1:52 PM   in response to: Dalija Prasnikar in response to: Dalija Prasnikar
Dalija Prasnikar wrote:

If ClassA instance is sole owner of ClassB instance then using [weak]
would solve cycle issue, but you must have reference counting enabled
for ClassB.

If ClassA creates the ClassB object, it stands to reason that ClassA
will also be destroying the object, too. In which case, normal object
management applies, ie give ClassA a destructor that destroys the
ClassB object, no reference counting necessary.

Unless ClassB is also derived from TInterfacedObject, in which case
yes, it has a reference count, so ClassA should have a strong reference
to the object and let normal reference counting semantics destroy it.

--
Remy Lebeau (TeamB)
Dalija Prasnikar

Posts: 2,325
Registered: 11/9/99
Re: Brewaking a cycle with Weak in Win32
Click to report abuse...   Click to reply to this thread Reply
  Posted: Jul 12, 2017 1:56 PM   in response to: Remy Lebeau (Te... in response to: Remy Lebeau (Te...
Remy Lebeau (TeamB) wrote:
Dalija Prasnikar wrote:

If ClassA instance is sole owner of ClassB instance then using [weak]
would solve cycle issue, but you must have reference counting enabled
for ClassB.

If ClassA creates the ClassB object, it stands to reason that ClassA
will also be destroying the object, too. In which case, normal object
management applies, ie give ClassA a destructor that destroys the
ClassB object, no reference counting necessary.

Unless ClassB is also derived from TInterfacedObject, in which case
yes, it has a reference count, so ClassA should have a strong reference
to the object and let normal reference counting semantics destroy it.

All true... I am already sleeping over here...

--
Dalija Prasnikar
https://twitter.com/dalijap
https://plus.google.com/+DalijaPrasnikar
Remy Lebeau (Te...


Posts: 9,447
Registered: 12/23/01
Re: Brewaking a cycle with Weak in Win32
Click to report abuse...   Click to reply to this thread Reply
  Posted: Jul 12, 2017 1:49 PM   in response to: Markus Humm in response to: Markus Humm
Markus Humm wrote:

given the following case would it be possible to break the described
cycle by using weak?

It would have been more useful if you had shown your actual code
instead of a long description of the code.

In any case, since ClassA is a reference-counted class, and the
lifetime of ClassB does not outlive its owning ClassA (right?), then
yes, ClassB needs a weak reference to ClassA, eg:

type
  ClassB = class;
 
  IntfA = interface
    procedure DoSomething;
  end;
 
  ClassA = class(TInterfacedObject, IntfA)
  private
    fClassB: ClassB;
  public
    constructor Create;
    destructor Destroy; override;
    procedure DoSomething;
  end;
 
  ClassB = class
  private
    [weak] fOwner: IntfA;
  public
    constructor Create(const Owner: IntfA);
    procedure DoSomething;
  end;
 
 
constructor ClassA.Create;
begin
  inherited;
  fClassB := ClassB.Create(Self);
end;
 
destructor ClassA.Destroy;
begin
  fClassB.Free;
  inherited;
end;
 
constructor ClassB.Create(const Owner: IntfA);
begin
  inherited Create;
  fOwner := AOwner;
end;
 
procedure ClassB.DoSomething;
begin
  fOwner.DoSomething;
end;

Note that [weak] was not added to the desktop compilers (Windows and
OSX) until 10.1 Berlin. For earlier versions, you have to use an
untyped Pointer instead, type-casting it when needed:

type
  ClassB = class;
 
  IntfA = interface
    procedure DoSomething;
  end;
 
  ClassA = class(TInterfacedObject, IntfA)
  private
    fClassB: ClassB;
  public
    constructor Create;
    destructor Destroy; override;
    procedure DoSomething;
  end;
 
{$IF DEFINED(WEAKINTFREF) OR (CompilerVersion >= 31)}
  {$DEFINE HAS_WEAK_INTF}
{$IFEND}
 
  ClassB = class
  private
    {$IFDEF HAS_WEAK_INTF}
    [weak] fOwner: IntfA;
    {$ELSE}
    fOwner: Pointer;
    {$ENDIF}
  public
    constructor Create(const Owner: IntfA);
    procedure DoSomething;
  end;
 
 
constructor ClassA.Create;
begin
  inherited;
  fClassB := ClassB.Create(Self);
end;
 
destructor ClassA.Destroy;
begin
  fClassB.Free;
  inherited;
end;
 
constructor ClassB.Create(const Owner: IntfA);
begin
  inherited Create;
  fOwner := AOwner;
end;
 
procedure ClassB.DoSomething;
begin
  {$IFDEF HAS_WEAK_INTF}fOwner{$ELSE}IntfA(fOwner){$ENDIF}.DoSomething;
end;

Alternatively, store an object pointer instead of an interface pointer,
then you only have to worry about [weak] on ARC-based compilers:

type
  ClassB = class;
 
  ClassA = class(TInterfacedObject, IntfA)
  private
    fClassB: ClassB;
  public
    constructor Create;
    destructor Destroy; override;
  end;
 
  ClassB = class
  private
    {$IFDEF AUTOREFCOUNT}[weak]{$ENDIF} fOwner: ClassA;
  public
    constructor Create(const Owner: ClassA);
  end;
 
 
constructor ClassA.Create;
begin
  inherited;
  fClassB := ClassB.Create(Self);
end;
 
destructor ClassA.Destroy;
begin
  fClassB.Free;
  inherited;
end;
 
constructor ClassB.Create(const Owner: ClassA);
begin
  inherited Create;
  fOwner := AOwner;
end;
 
procedure ClassB.DoSomething;
begin
  fOwner.DoSomething;
end;


Now if we free any "ClassA" instance OnBeforeDestroy of it is being
called

I assume you mean BeforeDestruction() instead.

and in that it is being found out that the reference count of it is
still 1, as the instance of "ClassB" it holds and which is freed in
the not yet run destructor still havs a valid interface reference to
"IntfA".

Yes, that is exactly the type of circular reference that you need to
use a weak reference to break.

If we would try to free the "ClassB" object before that one would nil
the interface reference, the reference count would go to 0 and the
object would be freed, which would be bad as well.

Only the owning ClassA object should free the ClassB object, which
means ClassB should not hold a strong reference to IntfA, only a weak
reference.

Currently we deactivated refcounting in "ClassA" by overwriting
_AddRef and _Release

You do not need to do that, nor should you.

but the question now is, could that be avoided if the interface
reference field in "ClassB" is being declared [weak]?

Yes.

Passing self as a const parameter doesn't help.

It does help, but only for purposes of passing the ClassA object to
ClassB withou touching the reference count. It does not help ClassB
store a weak reference to ClassA.

--
Remy Lebeau (TeamB)
Markus Humm

Posts: 5,113
Registered: 11/9/03
Re: Brewaking a cycle with Weak in Win32
Click to report abuse...   Click to reply to this thread Reply
  Posted: Jul 13, 2017 8:43 AM   in response to: Markus Humm in response to: Markus Humm
Hello,

thanks to both of you answering this.
You confirmed what I already thought would work.

Greetings

Markus
Legend
Helpful Answer (5 pts)
Correct Answer (10 pts)

Server Response from: ETNAJIVE02