Version 1.7 of ai12s/ai12-0279-1.txt

Unformatted version of ai12s/ai12-0279-1.txt version 1.7
Other versions for file ai12s/ai12-0279-1.txt

!standard D.2.1(1.5/2)          18-10-22 AI12-0279-1/04
!standard D.2.1(7/5)
!class binding interpretation 18-05-14
!status Amendment 1-2012 18-11-26
!status ARG Approved 9-0-1 18-10-22
!status work item 18-05-14
!status received 18-05-11
!priority Medium
!difficulty Easy
!qualifier Omission
!subject Nonpreemptive Dispatching Needs More Dispatching Points
!summary
A Yield aspect is provided to ensure that the associated subprogram encounters at least one task dispatching point during each invocation.
!question
In non-preemptive dispatching it is necessary to have controlled points in the code where preemption is enabled, as a mechanism to keep the non-preemptive sections short and reduce blocking time for higher priority tasks. Unlike the preemptive case, these preemption points need to be under the programmer's control in order for the program semantics to be fully predictable. The current wording in the standard mentions "blocking of a task" as a preemption point. This can happen for instance when calling a closed entry or suspension object. However, if the entry is open or the suspension object is true, there is no preemption point, which introduces uncertainty on whether a preemption point is reached and makes it difficult to determine the blocking (non-preemptive) time for higher priority tasks.
Should this nondeterminism be eliminated? (Yes.)
!recommendation
The solution to this unpredictability is to define a Boolean Yield aspect that may be specified on subprogram declarations. If a call to a subprogram with the Yield aspect specified as true has not reached a task dispatching point before returning from the call, then a task dispatching point is inserted before the return. This simple change would make non-preemptive dispatching more predictable, while giving the programmer better control for placing the extra task dispatching points.
!wording
Add after D.2.1(1.5/2)
For a noninstance subprogram (including a generic formal subprogram), a generic subprogram, or an entry, the following language-defined aspect may be specified with an aspect_specification (see 13.1.1):
Yield
The type of aspect Yield is Boolean.
If directly specified, the aspect_definition shall be a static expression. If not specified (including by inheritance), the aspect is False.
If a Yield aspect is specified True for a primitive subprogram S of a type T, then the aspect is inherited by the corresponding primitive subprogram of each descendant of T. If the Yield aspect is specified for a dispatching subprogram that inherits the aspect, the specified value shall be confirming. If the Nonblocking aspect (see 9.5) of the associated callable entity is statically True, the Yield aspect shall not be specified as True.
Add after D.2.1(7/5)
If the Yield aspect has the value True, then a call to Yield is included within the body of the associated callable entity, and invoked immediately prior to returning from the body if and only if no other task dispatching points were encountered during the execution of the body.
!discussion
We considered having potentially blocking operations be task dispatching points, but that raised the possibility that low-level subprograms such as IO calls that normally do not block could generate excessive overhead if they were to yield on every call. In contrast, this approach gives better control to the programmer to specify where extra task dispatching points should be placed.
We considered specifying the Yield aspect on some of the language-defined subprograms such as Ada.Task_Identification.Abort_Task and Ada.Synchronous_Task_Control.Suspend_Until_True, but eventually decided that the Yield aspect as defined provides sufficient control to the programmer for adding extra task dispatching points, and it is expected that the aspect will typically be applied higher up in the call tree. Also there were concerns that forcing task dispatching points on any of the language defined subprograms could add unnecessary overhead where it was not needed.
One possible implementation model would be to declare a local constant initialized by copying a per-task count of task dispatching points, and then immediately prior to returning from the subprogram, a call to the Yield operation is performed if the per-task count has not changed. This count would presumably be represented as a modular type to avoid overflow.
!corrigendum D.2.1(1.5/2)
Insert after the paragraph:
Dispatching serves as the parent of other language-defined library units concerned with task dispatching.
the new paragraphs:
For a noninstance subprogram (including a generic formal subprogram), a generic subprogram, or an entry, the following language-defined aspect may be specified with an aspect_specification (see 13.1.1):
Yield
The type of aspect Yield is Boolean.
If directly specified, the aspect_definition shall be a static expression. If not specified (including by inheritance), the aspect is False.
If a Yield aspect is specified True for a primitive subprogram S of a type T, then the aspect is inherited by the corresponding primitive subprogram of each descendant of T. If the Yield aspect is specified for a dispatching subprogram that inherits the aspect, the specified value shall be confirming. If the Nonblocking aspect (see 9.5) of the associated callable entity is statically True, the Yield aspect shall not be specified as True.
!corrigendum D.2.1(7/5)
Insert after the paragraph:
A call of Yield is a task dispatching point.
the new paragraph:
If the Yield aspect has the value True, then a call to Yield is included within the body of the associated callable entity, and invoked immediately prior to returning from the body if and only if no other task dispatching points were encountered during the execution of the body.
!ASIS
No ASIS effect.
!ACATS test
An ACATS C-Test is needed to check that a call with the Yield aspect True will generate a task dispatching point, even if it does not block.
!appendix

