Sign in Join

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

Mailing lists / fpc-pascal / 2023-05

Freeing memory with exceptions

36 messages · 8 people · –

Reply by email Subscribe to this list Your subscription

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

Hairy Pixels

I've never really used exceptions myself in Pascal (with the exception of breaking out of deeply recursive function calls) so I don't know all the rules.

In this example lets say you call Test() which allocates some memory and then calls out to another function in another unit which raises (the programmer doesn't know this because FPC doesn't mark the function as throwing exceptions). Now the Test() function will exit early before freeing the memory.

What are you supposed to do here? The only thing I can think of is to wrap every function in try..finally which COULD raise an exception but that's a huge mess because literally any function could raise.

====================================

procedure DoThis;
begin
  raise Exception.Create('dead');
end;
procedure Test;
begin
  TObject.Create;
  // call some code in other unit which raise an exception
  DoThis;
end;
begin
  try
    Test;
  except
    writeln('failed'); // TObject now leaks
  end;
end.

Regards,
Ryan Joseph

Michael Van Canneyt
to Hairy Pixels
Hairy Pixels wrote 25 lines
I've never really used exceptions myself in Pascal (with the exception of breaking out of deeply recursive function calls) so I don't know all the rules. In this example lets say you call Test() which allocates some memory and then calls out to another function in another unit which raises (the programmer doesn't know this because FPC doesn't mark the function as throwing exceptions). Now the Test() function will exit early before freeing the memory. What are you supposed to do here? The only thing I can think of is to wrap every function in try..finally which COULD raise an exception but that's a huge mess because literally any function could raise. ==================================== procedure DoThis; begin raise Exception.Create('dead'); end; procedure Test; begin TObject.Create; // call some code in other unit which raise an exception DoThis; end;

Your example will leak memory in any case, even if there is no exception, since you're not freeing the object anywhere..

Assuming the result of A is not used outside of Test, the following is the only solution:

procedure Test;
var
  A : TObject;
begin
   A:=TObject.Create;
   Try
     // call some code in other unit which raise an exception
     DoThis;
   finally
     A.Free
   end;
end;

You can try to use interfaces, they will be managed by the compiler.

Alternatively, using generics and management operators you can create a record that will automatically free the object at the end of the procedure.

Michael.

Hairy Pixels
to Michael Van Canneyt
On May 21, 2023, at 2:47 PM, Michael Van Canneyt via fpc-pascal wrote: Your example will leak memory in any case, even if there is no exception, since you're not freeing the object anywhere..

doh, dumb example on my behalf.

Show quoted text 19 lines
Assuming the result of A is not used outside of Test, the following is the only solution: procedure Test; var A : TObject; begin A:=TObject.Create; Try // call some code in other unit which raise an exception DoThis; finally A.Free end; end; You can try to use interfaces, they will be managed by the compiler.

This is what I was worried about, wrapping all functions or needing full ARC on all types. Very risk to opt in to this design I would say. I remain not a fan of exceptions. :)

Regards,
Ryan Joseph

Michael Van Canneyt
to Hairy Pixels
Hairy Pixels wrote 30 lines
> On May 21, 2023, at 2:47 PM, Michael Van Canneyt via fpc-pascal wrote: > > Your example will leak memory in any case, even if there is no exception, > since you're not freeing the object anywhere.. doh, dumb example on my behalf. > > Assuming the result of A is not used outside of Test, the following is the > only solution: > > procedure Test; > > var > A : TObject; > begin > A:=TObject.Create; > Try > // call some code in other unit which raise an exception > DoThis; > finally > A.Free > end; > end; > > You can try to use interfaces, they will be managed by the compiler. This is what I was worried about, wrapping all functions or needing full ARC on all types. Very risk to opt in to this design I would say. I remain not a fan of exceptions. :)

They're used all over the place in the RTL and FCL, so you better take them into account.

The compiler will do this wrapping anyway if you use ansistrings, so the approach with e.g. a generic record will not cause a lot of overhead in most cases.

Michael.

Steve Litt
to Michael Van Canneyt: Your example will leak memory in any case, even if there is no exception, since you're…

Michael Van Canneyt via fpc-pascal said on Sun, 21 May 2023 09:47:15 +0200 (CEST)

Show quoted text 58 lines
On Sun, 21 May 2023, Hairy Pixels via fpc-pascal wrote: > I've never really used exceptions myself in Pascal (with the > exception of breaking out of deeply recursive function calls) so I > don't know all the rules. > > In this example lets say you call Test() which allocates some memory > and then calls out to another function in another unit which raises > (the programmer doesn't know this because FPC doesn't mark the > function as throwing exceptions). Now the Test() function will exit > early before freeing the memory. > > What are you supposed to do here? The only thing I can think of is > to wrap every function in try..finally which COULD raise an > exception but that's a huge mess because literally any function > could raise. > > ==================================== > > procedure DoThis; > begin > raise Exception.Create('dead'); > end; > > procedure Test; > begin > TObject.Create; > // call some code in other unit which raise an exception > DoThis; > end; Your example will leak memory in any case, even if there is no exception, since you're not freeing the object anywhere.. Assuming the result of A is not used outside of Test, the following is the only solution: procedure Test; var A : TObject; begin A:=TObject.Create; Try // call some code in other unit which raise an exception DoThis; finally A.Free end; end; You can try to use interfaces, they will be managed by the compiler. Alternatively, using generics and management operators you can create a record that will automatically free the object at the end of the procedure. Michael.

