Version 1.7 of ai12s/ai12-0098-1.txt
!standard 9.7.4(13) 14-09-29 AI12-0098-1/02
!class ramification 14-05-09
!status Corrigendum 2015 14-07-14
!status WG9 Approved 14-10-20
!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
(usually abbreviated ATC) 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 complete); 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 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