From: Tullio Vardanega
Sent: Friday, May 11, 2018  2:37 AM

The (short but intense) burst of AIs from the IRTAW group shows evidence that
they (me included) recently had a productive meeting :) In addition to Alan
Burns' AI with the revised wording of the Deadline Floor Protocol, and Brad
Moore's cut at lock-free atomic operations, I was tasked to submit another --
very simple indeed -- AI with a binding interpretation of the semantics of
non-blocking dispatching, which I attach below.

[This attached version /01 of this AI - Editor.]

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

From: Tullio Vardanega
Sent: Friday, May 11, 2018  2:43 AM

Ehm.
Thanks to the echoing service, I was that what I wrote in premise to the AI I
have just sent reads rather weird.
The subject matter is the semantics of potentially blocking operations in
nonpreemptive dispatching.
Apologies for botching up the wording ...

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

From: Brad Moore
Sent: Friday, May 11, 2018  6:54 PM

I thought of something today, unfortunately after Tullio had sent the AI to
the ARG.

We should probably add a couple paragraphs in the Discussion section stating
that we considered defining the rules to target more precisely the preemption
points (for example making a call on an open entry, or suspension object, or
making calls to Yield, etc) rather than the blanket rule of invoking any
potentially blocking operation.

I think the justification is that the rules are simpler, easier to maintain
and implement, and provides more preemption points that might be useful for
analysis including more likely to break up long bouts of processing, which
was part of the original motivation.

The second paragraph should say something about the case where applications
have not applied the nonblocking aspect to the various subprogram
specifications in the application. An example of this would be legacy systems.
The default is that most such calls have nonblocking=>false, which means they
are potentially blocking. There could be an extremely large number of
preemption points in such an application, which might potentially be
prohibitively expensive. Is this a problem?

This also raises a similar question in my mind, relating to the Nonblocking
aspect. Consider a legacy system where a PO calls subprograms in some non-pure
package. If these are now considered to be potentially blocking due to lack of
a Nonblocking aspect on the specification of the subprogram, the compiler
would start to flag these as errors. Is this a backwards compatibility
problem?

One might be able to apply a configuration pragma to say that the Program_Unit
is by default NonBlocking, but then presumably the compiler might start
flagging routines that actually are potentially blocking as having an
incompatible default, requiring code changes to fix.

The description of Nonblocking aspect in RM 9.5 doesn't mention any backwards
incompatibility. Am I missing something here?

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

From: Randy Brukardt
Sent: Monday, May 14, 2018  10:26 PM

> I thought of something today, unfortunately after Tullio had sent the
> AI to the ARG.
>
> We should probably add a couple paragraphs in the Discussion section
> stating that we considered defining the rules to target more precisely
> the preemption points (for example making a call on an open entry, or
> suspension object, or making calls to Yield,
> etc) rather than the blanket rule of invoking any potentially blocking
> operation.
>
> I think the justification is that the rules are simpler, easier to
> maintain and implement, and provides more preemption points that might
> be useful for analysis including more likely to break up long bouts of
> processing, which was part of the original motivation.

You probably should have just proposed some wording to add to the AI; that
would have been quicker than outlining it and hoping that someone else
writes it. I can add it if you write it (and it does seem like it would
clarify the AI).

> The second paragraph should say something about the case where
> applications have not applied the nonblocking aspect to the various
> subprogram specifications in the application. An example of this would
> be legacy systems. The default is that most such calls have
> nonblocking=>false, which means they are potentially blocking. There
> could be an extremely large number of preemption points in such an
> application, which might potentially be prohibitively expensive. Is
> this a problem?

You're confused. "Potentially blocking" is purely a dynamic concept that
hasn't been changed much from Ada 95's version. (The only change is to avoid
a requirement to make a check on every routine *except* those with
Nonblocking.)

"Nonblocking" is a purely static concept. Only language-defined subprograms
that are not Nonblocking are "potentially blocking", and that's the same set
of subprograms as in the past (except for a couple of bugs that got identified
and fixed during that process). It has *no* effect on user-defined subprograms
vis-a-vis Detect_Blocking or the bounded error.

