From: Florian Weimer on
It looks as if I might need to do some Ada maintenance programming
soon. I can use the GNAT from GCC 4.3.

Are there any new ways to ensure resource cleanup?

I tried to use Ada.Finalization.Limited_Controlled in the past, but
there were several issues with it: there was some run-time overhead
(because of the tag and because the finalizer is abort-deferred, which
seemed to defeat inlining and scalar replacement of aggregates), it
was impossible to instantiate generics containing such types below the
library level, and having multiple different types inherting from
Limited_Controled in the same package often resulted in multi-dispatch
errors (and using 'Class required exposing the tagged nature of the
type in the interface, with has other drawbacks).

Explicit cleanup using cleanup subprograms is okay, too, provided that
there is some tool to ensure that they are used properly. This is
despite the rather cumbersome syntax:

declare
X : Object;

begin
Init (X);
begin
Make_Use_Of (X);
exception
when others =>
Cleanup (X);
raise;
end;
Cleanup (X);
end;

But I'd really have a tool that ensures that Init and Cleanup calls
are properly paired in this way.

Are there other approaches I don't know about yet?
From: Robert A Duff on
Florian Weimer <fw(a)deneb.enyo.de> writes:

> It looks as if I might need to do some Ada maintenance programming
> soon. I can use the GNAT from GCC 4.3.
>
> Are there any new ways to ensure resource cleanup?
>
> I tried to use Ada.Finalization.Limited_Controlled in the past, but
> there were several issues with it: there was some run-time overhead

AdaCore is working on a more efficient implementation of finalization.

> (because of the tag and because the finalizer is abort-deferred, ...

I think if you use the appropriate pragma Restrictions, so the
compiler knows there are no aborts, it will avoid the cost
of deferring and undeferring aborts (which is quite high
on some systems).

>...which
> seemed to defeat inlining

Inlining of what? The Initialize and Finalize calls?
It seems feasible to inline them in most cases, but
I'm not sure if GNAT is capable of that. Try it.

>...and scalar replacement of aggregates), it
> was impossible to instantiate generics containing such types below the
> library level,...

Ada 2005 allows such nesting (with or without generics).

.....and having multiple different types inherting from
> Limited_Controled in the same package often resulted in multi-dispatch
> errors (and using 'Class required exposing the tagged nature of the
> type in the interface, with has other drawbacks).

Yes, but in my experience these are minor issues.
Another way to avoid multi-dispatch is to put the
procedure in a nested package. Both workarounds
are annoying, I admit.

> Explicit cleanup using cleanup subprograms is okay, too, provided that
> there is some tool to ensure that they are used properly. This is
> despite the rather cumbersome syntax:
>
> declare
> X : Object;
>
> begin
> Init (X);
> begin
> Make_Use_Of (X);
> exception
> when others =>
> Cleanup (X);
> raise;
> end;
> Cleanup (X);
> end;
>
> But I'd really have a tool that ensures that Init and Cleanup calls
> are properly paired in this way.

You could wrap this is a procedure:

procedure With_Cleanup (Action : not null access procedure (...));

so you only have to write the above pattern once (per type that needs
cleanup), and you can call it with any Action procedure you like.

Note that this is slightly different from using Limited_Controlled,
because it does not clean up in case of abort. If you don't use
abort, then this is not an issue.

> Are there other approaches I don't know about yet?

I think that about covers it.

- Bob
From: Jean-Pierre Rosen on
Florian Weimer a �crit :

> declare
> X : Object;
>
> begin
> Init (X);
> begin
> Make_Use_Of (X);
> exception
> when others =>
> Cleanup (X);
> raise;
> end;
> Cleanup (X);
> end;
>
> But I'd really have a tool that ensures that Init and Cleanup calls
> are properly paired in this way.
>
Such a tool exists, of course ;-). AdaControl rule Unsafe_Paired_Calls.

--
---------------------------------------------------------
J-P. Rosen (rosen(a)adalog.fr)
Visit Adalog's web site at http://www.adalog.fr
From: Florian Weimer on
* Robert A. Duff:

>> (because of the tag and because the finalizer is abort-deferred, ...
>
> I think if you use the appropriate pragma Restrictions, so the
> compiler knows there are no aborts, it will avoid the cost
> of deferring and undeferring aborts (which is quite high
> on some systems).

What is the appropriate pragma? This doesn't seem to have an effect:

pragma Restrictions (No_Asynchronous_Control);

>>...which
>> seemed to defeat inlining
>
> Inlining of what? The Initialize and Finalize calls?

Most of the benefit of inlining them, because there are multiple
subprogram calls involved, including indirect ones. I doubt GCC
treats them as intrinsics, so they interfere with register allocation
etc. GCC doesn't seem to be able to devirtualize the implicit call to
Finalize, either.

I don't understand why GNAT needs to maintain a separate finalization
list, either. C++ use regular exception handling for this task.

> You could wrap this is a procedure:
>
> procedure With_Cleanup (Action : not null access procedure (...));
>
> so you only have to write the above pattern once (per type that needs
> cleanup), and you can call it with any Action procedure you like.

Yes, I need to try that. Back when the original code was written,
anonymous access-to-subprogram types were still rather buggy.
From: Robert A Duff on
Florian Weimer <fw(a)deneb.enyo.de> writes:

> * Robert A. Duff:
>
>>> (because of the tag and because the finalizer is abort-deferred, ...
>>
>> I think if you use the appropriate pragma Restrictions, so the
>> compiler knows there are no aborts, it will avoid the cost
>> of deferring and undeferring aborts (which is quite high
>> on some systems).
>
> What is the appropriate pragma? This doesn't seem to have an effect:
>
> pragma Restrictions (No_Asynchronous_Control);

That one says "I promise not to say 'with Ada.Asynchronous_Task_Control'",
which is not related to aborts. It is considered obsolescent, because
there's now a more general No_Dependence restriction.

To get rid of aborts, I think you need both No_Abort_Statements
and Max_Asynchronous_Select_Nesting => 0. I believe this will
cause the overhead of abort deferral to go away, but to be sure,
you should try it.

>>>...which
>>> seemed to defeat inlining
>>
>> Inlining of what? The Initialize and Finalize calls?
>
> Most of the benefit of inlining them, because there are multiple
> subprogram calls involved, including indirect ones. I doubt GCC
> treats them as intrinsics, so they interfere with register allocation
> etc. GCC doesn't seem to be able to devirtualize the implicit call to
> Finalize, either.
>
> I don't understand why GNAT needs to maintain a separate finalization
> list, either. C++ use regular exception handling for this task.

The new implementation of finalization I mentioned avoids those
lists. Except that lists are needed in the case of heap-allocated
objects, because they need to be finalized when the scope of the
access type is left (unless finalized early by Unchecked_Deallocation).
C++ does not require that, so is easier to implement, at the cost
of possibly missing some finalizations.

But anyway, efficient finalization is most important for stack-allocated
objects.

>> You could wrap this is a procedure:
>>
>> procedure With_Cleanup (Action : not null access procedure (...));
>>
>> so you only have to write the above pattern once (per type that needs
>> cleanup), and you can call it with any Action procedure you like.
>
> Yes, I need to try that. Back when the original code was written,
> anonymous access-to-subprogram types were still rather buggy.

And inefficient, because they used trampolines. GNAT got rid of
trampolines except in some corner cases. Note that trampolines
will cause your program to crash if DEP is enabled on windows
(or the equivalent feature on Linux). There's a restriction
for that, too -- search the GNAT docs for "trampoline".

- Bob