!standard D.13(6/4) 16-05-31 AC95-00277/00 !class Amendment 16-05-31 !status received no action 16-05-31 !status received 16-01-13 !subject Allow Synchronous_Barriers in Ravenscar !summary !appendix From: Tucker Taft Sent: Wednesday, January 13, 2016 9:25 AM One of our engineers found the new restriction in Ravenscar against Synchronous_Barriers painful, as many of his examples were based on that. I managed to implement Synchronous_Barriers pretty easily and portably using a protected object and an array of suspension objects. This implies to me that the restriction is not well justified, and it seems unfortunate to lose this capability, particularly given that it is often implemented quite efficiently in the underlying RTOS. See below for the portable implementation. Example results of running the test program "bar_test" are as follows: Number 1 is about to wait for barrier Number 3 is about to wait for barrier Number 2 is about to wait for barrier Number 2 returned from wait, Notified = TRUE Number 1 returned from wait, Notified = FALSE Number 3 returned from wait, Notified = FALSE --------- with Ada.Synchronous_Task_Control; use Ada.Synchronous_Task_Control; package Raven_Sync_Barriers is pragma Preelaborate(Raven_Sync_Barriers); subtype Barrier_Limit is Positive range 1 .. 1000; type Synchronous_Barrier (Release_Threshold : Barrier_Limit) is limited private; procedure Wait_For_Release (The_Barrier : in out Synchronous_Barrier; Notified : out Boolean); private type Susp_Obj_Array is array (Positive range <>) of Suspension_Object; protected type Protected_Barrier (Release_Threshold : Barrier_Limit) is procedure Arrive(Arrival_Index : out Barrier_Limit); -- Keep track of the arrival index for each task private Num_Arrived : Natural range 0 .. Barrier_Limit'Last := 0; -- Number of tasks that have arrived at barrier end Protected_Barrier; type Synchronous_Barrier (Release_Threshold : Barrier_Limit) is limited record Suspended_Tasks : Susp_Obj_Array (2 .. Release_Threshold); Barrier : Protected_Barrier (Release_Threshold); end record; end Raven_Sync_Barriers; package body Raven_Sync_Barriers is protected body Protected_Barrier is procedure Arrive(Arrival_Index : out Barrier_Limit) is -- Keep track of the arrival index for each task begin if Num_Arrived >= Release_Threshold then -- Too many tasks raise Program_Error; else -- Count tasks that have arrived Num_Arrived := Num_Arrived + 1; Arrival_Index := Num_Arrived; end if; end Arrive; end Protected_Barrier; procedure Wait_For_Release (The_Barrier : in out Synchronous_Barrier; Notified : out Boolean) is Arrival_Index : Barrier_Limit; begin The_Barrier.Barrier.Arrive (Arrival_Index); if Arrival_Index < The_Barrier.Release_Threshold then -- Go to sleep Suspend_Until_True (The_Barrier.Suspended_Tasks (Arrival_Index + 1)); Notified := False; else -- Wake everybody else up for I in The_Barrier.Suspended_Tasks'Range loop Set_True (The_Barrier.Suspended_Tasks (I)); end loop; Notified := True; -- This is the lucky task end if; end Wait_For_Release; end Raven_Sync_Barriers; with Raven_Sync_Barriers; use Raven_Sync_Barriers; with Ada.Text_IO; use Ada.Text_IO; procedure bar_test is Z : Synchronous_Barrier(3); task type TT(Me : Positive); task body TT is Notified : Boolean := False; begin Put_Line ("Number" & Positive'Image(Me) & " is about to wait for barrier"); Wait_For_Release(Z, Notified); Put_Line ("Number" & Positive'Image(Me) & " returned from wait, Notified = " & Boolean'Image(Notified)); end TT; T1 : TT(1); T2 : TT(2); T3 : TT(3); begin null; end bar_test; *************************************************************** From: Randy Brukardt Sent: Wednesday, January 13, 2016 1:24 PM > One of our engineers found the new restriction in Ravenscar against > Synchronous_Barriers painful, as many of his examples were based on > that. I managed to implement Synchronous_Barriers pretty easily and > portably using a protected object and an array of suspension objects. At the risk of treading in areas that I don't understand that perfectly, it seems to me that your implementation is too easy. It doesn't quite implement the semantics of a synchronous barrier. In particular: ... > procedure Wait_For_Release (The_Barrier : in out Synchronous_Barrier; > Notified : out Boolean) is > Arrival_Index : Barrier_Limit; > begin ... > -- Wake everybody else up > for I in The_Barrier.Suspended_Tasks'Range loop > Set_True (The_Barrier.Suspended_Tasks (I)); > end loop; The above loop wakes up the tasks one at a time, in the order that they arrived. A synchronous barrier wakes up all of the tasks at the same time. That could matter if tasks of different priorities are involved. In the worst case, the task that triggers the release has a lower priority than one of the other tasks that is getting woken up. With the above implementation, that would cause unbounded priority inversion, as the newly ready high-priority task would suspend the task that is doing the waking up, meaning that a task with even a higher priority would never get to be ready. Specifically, consider 3 tasks: A (priority 2) B (priority 3) C (priority 4) These all suspend on a suspension object on a monoprocessor. With a correct implementation of a synchronous barrier, it doesn't matter what order these arrive in; they'll all get woken up together and then C will be the first to run (as it has a higher priority). With your implementation, the order of arrival matters. In one of the orders, the wrong thing happens: Task C (highest priority) is running. Task C waits on an external event. Task B (next priority) begins running. Task B calls the synchronous barrier with a limit of 3 and suspends on a suspension object. Task A starts running. Task C's external event occurs, preempting task A, so it begins running. Task C calls the synchronous barrier and suspends on a suspension object. Task A starts running. Task A calls the synchronous barrier. Task A is the third arrival, so the release loop begins to run. The first task released from its suspension object is task B. Since its priority is higher than A, it preempts A. Oops. At this point, task A will never get to release task C (the highest priority task) until task B blocks. That's unbounded priority inversion. ---------- It might be possible to fix your implementation if you were to ensure that the highest priority task is always released first. But that clearly is going to add some additional overhead (depending on how expensive retrieving the priority of a task is), and surely isn't quite as *easy* as advertised. And I'm not convinced that works properly on a multiprocessor (with some of the tasks running on a different CPU), as the ready loop still could be suspended on one CPU when it ought to have allowed lower priority tasks on some other CPU to run. If you could do the above inside of a protected action, you'd be OK, but I can't figure out how to do that (you can call Set_True inside of a protected action, as it's not a potentially blocking operation, but you can't call Suspend_Until_True). Note that you still need to wake up the tasks in priority order lest a lower-priority task on a different CPU run for a bit before a higher-priority task is made ready and suspends it. As I understand it, the reason for putting synchronous barriers in the language is to make kernel support for those available to Ada programmers. If it is not in the kernel (and we don't want to add to the complexity of Ravenscar kernels), then a relatively expensive implementation like yours (at least if implemented correctly) is a fraud. On top of which, a Ravenscar programmer would be better served using a simple implementation that only meets the requirements that they actually expect to use. (That is, if all of the tasks using a synchronous barrier are the same priority, your implementation here is a better choice than one that is correct in the face of priorities. Ergo, I still think that we have this right; someone that needs simple synchronous barriers in Ravenscar can use your package, and any priority inversion will be obvious in the code. And if they need them in a complex setup, that is hard to do and probably is out of scope for Ravenscar, as it requires kernel cooperation. *************************************************************** From: Tucker Taft Sent: Wednesday, January 13, 2016 1:54 PM > At the risk of treading in areas that I don't understand that perfectly, it > seems to me that your implementation is too easy. It doesn't quite implement > the semantics of a synchronous barrier. > Good point. Note that Tullio saw this and had the following comment: "I latch onto your nice quick implementation example around AI12-0073. (It is indeed so that the case of truly synchronous release can be analysed under the Ravenscar principles, and your example shows that the runtime costs may not be too high.)" which implies that the proposed implementation doesn't seem too costly to him, and that a synchronous release is analyzable. Nevertheless, I agree with your concern about priority inversion, hence consider the revised implementation below, where the calls on "Set_True" are performed in a protected operation. --- with Ada.Synchronous_Task_Control; use Ada.Synchronous_Task_Control; package Raven_Sync_Barriers is pragma Preelaborate(Raven_Sync_Barriers); subtype Barrier_Limit is Positive range 1 .. 1000; type Synchronous_Barrier (Release_Threshold : Barrier_Limit) is limited private; procedure Wait_For_Release (The_Barrier : in out Synchronous_Barrier; Notified : out Boolean); private type Susp_Obj_Array is array (Positive range <>) of Suspension_Object; protected type Protected_Barrier (Release_Threshold : Barrier_Limit) is procedure Arrive(Arrival_Index : out Barrier_Limit); -- Keep track of the arrival index for each task procedure Wake_All(Suspended_Tasks : in out Susp_Obj_Array); -- Wake all tasks in given suspension-object array private Num_Arrived : Natural range 0 .. Barrier_Limit'Last := 0; -- Number of tasks that have arrived at barrier end Protected_Barrier; type Synchronous_Barrier (Release_Threshold : Barrier_Limit) is limited record Suspended_Tasks : Susp_Obj_Array (2 .. Release_Threshold); Barrier : Protected_Barrier (Release_Threshold); end record; end Raven_Sync_Barriers; package body Raven_Sync_Barriers is protected body Protected_Barrier is procedure Arrive(Arrival_Index : out Barrier_Limit) is -- Keep track of the arrival index for each task begin if Num_Arrived >= Release_Threshold then -- Too many tasks raise Program_Error; else -- Count tasks that have arrived Num_Arrived := Num_Arrived + 1; Arrival_Index := Num_Arrived; end if; end Arrive; procedure Wake_All(Suspended_Tasks : in out Susp_Obj_Array) is -- Wake all tasks in given suspension-object array begin for I in Suspended_Tasks'Range loop Set_True (Suspended_Tasks (I)); end loop; end Wake_All; end Protected_Barrier; procedure Wait_For_Release (The_Barrier : in out Synchronous_Barrier; Notified : out Boolean) is Arrival_Index : Barrier_Limit; begin The_Barrier.Barrier.Arrive (Arrival_Index); if Arrival_Index < The_Barrier.Release_Threshold then -- Go to sleep Suspend_Until_True (The_Barrier.Suspended_Tasks (Arrival_Index + 1)); Notified := False; else -- Wake everybody else up The_Barrier.Barrier.Wake_All (The_Barrier.Suspended_Tasks); Notified := True; -- This is the lucky task end if; end Wait_For_Release; end Raven_Sync_Barriers; *************************************************************** From: Tullio Vardanega Sent: Wednesday, January 13, 2016 2:47 PM Of course, Set_True must occur in a protected operation. My mind tricked my eyes, reading that in the previous version, for it had to be so to avoid the risk that Randy noted. *************************************************************** From: Randy Brukardt Sent: Wednesday, January 13, 2016 4:30 PM Ah, but that doesn't (completely) fix the problem on a multiprocessor. (Indeed, I originally intended to make that point, but when I realized that you weren't using a protected action, I changed tact.) [Recall that Ravenscar tasks are statically bound to CPUs.] Specifically, consider the following: Task A assigned to CPU 0 (priority 2, but not really important) Task B assigned to CPU 1 (priority 3) Task C assigned to CPU 1 (priority 4) Other tasks assigned to CPU 0 (priority 3 or higher). The scenario starts as before: Task C (highest priority) is running on CPU 1. Task C waits on an external event. Task B (next priority) begins running on CPU 1. Task B calls the synchronous barrier with a limit of 3 and suspends on a suspension object. Task C's external event occurs, so it begins running on CPU 1. Task C calls the synchronous barrier and suspends on a suspension object. Task begins running on CPU 0. Task A calls the synchronous barrier. Task A is the third arrival, so the release loop begins to run. The first task released from its suspension object is task B. Since it runs on CPU 1, which is idle, it begins to run immediately. Later, the second task released from its suspension object is task C. Since it runs on CPU 1 and has a higher priority than task C, it has to preempt task B on that CPU. The problem here is that in the canonical semantics, task B doesn't run at all; task C should immediately become ready and start executing. Note that one can make the delay in starting task C arbitrarily long simply by having more tasks that belong to CPU 0 add themselves to the synchronous barrier between task B and task C. For instance, if releasing a task takes 10 microseconds, then if there is 100 tasks on the synchronous barrier, task B could execute for a millisecond before getting preempted. A task can do a lot in a millisecond on modern hardware! Now, I realize that this scenario is likely to be rare, and the case where the early, incorrect execution of task B matters is even rarer. But I don't think it is fair to say that your code is a correct implementation of a Synchronous Barrier for a multiprocessor. And its these sorts of subtleties that worry me. (After all, if I can see these problems on a cursory look, what sort of things that I don't even understand might show up???) To be truly correct, your implementation has to release the highest priority tasks first, because they might run immediately on some other CPU. And, as I mentioned before, the overhead of that depends on the cost of reading the priority of a task (something I don't have a feel for on a multiprocessor - I'd expect it to be relatively expensive on CPUs with limited sharing of memory). *************************************************************** From: Tucker Taft Sent: Tuesday, January 13, 2016 5:10 PM > ... >> Nevertheless, I agree with your concern about priority inversion, >> hence consider the revised implementation below, where the calls on >> "Set_True" >> are performed in a protected operation. > > Ah, but that doesn't (completely) fix the problem on a multiprocessor. > ... > The problem here is that in the canonical semantics, task B doesn't > run at all; task C should immediately become ready and start executing. > The word "immediately" doesn't appear in the description of Synchronous_Barriers, nor is there any explicit requirement for the tasks to be released in priority order. Here is what the RM says: "Each call to Wait_For_Release blocks the calling task until the number of blocked tasks associated with the Synchronous_Barrier object is equal to Release_Threshold, at which time all blocked tasks are released. Notified is set to True for one of the released tasks, and set to False for all other released tasks." It says all blocked tasks are released, but nothing about order or some requirement for simultaneity (which doesn't really make sense given that releasing any task requires some amount of processing). > ... To be truly correct, your implementation has to release the > highest priority tasks first, because they might run immediately on some > other CPU. ... I don't find any requirement for this in the RM description. What am I missing? *************************************************************** From: Randy Brukardt Sent: Tuesday, January 13, 2016 5:57 PM ... > > ... To be truly correct, your implementation has to release the > > highest priority tasks first, because they might run > immediately on some other CPU. ... > > I don't find any requirement for this in the RM description. > What am I missing? I think it's more that the RM is missing that text. My understanding of this feature was that it needs a special mechanism because it is intended to describe existing kernel features where the tasks are all made ready at once (I've always assumed indivisibly, although that might be my own invention). Indeed, the !problem statement in AI05-0174-1 (which introduced this feature) says: "As general purpose computing is moving to parallel architectures and eventually to massively parallel machines, there is a need to efficiently schedule many (hundreds or thousands) of tasks using barrier primitives. The POSIX OS interface provides a barrier primitive where N tasks wait on a barrier and are released simultaneously when all N are ready to execute. Functionality like this should be added to Ada." If you don't expect truly simultaneous release, then you can implement this trivially with queuing/requeuing on a protected object. You don't need any special features at all (and you're better off not using such features because the resulting code is much more familiar and understandable to Ada programmers, and visible to the compiler and tools). In that case, there is no reason whatsoever for this feature to have ever gotten into Ada in the first place - there is no need for any kernel involvement nor the mental overhead of yet another Ada feature. Besides, the name is "synchronous" barriers. What's synchronous about them other than the release? The name itself doesn't make sense if the release isn't all at once. Now, I understand that such a PO would not be allowed in Ravenscar, but that seems to be part of the simplification of Ravenscar. There's no reason to allow creeping featurism in Ravenscar, where people continually find ways to end-run the restrictions to do things that defeat the original purpose. I'd rather leave it to ITRAW to rationally extend Ravenscar than to punch holes in it here and there when someone finds it convenient. (Especially if that means damaging the purpose of synchronous barriers.) I suppose the wording "simultaneous" is missing because of concern that implementations that don't have kernel support would have trouble implementing it (as you say, there is some amount of processing needed for each task, especially problematic on distributed schedulers). But we should have done a better job of conveying the intent that the release appear simultaneous (meaning that higher priority tasks get released first, if an order is necessary). An all-Ada implementation ought to get as close as possible to the Posix primitive ideal, not sloppily allow lower priority tasks to execute for a while when they shouldn't be running. *************************************************************** From: Tucker Taft Sent: Tuesday, January 13, 2016 7:13 PM I think you are optimistic that Posix achieves "simultaneity." I don't see how that is possible, even with hardware support, and especially on a multiprocessor. You still have to put the tasks on the run queues somewhere, and if there are multiple processors involved, you need to signal the other processors that something has changed. I suppose you could order the tasks by priority on the barrier, but Posix doesn't seem to require that: From http://pubs.opengroup.org/onlinepubs/009695399/functions/pthread_barrier_wait.html: "The pthread_barrier_wait() function shall synchronize participating threads at the barrier referenced by barrier. The calling thread shall block until the required number of threads have called pthread_barrier_wait() specifying the barrier. "... Applications using this function may be subject to priority inversion, as discussed in the Base Definitions volume of IEEE Std 1003.1-2001, Section 3.285, Priority Inversion." *************************************************************** From: Steve Baird Sent: Tuesday, January 13, 2016 10:17 PM > I think you are optimistic that Posix achieves "simultaneity." I > don't see how that is possible, even with hardware support, and > especially on a multiprocessor. The phrase the RM uses in the analogous situation for task activation is "are activated together". It seems like the requirements here are similar to starting up a group of tasks that are being "activated together", but that's only based on my intuition. *************************************************************** From: Tucker Taft Sent: Tuesday, February 9, 2016 10:57 AM Getting back to this, I would like to put this on the agenda for Pisa. There still does not seem to be sufficient justification for disallowing synchronous barriers in Ravenscar. *************************************************************** From: Randy Brukardt Sent: Tuesday, February 9, 2016 6:52 PM > Getting back to this, I would like to put this on the agenda for Pisa. > There still does not seem to be sufficient justification for > disallowing synchronous barriers in Ravenscar. You will need a second to reopen a decided AI. We have rules about that to prevent people from churning AIs forever. [Editor's note: A second never appeared.] I remind you of this because I'm against reopening this AI at this time. A number of reasons for that: (1) IRTAW is looking at ways to extend Ravenscar. One hoped that they will have some specific proposals by that time. I don't see any good reason to adopt a piece-meal approach to extending Ravenscar. (My preference is that "extended Ravenscar" is a separate profile, but that's obviously TBD and somewhat out of my area anyway.) (2) Assuming true simultaneity is not required, then clearly a synchronous barrier could be implemented with an Ada PO if there is no underlying support. But that PO would not be allowed by Ravenscar. That means a bare-machine Ravenscar implementation is going to have to support some capabilities that are not currently needed by a Ravenscar implementation. (3) It would seem necessary to require a static number of tasks in the barrier for it to make sense in Ravenscar; everything else has to be specified statically. That means some rule about the discriminant of a synchronous barrier would be needed, meaning we would have to have a new restriction to support that. We can't just "revert" the existing AI! (4) If there is actually is an easy way to implement a synchronous barrier in a bare machine Ravenscar implementation (assuming the number of tasks is static), then the same would hold true for a sufficiently limited protected object. (Specifically, one where all of the queues have statically limited lengths, using the Max_Entry_Queue_Length aspect to specify that.) Allowing ONLY the synchronous barrier in Ravenscar would essentially force the use of a low-level construct to get entry queuing, rather than a higher-level construct (a PO). That would make Ravenscar programs even lower-level than they already are -- a step in the wrong direction. Especially as (1) is ongoing. I certainly could see that an extended Ravenscar should allow both longer queues and synchronous barriers, but I think we ought to wait and possibly revisit this once IRTAW makes a proposal or clearly decides not to. Especially as there is no rush to make a change here; nothing we do now will be effective until 2018 at the earliest. And of course, an implementation can support an extended Ravenscar at any time. No one HAS to use Ravenscar proper! *************************************************************** From: Tucker Taft Sent: Tuesday, February 9, 2016 9:02 PM > You will need a second to reopen a decided AI. We have rules about > that to prevent people from churning AIs forever. OK, hoping for a second from someone! > > I remind you of this because I'm against reopening this AI at this > time. A number of reasons for that: > > (1) IRTAW is looking at ways to extend Ravenscar. One hoped that they > will have some specific proposals by that time. I don't see any good > reason to adopt a piece-meal approach to extending Ravenscar. (My > preference is that "extended Ravenscar" is a separate profile, but > that's obviously TBD and somewhat out of my area anyway.) This seemed like a special case, in that it was, to some extent, *removed* from Ravenscar. *************************************************************** From: Randy Brukardt Sent: Wednesday, February 10, 2016 1:55 PM ... > > (1) IRTAW is looking at ways to extend Ravenscar. One hoped that > > they will have some specific proposals by that time. I don't see any > > good reason to adopt a piece-meal approach to extending Ravenscar. > > (My preference is that "extended Ravenscar" is a separate profile, > > but that's obviously TBD and somewhat out of my area anyway.) > > This seemed like a special case, in that it was, to some extent, > *removed* from Ravenscar. I think this is the crux of our difference of opinion. My understanding of Ravenscar was that it was a carefully considered set of features for (a) small, simple runtime and (b) analyzability (not necessarily in that order). Thus, any new feature has to be carefully analyzed for its effect on those goals -- in the absence of such a consideration, it has to be considered out. (The same will hold for the proposed parallel loop feature, for instance.) The RM, for historical reasons, isn't written that way. So when we forget completely about Ravenscar when defining a new feature, it appears to be in. But that just reflects a bug in the way the Standard is constructed, not some sort of technical opinion. A new feature still should default to out of Ravenscar and certainly should be considered out unless we *explicitly* decide to add it. Which definitely did not happen in this case. So I see no reason to think of this any difference for this feature from any other new feature, otherwise we're elevating the status of clear Standard bugs (it's clear that an unrestricted synchronous barrier has no place in Ravenscar, which requires everything to be determinable statically) to have some sort of mystical meaning. (That would be a clear repudiation of the Dewar rule, which we use as a critical part of interpreting the Standard.) *************************************************************** From: Tucker Taft Sent: Wednesday, February 10, 2016 3:45 PM >> This seemed like a special case, in that it was, to some extent, >> *removed* from Ravenscar. > ... The RM, for historical reasons, isn't written that way. So when we > forget completely about Ravenscar when defining a new feature, it appears to > be in. ... Fair enough. Alan has proposed the IRTAW take a look at Synchronous_Barriers, so hopefully by the time of the next ARG meeting we will have IRTAW's view on this. They have in the past been pretty conservative about accepting Ada features into Ravenscar, so they are probably going to consider this carefully. *************************************************************** From: Alan Burns Sent: Tuesday, February 16, 2016 8:22 AM Just looking over these emails with respect of writing an overview of the issues for IRTAW. Tuck, can your approach not be changed so that the calls of Set_True all occur from within the PO? (when it realises that the last task is in). The PO obviously has a higher priority than all the clients, so the 'synchronous' release that Randy wants is provided (as much as it can on a multiprocessor). Am I missing something? *************************************************************** From: Tucker Taft Sent: Tuesday, February 16, 2016 9:02 AM > Just looking over these emails with respect of writing an overview of > the issues for IRTAW. > > Tuck, can your approach not be changed so that the calls of Set_True > all occur from within the PO? (when it realises that the last task is > in). The PO obviously has a higher priority than all the clients, so > the 'synchronous' release that Randy wants is provided (as much as it can > on a multiprocessor). Yes, moving the Set_True's into the PO is exactly what I did in the subsequent e-mail message, and I believe that provides the desired synchronous release. > Am I missing something? No, except perhaps the subsequent reply in the long chain of e-mails. Below is the slightly updated version again. ----- with Ada.Synchronous_Task_Control; use Ada.Synchronous_Task_Control; package Raven_Sync_Barriers is pragma Preelaborate(Raven_Sync_Barriers); subtype Barrier_Limit is Positive range 1 .. 1000; type Synchronous_Barrier (Release_Threshold : Barrier_Limit) is limited private; procedure Wait_For_Release (The_Barrier : in out Synchronous_Barrier; Notified : out Boolean); private type Susp_Obj_Array is array (Positive range <>) of Suspension_Object; protected type Protected_Barrier (Release_Threshold : Barrier_Limit) is procedure Arrive(Arrival_Index : out Barrier_Limit); -- Keep track of the arrival index for each task procedure Wake_All(Suspended_Tasks : in out Susp_Obj_Array); -- Wake all tasks in given suspension-object array private Num_Arrived : Natural range 0 .. Barrier_Limit'Last := 0; -- Number of tasks that have arrived at barrier end Protected_Barrier; type Synchronous_Barrier (Release_Threshold : Barrier_Limit) is limited record Suspended_Tasks : Susp_Obj_Array (2 .. Release_Threshold); Barrier : Protected_Barrier (Release_Threshold); end record; end Raven_Sync_Barriers; package body Raven_Sync_Barriers is protected body Protected_Barrier is procedure Arrive(Arrival_Index : out Barrier_Limit) is -- Keep track of the arrival index for each task begin if Num_Arrived >= Release_Threshold then -- Too many tasks raise Program_Error; else -- Count tasks that have arrived Num_Arrived := Num_Arrived + 1; Arrival_Index := Num_Arrived; end if; end Arrive; procedure Wake_All(Suspended_Tasks : in out Susp_Obj_Array) is -- Wake all tasks in given suspension-object array begin for I in Suspended_Tasks'Range loop Set_True (Suspended_Tasks (I)); end loop; end Wake_All; end Protected_Barrier; procedure Wait_For_Release (The_Barrier : in out Synchronous_Barrier; Notified : out Boolean) is Arrival_Index : Barrier_Limit; begin The_Barrier.Barrier.Arrive (Arrival_Index); if Arrival_Index < The_Barrier.Release_Threshold then -- Go to sleep Suspend_Until_True (The_Barrier.Suspended_Tasks (Arrival_Index + 1)); Notified := False; else -- Wake everybody else up The_Barrier.Barrier.Wake_All (The_Barrier.Suspended_Tasks); Notified := True; -- This is the lucky task end if; end Wait_For_Release; end Raven_Sync_Barriers; *************************************************************** From: Alan Burns Sent: Friday, May 20, 2016 2:33 AM [Part of another message - Editor.] IRTAW did discuss these issue at length, sorry for being slow in reporting. The view of the IRTAW was that ... 2) Synchronous Barriers should not be included in the Ravenscar profile. The resistance to changes to Ravenscar is high, and there are work rounds. ***************************************************************