Version 1.4 of ai12s/ai12-0098-1.txt

Unformatted version of ai12s/ai12-0098-1.txt version 1.4
Other versions for file ai12s/ai12-0098-1.txt

!standard 9.7.4(13)          14-07-14 AI12-0098-1/01
!class ramification 14-05-09
!status Corrigendum 2015 14-07-14
!status ARG Approved 6-0-1 14-06-29
!status work item 14-05-09
!status received 14-04-10
!priority Low
!difficulty Medium
!subject Problematic examples for ATC
!summary
The examples in 9.7.4 assume appropriate abort completion points.
!question
9.7.4 contains two examples demonstrating how asynchronous transfer of control can be used. But neither of these examples can be assumed to work as described.
When the entry call or delay is completed, the abortable part is aborted. However, this doesn't mean the abortable part stops executing immediately. Ada only requires abortion at "abort completion points". (Annex D has additional requirements, but these examples are in the core language and should depend only on core requirements.)
In the first example, an input operation is not an abort completion point. Thus, the abortable part need not complete until some later time.
In the second example, if Horribly_Complicated_Recursive_Function contains only calculations, it won't have any abort completion points and therefore may not complete when the delay expires.
Unless the implementation is claiming Annex D support (and thus supporting D.6), neither of these examples need work as explained in the standard. Do we need some clarification here? (Yes.)
!response
The examples accurately reflect the intended use of the feature. However, the questioner is correct that neither of these examples are required to work usefully in the absence of D.6.
Therefore, we add a note that the examples assume abort completion points within the abortable part.
!wording
Add after 9.7.4(13):
Note that these examples presume that there are abort completion points within the execution of the abortable_part.
!discussion
9.8 says that a task only needs to be aborted at abort completion points. Of course, the implementation could support aborting it at other points, but that's not required of an Ada implementation (unless Annex D is supported, which is a separate issue).
Thus, we add an explanation after the examples that we're assuming that the abortable_parts include abort completion points.
For the second example, this is perfectly reasonable and possible -- the programmer must write their calculation so that it contains abort completion points, if they want to have a portable calculation.
For the first example, this comment is more problematical. Clearly, the user cannot control whether a language-defined subprogram contains any abort completion points. They can put one or more into Process_Command, but the I/O routines cannot be assumed to be abortable.
The question came up because the first example did not work on a Windows compiler. The initial response from an Ada language designer was that the compiler should be fixed. But Windows I/O operations are not interruptable in general (other threads can run, but the thread doing I/O has to wait for that I/O to completion); there are ways to make I/O operations interruptable but those have significant costs in complexity and overhead. Adding overhead to I/O in Windows programs just so ATC works are described in the examples would be silly and counterproductive.
(Aside: The problem occurs on Windows if a 1-thread implementation of ATC is used. One could use a 2-thread model to implement ATC on Windows to side-step these issues, but that would encur costs to create a thread for each ATC, as well as ensuring that exceptions are delivered properly. It's probable that such an implementation would allow ATC to "work", but it might be too slow to use in practice.)
We will not try to address this problem; if Get_Line doesn't include an abort completion point, the example probably won't work as intended. Hopefully, implementers will not be pressured into providing high-overhead I/O implementations just to make ATC examples like this one to work.
!corrigendum 9.7.4(13)
Insert after the paragraph:
select delay 5.0; Put_Line("Calculation does not converge"); then abort -- This calculation should finish in 5.0 seconds; -- if not, it is assumed to diverge. Horribly_Complicated_Recursive_Function(X, Y); end select;
the new paragraph:
Note that these examples presume that there are abort completion points within the execution of the abortable_part.
!ASIS
No ASIS effect.
!ACATS test
An ACATS C-Test could be created to check this, but it would be of low value.
!appendix

!topic Problematic examples in 9.7.4
!reference 9.7.4(10-13)
!from Adam Beneschan 14-04-10
!discussion

RM 9.7.4 contains two examples demonstrating how asynchronous transfer of
control can be used:

===============================================================================
Example of a main command loop for a command interpreter:

      loop
          select
              Terminal.Wait_For_Interrupt;
              Put_Line("Interrupted");
          then abort
              -- This will be abandoned upon terminal interrupt
              Put_Line("-> ");
              Get_Line(Command, Last);
              Process_Command(Command(1..Last));
          end select;
      end loop;

Example of a time-limited calculation:

      select
         delay 5.0;
         Put_Line("Calculation does not converge");
      then abort
         -- This calculation should finish in 5.0 seconds;
         --  if not, it is assumed to diverge.
         Horribly_Complicated_Recursive_Function(X, Y);
      end select;
===============================================================================

The problem is that neither of these examples is guaranteed to work as claimed.
When the entry call or delay is completed, the abortable part is aborted.
However, this doesn't mean the abortable part stops executing immediately.

In the first example, 9.8 doesn't list an input operation as an abort completion
point; also, the discussion of task states in 9(10) implies that the task isn't
considered to be "blocked" since it's not held up "as part of some task
interaction".  This means that the abortable part doesn't have to complete
immediately.  Based on a discussion today on comp.lang.ada, it appears that on
the Windows implementation of GNAT, the Get_Line does not terminate immediately
when the abortable part is aborted.

In the second example, if Horribly_Complicated_Recursive_Function contains only
calculations, it won't have any abort completion points and therefore may not
complete when the delay expires.

(Note that on Windows, according to my understanding, it's not generally
possible to force a thread to terminate or to jump out of the code sequence to
call an exception handler, except by using API calls that should be used only in
emergencies.  The target thread itself has to cooperate by polling for
interrupts and using different versions of API calls that perform interruptible
I/O operations.  I may not have all the details right.)

Based on this, I think the RM needs to be modified:

(1) The comment "this will be abandoned upon terminal interrupt" should be
removed, since it's making an assertion that is not necessarily true;

(2) there should be some clarifying text that the examples may not work as
shown, except that the second example can be guaranteed to work if
Horribly_Complicated_Recursive_Function contains abort completion points, such
as "delay 0.0", at strategic times.

****************************************************************

From: Tucker Taft
Sent: Thursday, April 10, 2014  1:44 PM

I am reluctant to change these examples. They establish intent for the
appropriate use of ATC, and if GNAT doesn't support them on Windows, that should
probably be addressed, rather than backpedaling on the intent.

****************************************************************


Questions? Ask the ACAA Technical Agent