The Bounded Error for potentially blocking can be detected early, if the
subprogram itself is potentially blocking. This is a bug in Ada 95 (a useless
permission that causes problems with other rules, specifically
Detect_Blocking), but given the recent rule changes, such a subprogram is not
itself potentially blocking. I'm pretty sure you would not want to give
permission to do context switches on arbitrary calls. In any case, this is an
Ada 95 issue that we *fixed* recently, not something introduced. Without the
fixes, you would have been right; Ada 95/2005 potentially blocking would not
have been appropriate for defining dispatching points, since it applied to
virtually every subprogram and it is very difficult to tell which routines it
did or didn't apply to.

> This also raises a similar question in my mind, relating to the
> Nonblocking aspect. Consider a legacy system where a PO calls
> subprograms in some non-pure package. If these are now considered to
> be potentially blocking due to lack of a Nonblocking aspect on the
> specification of the subprogram, the compiler would start to flag
> these as errors. Is this a backwards compatibility problem?

This does not happen. All language-defined subprograms are specified one way
or the other (to match the previous definitions), and other routines are never
potentially blocking for this reason.

> One might be able to apply a configuration pragma to say that the
> Program_Unit is by default NonBlocking, but then presumably the
> compiler might start flagging routines that actually are potentially
> blocking as having an incompatible default, requiring code changes to
> fix.
>
> The description of Nonblocking aspect in RM 9.5 doesn't mention any
> backwards incompatibility.
> Am I missing something here?

Yes, read 9.5(56/5) and the associated AARM note carefully. :-) You're
imagining a problem that does not exist.

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

From: Randy Brukardt
Sent: Monday, May 14, 2018  10:57 PM

>!recommendation
>
>The solution to this unpredictability is to declare all potentially
blocking
>operations to be preemption points. In this case, all calls to an entry
>or wait operations on a suspension point are preemption points
>regardless of whether they are open or not. This simple change would
>make non-preemptive dispatching more predictable.

For the record, this brings this policy much closer to how Janus/Ada handles
Ada tasking. Janus/Ada reschedules on any call to the task supervisor that can
cause a task to be suspended, whether or not it actually suspends. (This
happened more for practicality than any plan -- since the task supervisor has
its own separate context a switch is needed to execute it, and then selecting
the next task to run on the way out handles all cases the same way [blocking,
delay expiration, etc.]).

Janus/Ada does not however, preempt on any call to a potentially blocking
subprogram (with the exception of the Yield ones, which are mapped to
supervisor calls) -- doing that on I/O would be rather expensive, especially
in the character-at-a-time case. I wonder if this would be better if you
didn't require preemption at most language-defined subprograms that are
potentially blocking - specifically the large number of I/O routines that are
blocking. Most of those might have some internal locking, but often let the
OS do that if necessary. (That is the main reason I haven't implemented
Detect_Blocking; I don't know of any way to do that that doesn't make I/O much
more expensive in non-tasking cases.)

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

From: Randy Brukardt
Sent: Monday, May 14, 2018  11:28 PM

> The (short but intense) burst of AIs from the IRTAW group shows
> evidence that they (me included) recently had a productive meeting :)
> In addition to Alan Burns' AI with the revised wording of the Deadline
> Floor Protocol, and Brad Moore's cut at lock-free atomic operations, I
> was tasked to submit another -- very simple indeed -- AI with a
> binding interpretation of the semantics of non-blocking dispatching,
> which I attach below.

You formatted it like an Amendment, but classified it as a Binding
Interpretation, which has !question and !recommendation sections, not !problem
and !proposal. I've fixed this up, taking the second paragraph of the !problem
as the recommendation and adding a question (no question mark and some
reviewers get upset).

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

From: Tullio Vardanega
Sent: Monday, May 14, 2018  11:56 PM

Thanks, Randy.

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

From: Tullio Vardanega
Sent: Tuesday, May 15, 2018  11:41 AM

I am with Randy in the regard of Brad's concern below.
What this binding-interpretation AI wants to address are solely the
language-defined subprograms that are not Nonblocking. I do not think this
requires additional text, other than perhaps recalling this one-liner (that
Randy may want to nail into legal speak):

!summary
All potentially blocking operations (that is, all the language-defined
subprograms that are not Nonblocking) are preemption points for the
Non-Preemptive Dispatching Policy

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

