!standard D.7 (19.1/2) 10-10-31 AI05-0189-1/04 !standard H.4(23.3/2) !standard C.7.1(3/1) !standard C.7.1(4) !standard C.7.1(8) !standard C.7.1(10) !class amendment 09-11-03 !status Amendment 2012 10-11-22 !status ARG Approved 9-0-0 10-10-31 !status work item 09-11-03 !status received 09-11-03 !priority Low !difficulty Easy !subject Restriction No_Standard_Storage_Pools_After_Elaboration !summary Provide a restriction that disallows use of allocators from standard storage pools after library unit elaboration is complete. Also provide additional operations in Task_Identification to permit identifying the environment task, and whether a task has completed its activation, to enable user-defined storage managers to enforce a corresponding restriction. !problem It is quite common that a resource-constrained long-lived application wants to avoid any heap allocation after library unit elaboration is complete, to ensure that there is no heap growth during the ongoing execution of the program. !proposal (see summary) !wording Modify C.7.1(3/1): function Current_Task return Task_Id; { function Environment_Task return Task_Id;} procedure Abort_Task(T : in Task_Id); Add after C.7.1(4): function Activation_Is_Complete(T : Task_Id) return Boolean; Add after C.7.1(8): The function Environment_Task returns a value that identifies the environment task. Add after C.7.1(10): The function Activation_Is_Complete returns True if the task identified by T has completed its activation (whether successfully or not). It returns False otherwise. If T identifies the environment task, Activation_Is_Complete returns True after the elaboration of the library_items of the partition has completed. Add after D.7(19.1/2): No_Standard_Storage_Pools_After_Elaboration Specifies that an allocator using a standard storage pool (see 13.11) shall not occur within a parameterless library subprogram, nor within the handled_sequence_of_statements of a task body. For the purposes of this rule, an allocator of a type derived from a formal access type does not use a standard storage pool. At run-time, Storage_Error is raised if an allocator using a standard storage pool is evaluated after the elaboration of the library_items of the partition has completed. Revise H.4(23.3/2) as follows: * the following restrictions defined in D.7: No_Task_Hierarchy, No_Abort_Statement, No_Implicit_Heap_Allocation {, No_Standard_Storage_Pools_After_Elaboration}; and !discussion We considered restricting allocators to the environment task, but felt it was important to allow non-environment tasks to perform allocators during their activation. Note that allowing non-environment tasks to perform allocators means that synchronization is still required during heap allocation. We do not require this restriction to be entirely enforced prior to execution. We perhaps could do so, but to do it completely could require significant static analysis. The original proposal disallowed all allocators after elaboration, but it seemed preferable to allow user-defined storage pool implementations to enforce the restriction themselves by providing enough information via additional Task_Identification queries. !examples ** TBD ** !corrigendum C.7.1(3/1) @drepl @xcode< @b Image (T : Task_Id) @b String; @b Current_Task @b Task_Id; @b Abort_Task (T : @b Task_Id);> @dby @xcode< @b Image (T : Task_Id) @b String; @b Current_Task @b Task_Id; @b Environment_Task @b Task_Id; @b Abort_Task (T : @b Task_Id);> !corrigendum C.7.1(4) @drepl @xcode< @b Is_Terminated(T : Task_Id) @b Boolean; @b Is_Callable (T : Task_Id) @b Boolean; @b ... -- @I @b Ada.Task_Identification;> @dby @xcode< @b Is_Terminated (T : Task_Id) @b Boolean; @b Is_Callable (T : Task_Id) @b Boolean; @b Activation_Is_Complete (T : Task_Id) @b Boolean; @b ... -- @I @b Ada.Task_Identification;> !corrigendum C.7.1(8) @dinsa The function Current_Task returns a value that identifies the calling task. @dinst The function Environment_Task returns a value that identifies the environment task. !corrigendum C.7.1(10) @dinsa The functions Is_Terminated and Is_Callable return the value of the corresponding attribute of the task identified by T. @dinst The function Activation_Is_Complete returns True if the task identified by T has completed its activation (whether successfully or not). It returns False otherwise. If T identifies the environment task, Activation_Is_Complete returns True after the elaboration of the @fas of the partition has completed. !corrigendum D.7(19.1/2) @dinsa @xhang<@xterm Max_Entry_Queue_Length defines the maximum number of calls that are queued on an entry. Violation of this restriction results in the raising of Program_Error at the point of the call or requeue.> @dinss @xhang<@xterm> Specifies that an allocator using a standard storage pool (see 13.11) shall not occur within a parameterless library subprogram, nor within the @fa of a task body. For the purposes of this rule, an allocator of a type derived from a formal access type does not use a standard storage pool. @xindents of the partition has completed.> !corrigendum H.4(23.3/2) @drepl @xbullet @dby @xbullet !ACATS test ACATS B and C tests are needed. !ASIS There is no effect on ASIS; ASIS doesn't do anything special for restrictions (they're just pragma parameters). !appendix From: Tucker Taft Sent: Friday, June 12, 2009 6:23 AM This is an official request from Ada Switzerland for a restriction that disallows allocation after library-unit elaboration is complete. [Editor's note: Not sure when Tucker becaome Swiss! I presume he meant to say that he is relaying an official request from Ada Switzerland, or some such thing.] **************************************************************** From: Jean-Pierre Rosen Sent: Friday, June 12, 2009 12:05 PM No_Local_Allocator seems to come close... **************************************************************** From: Tucker Taft Sent: Friday, June 12, 2009 3:44 PM True, but it is too limiting. It means that you can't use allocators in subprograms, even if they are called only during library-unit elaboration. That pretty much defeats any kind of abstraction. Many embedded systems have the model of allowing dynamic allocation during start up, but not thereafter. Ada really ought to support that. What I would imagine is a restriction that disallows use of a default storage pool after elaboration is complete, while allowing user-defined storage pools to check in the Allocate procedure against some global flag that indicates whether library-level elaboration is complete. **************************************************************** From: Tullio Vardanega Sent: Saturday, June 13, 2009 3:05 AM Something along the line of Tucker's outline would be very useful indeed. At present we often find ourselves in the need of cheating ourselves and the compiler by removing pragma Restrictions to permit the elaboration code to use dynamic memory allocation, but in that manner we lose the ability to enforce the restriction for the code executed after elaboration. **************************************************************** From: Tucker Taft Sent: Monday, June 7, 2010 10:53 AM Here is a minor update to the AI on the restriction No_Allocators_After_Elaboration. [This is version /02 of the AI - Editor.] Note that the minutes implied that library-level tasks weren't certain to be activated until the "begin" of the main subprogram. Actually, they must be activated before the main subprogram is invoked at all. (See 10.2(20-21) which indicates that the call on the main subprogram follows the "begin" of the conceptual environment task body.) **************************************************************** From: Steve Baird Sent: Monday, June 7, 2010 1:32 PM > An allocator shall not occur after the begin of a task body nor in the main subprogram. This is fundamentally a rule about a runtime check and the (not particularly common) statically detectable cases listed above don't seem to me to be worth special treatment. > If an implementation chooses to detect a violation of this restriction > at run-time, Storage_Error should be raised; Why is this optional? Do you think that the performance advantage of this approach outweighs the portability costs? **************************************************************** From: Steve Baird Sent: Monday, June 7, 2010 1:47 PM > Why is this optional? Do you think that the performance advantage of > this approach outweighs the portability costs? It's a significant overhead to require the testing of a global variable on every single allocation, so it seems reasonable not to require it. I suppose we could associate a check name with it, so that the check could be specifically suppressed? **************************************************************** From: Tucker Taft Sent: Monday, June 7, 2010 1:52 PM >> An allocator shall not occur after the begin of a task body nor in >> the main subprogram. > > This is fundamentally a rule about a runtime check and the (not > particularly common) statically detectable cases listed above don't > seem to me to be worth special treatment. The straw vote was 5-3-3 to keep a static check on task bodies. So that's why it is there. >> If an implementation chooses to detect a violation of this >> restriction at run-time, Storage_Error should be raised; > > Why is this optional? Do you think that the performance advantage of > this approach outweighs the portability costs? This is the standard way that restrictions are worded. See, e.g., D.7(20) on Max_Storage_At_Blocking. **************************************************************** From: Steve Baird Sent: Monday, June 7, 2010 2:05 PM > The straw vote was 5-3-3 to keep a static check on task bodies. So > that's why it is there. > Fine. > This is the standard way that restrictions are worded. > See, e.g., D.7(20) on Max_Storage_At_Blocking. That's a bad example because the meaning of what is being checked there is very implementation dependent. Since Max_Asynchronous_Select_Nesting has the same sort of wording, however, I do see your point. **************************************************************** From: Jean-Pierre Rosen Sent: Monday, June 7, 2010 2:42 PM >> Why is this optional? Do you think that the performance advantage of >> this approach outweighs the portability costs? > > It's a significant overhead to require the testing of a global > variable on every single allocation, so it seems reasonable not to > require it. I suppose we could associate a check name with it, so that > the check could be specifically suppressed? What good is there in putting a restriction if it not checked neither at compile-time nor at run-time? I'd rather require a run-time check, and allow the compiler to reject the restriction if it is not supported. **************************************************************** From: Tucker Taft Sent: Monday, June 7, 2010 2:54 PM As I told Steve, this is merely a question of consistency with other restrictions. Exactly how restrictions are enforced has pretty much always been to some degree implementation defined. **************************************************************** From: Tucker Taft Sent: Saturday, October 30, 2010 9:43 PM Here is an update to the No_Allocators_After_Elaboration restriction AI. [This is version /03 of the AI - Editor.] At the last ARG meeting, we had decided to limit this restriction to allocators using a default storage pool, and add some queries in Task_Identification to enable user-defined storage pool implementations to enforce the restriction themselves. To correspond to the change in semantics of the restriction, we have changed the name of the restriction to No_Default_Storage_Pools_After_Elaboration. **************************************************************** From: Randy Brukardt Sent: Saturday, October 30, 2010 11:15 PM Silly bug: the function is defined as Activation_Is_Completed, but the text identifies Activation_Is_Complete. ****************************************************************