From mboxrd@z Thu Jan 1 00:00:00 1970 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on polar.synack.me X-Spam-Level: X-Spam-Status: No, score=-1.9 required=5.0 tests=BAYES_00 autolearn=unavailable autolearn_force=no version=3.4.4 Path: eternal-september.org!reader01.eternal-september.org!reader02.eternal-september.org!news.eternal-september.org!mx02.eternal-september.org!.POSTED!not-for-mail From: Jeffrey Carter Newsgroups: comp.lang.ada Subject: Re: How to: communication between multiple tasks using protected objects - with no polling? Date: Wed, 21 Jan 2015 11:39:00 -0700 Organization: Also freenews.netfront.net; news.tornevall.net; news.eternal-september.org Message-ID: References: <32208488-3a04-4d2a-8c64-840502dcf96d@googlegroups.com> Mime-Version: 1.0 Content-Type: text/plain; charset=windows-1252 Content-Transfer-Encoding: 7bit Injection-Date: Wed, 21 Jan 2015 18:38:35 +0000 (UTC) Injection-Info: mx02.eternal-september.org; posting-host="7519078e121dffe718f7ef9b6511f660"; logging-data="2861"; mail-complaints-to="abuse@eternal-september.org"; posting-account="U2FsdGVkX1/Jf5++240qrSiopOHuS6k/1kvkw+YPKTM=" User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:31.0) Gecko/20100101 Thunderbird/31.4.0 In-Reply-To: Cancel-Lock: sha1:0znz6cMteeMz/fj9adGukfXPic4= Xref: news.eternal-september.org comp.lang.ada:24673 Date: 2015-01-21T11:39:00-07:00 List-Id: 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