comp.lang.ada
 help / color / mirror / Atom feed
* 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