comp.lang.ada
 help / color / mirror / Atom feed
From: NCOHEN@IBM.COM (Norman Cohen)
Subject: Pragma SHARED, tasks and shared variables
Date: 28 Jul 88 14:12:26 GMT	[thread overview]
Message-ID: <072888.101228.ncohen@ibm.com> (raw)

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

             reply	other threads:[~1988-07-28 14:12 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
1988-07-28 14:12 Norman Cohen [this message]
  -- strict thread matches above, loose matches on Subject: below --
1988-07-28  1:03 Pragma SHARED, tasks and shared variables "Vladimir Ivanovic, x3-7786"
1988-07-28 16:33 ` Jonathan P. Biggar
replies disabled

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