* A suggestion for resource management
@ 2010-08-21 16:20 Florian Weimer
2010-08-21 19:07 ` Dmitry A. Kazakov
` (2 more replies)
0 siblings, 3 replies; 20+ messages in thread
From: Florian Weimer @ 2010-08-21 16:20 UTC (permalink / raw)
Here's a proposal for a resource management pragma, tentatively called
pragma Scoped. It is similar to the scope(exit) directive in D, the
C++ scope guard idom, and (to a lesser degree) Go's rescue statement.
The syntax is:
pragma Scoped (procedure_call_statment);
The legality rules and static semantics are the same as for the old
(one-argument) pragma Debug implemented by GNAT, with the additional
restriction that Scoped pragmas may only appear in a
handled_sequence_of_statements or in a declarative_part. (This
additional restriction is necessary because otherwise, the pragma
could be executed multiple times, and it is difficult to decide what
should happen in this case.)
Regarding dynamic semantics: When a Scoped pragma which is part of a
handled_sequence_of_statements is executed or when it is elaborated as
part of a declarative_part, the procedure_name or procedure_prefix and
actual parameters of the procedure_call_statment are evaluated.
If the corresponding handled_sequence_of_statements completes
subsequently (or if the declarative_part completes abnormally, without
ever entering the handled_sequence_of_statements), the procedure call
is performed, using the prefix and parameters as evaluated previously.
After that, objects are finalized as necessary. (Finalization of
parameters is a reason why there are no specific pragmas for normal
and abnormal completion---the arguments would have to be finalized,
despite the pragma suggesting that nothing happens in that case.) The
procedure calls happen in the reverse order of the execution of the
pragmas, and before objects are finalized which were elaborated prior
to execution of the pragma.
If the procedure call itself completes abnormally, Program_Error is
raised. (Program_Error is not raised if the earlier parameter
evaluation completes abnormally.)
The example at the end of section B.4 could use the Scoped pragma in
this way:
procedure Test_External_Formats is
...
COBOL_File : File_Type;
...
begin
Open (COBOL_File, Name => "Some_File");
pragma Scoped (Close (COBOL_File));
loop
...
exception
when End_Error => ...
end Test_External_Formats;
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: A suggestion for resource management
2010-08-21 16:20 A suggestion for resource management Florian Weimer
@ 2010-08-21 19:07 ` Dmitry A. Kazakov
2010-08-21 19:47 ` Florian Weimer
2010-08-21 20:34 ` Niklas Holsti
2010-08-22 13:09 ` stefan-lucks
2 siblings, 1 reply; 20+ messages in thread
From: Dmitry A. Kazakov @ 2010-08-21 19:07 UTC (permalink / raw)
On Sat, 21 Aug 2010 18:20:29 +0200, Florian Weimer wrote:
> procedure Test_External_Formats is
> ...
> COBOL_File : File_Type;
> ...
>
> begin
> Open (COBOL_File, Name => "Some_File");
> pragma Scoped (Close (COBOL_File));
>
> loop
> ...
> exception
> when End_Error => ...
> end Test_External_Formats;
What is wrong with having a destructor for File_Type? If the language is to
be extended why not to fix that first?
--
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: A suggestion for resource management
2010-08-21 19:07 ` Dmitry A. Kazakov
@ 2010-08-21 19:47 ` Florian Weimer
2010-08-21 20:53 ` Dmitry A. Kazakov
0 siblings, 1 reply; 20+ messages in thread
From: Florian Weimer @ 2010-08-21 19:47 UTC (permalink / raw)
* Dmitry A. Kazakov:
> On Sat, 21 Aug 2010 18:20:29 +0200, Florian Weimer wrote:
>
>> procedure Test_External_Formats is
>> ...
>> COBOL_File : File_Type;
>> ...
>>
>> begin
>> Open (COBOL_File, Name => "Some_File");
>> pragma Scoped (Close (COBOL_File));
>>
>> loop
>> ...
>> exception
>> when End_Error => ...
>> end Test_External_Formats;
>
> What is wrong with having a destructor for File_Type? If the language is to
> be extended why not to fix that first?
You can't always change libraries in that way. Adding destructors has
both syntactic and run-time overhead. The run-time overhead can be
minimized by a sufficiently advanced compiler (perhaps relying on
whole-program optimization to detect the lack of task aborts). I'm
more concerned with the syntactic overhead.
There are some cases where the destructor solution doesn't really
apply. For instance, the cleanup operation might need two parameters,
one denoting the object, and a second one decribing where to put it.
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: A suggestion for resource management
2010-08-21 16:20 A suggestion for resource management Florian Weimer
2010-08-21 19:07 ` Dmitry A. Kazakov
@ 2010-08-21 20:34 ` Niklas Holsti
2010-08-21 21:01 ` Florian Weimer
2010-08-22 13:09 ` stefan-lucks
2 siblings, 1 reply; 20+ messages in thread
From: Niklas Holsti @ 2010-08-21 20:34 UTC (permalink / raw)
Florian Weimer wrote:
> Here's a proposal for a resource management pragma, tentatively called
> pragma Scoped. It is similar to the scope(exit) directive in D, the
> C++ scope guard idom, and (to a lesser degree) Go's rescue statement.
>
> The syntax is:
>
> pragma Scoped (procedure_call_statment);
I strongly dislike the idea of using "pragma" to introduce such
non-obvious flow of control.
Flow of control should be clearly represented in the syntactic
structure, with textual order matching execution order. The current
exception-handling syntax is good in that respect.
> Regarding dynamic semantics: When a Scoped pragma which is part of a
> handled_sequence_of_statements is executed or when it is elaborated as
> part of a declarative_part, the procedure_name or procedure_prefix and
> actual parameters of the procedure_call_statment are evaluated.
... and, if I understand your suggestion, the evaluated parameters are
saved somewhere, in case they are needed for the later call. I think
this can cause large run-time overheads, as well as confusion and
errors. How "deeply" are the parameters saved? The semantics will depend
on whether a type is passed by value or by access, right? Also, saved
parameters of access type may become unusable (hanging pointers) if the
statement_sequence deallocates the accessed object. The state of the
relevant objects may also change in some other non-obvious but
significant ways that invalidate the saved parameter values.
You did not discuss if "pragma Scoped" could be executed conditionally,
as in:
Do_Something ( ... Success => Ok);
if Ok then
pragma Scoped (Undo_Something (...));
end if;
Is this included in your suggestion?
> The example at the end of section B.4 could use the Scoped pragma in
> this way:
>
> procedure Test_External_Formats is
> ...
> COBOL_File : File_Type;
> ...
>
> begin
> Open (COBOL_File, Name => "Some_File");
> pragma Scoped (Close (COBOL_File));
>
> loop
> ...
> exception
> when End_Error => ...
> end Test_External_Formats;
If I understand your suggestion correctly, more or less the same
behaviour can be achieved in current Ada by a statement block:
procedure Test_External_Formats is
...
COBOL_File : File_Type;
...
begin
Open (COBOL_File, Name => "Some_File");
begin
loop
...
end loop;
exception
when End_Error => ...
end;
Close (COBOL_File);
end Test_External_Formats;
I find that form clear and brief enough, although I admit that nesting
depth may become awkwardly large (the remedy is to split the procedure).
I'm not sure if your suggestion is that the "pragma" procedure call
(here, Close (COBOL_File)) should be performed also if an exception is
propagated from the exception handler of the
handled_sequence_of_statements. My version above does not perform Close
in that case. If Close should be performed in that case, I would very
much prefer new syntax for a "finally" structure placed at the end of
the handled_sequence_of_statements, after the exception handler if any,
over the "pragma" suggestion.
In summary, the present language already has tools for this need, either
finalization (as Dmitry suggested) or the nested block shown above.
Cases where the present tools are not ideal could be covered by a new
"finally" construct.
--
Niklas Holsti
Tidorum Ltd
niklas holsti tidorum fi
. @ .
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: A suggestion for resource management
2010-08-21 19:47 ` Florian Weimer
@ 2010-08-21 20:53 ` Dmitry A. Kazakov
2010-08-21 21:09 ` Florian Weimer
0 siblings, 1 reply; 20+ messages in thread
From: Dmitry A. Kazakov @ 2010-08-21 20:53 UTC (permalink / raw)
On Sat, 21 Aug 2010 21:47:05 +0200, Florian Weimer wrote:
> * Dmitry A. Kazakov:
>
>> On Sat, 21 Aug 2010 18:20:29 +0200, Florian Weimer wrote:
>>
>>> procedure Test_External_Formats is
>>> ...
>>> COBOL_File : File_Type;
>>> ...
>>>
>>> begin
>>> Open (COBOL_File, Name => "Some_File");
>>> pragma Scoped (Close (COBOL_File));
>>>
>>> loop
>>> ...
>>> exception
>>> when End_Error => ...
>>> end Test_External_Formats;
>>
>> What is wrong with having a destructor for File_Type? If the language is to
>> be extended why not to fix that first?
>
> You can't always change libraries in that way. Adding destructors has
> both syntactic and run-time overhead.
I don't see why. Without class-wide objects there is no overhead,
everything is statically known. Neither there is any syntactic overhead.
File_Type is extended just once and then used everywhere. In fact it is
your solution that has distributed overhead (and error-prone too), because
the pragma has to be used everywhere a file is opened. BTW, file handle
File_Type should not be opened, it should be constructed. There should be
no invalid File_Type, at least the library should not encourage them as the
Ada library does and in your example follows.
> The run-time overhead can be
> minimized by a sufficiently advanced compiler (perhaps relying on
> whole-program optimization to detect the lack of task aborts). I'm
> more concerned with the syntactic overhead.
>
> There are some cases where the destructor solution doesn't really
> apply. For instance, the cleanup operation might need two parameters,
> one denoting the object, and a second one decribing where to put it.
I would use mix-in in such rare cases. However, I try to avoid clean-ups.
It is a bad pattern.
--
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: A suggestion for resource management
2010-08-21 20:34 ` Niklas Holsti
@ 2010-08-21 21:01 ` Florian Weimer
2010-08-22 10:53 ` Niklas Holsti
2010-08-22 11:32 ` Georg Bauhaus
0 siblings, 2 replies; 20+ messages in thread
From: Florian Weimer @ 2010-08-21 21:01 UTC (permalink / raw)
* Niklas Holsti:
> Florian Weimer wrote:
>> Here's a proposal for a resource management pragma, tentatively called
>> pragma Scoped. It is similar to the scope(exit) directive in D, the
>> C++ scope guard idom, and (to a lesser degree) Go's rescue statement.
>>
>> The syntax is:
>>
>> pragma Scoped (procedure_call_statment);
>
> I strongly dislike the idea of using "pragma" to introduce such
> non-obvious flow of control.
In a sense, it's just repeating what Unchecked_Deallaction does behind
the scenes.
> Flow of control should be clearly represented in the syntactic
> structure, with textual order matching execution order. The current
> exception-handling syntax is good in that respect.
I understand that goal. However, this approach separates the
initialization from the clean-up code, and the clean-up code might
need repeating in several places. This cannot be good.
>> Regarding dynamic semantics: When a Scoped pragma which is part of a
>> handled_sequence_of_statements is executed or when it is elaborated as
>> part of a declarative_part, the procedure_name or procedure_prefix and
>> actual parameters of the procedure_call_statment are evaluated.
>
> ... and, if I understand your suggestion, the evaluated parameters are
> saved somewhere, in case they are needed for the later call. I think
> this can cause large run-time overheads, as well as confusion and
> errors. How "deeply" are the parameters saved?
renaming-declaration-deeply, so it really depends on the types
involved. This happens with any procedure call, so the overhead
cannot be huge.
> The semantics will depend on whether a type is passed by value or by
> access, right?
Not really. The idea is to avoid strange action-at-a-distance if the
actual arguments change their value between execution of the pragma
and the embedded procedure call.
> Also, saved parameters of access type may become unusable (hanging
> pointers) if the statement_sequence deallocates the accessed object.
Ordinary procedure calls suffer from this problem, too. As far as I
can see, this can only happen with Unchecked_Deallaction, so it is
acceptable.
> You did not discuss if "pragma Scoped" could be executed
> conditionally, as in:
>
> Do_Something ( ... Success => Ok);
> if Ok then
> pragma Scoped (Undo_Something (...));
> end if;
>
> Is this included in your suggestion?
The branches of an if statement aren't a
handled_sequence_of_statements, so it's not allowed.
>> The example at the end of section B.4 could use the Scoped pragma in
>> this way:
>>
>> procedure Test_External_Formats is
>> ...
>> COBOL_File : File_Type;
>> ...
>>
>> begin
>> Open (COBOL_File, Name => "Some_File");
>> pragma Scoped (Close (COBOL_File));
>>
>> loop
>> ...
>> exception
>> when End_Error => ...
>> end Test_External_Formats;
>
> If I understand your suggestion correctly, more or less the same
> behaviour can be achieved in current Ada by a statement block:
>
> procedure Test_External_Formats is
> ...
> COBOL_File : File_Type;
> ...
> begin
> Open (COBOL_File, Name => "Some_File");
> begin
> loop
> ...
> end loop;
> exception
> when End_Error => ...
> end;
> Close (COBOL_File);
You also need this here:
exeption
when others =>
Close (COBOL_File);
raise;
plus another level of nesting because you shouldn't call Close when
Open results in an exception, and when Close itself raises an
exception.
> end Test_External_Formats;
>
> I find that form clear and brief enough, although I admit that
> nesting depth may become awkwardly large (the remedy is to split the
> procedure).
No one will write such code unless forced to do so by some static
analysis tool. It's rather messy. It's actually pretty difficult to
see if all clean-ups occur as needed. If the subprogram needs two
objects which require clean-up, it will not fit on a single screen
anymore. Splitting it up into two outer subprograms which perform
initialization and cleanup for each object and another one which does
the bulk of the work will not add much value to the reader, either.
> I'm not sure if your suggestion is that the "pragma" procedure call
> (here, Close (COBOL_File)) should be performed also if an exception is
> propagated from the exception handler of the
> handled_sequence_of_statements. My version above does not perform
> Close in that case. If Close should be performed in that case, I would
> very much prefer new syntax for a "finally" structure placed at the
> end of the handled_sequence_of_statements, after the exception handler
> if any, over the "pragma" suggestion.
"finally" was state-of-the-art in 1995. I'm not sure if it still can
be considered good language design. Most languages which strive for
exception safety are gradually moving to different constructs (even
Java). "finally" is much less syntactically heavyweight than nested
exception handling blocks, but programmers still do not use it
consistently.
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: A suggestion for resource management
2010-08-21 20:53 ` Dmitry A. Kazakov
@ 2010-08-21 21:09 ` Florian Weimer
2010-08-22 6:40 ` Dmitry A. Kazakov
2010-08-23 23:22 ` Randy Brukardt
0 siblings, 2 replies; 20+ messages in thread
From: Florian Weimer @ 2010-08-21 21:09 UTC (permalink / raw)
* Dmitry A. Kazakov:
>> You can't always change libraries in that way. Adding destructors has
>> both syntactic and run-time overhead.
>
> I don't see why. Without class-wide objects there is no overhead,
> everything is statically known.
You need escape analysis or inter-procedural scalar replacement to
eliminate the tag. But it certainly can be done, and the additional
word will not hurt in all but the most extreme cases.
(There's a GNAT-specific inefficiency, but it is reportedly being
tackled.)
> BTW, file handle File_Type should not be opened, it should be
> constructed. There should be no invalid File_Type,
There are cases where you need the explicit Close operation, so you
still end up with an invalid state.
How would you handle an exception raised by construction, without
extending the handler to cover undue regions of code?
>> There are some cases where the destructor solution doesn't really
>> apply. For instance, the cleanup operation might need two parameters,
>> one denoting the object, and a second one decribing where to put it.
>
> I would use mix-in in such rare cases. However, I try to avoid clean-ups.
> It is a bad pattern.
And you suggest to use Finalize procedures instead? Hmm.
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: A suggestion for resource management
2010-08-21 21:09 ` Florian Weimer
@ 2010-08-22 6:40 ` Dmitry A. Kazakov
2010-08-23 23:22 ` Randy Brukardt
1 sibling, 0 replies; 20+ messages in thread
From: Dmitry A. Kazakov @ 2010-08-22 6:40 UTC (permalink / raw)
On Sat, 21 Aug 2010 23:09:38 +0200, Florian Weimer wrote:
> * Dmitry A. Kazakov:
>
>> BTW, file handle File_Type should not be opened, it should be
>> constructed. There should be no invalid File_Type,
>
> There are cases where you need the explicit Close operation, so you
> still end up with an invalid state.
It is difficult to construct such cases. One I can remember is the socket
closed from outside to break blocking recv.
> How would you handle an exception raised by construction, without
> extending the handler to cover undue regions of code?
Roll back everything elaborated above and propagate.
>>> There are some cases where the destructor solution doesn't really
>>> apply. For instance, the cleanup operation might need two parameters,
>>> one denoting the object, and a second one decribing where to put it.
>>
>> I would use mix-in in such rare cases. However, I try to avoid clean-ups.
>> It is a bad pattern.
>
> And you suggest to use Finalize procedures instead? Hmm.
Yes, it requires a bit more up front work, but it always pays off. It is
much simpler to maintain.
--
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: A suggestion for resource management
2010-08-21 21:01 ` Florian Weimer
@ 2010-08-22 10:53 ` Niklas Holsti
2010-08-22 15:29 ` Florian Weimer
2010-08-22 16:12 ` Florian Weimer
2010-08-22 11:32 ` Georg Bauhaus
1 sibling, 2 replies; 20+ messages in thread
From: Niklas Holsti @ 2010-08-22 10:53 UTC (permalink / raw)
Florian Weimer wrote:
> * Niklas Holsti:
>
>> Florian Weimer wrote:
>>> Here's a proposal for a resource management pragma, tentatively called
>>> pragma Scoped. It is similar to the scope(exit) directive in D, the
>>> C++ scope guard idom, and (to a lesser degree) Go's rescue statement.
>>>
>>> The syntax is:
>>>
>>> pragma Scoped (procedure_call_statment);
>> I strongly dislike the idea of using "pragma" to introduce such
>> non-obvious flow of control.
>
> In a sense, it's just repeating what Unchecked_Deallaction does behind
> the scenes.
I don't understand your comment at all. An instance of
Unchecked_Deallocation is explicitly executed where it is called, not at
some later point in time, and no pragma is involved. Or do you mean
Finalize for controlled types?
>> Flow of control should be clearly represented in the syntactic
>> structure, with textual order matching execution order. The current
>> exception-handling syntax is good in that respect.
>
> I understand that goal. However, this approach separates the
> initialization from the clean-up code,
I understand your point, but these are conflicting goals, as long as
text remains one-dimensional, so it is a judgement call.
> and the clean-up code might
> need repeating in several places. This cannot be good.
I take it you mean that one and the same occurrence of an "Open" call
may require more than one occurrence of a "clean-up" call of "Close".
But in some cases the converse happens, too: One and the same call of
"Close" may clean up after different (alternative) "Open"s. That's life...
To focus the discussion, your proposal contains the following four
distinct parts, as I see it:
1. The introduction of a new executable construct (in your proposal, a
pragma Scoped) that, *when* and *if* executed, dynamically "schedules"
something else (the procedure call) to be executed at a later point in
the execution (just before the "end" of the handled_sequence_of_statements).
Advantages: The dynamic semantics gives LIFO nesting of initialization
and finalization actions, without a corresponding nested syntax, even if
exceptions occur (that is, transparently with respect to exceptions).
Disadvantages: Flow of control differs strongly from text order.
If your application or coding style puts, in the same subprogram, many
things that need such clean-up (several levels of dynamic nesting), I
understand that the advantages may be valued higher than the disadvantages.
2. The syntax of that new executable construct. You suggest a pragma,
and I don't like that. I think pragmas should be "compiler directives"
(RM 2.8(1)) and should not contribute materially to the algorithm that
the program implements. I accept that there are already some pragmas
that are really executable statements, such as pragma Assert, but I see
those as special cases that are not part of the program's algorithm.
3. The nature and form of the code that is "scheduled" for later
execution. You suggest a procedure call; I don't see why such a feature
should not allow any form of statement.
4. The dynamic semantics of the "scheduled" code, in particular whether
some parts of it are evaluated and fixed at the time of "scheduling", as
you suggest, instead of later when the code is executed.
If point (1) is accepted, I would like a non-pragma syntax, preferably
using existing keywords. For example:
at end do some-statement;
(Following normal Ada principles, this should really be closed by an
"end at", or even "end at end", but that may be too much...)
There could be an optional subprogram or block identifier between the
"end" and "do", as in
at end of Test_External_Formats do Close (COBOL_File);
>>> Regarding dynamic semantics: When a Scoped pragma which is part of a
>>> handled_sequence_of_statements is executed or when it is elaborated as
>>> part of a declarative_part, the procedure_name or procedure_prefix and
>>> actual parameters of the procedure_call_statment are evaluated.
>> ... and, if I understand your suggestion, the evaluated parameters are
>> saved somewhere, in case they are needed for the later call. I think
>> this can cause large run-time overheads, as well as confusion and
>> errors. How "deeply" are the parameters saved?
>
> renaming-declaration-deeply, so it really depends on the types
> involved. This happens with any procedure call, so the overhead
> cannot be huge.
Yes, it happens at every procedure call, but the values are then
normally discarded after the procedure call. Here, they would not be
discarded until the "end". But I agree that a renaming is not expensive.
However, if the evaluate-and-save is *only* equivalent to renaming, you
can get weird effects where a scalar variable seems to have different
values at different places in the parameter list. For example, consider
(using the pragma syntax):
begin
I : Integer := 4;
A : array (1 .. 10) of Float := ( some values );
pragma Scoped (Foo (I, A(I)));
begin
I := 9;
end;
I think your suggestion is that the pragma is equivalent to:
Saved_I : Integer renames I;
Saved_AI : Float renames A(I);
followed (before the "end") by the call
Foo (Saved_I, Saved_AI);
The call that is executed before the "end" is then Foo (9, A(4)), where
the variable "I" seems to have two different values, 4 and 9.
Moreover, what happens if an actual parameter is a function call? Should
the function be called at the point of the "pragma Scoped", or later?
>> The semantics will depend on whether a type is passed by value or by
>> access, right?
>
> Not really. The idea is to avoid strange action-at-a-distance if the
> actual arguments change their value between execution of the pragma
> and the embedded procedure call.
But the example above shows that this produces other (?) strange
effects, since renaming "fixes" the value of a variable in some contexts
but not in others.
>> Also, saved parameters of access type may become unusable (hanging
>> pointers) if the statement_sequence deallocates the accessed object.
>
> Ordinary procedure calls suffer from this problem, too.
My point is that the early evaluation of the parameters at the point of
the pragma suggests that the procedure call occurs in that state, but
actually the state may have changed, and the evaluated and saved
parameters only capture some part of the state. This is confusing; when
the call occurs, before the "end", the parameters are a mixture of the
two states. In the example above, "I" seems to have two values...
> As far as I
> can see, this can only happen with Unchecked_Deallaction, so it is
> acceptable.
Why? Do you never use Unchecked_Deallocation? AFAIK it is the only way
to recover global memory resources in Ada, so it is a normal part of Ada
programming.
I think the clearest solution to point (4) is to say that all of the
"scheduled" code is evaluated and executed later (before the "end").
If the programmer needs to save some values that may change, this can be
done with ordinary declarations or statements, for example
Saved_I : constant Integer := I;
Saved_AI : constant Float := A(I);
>> You did not discuss if "pragma Scoped" could be executed
>> conditionally, as in:
>>
>> Do_Something ( ... Success => Ok);
>> if Ok then
>> pragma Scoped (Undo_Something (...));
>> end if;
>>
>> Is this included in your suggestion?
>
> The branches of an if statement aren't a
> handled_sequence_of_statements, so it's not allowed.
Ok, I misunderstood your suggestion: you would only allow pragma Scoped
on the "top" level in a handled_sequence_of_statements.
But why make this limitation? It seems to me not unlikely that some
application may conditionally Open a COBOL_File, and then a similarly
conditional Close should occur at the end. One could even let "pragma
Scoped" be used in loops and execute the clean-up code just before the
present iteration of the loop ends.
>> I would
>> very much prefer new syntax for a "finally" structure placed at the
>> end of the handled_sequence_of_statements, after the exception handler
>> if any, over the "pragma" suggestion.
>
> "finally" was state-of-the-art in 1995.
That is not an argument by itself.
> I'm not sure if it still can
> be considered good language design. Most languages which strive for
> exception safety are gradually moving to different constructs (even
> Java).
That could be an argument. Can you present these "different constructs"
here as examples that we could compare to "finally" and to your proposal?
--
Niklas Holsti
Tidorum Ltd
niklas holsti tidorum fi
. @ .
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: A suggestion for resource management
2010-08-21 21:01 ` Florian Weimer
2010-08-22 10:53 ` Niklas Holsti
@ 2010-08-22 11:32 ` Georg Bauhaus
2010-08-23 23:37 ` Randy Brukardt
1 sibling, 1 reply; 20+ messages in thread
From: Georg Bauhaus @ 2010-08-22 11:32 UTC (permalink / raw)
On 8/21/10 11:01 PM, Florian Weimer wrote:
> * Niklas Holsti:
>> [One may use blocks, this is Ada.]
>>
>> I find that form clear and brief enough, although I admit that
>> nesting depth may become awkwardly large (the remedy is to split the
>> procedure).
>
> No one will write such code unless forced to do so by some static
> analysis tool. It's rather messy.
(Even though there is some evidence that block nesting and
disciplined manual cleanup has been considered messy here
and there, backing this claim with data will help.
Recent Python development may be a source of evidence, e.g.
counting cleanup Bugs in old Python from a big installation
such as Google App Engine, versus new Python and whatever
programs are available for statistical analysis.)
Wouldn't a new type based hook into the object life cycle
fit the language better? At least if you use types to
encapsulate objects that belong together.
Wouldn't have compatibility problems, either, would it?
Scope(whatever) ...; looks like a solution typical of {} languages:
compare its "flexibility" with the "flexibility" of goto in C and Ada,
respectively. I find it too compiler-writer-oriented, and somewhat
ad hoc in use. How can it be bridled?
Just my 2c.
Georg
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: A suggestion for resource management
2010-08-21 16:20 A suggestion for resource management Florian Weimer
2010-08-21 19:07 ` Dmitry A. Kazakov
2010-08-21 20:34 ` Niklas Holsti
@ 2010-08-22 13:09 ` stefan-lucks
2010-08-22 14:30 ` Florian Weimer
2010-08-22 15:09 ` Florian Weimer
2 siblings, 2 replies; 20+ messages in thread
From: stefan-lucks @ 2010-08-22 13:09 UTC (permalink / raw)
On Sat, 21 Aug 2010, Florian Weimer wrote:
> The example at the end of section B.4 could use the Scoped pragma in
> this way:
>
> procedure Test_External_Formats is
> ...
> COBOL_File : File_Type;
> ...
>
> begin
> Open (COBOL_File, Name => "Some_File");
> pragma Scoped (Close (COBOL_File));
>
> loop
> ...
> exception
> when End_Error => ...
> end Test_External_Formats;
>
I don't quite why this needs an extra pragma -- your example is,
apparently, easy to handle by current Ada's capacities:
procedure New_Test_External_Formats is
...
COBOL_File : File_Type;
procedure Close_Cobol_File is
begin
Close(COBOL_FILE);
end Close_Cobol_File;
Finisher: Finish_Package.Finisher;
-- The type Finish_Package.Finisher is derived from
-- Ada.Finalization.Limited_Controlled.
...
begin
Open (COBOL_File, Name => "Some_File");
-- pragma Scoped (Close (COBOL_File));
-- we don't need the pragma
Finisher.Set(Close_Cobol_File'Access);
-- we will call Close_Cobol_File
-- when Finisher goes out of scope
loop
...
exception
when End_Error => ...
end Test_External_Formats;
The only disadvantage of this approach, in comparison to your pragma, is
the need to define a parameterless procedure Close_Cobol_File. But
defining such parameterless procedures is a design pattern you get used to
anyway, when working with Ada.Containers.
--
------ Stefan Lucks -- Bauhaus-University Weimar -- Germany ------
Stefan dot Lucks at uni minus weimar dot de
------ I love the taste of Cryptanalysis in the morning! ------
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: A suggestion for resource management
2010-08-22 13:09 ` stefan-lucks
@ 2010-08-22 14:30 ` Florian Weimer
2010-08-22 15:09 ` Florian Weimer
1 sibling, 0 replies; 20+ messages in thread
From: Florian Weimer @ 2010-08-22 14:30 UTC (permalink / raw)
> I don't quite why this needs an extra pragma -- your example is,
> apparently, easy to handle by current Ada's capacities:
>
> procedure New_Test_External_Formats is
> ...
> COBOL_File : File_Type;
>
> procedure Close_Cobol_File is
> begin
> Close(COBOL_FILE);
> end Close_Cobol_File;
>
> Finisher: Finish_Package.Finisher;
> -- The type Finish_Package.Finisher is derived from
> -- Ada.Finalization.Limited_Controlled.
> ...
Or even this:
COBOL_File : File_Type;
package Finisher_COBOL_File is
new Finisher (File_Type, COBOL_File, Close);
That's actually quite nice, and can be implemented in Ada 2005.
Unfortunately, GNAT still generates horrible code for this case, but
that can be fixed.
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: A suggestion for resource management
2010-08-22 13:09 ` stefan-lucks
2010-08-22 14:30 ` Florian Weimer
@ 2010-08-22 15:09 ` Florian Weimer
1 sibling, 0 replies; 20+ messages in thread
From: Florian Weimer @ 2010-08-22 15:09 UTC (permalink / raw)
> I don't quite why this needs an extra pragma -- your example is,
> apparently, easy to handle by current Ada's capacities:
>
> procedure New_Test_External_Formats is
> ...
> COBOL_File : File_Type;
>
> procedure Close_Cobol_File is
> begin
> Close(COBOL_FILE);
> end Close_Cobol_File;
>
> Finisher: Finish_Package.Finisher;
> -- The type Finish_Package.Finisher is derived from
> -- Ada.Finalization.Limited_Controlled.
> ...
Or even this:
COBOL_File : File_Type;
package Finisher_COBOL_File is
new Finisher (File_Type, COBOL_File, Close);
That's actually quite nice, and can be implemented in Ada 2005.
Unfortunately, GNAT still generates horrible code for this case, but
that can be fixed. (One trivial improvement is to use your Finisher
type in the implementation of the Finisher package; further
improvements need some compiler work.)
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: A suggestion for resource management
2010-08-22 10:53 ` Niklas Holsti
@ 2010-08-22 15:29 ` Florian Weimer
2010-08-22 16:12 ` Florian Weimer
1 sibling, 0 replies; 20+ messages in thread
From: Florian Weimer @ 2010-08-22 15:29 UTC (permalink / raw)
* Niklas Holsti:
>> In a sense, it's just repeating what Unchecked_Deallaction does behind
>> the scenes.
>
> I don't understand your comment at all. An instance of
> Unchecked_Deallocation is explicitly executed where it is called, not
> at some later point in time, and no pragma is involved. Or do you mean
> Finalize for controlled types?
Exactly, Unchecked_Deallocation arranges for calls to a somewhat
non-obvious set of Finalize subprogramms.
> However, if the evaluate-and-save is *only* equivalent to renaming,
> you can get weird effects where a scalar variable seems to have
> different values at different places in the parameter list. For
> example, consider (using the pragma syntax):
Oh, so scalars need to be copied after all. 8-)
> Moreover, what happens if an actual parameter is a function call?
> Should the function be called at the point of the "pragma Scoped", or
> later?
At the point of the pragma.
> I think the clearest solution to point (4) is to say that all of the
> "scheduled" code is evaluated and executed later (before the "end").
>> The branches of an if statement aren't a
>> handled_sequence_of_statements, so it's not allowed.
>
> Ok, I misunderstood your suggestion: you would only allow pragma
> Scoped on the "top" level in a handled_sequence_of_statements.
>
> But why make this limitation? It seems to me not unlikely that some
> application may conditionally Open a COBOL_File, and then a similarly
> conditional Close should occur at the end. One could even let "pragma
> Scoped" be used in loops and execute the clean-up code just before the
> present iteration of the loop ends.
There is an ambiguity whether the cleanup will occur at the end of the
sequence_of_statements, or at the end of some outer scope. As a case
in point, in the "if" case, you suggest to prefer outer scope, but for
the loop case, you want local cleanup. I couldn't decide which one is
the right approach, so I made both illegal.
>> I'm not sure if it still can
>> be considered good language design. Most languages which strive for
>> exception safety are gradually moving to different constructs (even
>> Java).
>
> That could be an argument. Can you present these "different
> constructs" here as examples that we could compare to "finally" and to
> your proposal?
Python:
<http://docs.python.org/reference/compound_stmts.html#with>
<http://www.python.org/dev/peps/pep-0343/>
Java:
<http://blogs.sun.com/darcy/entry/project_coin_updated_arm_spec>
C# (which contained it from the start):
<http://msdn.microsoft.com/en-us/library/yh598w02%28VS.80%29.aspx>
Go (with some peculiar semantics):
<http://golang.org/doc/go_spec.html#Handling_panics>
Perl 6 apparently has LEAVE (as a more dynamic variant of END), but I
don't know if it is actually used.
For evidence that try-finally doesn't work, it's instructive to run a
suitable static analyzer on a code base for the first time. Even if
the developers are competent, you'll find rule violations, several of
them caused by a desire to reduce syntactic overhead.
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: A suggestion for resource management
2010-08-22 10:53 ` Niklas Holsti
2010-08-22 15:29 ` Florian Weimer
@ 2010-08-22 16:12 ` Florian Weimer
2010-08-23 12:25 ` Niklas Holsti
1 sibling, 1 reply; 20+ messages in thread
From: Florian Weimer @ 2010-08-22 16:12 UTC (permalink / raw)
* Niklas Holsti:
>> In a sense, it's just repeating what Unchecked_Deallaction does behind
>> the scenes.
>
> I don't understand your comment at all. An instance of
> Unchecked_Deallocation is explicitly executed where it is called, not
> at some later point in time, and no pragma is involved. Or do you mean
> Finalize for controlled types?
Exactly, Unchecked_Deallocation arranges for calls to a somewhat
non-obvious set of Finalize subprogramms.
> However, if the evaluate-and-save is *only* equivalent to renaming,
> you can get weird effects where a scalar variable seems to have
> different values at different places in the parameter list. For
> example, consider (using the pragma syntax):
Oh, so scalars need to be copied after all. 8-)
> Moreover, what happens if an actual parameter is a function call?
> Should the function be called at the point of the "pragma Scoped", or
> later?
At the point of the pragma.
> I think the clearest solution to point (4) is to say that all of the
> "scheduled" code is evaluated and executed later (before the "end").
>> The branches of an if statement aren't a
>> handled_sequence_of_statements, so it's not allowed.
>
> Ok, I misunderstood your suggestion: you would only allow pragma
> Scoped on the "top" level in a handled_sequence_of_statements.
>
> But why make this limitation? It seems to me not unlikely that some
> application may conditionally Open a COBOL_File, and then a similarly
> conditional Close should occur at the end. One could even let "pragma
> Scoped" be used in loops and execute the clean-up code just before the
> present iteration of the loop ends.
There is an ambiguity whether the cleanup will occur at the end of the
sequence_of_statements, or at the end of some outer scope. As a case
in point, in the "if" case, you suggest to prefer outer scope, but for
the loop case, you want local cleanup. I couldn't decide which one is
the right approach, so I made both illegal.
>> I'm not sure if it still can
>> be considered good language design. Most languages which strive for
>> exception safety are gradually moving to different constructs (even
>> Java).
>
> That could be an argument. Can you present these "different
> constructs" here as examples that we could compare to "finally" and to
> your proposal?
Python:
<http://docs.python.org/reference/compound_stmts.html#with>
<http://www.python.org/dev/peps/pep-0343/>
Java:
<http://blogs.sun.com/darcy/entry/project_coin_updated_arm_spec>
C# (which contained it from the start):
<http://msdn.microsoft.com/en-us/library/yh598w02%28VS.80%29.aspx>
Go (with some peculiar semantics):
<http://blog.golang.org/2010/08/defer-panic-and-recover.html>
<http://golang.org/doc/go_spec.html#Handling_panics>
Perl 6 apparently has LEAVE (as a more dynamic variant of END), but I
don't know if it is actually used.
For evidence that try-finally doesn't work, it's instructive to run a
suitable static analyzer on a code base for the first time. Even if
the developers are competent, you'll find rule violations, several of
them caused by a desire to reduce syntactic overhead.
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: A suggestion for resource management
2010-08-22 16:12 ` Florian Weimer
@ 2010-08-23 12:25 ` Niklas Holsti
2010-09-04 19:09 ` Florian Weimer
0 siblings, 1 reply; 20+ messages in thread
From: Niklas Holsti @ 2010-08-23 12:25 UTC (permalink / raw)
Florian Weimer wrote:
> * Niklas Holsti:
>
>>> In a sense, it's just repeating what Unchecked_Deallaction does behind
>>> the scenes.
>> I don't understand your comment at all. An instance of
>> Unchecked_Deallocation is explicitly executed where it is called, not
>> at some later point in time, and no pragma is involved. Or do you mean
>> Finalize for controlled types?
>
> Exactly, Unchecked_Deallocation arranges for calls to a somewhat
> non-obvious set of Finalize subprogramms.
Yes, but these Finalize calls occur within the execution of an explicit
call of Unchecked_Deallocation, not "delayed" at some later point in the
program. If I call Foo (X), it is normal for Foo to do something with/to
X, including calling primitive operations of X. When
Unchecked_Deallocation (X) calls Finalize (X), it is thus within (my
expectation of) normal control flow.
The scheduling of a clean-up action for later execution is more like the
*implicit* calls of Finalize that occur when a controlled object goes
out of scope.
>> However, if the evaluate-and-save is *only* equivalent to renaming,
>> you can get weird effects where a scalar variable seems to have
>> different values at different places in the parameter list. For
>> example, consider (using the pragma syntax):
>
> Oh, so scalars need to be copied after all. 8-)
That does not solve the basic problem, because then, in "pragma Scoped
(Foo (X))", the value (old or new) of X that is (eventually) used in the
call would depend on whether X is scalar or not, which would be very
error-prone under maintenance. Also, what if X is a private type defined
in another package? Depending on its scalarity would break privacy.
I still think that all evaluation of the clean-up code should be delayed
until the clean-up is actually needed. If something needs to be saved at
the point of the pragma Scoped, the programmer should explicitly save it.
This could be assisted by an extension of the "at end" syntax to include
an optional set of declarations of objects that are elaborated at once,
but available later (and only) in the clean-up code, for example in this
way:
procedure Bar (...)
...
X : X_Type := Old_Value;
at end of Bar -- Not (yet) Ada!
with
Saved_X : constant X_Type := X;
-- Takes a copy of X as it is now (Old_Value).
-- The name Saved_X is visible only here
-- and in the "do" part.
do
Foo (Saved_X);
-- The name Saved_X is no longer visible.
begin
...
X := New_Value;
...
end Bar;
-- Here Foo is called with Saved_X (= Old_Value).
-- If X_Type needs finalization, Saved_X is finalized
-- immediately after this call of Foo.
>> Moreover, what happens if an actual parameter is a function call?
>> Should the function be called at the point of the "pragma Scoped", or
>> later?
>
> At the point of the pragma.
Thus, if the function returns a large composite object, that would have
to be saved. Another reason to delay all evaluation until the actual use
of the clean-up code.
Do you have an example that motivates why the parameters should be
evaluated early, as you suggest?
>> ... you would only allow pragma
>> Scoped on the "top" level in a handled_sequence_of_statements.
>>
>> But why make this limitation? It seems to me not unlikely that some
>> application may conditionally Open a COBOL_File, and then a similarly
>> conditional Close should occur at the end. One could even let "pragma
>> Scoped" be used in loops and execute the clean-up code just before the
>> present iteration of the loop ends.
>
> There is an ambiguity whether the cleanup will occur at the end of the
> sequence_of_statements, or at the end of some outer scope. As a case
> in point, in the "if" case, you suggest to prefer outer scope, but for
> the loop case, you want local cleanup.
The choice in the loop case is rather evident, isn't it? The program
should not be allowed to make two Open calls without an intervening
Close call, just as you implied in your original posting.
In the "if" case, a choice would have to be made. Choosing the end of
the enclosing subprogram (or loop) is the more flexible choice, since
the programmer can force the other alternative by nesting a begin-end
block in the "if".
With the "at end of <identifier> do" syntax, the clean-up point can be
indicated by the <identifier>, which could name a subprogram, a loop, or
a block.
> I couldn't decide which one is
> the right approach, so I made both illegal.
A conservative choice, but I could live with it.
>>> Most languages which strive for
>>> exception safety are gradually moving to different constructs (even
>>> Java).
>> That could be an argument. Can you present these "different
>> constructs" here as examples that we could compare to "finally" and to
>> your proposal?
Many thanks, your links are interesting references. Below, I comment on
the properties of each, relative to the questions we are discussing, and
which are:
(1): Do we need a new feature to schedule clean-up actions?
(2): The syntax of the feature.
(3): The form of the clean-up action.
(4): Possible early evaluation and saving of clean-up parameters.
> Python:
>
> <http://docs.python.org/reference/compound_stmts.html#with>
> <http://www.python.org/dev/peps/pep-0343/>
Need (1): Replaces a fairly complex nesting of try-except-finally, so
may well be useful and needed.
Syntax (2): Uses a specific syntax to define the objects ("context
managers") that initialize and finalize resources, and to delimit the
sequence of "subordinate" statements after which the clean-up should
occur. Thus more "structured" than Florian's proposal for Ada, whether
by "pragma Scoped" or by "at end do", neither of which delimits the
subordinate statements (they are all the remaining statements in the
handled_sequence_of_statements).
Form of clean-up (3): Call of the "exit" method for the context manager
object(s), with parameters that indicate normal or exceptional
completion of the subordinate statements. Thus only a single form is
allowed, and the present proposals for Ada are more flexible.
Saving parameters (4): Only the identity of the "exit" method for the
context manager is saved. The "exit" method has no programmer-defined
parameters, so the question of saving parameter values does not arise.
However, the construct also creates the context manager object, so
values can be saved there, if the programmer desires. This is similar to
my suggestion (no automatic early evaluation and saving).
> Java:
>
> <http://blogs.sun.com/darcy/entry/project_coin_updated_arm_spec>
Need (1): Replaces a nesting of try-catches-finally, in the same way as
for Python.
Syntax (2): Extends the Java "try" statement to include a "resource
specification" part. Similar to the Python syntax, and thus similarly
different from the proposed Ada syntax. Complicated by the existence of
"suppressed exceptions" in Java (a new concept for me).
Form of clean-up (3): Call of the parameterless "close" method for the
resource object(s), thus more restricted even than the Python case.
Saving parameters (4): None for the parameterless "close", but, as in
the Python case, the resource object is created as part of the construct
and can thus save state, if the programmer desires.
> C# (which contained it from the start):
>
> <http://msdn.microsoft.com/en-us/library/yh598w02%28VS.80%29.aspx>
Very similar to the Java case, but seems a bit simpler; the translation
is a "try-finally" statement with no "catches" and no nesting. The
"resource objects" are immutable in the subordinate statements, perhaps
to ensure that state is saved for the clean-up (point 4).
> Go (with some peculiar semantics):
>
> <http://blog.golang.org/2010/08/defer-panic-and-recover.html>
> <http://golang.org/doc/go_spec.html#Handling_panics>
Need (1): The feature is motivated in the same way as a "finally"
construct, or automatic finalization of objects, which seem absent from
Go (caution: this is the first time I look at Go).
Syntax (2): A "defer" statement which is very similar to the proposed
"pragma Scoped" or "at end do". A clean-up action is dynamically
scheduled to be executed at the end of the containing subprogram, and
the syntax does not delimit or contain the subordinate statements. The
"defer" statement does not identify the relevant resource objects or
context managers, to use terms from other languages.
Form of clean-up (3): A function or method call. The function can be
given "in-line" in the defer statement (a "function literal").
Flexibility thus similar to the proposed "at end do <statement>".
Saving parameters (4): The parameters to the deferred function or method
call are evaluated and saved. IIUC this is a real copy, not a renaming,
and differs from a deep copy only if the parameter value is a pointer or
has pointer components. No motivation is given for this level of "saving".
Florian's proposal is thus most similar to the Go "defer" statement, and
rather different from the constructs in Python, Java, and C#.
By the way, Florian, in your original posting you said:
Florian Weimer wrote:
> Here's a proposal for a resource management pragma. It is similar
> to the scope(exit) directive in D, the C++ scope guard idom, and
> (to a lesser degree) Go's rescue statement.
It seems to me that the Go "rescue" statement is Go's form of an
exception handler/catcher, and not at all like pragma Scoped. I assume
you meant the "defer" statement, Florian, is that right?
I found the D construct "scope(exit)" explained here:
http://www.digitalmars.com/d/2.0/statement.html#ScopeGuardStatement
My analysis of the D feature:
Need (1): Not explained. There are three variants of the feature,
respectively for executing a clean-up always, on normal termination, or
on exceptional termination. If several variants are used in the same
scope, the corresponding try-catch-finally structure could be quite complex.
Syntax (2): Similar to "defer" in Go and to the proposed "pragma Scoped"
or "at end do". Does not use a pragma. The subordinate statements are
not enclosed, and the resource objects are not identified.
Form of clean-up (3): Any kind of statement (but one that must not
propagate an exception), thus similar to "at end do".
Saving parameters (4): None, as far as I can see.
> Perl 6 apparently has LEAVE (as a more dynamic variant of END), but I
> don't know if it is actually used.
I did not look at this one. Sorry, but my loathing of Perl was too
strong :-)
My summary of the comparison:
Need (1): Ok, there seems to be a need, or a least a trend, for this
kind of thing, so we should at least consider it for Ada too.
Syntax (2):
- Florian's proposal is unique in suggesting a "pragma" syntax. All the
other languages use syntax that is made part of the core language and is
not just a "compiler directive".
- The other languages are split between a structured (nested) syntax and
a more execution-oriented syntax. In the former, the subordinate
statements are enclosed in the syntactic structure like the (extended)
Java "try" statement. In the latter, for example the Go "defer" and the
D "scope(exit)", the subordinate statements are all the remaining
statements in the enclosing subprogram or scope. Florian's proposal for
Ada falls in the latter camp.
Form of clean-up (3): Some languages limit this to a single call of a
subprogram, sometimes a specific subprogram or method (close, exit),
perhaps even parameterless. Florian's proposal of a call to a
programmer-chosen subprogram, with possible programmer-chosen
parameters, is more flexible. But some other languages allow any
statement as clean-up code.
Saving parameters (4): Only the Go language and Florian's proposal have
this feature. I think it needs much more motivation to be supported, and
I continue to think that it will be hard to define in a clear way for
Ada, especially if arbitrary statements are allowed as clean-up code.
One point that is not addressed in Florian's proposal is making the
clean-up code aware of the exceptional or normal termination of the
subordinate statements. This is possible in Python and D. Should it be
possible in Ada?
> For evidence that try-finally doesn't work, it's instructive to run a
> suitable static analyzer on a code base for the first time. Even if
> the developers are competent, you'll find rule violations, several of
> them caused by a desire to reduce syntactic overhead.
Concrete examples would be interesting.
--
Niklas Holsti
Tidorum Ltd
niklas holsti tidorum fi
. @ .
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: A suggestion for resource management
2010-08-21 21:09 ` Florian Weimer
2010-08-22 6:40 ` Dmitry A. Kazakov
@ 2010-08-23 23:22 ` Randy Brukardt
1 sibling, 0 replies; 20+ messages in thread
From: Randy Brukardt @ 2010-08-23 23:22 UTC (permalink / raw)
"Florian Weimer" <fw@deneb.enyo.de> wrote in message
news:8739u73c6l.fsf@mid.deneb.enyo.de...
...
> You need escape analysis or inter-procedural scalar replacement to
> eliminate the tag. But it certainly can be done, and the additional
> word will not hurt in all but the most extreme cases.
Why would you bother? The tag itself isn't very large and exists once per
type. The tag component is usually a word or two per object; it isn't going
to matter unless there are 10s of thousands of objects. (And if there *are*
10s of thousands of objects, the overhead of finalizing the objects, no
matter what you call it [such as calling Close in this example], is going to
be a much greater problem. Tiny, common objects can't require cleanup,
period.)
Randy.
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: A suggestion for resource management
2010-08-22 11:32 ` Georg Bauhaus
@ 2010-08-23 23:37 ` Randy Brukardt
0 siblings, 0 replies; 20+ messages in thread
From: Randy Brukardt @ 2010-08-23 23:37 UTC (permalink / raw)
"Georg Bauhaus" <rm-host.bauhaus@maps.futureapps.de> wrote in message
news:4c710ab7$0$6882$9b4e6d93@newsspool2.arcor-online.net...
...
> Scope(whatever) ...; looks like a solution typical of {} languages:
> compare its "flexibility" with the "flexibility" of goto in C and Ada,
> respectively. I find it too compiler-writer-oriented, and somewhat
> ad hoc in use. How can it be bridled?
I agree, but I don't think it is "compiler-writer-oriented", because it
doesn't fit into the things an Ada compiler is already doing. Which means it
would be a massive new mess, unless it was implemented in terms of existing
things. I'd probably implement it with an anonymous controlled object having
the procedure call (conditionally) called from the Finalize routine.
Probably would be a lot more expensive than just doing it right (using
controlled types).
Moreover, using pragmas for dynamic content is really an abuse of the
concept, IMHO. The fact that the concept has been abused in the past doesn't
make it OK to abuse it more.
Randy.
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: A suggestion for resource management
2010-08-23 12:25 ` Niklas Holsti
@ 2010-09-04 19:09 ` Florian Weimer
2010-09-07 10:14 ` Niklas Holsti
0 siblings, 1 reply; 20+ messages in thread
From: Florian Weimer @ 2010-09-04 19:09 UTC (permalink / raw)
* Niklas Holsti:
> That does not solve the basic problem, because then, in "pragma Scoped
> (Foo (X))", the value (old or new) of X that is (eventually) used in
> the call would depend on whether X is scalar or not, which would be
> very error-prone under maintenance.
Finalization of components is quite magic, too.
> Also, what if X is a private type defined in another package?
> Depending on its scalarity would break privacy.
That information already leaks during exception handling.
In retrospect, the construct suggested by Stefan (with the additional
tweak I mentioned) should work well enough, it just introduces a
pointless identifier. Curiously, it shares many of the things you
criticize about pragma Scoped.
This succient notation was not possible in Ada 95, so I'm not too
embarrassed that I missed this option.
> By the way, Florian, in your original posting you said:
>
> Florian Weimer wrote:
>> Here's a proposal for a resource management pragma. It is similar
>> to the scope(exit) directive in D, the C++ scope guard idom, and
>> (to a lesser degree) Go's rescue statement.
>
> It seems to me that the Go "rescue" statement is Go's form of an
> exception handler/catcher, and not at all like pragma Scoped. I assume
> you meant the "defer" statement, Florian, is that right?
Yes, I keep confusing the Go terms.
> - Florian's proposal is unique in suggesting a "pragma" syntax. All
> the other languages use syntax that is made part of the core language
> and is not just a "compiler directive".
The idea was to implement it in an existing compiler if the semantics
are well-received.
>> For evidence that try-finally doesn't work, it's instructive to run a
>> suitable static analyzer on a code base for the first time. Even if
>> the developers are competent, you'll find rule violations, several of
>> them caused by a desire to reduce syntactic overhead.
>
> Concrete examples would be interesting.
In Java, I often see programmers using FileInputStream or Socket
without try-finally blocks, relying on the garbage collector to close
file descriptors. Sorry, I can't be more specific than that. I'll
try to remember to post a follow-up when I see something similar in an
open code base.
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: A suggestion for resource management
2010-09-04 19:09 ` Florian Weimer
@ 2010-09-07 10:14 ` Niklas Holsti
0 siblings, 0 replies; 20+ messages in thread
From: Niklas Holsti @ 2010-09-07 10:14 UTC (permalink / raw)
Florian Weimer wrote:
> In retrospect, the construct suggested by Stefan (with the additional
> tweak I mentioned) should work well enough, it just introduces a
> pointless identifier.
For reference, since those posts appeared some time ago, here is a part
of your pragma proposal:
> On Sat, 21 Aug 2010, Florian Weimer wrote:
>
>> The example at the end of section B.4 could use the Scoped pragma in
>> this way:
>>
>> procedure Test_External_Formats is
>> ...
>> COBOL_File : File_Type;
>> ...
>>
>> begin
>> Open (COBOL_File, Name => "Some_File");
>> pragma Scoped (Close (COBOL_File));
>>
>> loop
>> ...
>> exception
>> when End_Error => ...
>> end Test_External_Formats;
>>
And here is Stefan's construct:
stefan-lucks@see-the.signature wrote:
> I don't quite why this needs an extra pragma -- your example is,
> apparently, easy to handle by current Ada's capacities:
>
> procedure New_Test_External_Formats is
> ...
> COBOL_File : File_Type;
>
> procedure Close_Cobol_File is
> begin
> Close(COBOL_FILE);
> end Close_Cobol_File;
>
> Finisher: Finish_Package.Finisher;
> -- The type Finish_Package.Finisher is derived from
> -- Ada.Finalization.Limited_Controlled.
> ...
>
> begin
> Open (COBOL_File, Name => "Some_File");
> -- pragma Scoped (Close (COBOL_File));
> -- we don't need the pragma
> Finisher.Set(Close_Cobol_File'Access);
> -- we will call Close_Cobol_File
> -- when Finisher goes out of scope
> loop
> ...
> exception
> when End_Error => ...
> end Test_External_Formats;
>
> The only disadvantage of this approach, in comparison
> to your pragma, is the need to define a parameterless
> procedure Close_Cobol_File. But defining such parameterless
> procedures is a design pattern you get used to anyway,
> when working with Ada.Containers.
And finally your tweak on Stefan's proposal:
Florian Weimer wrote:
> Or even this:
>
> COBOL_File : File_Type;
> package Finisher_COBOL_File is
> new Finisher (File_Type, COBOL_File, Close);
> Curiously, it [Stefan's construct] shares many of the
> things you [Niklas] criticize about pragma Scoped.
I would like to discuss this. I criticized your "pragma Scoped"
suggestion on these points:
- Using a pragma to define flow of control. Stefan's construct does not
use a pragma, so it escapes this criticism.
- Violation of the principle that flow of control should match textual
order (the procedure_call_statement in pragma Scoped would be executed
later, at end of scope). This criticism can be applied to any use of
implicitly invoked finalization, so it does apply to Stefan's construct,
but it applies even more to my suggested alternative to pragma Scoped,
which was new syntax of the form "at end do <statement>". However, since
Ada now supports automatic, implicit finalization (a good thing), this
principle cannot always be followed, and (as I admitted earlier) there
are other arguments against the principle, too. I don't think we need to
discuss it further.
- The difficulty of defining and using your suggestions for the early
evaluation and saving of the parameter values of the
procedure_call_statement in "pragma Scoped". This does not apply to
Stefan's construct since it does not save any parameters (because it is
limited to a parameterless clean-up procedure). It might apply to your
tweak in which the clean-up procedure has one parameter (COBOL_File in
the example) that (depending on its generic formal mode) is perhaps
evaluated and saved in the instantiation of the generic package
Finisher. Do you propose mode "in" or mode "in out" for this parameter
of package Finisher?
- The fact that "pragma Scoped" was limited to scheduling a single
procedure_call_statement at end of scope, rather than any kind of
statement or statement sequence. Stefan's construct, and your tweak,
have this limitation too, and even limit the kind of parameters that the
call can have.
In summary, as I see it, all these suggestions try to create a kind of
"ad hoc" finalization code that could also have been done by wrapping
the resource (COBOL_File) in a controlled type. The question is, do we
need such an "ad hoc" finalization feature?
Stefan showed that it can be done in the existing language by using a
kind of general finalizable object or package that can play host to an
arbitrary clean-up procedure. To me, this seems to introduce almost as
much clutter as the nested block and exception-handling syntax that you
considered cumbersome. Stefan says that such parameterless "helper"
procedures are common when using Ada.Containers, but I believe that
there are current proposals for new Ada syntax to avoid such procedures
and instead write their statements "in line", for example in loops that
iterate over containers, or statements that find and update a specific
element in a container.
>> - Florian's proposal is unique in suggesting a "pragma" syntax. All
>> the other languages use syntax that is made part of the core language
>> and is not just a "compiler directive".
>
> The idea was to implement it in an existing compiler if the semantics
> are well-received.
If you were to implement this pragma in an existing compiler as an
extension -- which is of course your right -- and if the pragma were
then extensively used, it would tend to constrain later standardization
and force the standard to use or at least support such a pragma, to
which my reaction is still negative.
Is it really much simpler to implement "pragma Scoped" than to implement
"at end do <statement>"? After all, both are syntactic sugar for
Stefan's construct.
>>> For evidence that try-finally doesn't work, it's instructive
>>> to run a suitable static analyzer on a code base for the
>>> first time. Even if the developers are competent, you'll
>>> find rule violations, several of them caused by a desire to
>>> reduce syntactic overhead.
>> Concrete examples would be interesting.
>
> In Java, I often see programmers using FileInputStream or Socket
> without try-finally blocks, relying on the garbage collector to close
> file descriptors.
And of course garbage collection can be indefinitely delayed. Good
example, thanks. I don't know Java well (on my list...) but IIUC Java
does not provide end-of-scope finalization, as Ada does for controlled
objects, so Stefan's construct is not available and try-finally is the
only option left in Java. Perhaps it would be easier to use controlled
objects than try-finally.
I do understand the advantages of writing resource-deallocation
(clean-up) code close to the resource-allocation code, an argument for
"ad hoc" finalization. My main objections are to using a pragma and to
any automatic saving of clean-up state.
--
Niklas Holsti
Tidorum Ltd
niklas holsti tidorum fi
. @ .
^ permalink raw reply [flat|nested] 20+ messages in thread
end of thread, other threads:[~2010-09-07 10:14 UTC | newest]
Thread overview: 20+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2010-08-21 16:20 A suggestion for resource management Florian Weimer
2010-08-21 19:07 ` Dmitry A. Kazakov
2010-08-21 19:47 ` Florian Weimer
2010-08-21 20:53 ` Dmitry A. Kazakov
2010-08-21 21:09 ` Florian Weimer
2010-08-22 6:40 ` Dmitry A. Kazakov
2010-08-23 23:22 ` Randy Brukardt
2010-08-21 20:34 ` Niklas Holsti
2010-08-21 21:01 ` Florian Weimer
2010-08-22 10:53 ` Niklas Holsti
2010-08-22 15:29 ` Florian Weimer
2010-08-22 16:12 ` Florian Weimer
2010-08-23 12:25 ` Niklas Holsti
2010-09-04 19:09 ` Florian Weimer
2010-09-07 10:14 ` Niklas Holsti
2010-08-22 11:32 ` Georg Bauhaus
2010-08-23 23:37 ` Randy Brukardt
2010-08-22 13:09 ` stefan-lucks
2010-08-22 14:30 ` Florian Weimer
2010-08-22 15:09 ` Florian Weimer
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox