Recent

Author Topic: Introducing a "using" keyword for automatic resource management  (Read 11721 times)

circular

  • Hero Member
  • *****
  • Posts: 4462
    • Personal webpage
Hello Lazarus and Free Pascal Community,

I've been exploring ways to enhance our programming experience with Pascal, focusing on memory management and code simplification. Today, I'd like to propose an idea that, to the best of my knowledge, hasn't been discussed in our forums before: introducing a using keyword for automatic resource management in Pascal.

Motivation
One of the common challenges we face in Pascal is managing the lifecycle of objects, especially ensuring that objects are properly freed even when exceptions occur. While our current approach with try...finally blocks is effective, it can lead to verbose and nested code, especially with multiple objects.

Proposal
My proposal is to introduce a using keyword that would automatically manage the lifetime of objects within a block of code. This syntax would be similar to C# syntax. Here's a basic illustration of the syntax and functionality:

Code: Pascal  [Select][+][-]
  1. using myInstance1 := TMyObject.Create(nil),
  2.       myInstance2 := TMyObject.Create(myInstance1) do
  3. begin
  4.   myInstance1.DoSomething;
  5.   myInstance2.DoSomething;
  6.   ...
  7. end;
  8.  
This would be functionally equivalent to:
Code: Pascal  [Select][+][-]
  1. myInstance1 := TMyObject.Create(nil);
  2. try
  3.   myInstance2 := TMyObject.Create(myInstance1);
  4.   try
  5.     myInstance1.DoSomething;
  6.     myInstance2.DoSomething;
  7.     ...
  8.   finally
  9.     myInstance2.Free;
  10.   end;
  11. finally
  12.   myInstance1.Free;
  13. end;
  14.  

Benefits
  • Simplicity: reduces boilerplate code, making it easier to read and maintain.
  • Safety: ensures that objects are automatically freed, even if an exception occurs, reducing the risk of memory leaks.

Compatibility Consideration
To address compatibility concerns, the using keyword could be designed to be context-sensitive. This means it would only be interpreted as the new keyword when followed by an identifier, ensuring minimal impact on existing codebases.

Based on your feedback, I'm open to refining this proposal, drafting a more detailed document for further discussion, and exploring the possibility of creating a proof-of-concept.

Thank you for considering this idea. I believe it could make a significant positive impact on our development experience and I look forward to your constructive feedback.
Conscience is the debugger of the mind

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 12010
  • Debugger - SynEdit - and more
    • wiki
Re: Introducing a "using" keyword for automatic resource management
« Reply #1 on: March 07, 2024, 10:56:19 am »
But what if your deallocation isn't Free/Destroy?

What if you have "ReleaseReference" (which then may or may not destroy) or "Close"/"Disconnect", or "Commit"?
Or if you need a simple "Clear" at the end?

Also, leaving out the "free"/"Destroy" is to unspecific. The reader has no clue what happens at the end.
Just to remember: "Free" can be reintroduced.... So there even is a difference between Free and Destroy.

alpine

  • Hero Member
  • *****
  • Posts: 1412
Re: Introducing a "using" keyword for automatic resource management
« Reply #2 on: March 07, 2024, 11:46:51 am »
I've been exploring ways to enhance our programming experience with Pascal, focusing on memory management and code simplification. Today, I'd like to propose an idea that, to the best of my knowledge, hasn't been discussed in our forums before: introducing a using keyword for automatic resource management in Pascal.
I believe FPC has sufficient instrumentation for you to implement such a management, e.g. TInterfacedObject, advanced records.

Thaddy's 'Smart pointers revisited': https://forum.lazarus.freepascal.org/index.php/topic,46306.msg469864.html#msg469864 (your participation)
"I'm sorry Dave, I'm afraid I can't do that."
—HAL 9000

gidesa

  • Full Member
  • ***
  • Posts: 207
Re: Introducing a "using" keyword for automatic resource management
« Reply #3 on: March 07, 2024, 11:51:38 am »
Hello Martin,
in last Free Pascal reference guide v. 3.2.2 there is no information on "ReleaseReference", "Close"/"Disconnect", or "Commit" for a generic TObject instance.
Where is the info about that?



circular

  • Hero Member
  • *****
  • Posts: 4462
    • Personal webpage
