!standard D.2.1(1.2/2) 10-04-05 AI05-0166-1/04 !standard D.2.1(6/2) !standard 9.5.1(16) !standard D.2.4(2/2) !standard D.2.4(9/2) !class Amendment 09-10-22 !status Amendment 2012 10-04-05 !status ARG Approved 9-0-2 10-02-26 !status work item 09-10-22 !status received 09-10-22 !priority Medium !difficulty Easy !subject Yield for non-preemptive dispatching !summary A support package for non-preemptive dispatching is proposed that allows a task to offer to be preempted by a higher priority task, but not by one of equal priority. A further 'yield' procedure is included to improve the readability and maintainability of programs. It replaces the current need to include 'delay 0.0' to force a preemption. !problem With non-preemptive dispatching the only current non-blocking dispatching point is a non-blocking delay_statement. We need another kind so that a task in a system scheduled by non-preemptive dispatching can indicate that it is prepared to be preempted by tasks of higher priority but not by those of equal priority. !proposal Add to package Ada.Dispatching: procedure Yield; -- Bounded error if executed within a -- protected operation Add the following library package: package Ada.Dispatching.Non_Preemptive is pragma Preelaborate(Non_Preemptive); procedure Yield_To_Higher; procedure Yield_To_Same_Or_Higher renames Yield; end Ada.Dispatching.Non_Preemptive; With non-preemptive dispatching the only current non-blocking dispatching point is a non-blocking delay_statement - D.2.4(9/2). With this new package a call to Yield_To_Higher will cause a task switch if there is a runnable task with an active priority higher than the calling task's active priority. Note this can be called from within a protected object. To program cooperative scheduling a task explicitly includes the points at which it is able to be preempted. It does this by using either 'delay 0.0' or 'delay until Ada.Real_Time.Time_First' (the latter being the one available with Ravenscar). As these statements are not actually concerned with delaying the task they are potentially misleading (to the human reader/maintainer of the code). The procedure Yield is equivalent to these delay statements; and would therefore cause the same bounded error if called from within a protected action. !wording Modify package Ada.Dispatching (D.2.1(1.2/2)): package Ada.Dispatching is pragma Preelaborate[Pure](Dispatching);{ procedure Yield; } Dispatching_Error_Policy : exception; end Ada.Dispatching; Add after D.2.1(6/2): A call of Yield is a task dispatching point (see D.2.1). It is a bounded error to call Yield from within a protected action. Add after 9.5.1(14): * a call to Ada.Dispatching.Yield (see D.2.1); Add after D.2.4(2/2): The following language-defined library package exists: package Ada.Dispatching.Non_Preemptive is pragma Preelaborate(Non_Preemptive); procedure Yield_To_Higher; procedure Yield_To_Same_Or_Higher renames Yield; end Ada.Dispatching.Non_Preemptive; A call of Yield_To_Higher is a task dispatching point for this policy. If the task at the head of the highest priority ready queue has a higher active priority than the calling task then the calling task is preempted. AARM Ramification: For language-defined policies other than Non_Preemptive, a higher priority task should never be on a ready queue while a lower priority task is executed. Thus, for such policies, Yield_To_Higher does nothing. Replace D.2.4(9/2): For this policy, a non-blocking delay_statement, a call to Yield_To_Higher and a call to Yield_To_Same_Or_Higher or Yield are the only non-blocking events that are task dispatching points (see D.2.1). !discussion This AI was raised by an industrial user of non-preemptive scheduling, and was discussed and endorsed by the 14th IRTAW. Details are contained in the workshop paper: Providing Additional Real-Time Capability and Flexibility for Ada 2005, by Rod White. Since Yield has a side-effect, it is inappropriate to have it in a Pure package (the optimization rules for Pure packages would allow calls to it to be omitted, which would be bad). Thus we change the categorization of Ada.Dispatching to Preelaborate. This is potentially incompatible, but since the exception (the only preexisting contents of the package) is only raised by operations defined in "normal" (not Pure or Preelaborated) packages, it is unlikely to occur in practice. !example A task that iterates through an algorithm, but offers to be preempted by a higher priority task after each iteration would take the form: with Ada.Dispatching.Non_Preemptive; ... task Iterator; task body Iterator is ... Improving : boolean := True; begin ... while Improving loop -- code to improve quality of control -- might set improving to false Ada.Dispatching.Non_Preemptive.Yield_To_Higher; end loop; ... end Iterator; !corrigendum 9.5.1(14) @dinsa @xbullet @dinst @xbullet !corrigendum D.2.1(1.2/2) @drepl @xcode<@b Ada.Dispatching @b @b Pure(Dispatching); Dispatching_Policy_Error : @b; @b Ada.Dispatching;> @dby @xcode<@b Ada.Dispatching @b @b Preelaborate(Dispatching); @b Yield; Dispatching_Policy_Error : @b; @b Ada.Dispatching;> !corrigendum D.2.1(6/2) @dinsa Each processor also has one @i task, which is the task currently being executed by that processor. Whenever a task running on a processor reaches a task dispatching point it goes back to one or more ready queues; a task (possibly the same task) is then selected to run on that processor. The task selected is the one at the head of the highest priority nonempty ready queue; this task is then removed from all ready queues to which it belongs. @dinst A call of Yield is a task dispatching point. It is a bounded error to call Yield from within a protected action. !corrigendum D.2.4(2/2) @dinsa The @i@fa Non_Preemptive_FIFO_Within_Priorities is a task dispatching policy. @dinss The following language-defined library package exists: @xcode<@b Ada.Dispatching.Non_Preemptive @b @b Preelaborate(Non_Preemptive); @b Yield_To_Higher; @b Yield_To_Same_Or_Higher @b Yield; @b Ada.Dispatching.Non_Preemptive;> A call of Yield_To_Higher is a task dispatching point for this policy. If the task at the head of the highest priority ready queue has a higher active priority than the calling task then the calling task is preempted. !corrigendum D.2.4(9/2) @drepl For this policy, a non-blocking @fa is the only non-blocking event that is task dispatching point (see D.2.1). @dby For this policy, a non-blocking @fa, a call to Yield_To_Higher and a call to Yield_To_Same_Or_Higher or Yield are the only non-blocking events that are task dispatching points (see D.2.1). !ACATS test Add an ACATS C-Test of this package. !appendix From: Randy Brukardt Sent: Monday, April 5, 2010 11:44 AM In AI05-0166-1 (which defines Yield and Yield_to_Higher), we added a bullet to 9.5.1 (in the list of potentially blocking operations): * a call to Ada.Dispatching.Yield; But this is weird. First, it doesn't include Ada.Dispatching.Non_Preemptive.Yield_to_Higher, which surely can be blocking (for a non-preemptive policy, at least). Second, it is handled differently than all other language-defined subprograms. 9.5.1(18) says that language-defined subprograms are not potentially blocking unless they "are identified where they are defined" (or are a file I/O routine, not relevant here). So, by including Ada.Dispatching.Yield in this list and *not* identifying it where defined, we have a contradiction. Compare how Yield is defined to how Suspend_Until_True is defined: D.10(10) says specifically that it is a "potentially blocking operation". So I think those words need to be added after the other changes for Yield and Yield_to_Higher: "Yield is a potentially blocking operation (see 9.5.1)." and the same for Yield_to_Higher. And the bullet added to 9.5.1 should be dropped. Do we have to reopen the AI to do this, or just have the editor make the needed changes?? **************************************************************** From: Randy Brukardt Sent: Monday, April 5, 2010 11:59 AM I see another oddity. The definition of Yield in D.2.1: "A call of Yield is a task dispatching point (see D.2.1). It is a bounded error to call Yield from within a protected action." First, a cross-reference to the same clause is bogus. Second, saying something is a bounded error without any definition of the bounds is garbage. Besides, the definition of this as a potentially blocking operation (however that is done, preferably the way the Ada 9X team intended as outlined previously) has the right effect. So there should be a period after "point" and the rest should be dropped. (With the additional wording as noted previously added after.) P.S. John would prefer to say that there should be a point after "point", which sounds like the department of redundancy dept. **************************************************************** From: Randy Brukardt Sent: Monday, April 5, 2010 12:28 PM Apparently, we didn't look very carefully at this wording. The new wording for D.2.4(9/2) says: For this policy, a non-blocking @fa, a call to Yield_To_Higher and a call to Yield_To_Same_Or_Higher or Yield are the only non-blocking events that are task dispatching points (see D.2.1). This doesn't make much sense, since a call to Yield, et. al. can be a blocking operation (and clearly need to be "potentially blocking operations"). Since it explicitly mentions "non-blocking" for a delay statement, maybe it ought to mention "non-blocking" for the other two as well. But really the entire statement is bogus. No one cares about "non-blocking" anything, because by definition it didn't block. It's only interesting if the task dispatching point changes the running task, and in that case the task is blocked (stops running). So this isn't helpful, and there is no AARM note to explain what it is talking about. I'm sure I've forgotten what this is about, but the fact that that is possible is really bad. ****************************************************************