AI22-0059-1

!standard 9.10.1(20/5)                                      23-01-12  AI22-0059-1/01

!standard 9.10.1(21/5)

!standard 9.10.1(23/5)

!standard 13.11(17.1/5)

!standard 13.11(18/5)

!class Amendment 23-01-12

!status work item 23-01-12

!status received 23-01-12

!priority Low

!difficulty Medium

!subject Parallel_Calls aspect for types

!summary

We generalize the aspect Parallel_Calls (see 9.10.1) to apply to immutably limited or formal limited types, and to access-to-object types. If specified for an immutably or formal limited type, this means that Parallel_Calls applies to all primitive subprograms of the type, with the exception of the Initialize and Finalize operations for limited controlled types. If specified for an access type, it means that Parallel_Calls applies to the Allocate, Deallocate, and Storage_Size operations of the type of the access type's storage pool (which is true of all standard storage pools).

!issue

In analyzing the side effects of allocation and deallocation on storage pool objects. the issue arose of whether such effects are safe from conflicts due to unsynchronized references to shared variables. It has generally been presumed that allocation and deallocation are thread safe for "standard" storage pools, meaning that allocators for the same access type (with such a storage pool) are safe even if they occur concurrently in different threads of control. But there is no way to specify that a user-defined storage pool has a similar level of thread safety.

Recognizing this problem for user-defined storage pools led to a recognition that the problem is more general, in that there is no way to declare that an object of a given type can safely be operated on concurrently from multiple threads. Note that language-defined subprograms are required (in Annex A(3/5)) to provide thread safety, but only if the objects being operated on are different, so this is really talking about side effects on globals, if any.  And of course protected

operations and task entries can safely be called concurrently on the same protected/task object (though even with these there is still the possibility that such concurrent calls conflict because they have other by-reference or by-access parameters which are not themselves "synchronized" in any sense).

!recommendation

We propose to allow the specification of the aspect Parallel_Calls (see 9.10.1) on a type whose full type is an immutably limited type, a formal limited private type or extension, or an access-to-object type (including a formal access type).

If the Parallel_Calls aspect is True for an immutably or formal limited type, then it is equivalent to specifying Parallel_Calls => True for every primitive subprogram of the type, other than Initialize and Finalize in the case of a descendant of Limited_Controlled.

If the Parallel_Calls aspect is specified True for an access-to-object type then the aspect Parallel_Calls => True must apply to the subprograms Allocate, Deallocate, and Storage_Size of its storage pool type.

We define that standard storage pools always have Parallel_Calls as True for their primitive Allocate, Deallocate, and Storage_Size. This was already stated in informal English text.

!wording

Modify 9.10.1(20/5):

All_Parallel_Conflict_Checks

This policy includes the restrictions imposed by the  Known_Parallel_Conflict_Checks policy, and in addition disallows a parallel construct from reading or updating a variable that is global to the construct, unless it is a synchronized object{, the reading or updating is performed using a subprogram for which Parallel_Calls is True}, or unless the construct is a parallel loop, and the global variable is a part of a component of an array denoted by an indexed component with at least one index expression that statically denotes the loop parameter of the loop_parameter_specification or the chunk parameter of the parallel loop.

Modify 9.10.1(21/5):

All_Tasking_Conflict_Checks

This policy includes the restrictions imposed by the Known_Tasking_Conflict_Checks policy, and in addition disallows a task body from reading or updating a variable that is global to the task body, unless it is a synchronized object{, or the reading or updating is performed using a subprogram for which Parallel_Calls is True}.

Modify RM 9.10.1(23/5):

For a subprogram, {an access-to-object type, a type whose full type is immutably limited, or a formal limited private type or extension,} the following language-defined representation aspect may be specified.

Add after RM 9.10.1(25/5):

Specifying the Parallel_Calls aspect to be True for an access-to-object type indicates that Parallel_Calls is True for the Allocate, Deallocate, and Storage_Size primitive subprograms of the type of the storage pool of the access type.

Specifying the Parallel_Calls aspect to be True for a limited (or formal limited) type indicates that the full (or actual) type is a protected or task type, or that the Parallel_Calls aspect is True for all primitive subprograms of the type (or all visible primitives of the formal type), except for the Initialize and Finalize procedures in the case of a limited controlled type.

Modify 13.11(17.1/5):

The type(s) of the standard pool(s), and the primitive Allocate, Deallocate, and Storage_Size subprograms for the standard pool(s) are nonblocking. {In addition, the primitive Allocate, Deallocate, and Storage_Size subprograms for the standard pool(s) have the Parallel_Calls aspect specified as True.} [Concurrent invocations of these subprograms do not conflict with one another (see 9.10) when applied to standard storage pools.]

[Editor's note: The original last sentence states informally the property that is formally provided by the new aspect.]

Modify 13.11(18/5):

If Storage_Size is specified for an access type T, an implementation-defined pool P is used for the type. The Storage_Size of P is at least that requested, and the storage for P is reclaimed when the master containing the declaration of the access type is left. If the implementation cannot satisfy the request, Storage_Error is raised at the freezing point of type T. The storage pool P is used only for allocators returning type T or other access types specified to use T'Storage_Pool. Storage_Error is raised by an allocator returning such a type if the storage space of P is exhausted (additional memory is not allocated). The type of P{ is nonblocking.}[, and the]{The} primitive Allocate, Deallocate, and Storage_Size subprograms of P {have the Parallel_Calls aspect specified as True and }are nonblocking.

!discussion

We considered defining a new aspect for this purpose, but felt it was better to build on the Parallel_Calls aspect, as it would be confusing to have two closely related concepts using different aspects. We considered changing the name of the aspect to "Synchronizing" but felt

that might imply that some kind of lock was involved, when really all we are promising is that parallel calls are safe.

We make an exception for Initialize and Finalize as these are generally never invoked implicitly in parallel for a limited controlled type. The user might invoke one of these explicitly, but there is generally no guarantee that concurrent finalization or initialization even of a "trivial" controlled object is guaranteed to be implemented in a thread safe manner.

We presume that all operations on protected and task types support parallel calls, though it is conceivable that a user could construct one that would be unsafe, but we see no reason to try to account for such an unlikely special case. A friendly implementation could provide a warning if it were to detect such a situation.

!example

type Lim_I_F is limited interface;
procedure Update(X : in out Lim_I_F) is abstract;

generic
   type Formal_Acc is access Integer
          with Parallel_Calls;
          -- Actual type must have standard storage pool
          -- or have a user-defined storage pool with
          -- Allocate/Deallocate/Storage_Size with
          -- Parallel_Calls True.
               
   type Formal_Lim is limited new Lim_I_F with private
          with Parallel_Calls;
          --  Actual type must be protected, task, or have a primitive
          --  Update with Parallel_Calls True.
               
package Gen is
   function New_Acc (X : Integer) return Formal_Acc
          with Parallel_Calls;
   procedure Double_Update (Y : in out Formal_Lim)
          with Parallel_Calls;
end Gen;
   
package body Gen is
   function New_Acc (X : Integer) return Formal_Acc
          is (new Integer'(X));
          --  This satisfies Parallel_Calls True because
          --  type Acc has Parallel_Calls True.
           
   procedure Double_Update (Y : in out Formal_Lim) is
          --  This satisfies Parallel_Calls True because
          --  either Y is a task or a protected object,
          --  or procedure Update has Parallel_Calls True.
   begin
          Update (Y);
          Update (Y);
   end Double_Update;
end Gen;

 

!ACATS test

ACATS B- and C-Tests are needed to check that the new capabilities are supported.

!appendix

This AI was promoted from the held AI12-0415-1. The entire AI was moved here, with some improvements. There was no additional discussion.