Am I correctly understanding you that Pascal objects come off the heap and not the stack by default? Ouch! This is one of the C-isms I'd like to get away from. Is there a way to create objects from stack memory so there's no need to free?

NOTE: I've only rarely used OOP in Pascal.

Thanks,

SteveT

Steve Litt
Autumn 2022 featured book: Thriving in Tough Times http://www.troubleshooters.com/bookstore/thrive.htm

Steve Litt
to Michael Van Canneyt: Your example will leak memory in any case, even if there is no exception, since you're…

Michael Van Canneyt via fpc-pascal said on Sun, 21 May 2023 09:47:15 +0200 (CEST)

Show quoted text 16 lines
Assuming the result of A is not used outside of Test, the following is the only solution: procedure Test; var A : TObject; begin A:=TObject.Create; Try // call some code in other unit which raise an exception DoThis; finally A.Free end; end;

Perhaps there could be one procedure that frees and unwinds everything, since there will probably be more objects that need to be freed, and this procedure could be called by every finally.

SteveT

Steve Litt
Autumn 2022 featured book: Thriving in Tough Times http://www.troubleshooters.com/bookstore/thrive.htm

Michael Van Canneyt
to Steve Litt: Michael Van Canneyt via fpc-pascal said on Sun, 21 May 2023 09:47:15 +0200 (CEST) Am I…
Steve Litt wrote 65 lines
Michael Van Canneyt via fpc-pascal said on Sun, 21 May 2023 09:47:15 +0200 (CEST) > On Sun, 21 May 2023, Hairy Pixels via fpc-pascal wrote: > >> I've never really used exceptions myself in Pascal (with the >> exception of breaking out of deeply recursive function calls) so I >> don't know all the rules. >> >> In this example lets say you call Test() which allocates some memory >> and then calls out to another function in another unit which raises >> (the programmer doesn't know this because FPC doesn't mark the >> function as throwing exceptions). Now the Test() function will exit >> early before freeing the memory. >> >> What are you supposed to do here? The only thing I can think of is >> to wrap every function in try..finally which COULD raise an >> exception but that's a huge mess because literally any function >> could raise. >> >> ==================================== >> >> procedure DoThis; >> begin >> raise Exception.Create('dead'); >> end; >> >> procedure Test; >> begin >> TObject.Create; >> // call some code in other unit which raise an exception >> DoThis; >> end; > > Your example will leak memory in any case, even if there is no > exception, since you're not freeing the object anywhere.. > > Assuming the result of A is not used outside of Test, the following is > the only solution: > > procedure Test; > > var > A : TObject; > begin > A:=TObject.Create; > Try > // call some code in other unit which raise an exception > DoThis; > finally > A.Free > end; > end; > > You can try to use interfaces, they will be managed by the compiler. > > Alternatively, using generics and management operators you can > create a record that will automatically free the object at the > end of the procedure. > > Michael. Am I correctly understanding you that Pascal objects come off the heap and not the stack by default? Ouch! This is one of the C-isms I'd like to get away from.

C has no ++ has, and they're on the stack by default.

Is there a way to create objects from stack memory so there's no need to free?

Yes, you can use objects instead of classes. objects are on the stack. But they cannot be mixed with classes (for obvious reasons).

Michael.

Benito van der Zander
to Michael Van Canneyt: They're used all over the place in the RTL and FCL, so you better take them into account.…

Hi,

Show quoted text 5 lines
The compiler will do this wrapping anyway if you use ansistrings, so the approach with e.g. a generic record will not cause a lot of overhead in most cases.

But using strings or anything similar causes a lot of overhead

It is really bad

Bye,
Benito

Michael Van Canneyt wrote 53 lines
On Sun, 21 May 2023, Hairy Pixels via fpc-pascal wrote: > > >> On May 21, 2023, at 2:47 PM, Michael Van Canneyt via fpc-pascal >> wrote: >> >> Your example will leak memory in any case, even if there is no >> exception, >> since you're not freeing the object anywhere.. > > doh, dumb example on my behalf. > >> >> Assuming the result of A is not used outside of Test, the following >> is the >> only solution: >> >> procedure Test; >> >> var >> A : TObject; >> begin >>  A:=TObject.Create; >>  Try >>    // call some code in other unit which raise an exception >>    DoThis; >>  finally >>    A.Free >>  end; >> end; >> >> You can try to use interfaces, they will be managed by the compiler. > > This is what I was worried about, wrapping all functions or needing > full ARC on all types. Very risk to opt in to this design I would > say. I remain not a fan of exceptions. :) They're used all over the place in the RTL and FCL, so you better take them into account. The compiler will do this wrapping anyway if you use ansistrings, so the approach with e.g. a generic record will not cause a lot of overhead in most cases. Michael. _______________________________________________ fpc-pascal maillist  - https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Michael Van Canneyt
to Benito van der Zander
Benito van der Zander wrote 11 lines
Hi, > > The compiler will do this wrapping anyway if you use ansistrings, so the > approach with e.g. a generic record will not cause a lot of overhead in > most > cases. But using strings or anything similar causes a lot of overhead It is really bad

Depends on your code.

Nobody forces you to use ansistrings or unicodestrings. You can perfectly write your own library for string management if you are so inclined.

But I for one am grateful that I don't need to bother with memory management for strings, except perhaps on special occasions.

So I think it is really good.

Michael.

Benito van der Zander
to Michael Van Canneyt

Hi,

Depends on your code.

I wrote all my code in Delphi 4. From 1998 or so. I do not make new projects, only maintain old ones.

Delphi 4 felt much better.

Like take:

procedure test;
var s: string;
begin
   s:= 'abc';
end;

And Delphi 4 produces great looking output. 64 bytes. A few mov and push for exception handling:

FPC nowadays. 103 bytes. 4 functions calls for exception handling! 3 even if there are no exceptions. Unbelievable that it needs that many function calls

Show quoted text 5 lines
Nobody forces you to use ansistrings or unicodestrings. You can perfectly write your own library for string management if you are so inclined.

I wrote my own using pchars:
https://github.com/benibela/bbutils/blob/master/bbutils.pas#L328-L950

Sincerely,
Benito

Michael Van Canneyt wrote 33 lines
On Mon, 22 May 2023, Benito van der Zander via fpc-pascal wrote: > Hi, > >> >> The compiler will do this wrapping anyway if you use ansistrings, so >> the >> approach with e.g. a generic record will not cause a lot of overhead >> in most >> cases. > > But using strings or anything similar causes a lot of overhead > > It is really bad Depends on your code. Nobody forces you to use ansistrings or unicodestrings. You can perfectly write your own library for string management if you are so inclined. But I for one am grateful that I don't need to bother with memory management for strings, except perhaps on special occasions. So I think it is really good. Michael. _______________________________________________ fpc-pascal maillist  - https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Marco van de Voort
to Benito van der Zander
Benito van der Zander wrote 17 lines
Hi, > > Depends on your code. I wrote all my code in Delphi 4. From 1998 or so. I do not make new projects, only maintain old ones. Delphi 4 felt much better. Like take: procedure test; var s: string; begin   s:= 'abc'; end;

It is weird that your code calls setjmp? Are you using a non Windows platform?  Comparisons with Delphi should be done on Windows where the exception systems match. Apples to Apples please.

Benito van der Zander
to Marco van de Voort

Hi,

It is weird that your code calls setjmp? Are you using a non Windows platform?  Comparisons with Delphi should be done on Windows where the exception systems match. Apples to Apples please.

It is FPC on Linux.

And Delphi 4 on Linux (in WINE)

Even if it wants to do the Linux nonsense, FPC could at least inline fpc_setjmp in fpc_pushexceptaddr to make it only one function call.

I regret installing Linux. Everything worked better with Windows 98 and Delphi 4

Sincerely,
Benito

Marco van de Voort wrote 29 lines
On 23-5-2023 12:44, Benito van der Zander via fpc-pascal wrote: > Hi, >> >> Depends on your code. > > > I wrote all my code in Delphi 4. From 1998 or so. I do not make new > projects, only maintain old ones. > > Delphi 4 felt much better. > > Like take: > > procedure test; > var s: string; > begin >   s:= 'abc'; > end; It is weird that your code calls setjmp? Are you using a non Windows platform?  Comparisons with Delphi should be done on Windows where the exception systems match. Apples to Apples please. _______________________________________________ fpc-pascal maillist  - https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Marco van de Voort
to Benito van der Zander
Benito van der Zander wrote 8 lines
> It is weird that your code calls setjmp? Are you using a non Windows > platform?  Comparisons with Delphi should be done on Windows where > the exception systems match. Apples to Apples please. It is FPC on Linux. And Delphi 4 on Linux (in WINE)

Then also run FPC/win32 in wine for a real comparison.

Show quoted text 5 lines
 Even if it wants to do the Linux nonsense, FPC could at least inline fpc_setjmp in fpc_pushexceptaddr to make it only one function call. I regret installing Linux. Everything worked better with Windows 98 and Delphi 4

These kinds of statements are counter-productive.

Sven Barth
to Benito van der Zander: It is FPC on Linux. And Delphi 4 on Linux (in WINE) Even if it wants to do the Linux…

Benito van der Zander via fpc-pascal
schrieb am Mi., 24. Mai 2023, 13:00:

Show quoted text 11 lines
Hi, It is weird that your code calls setjmp? Are you using a non Windows platform? Comparisons with Delphi should be done on Windows where the exception systems match. Apples to Apples please. It is FPC on Linux. And Delphi 4 on Linux (in WINE)