Re: Introducing a "using" keyword for automatic resource management
« Reply #4 on: March 07, 2024, 12:27:47 pm »
But what if your deallocation isn't Free/Destroy?

What if you have "ReleaseReference" (which then may or may not destroy) or "Close"/"Disconnect", or "Commit"?
Or if you need a simple "Clear" at the end?
Thank you, Martin_fr, for your insightful feedback. My proposal is about memory management of the objects and not their specific features. The using syntax would still be fit though if the destructor would actually Close/Disconnect. This would actually make sense to do so in many cases.

Quote
Also, leaving out the "free"/"Destroy" is to unspecific. The reader has no clue what happens at the end.
Just to remember: "Free" can be reintroduced.... So there even is a difference between Free and Destroy.
This is a good point regarding the implementation. We can refine the code and replace the call to Free by a conditional call to Destroy if that's more reliable.

I believe FPC has sufficient instrumentation for you to implement such a management, e.g. TInterfacedObject, advanced records.
Thank you, Alpine, for your input. You're right that FPC offers mechanisms like TInterfacedObject and advanced records for managing object lifecycles, which are indeed powerful tools. However, my proposal aims to reduce the need for defining special objects or dealing with the boilerplate code often associated with these mechanisms. Furthermore, while these existing solutions work well for objects with parameterless constructors, they become less convenient when dealing with constructors that require parameters.
Conscience is the debugger of the mind

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 12010
  • Debugger - SynEdit - and more
    • wiki
Re: Introducing a "using" keyword for automatic resource management
« Reply #5 on: March 07, 2024, 12:32:55 pm »
Hello Martin,
in last Free Pascal reference guide v. 3.2.2 there is no information on "ReleaseReference", "Close"/"Disconnect", or "Commit" for a generic TObject instance.
Where is the info about that?
Those are things you could have on your own objects.

E.g.
-  if you have a database, then you may need try/finally to make sure a transaction is written (or aborted). But you may not actually want to destroy the db object.
- Close/Disconnect could be on networking objects. A server may re-use an object to accept the next connection, so it wont destroy it, but must make sure it disconnected the previous connection.
- Similar for the other methods.
  (ReleaseReference is on TRefCountedObject in LazUtils / manually ref counted)

Destroying an object is only a small fraction of the use cases for try/finally.

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 12010
  • Debugger - SynEdit - and more
    • wiki
Re: Introducing a "using" keyword for automatic resource management
« Reply #6 on: March 07, 2024, 12:51:57 pm »
Thank you, Martin_fr, for your insightful feedback. My proposal is about memory management of the objects and not their specific features.

But that is a really minor usecase...

The more important memory management becomes, the more an app should avoid throw-away instances. But without throw away instances it becomes all about resource management.  And those are done via the features like close/disconnect/...

That is why there are things like pools for resource using objects. But in order to return them, the using code must finish up its work.

That said, yes a lot of code uses throw away instances when it really shouldn't. But should that be encouraged?



Looking at your proposal, I think it goes way to far from what we have.

The top part (calling constructors) really does not need anything that try/finally does not have. And try/finally does not make it more complex... In the case that a constructor can fail
Code: Pascal  [Select][+][-]
  1. a := nil; b:= nil; //....
  2. try
  3.   a:= foo.create;
  4.   b:= foo.create;
  5. //....
Yes, it is some extra nil assignments. But for something that is a rare special case, those are not enough to justify a new feature....

For the finally block, there is a difference.
You need a finally block, that does execute each statement (or top level compound statement) in it.

Currently finally only runs to the first failure. And wrapping each line in finally into its own try/except or try/finally.... Well, there is a case that might have enough value to discuss.

So then, if anything at all
Code: Pascal  [Select][+][-]
  1. try
  2. //...
  3. finally do each
  4.   b.free;
  5.   a.free;  
  6.   begin foo; bar; end; // one statement, not guarding the sub statements
  7. end;
And it would run each of the statement, even if any one in there raises an exception

Of course it creates the same problem like your proposal.
If there is an exception in each of the destructors, then at the end of that block you have several exceptions... Which one to raise for the code that follows?

rvk

  • Hero Member
  • *****
  • Posts: 6922
