comp.lang.ada
 help / color / mirror / Atom feed
From: Jeffrey Carter <spam.jrcarter.not@spam.not.acm.org>
Subject: Re: How to: communication between multiple tasks using protected objects - with no polling?
Date: Wed, 21 Jan 2015 11:39:00 -0700
Date: 2015-01-21T11:39:00-07:00	[thread overview]
Message-ID: <m9orn5$2pd$1@dont-email.me> (raw)
In-Reply-To: <cd097f9b-3610-4456-a903-676ad9c2ef9f@googlegroups.com>

On 01/21/2015 10:16 AM, Esa Riihonen wrote:
> keskiviikko 21. tammikuuta 2015 2.47.29 UTC+2 Jeffrey Carter kirjoitti:
> 
>> select
>>    PO1.Get (...);
>> then abort
>>    PO2.Get (...);
>> end select;
>>
>> This might, however, result in both entries being executed.
> 
> If I understand correctly this will indeed work when selecting between two PO entries. And it wouldn't matter if both got executed (although I just now can't see how this could happen).

Why both might be executed is kind of complicated. There was a thread on here
about this some time ago. Consider the Ada-83 timed entry call:

select
   T.E;
or
   delay 1.0;
end select;

Ada 83 did not have protected types/objects or requeue, so if T.E was accepted
before the delay expired, the delay could be cancelled. If the delay expired
before the entry call was accepted then the entry call was aborted.

Ada 95 introduced protected types/objects and requeue. This changed the
semantics somewhat. Since the entry call (which can now be to a task or PO
entry) can be requeued "with abort", the delay can't be canceled when the entry
call is accepted, since a requeued with abort call can be aborted when the delay
expires. Instead, we have to wait until the entry call completes before we can
cancel the delay. If the delay expires while the entry call is being executed,
that's OK because the execution of an entry call is abort deferred.

Similar considerations apply to the ATC with both branches being entry calls and
result in the possibility of both calls being executed.

> However, there seems not to be a natural way to expand this for more than
> those two. This seems to be syntactically correct (compiles):
> 
>  select
>     PO1.Get (...);
>  then abort
>     select
>        PO2.Get (...);
>     then abort
>        PO3.Get (...);
>     end select
>  end select;

Yes, these can be nested as far as desired.

> But I'm not sure whether it would actually do what I want it to do. That is: suspend there until any of the entry barriers becomes true and then exit the whole outermost select statement?

Yes, but it might execute any number of the entry calls.

> Even if it works I have a (minor) concern that it is not 'pretty' as it doesn't reflect the mutual equality of the entries - and thus has a feel of 'hack' there ;)

It's not pretty and it's not a good idea. ATC is said to be a very complex and
heavyweight construct. I would only use it as a last resort.

>> type Q_ID is (Q1, Q2, ...);
>>
>> type Q_Item (Q : Q_ID := Q_ID'First) is record
>>    case Q is
>>    when Q1 =>
>>       Item_1 : Q1_Item;
>>    when Q2 =>
>>       Item_2 : Q2:Item;
>>    ...
>>    end case;
>> end record;
>>
>> protected Qs is
>>    entry Get (Item : out Q_Item);
>> private -- Qs
>>    Q_1 : Q1_Q;
>>    Q_2 : Q2_Q;
>>    ...
>> end Qs;
>>
>> protected body Qs is
>>    entry Get (Item : out Q_Item) when
>>       not Q_1.Is_Empty or not Q_2.Is_Empty or ...
>>    is
>>       -- Empty declarative part
>>    begin -- Get
>>       if not Q_1.Is_Empty then
>>          Item := (Q => Q1, Item_1 => Q_1.Get);
>>       elsif not Q_2.Is_Empty then
>>          item := (Q => Q2, Item_2 => Q_2.Get;
>>       ...
>>       end if;
>>    end Get;
>> end Qs;

I guess I was asleep when I wrote that. For one thing I left out a way to get
things on the Qs. A better approach would be

protected Queue is
   procedure Put (Item : in Q_Item);
   entry Get (Item : out Q_Item);
private -- Queue
   Q : Q_Item_Q;
end Queue;

protected body Queue is
   procedure Put (Item : in Q_Item) is
      -- Empty declarative part
   begin -- Put
      Q.Put (Item => Item);
   end Put;

   entry Get (Item : out Q_Item) when not Q.Is_Empty is
      -- Empty declarative part
   begin -- Get
      Q.Get (Item => Item);
   end Get;
end Queue;

If you're willing to restrict yourself to a single compiler vendor, Ada 12 has
synchronized queues as part of the standard library, and you could use one of
those instead of writing your own. For earlier versions of Ada, which are
supported by multiple compiler vendors, you could use PragmARC.Queue_Unbounded
(or Queue_Bounded) from the PragmAda Reusable Components rather than writing
your own. I guess it depends on whether writing your own PO is important for
your learning experience.

-- 
Jeff Carter
"Run away! Run away!"
Monty Python and the Holy Grail
58

  reply	other threads:[~2015-01-21 18:39 UTC|newest]

Thread overview: 18+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2015-01-20 22:36 How to: communication between multiple tasks using protected objects - with no polling? Esa Riihonen
2015-01-21  0:19 ` Hubert
2015-01-21 16:53   ` Esa Riihonen
2015-01-21 23:22     ` Hubert
2015-01-22 13:24       ` Esa Riihonen
2015-01-21  0:47 ` Jeffrey Carter
2015-01-21  8:11   ` Simon Wright
2015-01-21 17:16   ` Esa Riihonen
2015-01-21 18:39     ` Jeffrey Carter [this message]
2015-01-22 13:32       ` Esa Riihonen
2015-01-21  8:28 ` Dmitry A. Kazakov
2015-01-21 17:34   ` Esa Riihonen
2015-01-21 18:56     ` Jacob Sparre Andersen
2015-01-21 20:15       ` Dmitry A. Kazakov
2015-01-22 21:52         ` G.B.
2015-01-23  8:25           ` Dmitry A. Kazakov
2015-01-21 20:02     ` Dmitry A. Kazakov
2015-01-22 13:37       ` Esa Riihonen
replies disabled

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox