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,FREEMAIL_FROM autolearn=ham autolearn_force=no version=3.4.4 X-Google-Language: ENGLISH,ASCII-7-bit X-Google-Thread: ffc1e,fb45e48e8dddeabd X-Google-Attributes: gidffc1e,public X-Google-Thread: 103376,fb45e48e8dddeabd X-Google-Attributes: gid103376,public From: "Vladimir Olensky" Subject: Re: Ada Protected Object Tutorial #1 Date: 1999/12/17 Message-ID: X-Deja-AN: 561931338 References: <839toq$pu$1@bgtnsc03.worldnet.att.net> Organization: Posted via Supernews, http://www.supernews.com X-MimeOLE: Produced By Microsoft MimeOLE V4.72.3110.3 Newsgroups: comp.programming.threads,comp.lang.ada X-Complaints-To: newsabuse@supernews.com Date: 1999-12-17T00:00:00+00:00 List-Id: Robert A Duff wrote in message ... >kaz@ashi.footprints.net (Kaz Kylheku) writes: >> What if you want to block for something in the middle of the critical region? >> I assume that you can call a protected entry from within a protected procedure. > >No, you can't. If you want to block in the middle, then you have to >call the thing an "entry" and not a "procedure". One common thing to do >is to have an entry with a True barrier, so it always goes right ahead, >but then it can block by requeuing to a different entry (of the same >protected object, or a different one); it will then block on a new >barrier. Protected procedures, on the other had, can't block. Below is example with full explanation ( from Ada Rational ) which is an excellent illustration to that. Hope it will be helpful. Regards, Vladimir Olensky ======================================= Ada Rational is available at: http://www.adahome.com/LRM/95/Rationale/rat95html/rat95-contents.html ===================================== Our final example introduces the ability to requeue a call on another entry. It sometimes happens that a service needs to be provided in two parts and that the calling task has to be suspended after the first part until conditions are such that the second part can be done. Two entry calls are then necessary but attempts to program this in Ada 83 usually run into difficulties; race conditions can arise in the interval between the calls and there is often unnecessary visibility of the internal protocol. The example is of a broadcast signal. Tasks wait for some event and then when it occurs all the waiting tasks are released and the event reset. The difficulty is to prevent tasks that call the wait operation after the event has occurred, but before the signal can be reset, from getting through. In other words, we must reset the signal in preference to letting new tasks through. The requeue statement allows us to program such preference control. An implementation is protected Event is entry Wait; entry Signal; private entry Reset; Occurred: Boolean := False; end Event; protected body Event is entry Wait when Occurred is begin null; -- note null body end Wait; entry Signal when True is -- barrier is always true begin if Wait'Count > 0 then Occurred := True; requeue Reset; end if; end Signal; entry Reset when Wait'Count = 0 is begin Occurred := False; end Reset; end Event; Tasks indicate that they wish to wait for the event by the call Event.Wait; and the happening of the event is notified by some task calling Event.Signal; whereupon all the waiting tasks are allowed to proceed and the event is reset so that future calls of Wait work properly. The Boolean variable Occurred is normally false and is only true while tasks are being released. The entry Wait has no body but just exists so that calling tasks can suspend themselves on its queue while waiting for Occurred to become true. The entry Signal is interesting. It has a permanently true barrier and so is always processed. If there are no tasks on the queue of Wait (that is no tasks are waiting), then there is nothing to do and so it exits. On the other hand if there are tasks waiting then it must release them in such a way that no further tasks can get on the queue but then regain control so that it can reset the flag. It does this by requeuing itself on the entry Reset after setting Occurred to true to indicate that the event has occurred. The semantics of requeue are such that this completes the action of Signal. However, remember that at the end of the body of a protected entry or procedure the barriers are reevaluated for those entries which have tasks queued. In this case there are indeed tasks on the queue for Wait and there is also a task on the queue for Reset (the task that called Signal in the first place); the barrier for Wait is now true but of course the barrier for Reset is false since there are still tasks on the queue for Wait. A waiting task is thus allowed to execute the body of Wait (being null this does nothing) and the task thus proceeds and then the barrier evaluation repeats. The same process continues until all the waiting tasks have gone when finally the barrier of Reset also becomes true. The original task which called signal now executes the body of Reset thus resetting Occurred to false so that the system is once more in its initial state. The protected object as a whole is now finally left since there are no waiting tasks on any of the barriers. Note carefully that if any tasks had tried to call Wait or Signal while the whole process was in progress then they would not have been able to do so because the protected object as a whole was busy. This illustrates the two levels of protection and is the underlying reason why a race condition does not arise. Another consequence of the two levels is that it still all works properly even in the face of such difficulties as timed and conditional calls and aborts. The reader may recall, for example, that by contrast, the Count attribute for entries in tasks cannot be relied upon in the face of timed entry calls. A minor point to note is that the entry Reset is declared in the private part of the protected type and thus cannot be called from outside. Ada 95 also allows a task to have a private part containing private entries. The above example has been used for illustration only. The astute reader will have observed that the condition is not strictly needed inside Signal; without it the caller will simply always requeue and then immediately be processed if there are no waiting tasks. But the condition clarifies the description. Indeed, the very astute reader might care to note that we can actually program this example in Ada 95 without using requeue at all. A more realistic classic example is the disk scheduler where a caller is requeued if the head is currently over the wrong track. In this section we have outlined the main features of protected types. There are a number of detailed aspects that we have not covered. The general intent, however, should be clear. Protected types provide a data-oriented approach to synchronization which couples the high-level conditions (the barriers) with the efficiency of monitors. Furthermore the requeue statement provides a means of programming preference control and thus enables race conditions to be avoided. It must be remembered, of course, that the existing task model remains; the rendezvous will continue to be a necessary approach in many circumstances of a general nature (such as for directly passing messages). But the protected object provides a better paradigm for most data-oriented situations.