Re: Introducing a "using" keyword for automatic resource management
« Reply #7 on: March 07, 2024, 12:55:14 pm »
Code: Pascal  [Select][+][-]
  1.   myInstance2 := TMyObject.Create(myInstance1);
  2.   try
  3.     //...
  4.   finally
  5.     myInstance2.Free;
  6.   end;
  7.  
And isn't this considered "bad practice"? Freeing an object that belongs to another object.
In this case, myInstance1 would be the one who needs to free myInstance2, not directly though code.


alpine

  • Hero Member
  • *****
  • Posts: 1412
Re: Introducing a "using" keyword for automatic resource management
« Reply #8 on: March 07, 2024, 12:56:35 pm »
I believe FPC has sufficient instrumentation for you to implement such a management, e.g. TInterfacedObject, advanced records.
Thank you, Alpine, for your input. You're right that FPC offers mechanisms like TInterfacedObject and advanced records for managing object lifecycles, which are indeed powerful tools. However, my proposal aims to reduce the need for defining special objects or dealing with the boilerplate code often associated with these mechanisms. Furthermore, while these existing solutions work well for objects with parameterless constructors, they become less convenient when dealing with constructors that require parameters.
Not so inconvenient for constructing, consider (modeled after the Thaddy's one):
Code: Pascal  [Select][+][-]
  1. type
  2.   generic TAuto<T: class> = record
  3.   strict private type
  4.     TWatcher = class(TInterfacedObject)
  5.       private
  6.         FWatched: TObject;
  7.       public
  8.         constructor Create(const AWatched: TObject);
  9.         destructor Destroy; override;
  10.       end;
  11.   strict private
  12.     FValue: T;
  13.     FLifetime: IInterface;
  14.   public
  15.     constructor Create(const AValue: T);
  16.     class operator :=(const AValue: T): TAuto;
  17.     property Value: T read FValue;
  18.   end;
  19.  
  20. implementation
  21.  
  22. constructor TAuto.Create(const AValue: T);
  23. begin
  24.   FValue := AValue;
  25.   FLifetime := TWatcher.Create(FValue);
  26. end;
  27.  
  28. class operator TAuto. :=(const AValue: T): TAuto;
  29. begin
  30.   Result := TAuto.Create(AValue);
  31. end;

and then:
Code: Pascal  [Select][+][-]
  1.  var
  2.   A: specialize TAuto<TMyClass>;
  3. begin
  4.   A := TMyClass.Create({parameters ...});
  5.   A.Value.Foo(); {accessing the instance}
  6.  
  7.  
"I'm sorry Dave, I'm afraid I can't do that."
—HAL 9000

circular

  • Hero Member
  • *****
  • Posts: 4462
    • Personal webpage
Re: Introducing a "using" keyword for automatic resource management
« Reply #9 on: March 07, 2024, 03:25:29 pm »
And isn't this considered "bad practice"? Freeing an object that belongs to another object.
In this case, myInstance1 would be the one who needs to free myInstance2, not directly though code.
It goes without saying that in the example, myInstance2 uses myInstance1 rather than being a child of it.

But that is a really minor usecase...
In my experience, I find that it does happen a lot.

Quote
That said, yes a lot of code uses throw away instances when it really shouldn't. But should that be encouraged?
Are you suggesting to create a pool of objects for each and every class?

Looking at your proposal, I think it goes way to far from what we have.

Quote
Code: Pascal  [Select][+][-]
  1. a := nil; b:= nil; //....
  2. try
  3.   a:= foo.create;
  4.   b:= foo.create;
  5. //....
Yes, it is some extra nil assignments. But for something that is a rare special case, those are not enough to justify a new feature....
Well I do that sometimes, but I dont find it well structured. Now there are more cases where only one object is created, but even in that case, I would prefer the using statement.

Quote
If there is an exception in each of the destructors, then at the end of that block you have several exceptions... Which one to raise for the code that follows?
Handling this manually is cumbersome. The nested try...finally are the best solution in my view. But I don't like to write them.
Conscience is the debugger of the mind

alpine

  • Hero Member
  • *****
  • Posts: 1412
Re: Introducing a "using" keyword for automatic resource management
« Reply #10 on: March 07, 2024, 04:30:07 pm »
But that is a really minor usecase...
In my experience, I find that it does happen a lot.

Quote
That said, yes a lot of code uses throw away instances when it really shouldn't. But should that be encouraged?
Are you suggesting to create a pool of objects for each and every class?

Looking at your proposal, I think it goes way to far from what we have.
IMHO what Martin_fr wants to say is that the context here is very different from C#. What we have is a complete control over the memory management - just the opposite of C# (GC) where you don't have to destroy anything, but it happens what? : using statement - ensure the correct use of disposable objects

Quote
If there is an exception in each of the destructors, then at the end of that block you have several exceptions... Which one to raise for the code that follows?
Handling this manually is cumbersome. The nested try...finally are the best solution in my view. But I don't like to write them.
Speaking about personal preferences, I do not like to program in C#, so I'm using the FPC instead.
"I'm sorry Dave, I'm afraid I can't do that."
—HAL 9000

circular

  • Hero Member
  • *****
  • Posts: 4462
    • Personal webpage
Re: Introducing a "using" keyword for automatic resource management
« Reply #11 on: March 07, 2024, 04:57:44 pm »
What we have is a complete control over the memory management - just the opposite of C# (GC) where you don't have to destroy anything, but it happens what? : using statement - ensure the correct use of disposable objects
Yes, it is different because the GC can take some time to actually free the objects. In Pascal, the Free method plays the same role as Dispose and also frees the memory.

Quote
Speaking about personal preferences, I do not like to program in C#, so I'm using the FPC instead.
Preferences are subjective. Though I am not speaking of preferences, but of the fact that writing the nested try...finally is not satisfactory whatever the language. Ok, it does what we want, but it gets out of hand with only a few objects.

Speaking about personal preferences, I do not like to program in C# either. But would you argue against try...finally blocks because they are in C#?
« Last Edit: March 07, 2024, 05:04:35 pm by circular »
Conscience is the debugger of the mind

Thaddy

  • Hero Member
  • *****
  • Posts: 18672
  • Jungle wars. And failing health it seems.
Re: Introducing a "using" keyword for automatic resource management
« Reply #12 on: March 07, 2024, 06:05:27 pm »
The model you are referring to is called ARC memory management - automatic reference counting for everything - as opposed to GC memory management. Delphi has officially introduced it and subsequently abandoned it.
(They could not get it to work with their new LLVM back-end if I remember correctly)
But it still can be achieved with rigorous and strict keeping to only instantiation through and to com interfaces and variables.
« Last Edit: March 07, 2024, 06:13:00 pm by Thaddy »
Due to censorship, I changed this to "Nelly the Elephant". Keeps the message clear.

circular

  • Hero Member
  • *****
  • Posts: 4462
    • Personal webpage
Re: Introducing a "using" keyword for automatic resource management
« Reply #13 on: March 07, 2024, 06:13:11 pm »
Hi Thaddy,

I am not referring to reference counting. It is rather a syntax that simplifies a use case where we allocate and free an object. In fact, my idea here is that we don't need much reference counting nor garbage collection: we can have a lighter syntax that will include the freeing mechanism implicitly.
Conscience is the debugger of the mind

alpine

  • Hero Member
  • *****
  • Posts: 1412
Re: Introducing a "using" keyword for automatic resource management
« Reply #14 on: March 07, 2024, 06:23:56 pm »
Quote
Speaking about personal preferences, I do not like to program in C#, so I'm using the FPC instead.
Preferences are subjective. Though I am not speaking of preferences, but of the fact that writing the nested try...finally is not satisfactory whatever the language. Ok, it does what we want, but it gets out of hand with only a few objects.
Agreed.
That's why I'm using the TAuto<> extensively. Honestly, I'd like to have a static versions of the classes, like in C++, but I realize all the implications of that - so I should (better) live with the simpler ObjectPascal model.
 
Speaking about personal preferences, I do not like to program in C# either. But would you argue against try...finally blocks because they are in C#?
I would not.  Try...finally is just great. What I don't like is that the FPC (actually Delphi) becomes bit by bit like scripting crap which I despise.
"I'm sorry Dave, I'm afraid I can't do that."
—HAL 9000

 

TinyPortal © 2005-2018