From: Tullio Vardanega
Sent: Tuesday, May 15, 2018  11:49 AM

> For the record, this brings this policy much closer to how Janus/Ada handles
> Ada tasking. ...

Interesting ...
The case of I/O was outside of our (the IRTAW's) thinking, as it would be too
fine a granularity, counter productive for the intent of making the program more
analysable (In addition to costing a lot).

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

From: Tucker Taft
Sent: Tuesday, May 15, 2018  12:00 PM

> I am with Randy in the regard of Brad's concern below.
> What this binding-interpretation AI wants to address are solely the
> language-defined subprograms that are not Nonblocking. I do not think this
> requires additional text, other than perhaps recalling this one-liner (that
> Randy may want to nail into legal speak):

I don't quite follow.  Randy was recommending we do *not* make most
potentially-blocking language-defined subprograms into preemption points.
Rather, I think he we suggesting we identify the particular language-defined
subprograms that *must* be preemption points, and I suppose *allow* other
potentially-blocking subprograms to be preemption points, rather than require
them to be.

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

From: Tullio Vardanega
Sent: Tuesday, May 15, 2018  12:44 PM

If there was a place in the RM (other than the index) that itemized all such
subprograms, we might have thought of ticking off explicitly those that mattered
to our intent.

The (silent) boundary of our concern was chapter 9.
The RM index of terms, under "blocking, potential" also has:
- C.7.1(16), which we would not want to use for the AI purpose, but should
  certainly be a dispatching point for the nonpreemptive policy
- E.4(17) and E.5(23), which should be.

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

From: Randy Brukardt
Sent: Tuesday, May 15, 2018  2:14 PM

> ... Rather, I think he we suggesting we identify the particular
> language-defined subprograms that
> *must* be preemption points, and I suppose *allow* other
> potentially-blocking subprograms to be preemption points, rather than
> require them to be.

Since the purpose of the AI is to get *more* determinism, I doubt that having
some preemption points being optional helps that goal.

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

From: Randy Brukardt
Sent: Tuesday, May 15, 2018   2:38 PM

> If there was a place in the RM (other than the index) that itemized
> all such subprograms, we might have thought of ticking off explicitly
> those that mattered to our intent.

There is no such place, intentionally (there never has been, either).

> The (silent) boundary of our concern was chapter 9.
> The RM index of terms, under "blocking, potential" also has:
> - C.7.1(16), which we would not want to use for the AI purpose, but
> should certainly be a dispatching point for the nonpreemptive policy
> - E.4(17) and E.5(23), which should be.

Any such index entries that remains on subprogram are in deleted text and only
show in the AARM. So pay no attention to that! All of the English wording making
subprograms potentially blocking has been removed (or should have been removed -
if you find any outside of 9.5, tell me), along with any such index entries. The
index entries were never were consistently added in the first place.

You can find a complete list in the !discussion of AI12-0241-1 -- any nongeneric
unit with Nonblocking => False is potentially blocking. (Generic units aren't
potentially blocking, because it is the instance that actually defines the
subprograms, and that is user-defined. This includes all of the containers.
Also, packages like finalization only define abstract types/routines; the
user-defined routines that override those are the ones that do the work and
they're not potentially blocking since they're user-defined.)

Taking a quick look at that list, I would suggest simply excepting all of the
I/O routines from being a preemption point in this way (that's by far the
majority of them) -- they still could preempt if they actually execute a
potentially blocking operation, but they themselves do not do so.

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

From: Tucker Taft
Sent: Tuesday, May 15, 2018  3:41 PM

> Since the purpose of the AI is to get *more* determinism, I doubt that
> having some preemption points being optional helps that goal.

I would think having more preemption points is OK, but I defer to the RT
experts.

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

From: Erhard Ploedereder
Sent: Sunday, May 20, 2018  5:49 PM

The AI says:
> All potentially blocking operations are preemption points for the
> Non-Preemptive Dispatching Policy

The RM says:
> The following are defined to be potentially blocking operations:
>  ... << good stuff>>
>  a call on a subprogram whose body contains a potentially blocking
>   operation.

So, the rules combine to make every call site down the stack towards the
really potentially blocking operation a dispatching point.

I don't think that anybody wants that to happen.

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

From: Randy Brukardt
Sent: Thursday, May 31, 2018  10:03 PM

> The RM says:
> > The following are defined to be potentially blocking operations:
> >  ... << good stuff>>
> > . a call on a subprogram whose body contains a potentially
> blocking operation.

No, it doesn't. At least not the draft Ada 2020 RM, which is the one that
matters here!

More specifically, AI12-0249-1 removed that line and made it into a separate
rule because it messed up Detect_Blocking (see the AI for details). We
discussed and approved that during the January meeting. So you have to be
using the April version of the draft RM (the HTML version, since there is no
April PDF version) in order to see that.

> So, the rules combine to make every call site down the stack
> towards the really potentially blocking operation a dispatching point.

As noted above, they don't.

> I don't think that anybody wants that to happen.

And it doesn't.

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

From: Tullio Vardanega
Sent: Sunday, October 14, 2018  2:31 PM

At the Lisbon meeting, Brad and I were tasked to refine AI12-0279-1
along the lines of the discussion we had there. The gist of it was to
seek ways to obtain the desired effect avoiding unnecessary overhead.
In the record, this action was attached to Brad's homework.
Brad and I have come up with the following revision, which we believe
addresses the intent.

[This was followed by version /02 of the AI - Editor.]

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

From: Tullio Vardanega
Sent: Monday, October 15, 2018 11:57 AM

Apologies for stammering this AI.
I should have added to the first !wording para that Yield should default to
False, when the aspect is not specified. Thanks Alan for suggesting.

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

From: Randy Brukardt
Sent: Tuesday, October 16, 2018 10:17 PM

I added the following paragraph (stolen from aspect Pack) to the AI wording:

If directly specified, the aspect_definition shall be a static expression. If
not specified (including by inheritance), the aspect is False.

You didn't mention the first part, but you must be intending that based on the
rest of the text.

[This was added to version /02 of the AI - Editor.]

----

Some other comments:

>The Yield aspect with value True is nonoverridable. (see 13.1.1)

Aspects are either nonoverridable or they aren't. The value doesn't have
anything to do with it. Moreover, the term is only for type-related aspects, so
you're probably not getting whatever you are intending from this rule. And a
nitpick - the period should be after the ).

