Version 1.1 of acs/ac-00277.txt

Unformatted version of acs/ac-00277.txt version 1.1
Other versions for file acs/ac-00277.txt

!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.

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

Questions? Ask the ACAA Technical Agent