!standard D.2.4 (10/3) 18-05-14 AI12-0279-1/01 !class binding interpretation !class binding interpretation 18-05-14 !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 All potentially blocking operations are preemption points for the Non-Preemptive Dispatching Policy. !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 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. !wording Modify D.2.4 (10/3) For this policy, {invoking an operation that is potentially blocking,} [blocking or ]termination of a task, [a delay_statement], {and} a call to Yield_To_Higher[, and a call to Yield_To_Same_Or_Higher or Yield] are the only task dispatching points (see D.2.1). Modify D.2.4 (10.a/3) {Reason: We want higher predictability for determining the blocking time for higher priority tasks. We want starting any potentially blocking operation to be a dispatching point, as otherwise calling an open entry or a suspension object that is open would not trigger dispatching, but would if the entry or suspection object happened to be closed at the time of the call.} Ramification: {Any potentially blocking operation}[A delay_statement] is always a task dispatching point even if it is not blocking. Similarly, a call to Yield_To_Higher is never blocking, but it is a task dispatching point{.} In each of these cases, they can cause the current task to stop running (it is still ready). [Otherwise, the running task continues to run until it is blocked.] !discussion We don't need to say anything about calls such as Yield, Suspend_Until_True, or Suspend_Until_True_And_Set_Deadline, as these subprograms all have the Nonblocking aspect specified as False, and the new wording covers all potentially blocking operations, which includes these calls. !ASIS No ASIS effect. !ACATS test An ACATS C-Test is needed to check that a potentially blocking operation is a 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.) ****************************************************************