As Marco said, please compare using the same *target* platform otherwise you're comparing apples with oranges due to different exception handling mechanisms between Windows and Linux.

Even if it wants to do the Linux nonsense, FPC could at least inline fpc_setjmp in fpc_pushexceptaddr to make it only one function call.

fpc_setjmp can not be unlined because a) it's an assembly function and b) *relies* on being a separate function.

And fpc_pushexceptaddr not only accesses private state of the system unit, it's also a larger function and thus inlining it would not provide any benefit. Also combining them would not work, because they are for different purposes.

The low level exception handling code is the way it is for reasons.

Regards,
Sven

Hairy Pixels
to Benito van der Zander: I wrote all my code in Delphi 4. From 1998 or so. I do not make new projects, only…

You must have $H+ on and those are AnsiStrings? Why is there exception handling involved with AnsiString? I guess it needs this just in case an exception is thrown somewhere in the call stack?

Show quoted text 10 lines
On May 23, 2023, at 5:44 PM, Benito van der Zander via fpc-pascal wrote: Like take: procedure test; var s: string; begin s:= 'abc'; end;

Regards,
Ryan Joseph

Hairy Pixels
to Michael Van Canneyt: They're used all over the place in the RTL and FCL, so you better take them into account.…
Show quoted text 8 lines
On May 21, 2023, at 11:03 PM, Michael Van Canneyt via fpc-pascal wrote: They're used all over the place in the RTL and FCL, so you better take them into account. The compiler will do this wrapping anyway if you use ansistrings, so the approach with e.g. a generic record will not cause a lot of overhead in most cases.

I wanted to add to my example. What about this? The exception now provides a challenge to clean up without ARC in all places. Very risky right? My sense is that exceptions are only safe for fully ARC languages or garbage collected.

var
  obj: TObject;
begin
  try
    obj := TObject.Create;
    // This object list will free the items in another part of the program
    if CheckSomething(...) then
      begin
        list.Add(obj);
       addedToList := true;
    end;
   // maybe we passed it to another class also...
    // Raises an exception in another unit, caller doesn't know!
    DoThis;
  finally
    // we need to remember to remove the item from the list now and
    // what happens if we passed the reference to another class?
    // we need to check if this actually happened also so declare a local var to check
    if addedToList then
      list.Delete(..);
    if passedToOtherClass then
      otherClass.obj := nil;
    obj.Free;
  end;
end.

Regards,
Ryan Joseph

Michael Van Canneyt
to Hairy Pixels
Hairy Pixels wrote 15 lines
> On May 21, 2023, at 11:03 PM, Michael Van Canneyt via fpc-pascal wrote: > > They're used all over the place in the RTL and FCL, so you better take them > into account. > > The compiler will do this wrapping anyway if you use ansistrings, so the > approach with e.g. a generic record will not cause a lot of overhead in most > cases. I wanted to add to my example. What about this? The exception now provides a challenge to clean up without ARC in all places. Very risky right? My sense is that exceptions are only safe for fully ARC languages or garbage collected.

I see no problem, I am used to this.

This is Pascal, you need to manage your memory and hence your references, this is true even in the absence of exceptions: In your example you would also need to remember to remove your instance from the list after you free it, even if there are no exceptions.

That is why I usually use TComponent as a base class. It has an owner-owned concept and when you free the toplevel component, all children are also freed. It also has a "free notification" mechanism: when a referenced object is freed, you can get notified and can take appropriate action.

If you use that correctly, there is very little to worry about.

Michael.

Sven Barth
to Hairy Pixels: You must have $H+ on and those are AnsiStrings? Why is there exception handling involved…

Hairy Pixels via fpc-pascal schrieb am
Mi., 24. Mai 2023, 14:10:

You must have $H+ on and those are AnsiStrings? Why is there exception handling involved with AnsiString? I guess it needs this just in case an exception is thrown somewhere in the call stack?

Because Ansi- and UnicodeString are managed types. *All* managed types managed string types, interfaces, Variants, managed records) must be finalized correctly even if an exception occurs.

Regards,
Sven

Benito van der Zander
to Marco van de Voort: Then also run FPC/win32 in wine for a real comparison. These kinds of statements are…

Hi

Then also run FPC/win32 in wine for a real comparison.

Or perhaps against modern C++ on Linux would also be a real comparison

> FPC could at least inline fpc_setjmp in fpc_pushexceptaddr to make it > only one function call. > These kinds of statements are counter-productive.

That is a very productive optimization idea

Cheers,
Benito

Marco van de Voort wrote 25 lines
On 24-5-2023 13:00, Benito van der Zander via fpc-pascal wrote: > >> It is weird that your code calls setjmp? Are you using a non Windows >> platform?  Comparisons with Delphi should be done on Windows where >> the exception systems match. Apples to Apples please. > > It is FPC on Linux. > > And Delphi 4 on Linux (in WINE) Then also run FPC/win32 in wine for a real comparison. >  Even if it wants to do the Linux nonsense, FPC could at least inline > fpc_setjmp in fpc_pushexceptaddr to make it only one function call. > > I regret installing Linux. Everything worked better with Windows 98 > and Delphi 4 These kinds of statements are counter-productive. _______________________________________________ fpc-pascal maillist  - https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Sven Barth
to Benito van der Zander

