IVANOVIC%VAXR@CIRCUS.LLNL.GOV ("Vladimir Ivanovic, x3-7786") (07/28/88)
Can anyone answer the following question: 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? My intent is to get around the restrictions imposed by LRM 9.11(3) and 9.11(8) by having shared data structures whose types are not restricted to scalar and access types. My motivation is performance. I cannot afford the CPU strokes that encapsulating the data structures in a task and performing rendezvous with that task to access the data would cost. (And I cannot afford a faster CPU...) I realize that my particular implementation (VAX Ada v1.4) allows the use of pragma VOLATILE which is the same as pragma SHARED without the words "and whose type is a scalar or access type", essentially giving me what I want, but pragma VOLATILE is not portable across implementations.
NCOHEN@IBM.COM (Norman Cohen) (07/28/88)
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: <register X> := Reading_Pointer.ALL; WHILE NOT Shutdown_Requested LOOP Poll_Device( <register X> ); DELAY Next_Polling_Time - Calendar.Clock; Next_Polling_Time := Next_Polling_Time + Polling_Interval; END LOOP; Reading_Pointer.ALL := <register X>; 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
jonab@CAM.UNISYS.COM (Jonathan P. Biggar) (07/28/88)
In article <8807280055.AA27279@ajpo.sei.cmu.edu> IVANOVIC%VAXR@CIRCUS.LLNL.GOV ("Vladimir Ivanovic, x3-7786") writes: > I realize that my particular implementation (VAX Ada v1.4) allows > the use of pragma VOLATILE which is the same as pragma SHARED without > the words "and whose type is a scalar or access type", essentially > giving me what I want, but pragma VOLATILE is not portable across > implementations. However, pragma SHARED is not portable across implementations either. You cannot predict what any given implementation is going to consider a "scalar" type that can be accessed atomically. Implementations that support SHARED probably will not allow you to use it on a variable that is not scalar or access anyway. Try and look at your system again and see if you can change the way you allocate tasks in order to reduce the severity of your problem. For example, if you need to access the data structure several times in a short period, consider using a task that just acts as a semaphore, allowing another task to seize the resource, access the data structure directly, and then release the resource. Jon Biggar jonab@cam.unisys.com