Probably this should either be replaced by what you mean. Maybe add it to the
preceding paragraph:
   If a Yield aspect is specified True for a primitive subprogram S of a tagged
   type T, then the aspect is inherited by descendants of T (including the
   class-wide type T'Class). {If the Yield aspect is specified for a subprogram which
   inherits the value of the aspect, the specified value shall be confirming.}

---

On this line, you allow specifying Yield for an access-to-subprogram type. I'm
not sure how that is supposed to work. In general, compiler will have to try to
add a call to [subprogram] Yield before returning from a Yield => True routine
unless it can prove that some other blocking operation was (certainly) executed
by the routine. For a subprogram, that won't add overhead much of the time,
since the compiler will see the body and will be able to determine that the
needed overhead has already happened (or that it happens on most paths, adding
overhead only to specific paths). For an access-to-subprogram, the actual
subprogram is unknown, so such overhead would have to be added to every call
unconditionally. I suppose an implementation could use an alternative
implementation that when the Yield is False on the subprogram for which 'Access
is taken, a wrapper is generated that has the needed task dispatching point;
doing nothing otherwise. But that seems just as expensive in normal use (most
routines won't have the Yield => True).

I guess I would not include access-to-subprogram here; if someone wants to
ensure that some call on such a value was a task dispatching point, they can add
a Yield call manually. (I don't see this coming up often!)

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

From: Tullio Vardanega
Sent: Thursday, October 18, 2018 6:37 AM

My views interspersed with the text below.

...
>I added the following paragraph (stolen from aspect Pack) to the AI wording:
>
>If directly specified, the aspect_definition shall be a static expression.
>If not specified (including by inheritance), the aspect is False.
>
>You didn't mention the first part, but you must be intending that based on
>the rest of the text.

Yes, correct: fine fix.

...
>Probably this should either be replaced by what you mean. Maybe add it to
>the preceding paragraph:
>   If a Yield aspect is specified True for a primitive subprogram S of a
>   tagged type T, then the aspect is inherited by descendants of T (including
>   the class-wide type T'Class). {If the Yield aspect is specified for a
>   subprogram which inherits the value of the aspect, the specified value
>   shall be confirming.}

Yes, your proposed addition does what I and the IRTAW group meant.

...
>I guess I would not include access-to-subprogram here; if someone wants to
>ensure that some call on such a value was a task dispatching point, they can
>add a Yield call manually. (I don't see this coming up often!)

---
I appreciate the wisdom of avoiding unwanted overhead.
I suppose we can live with not including access-to-subprograms to this ruling
and let the programmer handle those cases manually.

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


Questions? Ask the ACAA Technical Agent