Fixing loopholes in DisposeOf

Fixing loopholes in DisposeOf

Anyone working with Delphi mobile (ARC) compilers knows that DisposeOf is fact of life and cannot be avoided. 

According to official roadmap "Linux compilers will be for Intel 64-bit server, LLVM-based and ARC-enabled"

So now seems like perfect time to address some existing loopholes in DisposeOf. Since it cannot be avoided, let's make it less error prone.

DisposeOf does not decrease reference count on calling reference
https://quality.embarcadero.com/browse/RSP-14681

DisposeOf should clean up instance inner managed types references
https://quality.embarcadero.com/browse/RSP-14682

First issue:

ARC way of saying you are done with object instance is nilling it. If you have code that should work under both compiler flavors you would call Free on it. On ARC compiler that translates to nilling the calling object reference. If there are no other strong references to that object it will undergo complete destruction process from calling destructor to releasing instance memory. But if you have to call DisposeOf on object instance, destructor will be called, but calling reference will not be nilled. So even if you don't have any other strong references to that object its memory will not get released at that point. 

Second issue:

Due to ARC nature, when you call DisposeOf and destroy the object, instance memory will not be deallocated so that reference counting can be safely completed when all strong references go out of scope. But since disposed object is no longer usable only thing needed for such process is objects outer memory shell. Inner managed fields are no longer needed. But they will still occupy memory waiting for ARC to finish its job. This can have quite undesired effects. Imagine your object instance holding large amounts of data in some string. You may think that after calling DisposeOf that memory will be cleared. Think again. It will not be released until ARC completes the process holding memory and producing undesireable side-effects depending on what kind of managed references you have there.
https://quality.embarcadero.com/browse/RSP-14681

Comments

  1. I'll have a second look, but at first sight these complaints are based on a partial understanding of the ARC model. Disposing objects should remain a corner-case exceptions.

    On the first point, this is just asking for Free to behave like FreeAndNil? That will never happen. DisponseAndNil could be an alternative option -- but not really convinced.

    On the second point, given the destructor is invoked, you can free "external" memory -- like the large string in your example. True, this could be partially automated, but hard to tell which should be the right boundary.

    ReplyDelete
  2. Marco Cantù Just a short comment. First one I am asking DisposeOf to work like DisposeOfAndNil on ARC compilers. Not Free.

    ReplyDelete
  3. Sure, I got it. But like Free has no knowledge of the calling reference (and this is why FreeAndNil is needed), likewise DisposeOf has no knowledge of the calling reference, and so it has no way to nil it. The option I see is introducing a parallel "DisposeOfAndNil". Now given this should not be a common coding style, I still have a few doubts.

    ReplyDelete
  4. The main issue that I see (and that in fact affected me in the past) is that DisposeOf sets the instance I am calling it on into the disposed state and calls the destructor but does not call CleanupInstance which then causes the disposed instance to hold any managed reference alive until its memory is really freed. This leads to cargo cult programmming where people now explicitly set everything to nil in their destructor that they would not have to otherwise. Or even worse calling DisposeOf on other references inside your instance.

    ReplyDelete
  5. Stefan Glienke And that comes back to Dalija's point that this generates the need for coding idioms which may be easily missed. We see, on one hand, that ARC is supposed to take care of all this, and on the other, that it does not.

    ReplyDelete
  6. I would like to have the possibility not to use ARC.

    ReplyDelete
  7. Marco Cantù I am not asking for DisposeOfAndNil method. Fix it in compiler. Just like Free on ARC translates to compiler injected nil assignment in case of DisposeOf it should first call DisposeOf and then assign nil.

    ReplyDelete
  8. I like the way as it is currently :D

    ReplyDelete
  9. Dalija Prasnikar > Since it cannot be avoided, let's make it more error prone.
    Usually one would want the programming process to be less error prone :-)

    I still think that I can manage memory manually better and more efficient without ARC.

    ReplyDelete
  10. Fortunately I've not had to touch ARC. And from all I've read, I'm going to do a lot to stay away from it... what a giant mess.

    ReplyDelete
  11. Asbjørn Heid ARC is your friend :D Give love to it :D

    ReplyDelete
  12. Leif Uneus Thanks. Edited. I was deciding between proof and prone and it came out the wrong way.

    ReplyDelete
  13. Horácio Filho You like what? I know you like ARC, but I hope you don't like it to be buggy leaving instances and their content sitting in memory once you are done with them. And I do hope that you are not using your objects once you have disposed them ;-)

    ReplyDelete
  14. Marco Cantù Hmm....

    People have been telling me "Give in to the ARC side, let it do the job". OK, so let ARC do the job. Currently when you call DisposeOf, ARC is not doing its job properly.

    And DisposeOf is far from being the corner case issue. You have to use it on every TComponent descendant instance in order to have its notification system working properly.

    The second you had to introduce direct calling of the destructor you broke the system (ARC).

    It has no place in ARC model, but I understand why it is needed in Delphi. All I want is to polish this part of the system so it would work more efficiently. 

    In first case nilling the reference is fully the ARC way. 

    In second case, you already have automated system - it is called CleanupInstance. The only thing you need to do is call it immediately after destructor is called instead of calling it before you release instance memory from heap. 

    In non ARC compiler sequence 

    destructor - cleanup - release 

    executes at once, and in ARC compiler (when DisposeOf is involved) you have "cut" it in wrong place as it should be 

    destructor - cleanup 
    release 

    and you have

    destructor
    cleanup - release

    ReplyDelete
  15. Dalija Prasnikar I wish you would show some actual real world examples where ARC screws everything all up. I have made lots of mobile apps and I haven't used DisposeOf yet. I haven't used Free either.
    I have never seen any mobile examples that used DisposeOf.

    ReplyDelete
  16. Douglas Rudd I am not saying that ARC screws everything up. I am saying that DisposeOf has flaws. If you didn't have to use it, that is fine. But you are using it indirectly because it is used in underlying frameworks, specifically FMX.

    ReplyDelete
  17. About TComponent and ARC, there are improvements coming (and some still possible). For example Berlin uses some more unsafe references.

    ReplyDelete

Post a Comment