Annotated Ada Reference ManualLegal Information
Contents   Index   References   Search   Previous   Next 

9.10 Shared Variables

Static Semantics

1
{shared variable (protection of)} {independently addressable} If two different objects, including nonoverlapping parts of the same object, are independently addressable, they can be manipulated concurrently by two different tasks without synchronization. Normally, any two nonoverlapping objects are independently addressable. However, if packing, record layout, or Component_Size is specified for a given composite object, then it is implementation defined whether or not two nonoverlapping parts of that composite object are independently addressable. 
1.a
Implementation defined: Whether or not two nonoverlapping parts of a composite object are independently addressable, in the case where packing, record layout, or Component_Size is specified for the object.
1.b
Implementation Note: Independent addressability is the only high level semantic effect of a pragma Pack. If two objects are independently addressable, the implementation should allocate them in such a way that each can be written by the hardware without writing the other. For example, unless the user asks for it, it is generally not feasible to choose a bit-packed representation on a machine without an atomic bit field insertion instruction, because there might be tasks that update neighboring subcomponents concurrently, and locking operations on all subcomponents is generally not a good idea.
1.c
Even if packing or one of the other above-mentioned aspects is specified, subcomponents should still be updated independently if the hardware efficiently supports it. 

Dynamic Semantics

2
[Separate tasks normally proceed independently and concurrently with one another. However, task interactions can be used to synchronize the actions of two or more tasks to allow, for example, meaningful communication by the direct updating and reading of variables shared between the tasks.] The actions of two different tasks are synchronized in this sense when an action of one task signals an action of the other task; {signal (as defined between actions)} an action A1 is defined to signal an action A2 under the following circumstances:
3
If A1 and A2 are part of the execution of the same task, and the language rules require A1 to be performed before A2;
4
If A1 is the action of an activator that initiates the activation of a task, and A2 is part of the execution of the task that is activated;
5
If A1 is part of the activation of a task, and A2 is the action of waiting for completion of the activation;
6
If A1 is part of the execution of a task, and A2 is the action of waiting for the termination of the task;
6.1/1
{8652/0031} {AI95-00118-01} If A1 is the termination of a task T, and A2 is either the evaluation of the expression T'Terminated or a call to Ada.Task_Identification.Is_Terminated with an actual parameter that identifies T (see C.7.1);
7
If A1 is the action of issuing an entry call, and A2 is part of the corresponding execution of the appropriate entry_body or accept_statement.
7.a
Ramification: Evaluating the entry_index of an accept_statement is not synchronized with a corresponding entry call, nor is evaluating the entry barrier of an entry_body.
8
If A1 is part of the execution of an accept_statement or entry_body, and A2 is the action of returning from the corresponding entry call;
9
If A1 is part of the execution of a protected procedure body or entry_body for a given protected object, and A2 is part of a later execution of an entry_body for the same protected object; 
9.a
Reason: The underlying principle here is that for one action to “signal” a second, the second action has to follow a potentially blocking operation, whose blocking is dependent on the first action in some way. Protected procedures are not potentially blocking, so they can only be "signalers," they cannot be signaled.
9.b
Ramification: Protected subprogram calls are not defined to signal one another, which means that such calls alone cannot be used to synchronize access to shared data outside of a protected object. 
9.c
Reason: The point of this distinction is so that on multiprocessors with inconsistent caches, the caches only need to be refreshed at the beginning of an entry body, and forced out at the end of an entry body or protected procedure that leaves an entry open. Protected function calls, and protected subprogram calls for entryless protected objects do not require full cache consistency. Entryless protected objects are intended to be treated roughly like atomic objects — each operation is indivisible with respect to other operations (unless both are reads), but such operations cannot be used to synchronize access to other nonvolatile shared variables. 
10
If A1 signals some action that in turn signals A2. 

Erroneous Execution

11
{erroneous execution (cause) [partial]} Given an action of assigning to an object, and an action of reading or updating a part of the same object (or of a neighboring object if the two are not independently addressable), then the execution of the actions is erroneous unless the actions are sequential. {sequential (actions)} Two actions are sequential if one of the following is true: 
12
One action signals the other;
13
Both actions occur as part of the execution of the same task; 
13.a
Reason: Any two actions of the same task are sequential, even if one does not signal the other because they can be executed in an “arbitrary” (but necessarily equivalent to some “sequential”) order. 
14
Both actions occur as part of protected actions on the same protected object, and at most one of the actions is part of a call on a protected function of the protected object. 
14.a
Reason: Because actions within protected actions do not always imply signaling, we have to mention them here explicitly to make sure that actions occurring within different protected actions of the same protected object are sequential with respect to one another (unless both are part of calls on protected functions). 
14.b
Ramification: It doesn't matter whether or not the variable being assigned is actually a subcomponent of the protected object; globals can be safely updated from within the bodies of protected procedures or entries. 
15
A pragma Atomic or Atomic_Components may also be used to ensure that certain reads and updates are sequential — see C.6.
15.a
Ramification: If two actions are “sequential” it is known that their executions don't overlap in time, but it is not necessarily specified which occurs first. For example, all actions of a single task are sequential, even though the exact order of execution is not fully specified for all constructs. 
15.b
Discussion: Note that if two assignments to the same variable are sequential, but neither signals the other, then the program is not erroneous, but it is not specified which assignment ultimately prevails. Such a situation usually corresponds to a programming mistake, but in some (rare) cases, the order makes no difference, and for this reason this situation is not considered erroneous nor even a bounded error. In Ada 83, this was considered an “incorrect order dependence” if the “effect” of the program was affected, but “effect” was never fully defined. In Ada 95, this situation represents a potential nonportability, and a friendly compiler might want to warn the programmer about the situation, but it is not considered an error. An example where this would come up would be in gathering statistics as part of referencing some information, where the assignments associated with statistics gathering don't need to be ordered since they are just accumulating aggregate counts, sums, products, etc. 

Wording Changes from Ada 95

15.c/2
{8652/0031} {AI95-00118-01} Corrigendum: Clarified that a task T2 can rely on values of variables that are updated by another task T1, if task T2 first verifies that T1'Terminated is True. 

Contents   Index   References   Search   Previous   Next 
Ada-Europe Sponsored by Ada-Europe