Benito van der Zander via fpc-pascal
schrieb am Do., 25. Mai 2023, 00:15:

Show quoted text 8 lines
Hi Then also run FPC/win32 in wine for a real comparison. Or perhaps against modern C++ on Linux would also be a real comparison

It wouldn't be, because that would compare two different languages. C++ doesn't have a concept of resource protection blocks like Object Pascal has.

Regards,
Sven

Hairy Pixels
to Sven Barth: Hairy Pixels via fpc-pascal schrieb am Mi., 24. Mai 2023, 14:10: Because Ansi- and…
Show quoted text 6 lines
On May 24, 2023, at 10:11 PM, Sven Barth via fpc-pascal wrote: You must have $H+ on and those are AnsiStrings? Why is there exception handling involved with AnsiString? I guess it needs this just in case an exception is thrown somewhere in the call stack? Because Ansi- and UnicodeString are managed types. *All* managed types managed string types, interfaces, Variants, managed records) must be finalized correctly even if an exception occurs.

That's a problem with exceptions then, they are baked into the language and impose a cost on all managed types now even if we use them or not. Even disabling the implicit stack frames (forgot what it's called) doesn't get around this right?

Regards,
Ryan Joseph

Hairy Pixels
to Michael Van Canneyt: I see no problem, I am used to this. This is Pascal, you need to manage your memory and…
Show quoted text 7 lines
On May 24, 2023, at 8:46 PM, Michael Van Canneyt via fpc-pascal wrote: I see no problem, I am used to this. This is Pascal, you need to manage your memory and hence your references, this is true even in the absence of exceptions: In your example you would also need to remember to remove your instance from the list after you free it, even if there are no exceptions.

My example is still not good. The problem I have is that once exceptions are used in a program calling any function now has a side effect of completely exiting the calling function so you need to be paranoid and wrap everything in try blocks just in case. A new added burden for manual memory management.

Why is this better than handling errors as values like a normal function result?

Rust is maybe the most major language which has not implemented exceptions but I'm seeing other new experimental languages go this way also. I've only used them in C# and Swift which are fully ARC but they appear to be a bad idea the industry is going against now after years of experience.

Regards,
Ryan Joseph

Sven Barth
to Hairy Pixels: That's a problem with exceptions then, they are baked into the language and impose a cost…

Hairy Pixels via fpc-pascal schrieb am
Do., 25. Mai 2023, 04:26:

Show quoted text 19 lines
> On May 24, 2023, at 10:11 PM, Sven Barth via fpc-pascal < > wrote: > > You must have $H+ on and those are AnsiStrings? Why is there exception handling involved with AnsiString? I guess it needs this just in case an exception is thrown somewhere in the call stack? > > Because Ansi- and UnicodeString are managed types. *All* managed types managed string types, interfaces, Variants, managed records) must be finalized correctly even if an exception occurs. > That's a problem with exceptions then, they are baked into the language and impose a cost on all managed types now even if we use them or not. Even disabling the implicit stack frames (forgot what it's called) doesn't get around this right?

Disabling implicit exception frames does exactly what it says in the tin and removes these automatically generated frames. This however opens you to potential memory leaks if an exception occurs, so one should think twice whether one uses this option.
Thankfully it's a local directive, so one can make this choice for every function.

But this is what "managed type" *means*: that the compiler and RTL will make sure that it's correctly freed as long as nothing unexpected happens.

Regards,
Sven

Michael Van Canneyt
to Hairy Pixels: That's a problem with exceptions then, they are baked into the language and impose a cost…
Hairy Pixels wrote 13 lines
> On May 24, 2023, at 10:11 PM, Sven Barth via fpc-pascal wrote: > > You must have $H+ on and those are AnsiStrings? Why is there exception handling involved with AnsiString? I guess it needs this just in case an exception is thrown somewhere in the call stack? > > Because Ansi- and UnicodeString are managed types. *All* managed types managed string types, interfaces, Variants, managed records) must be finalized correctly even if an exception occurs. > That's a problem with exceptions then, they are baked into the language and impose a cost on all managed types now even if we use them or not. Even disabling the implicit stack frames (forgot what it's called) doesn't get around this right?

Why do you insist it is a problem ?

Simply don't use managed types and don't use exceptions if you don't like the overhead they cause. It's still perfectly possible: avoid the sysutils unit and you're all set. The system unit does not use exceptions.

That 99.99% of people does use it, indicates they simply take the overhead because of the advantages that the managed types offer.

Which is not to say that FPC should not strive to minimize the overhead. Conceivably there is some gain possible on non-windows platforms.

Michael.

Sven Barth
to Hairy Pixels: My example is still not good. The problem I have is that once exceptions are used in a…

Hairy Pixels via fpc-pascal schrieb am
Do., 25. Mai 2023, 04:32:

