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=-0.8 required=5.0 tests=BAYES_00,INVALID_DATE autolearn=no autolearn_force=no version=3.4.4 Path: utzoo!utgpu!attcan!uunet!lll-winken!lll-tis!helios.ee.lbl.gov!nosc!ucsd!ucbvax!IBM.COM!NCOHEN From: NCOHEN@IBM.COM (Norman Cohen) Newsgroups: comp.lang.ada Subject: Pragma SHARED, tasks and shared variables Message-ID: <072888.101228.ncohen@ibm.com> Date: 28 Jul 88 14:12:26 GMT Sender: daemon@ucbvax.BERKELEY.EDU Organization: The Internet List-Id: Ref: INFO-ADA Volume 88 Issue 177 (Thu, Jul 28, 1988) Item #4 Vladimir Ivanovic asks: >> If I specify that every read or update to an variable whose type is an >> access type is a synchronization point for that variable by using the >> pragma SHARED and naming that variable [LRM 9.11(7)], can I be assured >> that the object designated by that variable will never be a local >> copy of that object? First let me rephrase the question, since the notion of a single access value designating one variable (a shared variable) at one time and another variable (a local copy) at another time doesn't make sense. The real question is whether, between synchronization points, a local copy can be used IN PLACE OF the variable designated by an access value. The first sentence of 9.11(2) informally defines a shared variable as a variable "accessible" by more than one task. Thus, if more than one task executes some task body in the scope of an access type, all variables in the collection associated with the access type are shared variables. Therefore, an optimizing compiler may assume, when compiling one task that uses a designated variable, that no other task updates the same designated variable between synchronization points. Consider, for example, the following code: TYPE Reading_Type IS DELTA ... RANGE ...; TYPE Reading_Pointer_Type IS ACCESS Reading_Type; Reading_Pointer : Reading_Pointer_Type; Shutdown_Requested : Boolean; Polling_Interval : CONSTANT Duration := ...; Next_Polling_Time : Calendar.Time := Calendar.Clock + Polling_Interval; PRAGMA Shared(Reading_Pointer); PRAGMA Shared(Shutdown_Requested); ... WHILE NOT Shutdown_Requested LOOP Poll_Device(Reading_Pointer.ALL); DELAY Next_Polling_Time - Calendar.Clock; Next_Polling_Time := Next_Polling_Time + Polling_Interval; END LOOP; Evaluation of the name Reading_Pointer.ALL examines the variable Reading_Pointer, so the Shared pragma makes that name a synchronization point for the variable Reading_Pointer, but NOT for the variable Reading_Pointer.ALL. The notion of a synchronization point for a particular variable (as opposed to for a task) is introduced in 9.11(9): The pragma SHARED can be used to specify that every read or update of a variable is a synchronization point for that variable; that is, the above assumptions always hold for the given variable (but not necessarily for other variables). Therefore, the loop above could be optimized, in accordance with 9.11, to the following: := Reading_Pointer.ALL; WHILE NOT Shutdown_Requested LOOP Poll_Device( ); DELAY Next_Polling_Time - Calendar.Clock; Next_Polling_Time := Next_Polling_Time + Polling_Interval; END LOOP; Reading_Pointer.ALL := ; Of course this optimization is illegitimate, even from a single-task point of view, if some other access-type variable contains the same access value as Reading_Pointer, and other statements refer to the variable designated by that other access-type variable. Thus, as a practical matter, the optimization can PROBABLY be defeated as follows: PROCEDURE Make_Value_Unknown(Pointer: OUT Reading_Pointer_Type) IS SEPARATE; Dummy_Pointer : Reading_Pointer_Type; -- type of Reading_Pointer ... Make_Value_Unknown(Dummy_Pointer); WHILE NOT Shutdown_Requested LOOP Poll_Device(Reading_Pointer.ALL); Dummy_Pointer.ALL := Dummy_Pointer.ALL + Dummy_Pointer.ALL; DELAY Next_Polling_Time - Calendar.Clock; Next_Polling_Time := Next_Polling_Time + Polling_Interval; END LOOP; ... SEPARATE(...) PROCEDURE Make_Value_Unknown(Pointer: OUT Reading_Pointer_Type) IS BEGIN Pointer := NEW Reading_Type'(0.0); -- Repeated addition of zero won't overflow END Make_Value_Unknown; Unless the optimizing compiler performs cross-compilation-unit optimizations at link time, or unless it actually compares pointer values before the loop, it will not be able to determine whether Dummy_Pointer = Reading_Pointer inside the loop. Thus, aside from any considerations about what other tasks are doing, the compiler will be unable to make a local copy of Reading_Pointer.ALL. Of course this solution is less than satisfying. It is tricky and obscure, it depends on certain assumptions about the behavior of the optimizing compiler, and it requires useless code to be added to the loop to reference Dummy_Pointer.ALL. I would much have preferred language rules that permitted optimizations with surprising consequences (both the kind permitted by LRM 9.11 and the kind permitted by LRM 11.6) only in the presence of a pragma explicitly requesting such optimizations. Norman Cohen IBM Research