Show quoted text 20 lines
> On May 24, 2023, at 8:46 PM, Michael Van Canneyt via fpc-pascal < > wrote: > > I see no problem, I am used to this. > > This is Pascal, you need to manage your memory and hence your references, this is true even in the absence of exceptions: In your example you would > also need to remember to remove your instance from the list after you free > it, even if there are no exceptions. My example is still not good. The problem I have is that once exceptions are used in a program calling any function now has a side effect of completely exiting the calling function so you need to be paranoid and wrap everything in try blocks just in case. A new added burden for manual memory management.

I'd say you're over thinking things. The main part is that you protect locally allocated memory (be it class instances or plain pointers) using "try... finally" and make sure other memory is for example deallocated in a destructor. If that's ensured then you can have exceptions bubble up as far as you're comfortable. E.g. in a LCL application if an exception is raised somewhere inside an event handler and it isn't explicitly handled there it will in the end result in a shown error dialog. Assuming all resources along the way were guarded by resource protection blocks life can just go on then when the user closes the dialog.

In essence: always protect the resources and only handle an exception explicitly if you can provide the user with a better error message than what the exception already provides or have can give them the ability to correct something.

Why is this better than handling errors as values like a normal function

result?

This would mean that every function (and procedure) that can fail or calls a routine that can fail would have to provide some kind of error channel and the user would then have to check this, cluttering their code with error checks (just look at how correctly error checked code with classic Pascal I/O looks with all the checks for IOResult). With exceptions you can simply concentrate on the meaty parts of your algorithms or business logic and let the language itself deal with the exceptional (pun intended).

Regards,
Sven

Michael Van Canneyt
to Hairy Pixels: My example is still not good. The problem I have is that once exceptions are used in a…
Hairy Pixels wrote 17 lines
> On May 24, 2023, at 8:46 PM, Michael Van Canneyt via fpc-pascal wrote: > > I see no problem, I am used to this. > > This is Pascal, you need to manage your memory and hence your references, this is true even in the absence of exceptions: In your example you would > also need to remember to remove your instance from the list after you free > it, even if there are no exceptions. My example is still not good. The problem I have is that once exceptions are used in a program calling any function now has a side effect of completely exiting the calling function so you need to be paranoid and wrap everything in try blocks just in case. A new added burden for manual memory management. Why is this better than handling errors as values like a normal function result?

Because it centralizes exit code in case of an error, and allows to have a meaningful result.

In C you need to do something like this:

Function MyFunction(out theresult : TResultType) : Integer;

begin
Allocate A
...
error condition 1:

     Free A, exit (1);
   ...
   Allocate B
   error condition 2:
     Free A, Free B, exit(2)
   Allocate C
   error condition 3:
    Free A, Free B, free C, exit(3);
   ...
   // etc
   // All went well, report 0
   theresult:=X;
   Free A, Free B, free C exit(0);
end.

(you can use a goto, which we all agree on is a bad idea to introduce in a language)

With exceptions, this becomes

Function MyFunction : TResultType;

begin

   try
     Allocate A
      ..
     allocate B
     ...
     Allocate C
     ...
     Result:=X;
   finally
     free A,B,C
   end;

end.

I'll take the latter any time.

Michael.

Sven Barth
to Mattias Gaertner

Mattias Gaertner via fpc-pascal schrieb
am Do., 25. Mai 2023, 08:23:

Show quoted text 10 lines
On Thu, 25 May 2023 07:55:39 +0200 Sven Barth via fpc-pascal wrote: >[...] > But this is what "managed type" *means*: that the compiler and RTL > will make sure that it's correctly freed as long as nothing > unexpected happens. ... and exceptions are expected.

Depends on the circumstances: in code with disabled implicit exception frames, but managed types exceptions are to be considered unexpected. But otherwise they are indeed expected.

Maybe better: As long as the user does not break the rules.

Agreed.

Regards,
Sven

Benito van der Zander
to Michael Van Canneyt: Why do you insist it is a problem ? Simply don't use managed types and don't use…

Hi,

That 99.99% of people does use it, indicates they simply take the overhead because of the advantages that the managed types offer.

Or they simply do not know about the overhead

Like I was writing all my code on Windows 98, and never noticed any overhead, until I started running benchmarks on  Linux.

Bye,
Benito

Michael Van Canneyt wrote 43 lines
On Thu, 25 May 2023, Hairy Pixels via fpc-pascal wrote: > > >> On May 24, 2023, at 10:11 PM, Sven Barth via fpc-pascal >> wrote: >> >> You must have $H+ on and those are AnsiStrings? Why is there >> exception handling involved with AnsiString? I guess it needs this >> just in case an exception is thrown somewhere in the call stack? >> >> Because Ansi- and UnicodeString are managed types. *All* managed >> types managed string types, interfaces, Variants, managed records) >> must be finalized correctly even if an exception occurs. >> > > That's a problem with exceptions then, they are baked into the language > and impose a cost on all managed types now even if we use them or > not. Even disabling the implicit stack frames (forgot what it's > called) doesn't > get around this right? Why do you insist it is a problem ? Simply don't use managed types and don't use exceptions if you don't like the overhead they cause. It's still perfectly possible: avoid the sysutils unit and you're all set. The system unit does not use exceptions. That 99.99% of people does use it, indicates they simply take the overhead because of the advantages that the managed types offer. Which is not to say that FPC should not strive to minimize the overhead. Conceivably there is some gain possible on non-windows platforms. Michael. _______________________________________________ fpc-pascal maillist  - https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Michael Van Canneyt
to Benito van der Zander
Benito van der Zander wrote 8 lines
Hi, > > That 99.99% of people does use it, indicates they simply take the overhead > because > of the advantages that the managed types offer. Or they simply do not know about the overhead

If people don't know there is overhead, that means it is negligible or unimportant.

So either way, all is well.

Michael.

Steve Litt
to Benito van der Zander: It is FPC on Linux. And Delphi 4 on Linux (in WINE) Even if it wants to do the Linux…

Benito van der Zander via fpc-pascal said on Wed, 24 May 2023 13:00:04 +0200

I regret installing Linux. Everything worked better with Windows 98 and Delphi 4

Why not just take your Win98 install disk, install win98, then take your Delphi 4 install disk, install Delphi 4, and you're all set. And you can recover the 39 bytes you lost to Linux.

SteveT

Steve Litt
Autumn 2022 featured book: Thriving in Tough Times http://www.troubleshooters.com/bookstore/thrive.htm

Thomas Kurz
to Benito van der Zander: Or they simply do not know about the overhead Like I was writing all my code on Windows…

I do not think that this discussion is of any value. If you need to care for every byte, you'd better consider using asm. In times of e-mail clients using far more than 100 MB of disk and RAM memory or simple Android apps having several dozens of megabytes because of the included trackers, it seems ridiculous to discuss about 39 bytes!

Earlier message 64 lines
----- Original Message ----- From: Benito van der Zander via fpc-pascal To: Sent: Thursday, May 25, 2023, 22:49:08 Subject: [fpc-pascal] Freeing memory with exceptions Hi, > That 99.99% of people does use it, indicates they simply take the > overhead because > of the advantages that the managed types offer. Or they simply do not know about the overhead Like I was writing all my code on Windows 98, and never noticed any overhead, until I started running benchmarks on Linux. Bye, Benito On 25.05.23 07:58, Michael Van Canneyt via fpc-pascal wrote: > On Thu, 25 May 2023, Hairy Pixels via fpc-pascal wrote: >>> On May 24, 2023, at 10:11 PM, Sven Barth via fpc-pascal >>> wrote: >>> You must have $H+ on and those are AnsiStrings? Why is there >>> exception handling involved with AnsiString? I guess it needs this >>> just in case an exception is thrown somewhere in the call stack? >>> Because Ansi- and UnicodeString are managed types. *All* managed >>> types managed string types, interfaces, Variants, managed records) >>> must be finalized correctly even if an exception occurs. >> That's a problem with exceptions then, they are baked into the language >> and impose a cost on all managed types now even if we use them or >> not. Even disabling the implicit stack frames (forgot what it's >> called) doesn't >> get around this right? > Why do you insist it is a problem ? > Simply don't use managed types and don't use exceptions if you don't > like the > overhead they cause. It's still perfectly possible: avoid the sysutils > unit > and you're all set. The system unit does not use exceptions. > That 99.99% of people does use it, indicates they simply take the > overhead because > of the advantages that the managed types offer. > Which is not to say that FPC should not strive to minimize the overhead. > Conceivably there is some gain possible on non-windows platforms. > Michael. > _______________________________________________ > fpc-pascal maillist - > https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Hairy Pixels
to Michael Van Canneyt: Because it centralizes exit code in case of an error, and allows to have a meaningful…
Show quoted text 26 lines
On May 25, 2023, at 1:10 PM, Michael Van Canneyt via fpc-pascal wrote: In C you need to do something like this: Function MyFunction(out theresult : TResultType) : Integer; begin Allocate A ... error condition 1: Free A, exit (1); ... Allocate B error condition 2: Free A, Free B, exit(2) Allocate C error condition 3: Free A, Free B, free C, exit(3); ... // etc // All went well, report 0 theresult:=X; Free A, Free B, free C exit(0); end.

Indeed this is an ideal example of why they were invented and it makes sense here. Honestly most of this mess could be cleaned up with smart pointers also and not upset control flow like exceptions do.

I'm doing work with Swift right now and in particular a web framework that makes lots of database calls, all of which can fail so we use exceptions everywhere. The thing that Swift does well and FPC does not, is that it labels the functions as "throws" and calls have a "try" keyword before them so you know how the control flow is actually working.

Because FPC doesn't do this any function could throw errors and thus those stack frames are inserted everywhere and add needless overhead when exceptions aren't used. It's also nice to not have hidden control flow potential on any function call

Regards,
Ryan Joseph

Michael Van Canneyt
to Hairy Pixels
Hairy Pixels wrote 34 lines
> On May 25, 2023, at 1:10 PM, Michael Van Canneyt via fpc-pascal wrote: > > > In C you need to do something like this: > > Function MyFunction(out theresult : TResultType) : Integer; > > begin > Allocate A > ... > error condition 1: > Free A, exit (1); > ... > Allocate B > error condition 2: > Free A, Free B, exit(2) > > Allocate C > error condition 3: > Free A, Free B, free C, exit(3); > ... > // etc > // All went well, report 0 > theresult:=X; > Free A, Free B, free C exit(0); > end. Indeed this is an ideal example of why they were invented and it makes sense here. Honestly most of this mess could be cleaned up with smart pointers also and not upset control flow like exceptions do. I'm doing work with Swift right now and in particular a web framework that makes lots of database calls, all of which can fail so we use exceptions everywhere. The thing that Swift does well and FPC does not, is that it labels the functions as "throws" and calls have a "try" keyword before them so you know how the control flow is actually working. Because FPC doesn't do this any function could throw errors and thus those stack frames are inserted everywhere and add needless overhead when exceptions aren't used. It's also nice to not have hidden control flow potential on any function call

The rule is simple in FPC: You should always assume exceptions everywhere.

Simply because system errors are also transformed to exceptions, and thus any function can indeed throw errors:

an "Access Violation" error can happen on any level of the code, or an "out of memory" or some other signal by the system or even CPU (think overflow).

If sysutils is used, these are transformed to an exception, regardless of whether the code itself uses exceptions to report errors or not.

The only way to know for sure there will not be an exception is not to use sysutils anywhere in your code or anyone elses code.

Delphi introduced a coupling between sysutils and exceptions, and now we're stuck with that.

Today, you'd need to write your RTL to get rid of them.

Michael.

Steve Litt
to Michael Van Canneyt

Michael Van Canneyt via fpc-pascal said on Tue, 30 May 2023 16:55:16 +0200 (CEST)

Show quoted text 71 lines
On Tue, 30 May 2023, Hairy Pixels via fpc-pascal wrote: > > >> On May 25, 2023, at 1:10 PM, Michael Van Canneyt via fpc-pascal >> wrote: >> >> >> In C you need to do something like this: >> >> Function MyFunction(out theresult : TResultType) : Integer; >> >> begin >> Allocate A >> ... >> error condition 1: >> Free A, exit (1); >> ... >> Allocate B >> error condition 2: >> Free A, Free B, exit(2) >> >> Allocate C >> error condition 3: >> Free A, Free B, free C, exit(3); >> ... >> // etc >> // All went well, report 0 >> theresult:=X; >> Free A, Free B, free C exit(0); >> end. > > Indeed this is an ideal example of why they were invented and it > makes sense here. Honestly most of this mess could be cleaned up > with smart pointers also and not upset control flow like exceptions > do. > > I'm doing work with Swift right now and in particular a web > framework that makes lots of database calls, all of which can fail > so we use exceptions everywhere. The thing that Swift does well and > FPC does not, is that it labels the functions as "throws" and calls > have a "try" keyword before them so you know how the control flow is > actually working. > > Because FPC doesn't do this any function could throw errors and thus > those stack frames are inserted everywhere and add needless overhead > when exceptions aren't used. It's also nice to not have hidden > control flow potential on any function call The rule is simple in FPC: You should always assume exceptions everywhere. Simply because system errors are also transformed to exceptions, and thus any function can indeed throw errors: an "Access Violation" error can happen on any level of the code, or an "out of memory" or some other signal by the system or even CPU (think overflow). If sysutils is used, these are transformed to an exception, regardless of whether the code itself uses exceptions to report errors or not. The only way to know for sure there will not be an exception is not to use sysutils anywhere in your code or anyone elses code. Delphi introduced a coupling between sysutils and exceptions, and now we're stuck with that. Today, you'd need to write your RTL to get rid of them. Michael.

Would this work?...

Have a global self-kill object containing a list of every object that's been allocated. After every successful allocation, call the self-kill object method to add the object onto the list. On every successful free, call self-kill method to remove that object from the list. On programmer-defined exceptions, call self-kill's method to free everything on the list and then free itself, after which the program can terminate.

I don't know about other operating systems, but on Linux a crashed program gives up all its memory, even leaked memory, upon termination, so I'm not sure why this attention to memory leak on abnormal termination is even necessary.

SteveT

Steve Litt
Autumn 2022 featured book: Thriving in Tough Times http://www.troubleshooters.com/bookstore/thrive.htm

Sven Barth
to Steve Litt

Steve Litt via fpc-pascal schrieb am Di.,
30. Mai 2023, 21:57:

Show quoted text 5 lines
I don't know about other operating systems, but on Linux a crashed program gives up all its memory, even leaked memory, upon termination, so I'm not sure why this attention to memory leak on abnormal termination is even necessary.

An exception does not have to end with the program's termination. And if the program does not terminate due to an exception it makes sense to avoid memory leaks as the program might be long running.

Regards,
Sven