Status of library tasks when the main program terminates AI-00399/14 1 89-06-16 BI WA | !standard 09.04 (13) 89-06-16 AI-00399/14 !class binding interpretation 86-09-11 | !status WG9-approved 89-06-16 !status ARG-approved 88-10-03 (reviewed) !status ARG-approved (12-0-0) 88-01-19 (pending editorial review) !status panel/committee-approved 87-02-17 (pending editorial review) !status approved by WG9/Ada Board (provisional) 86-11-18 !status panel/committee-approved 86-10-15 (reviewed) !status panel/committee-approved (6-0-0) 86-09-11 (pending editorial review) !status work-item 86-05-20 !status returned to committee by WG9 86-05-09 !status committee-approved (8-0-0) 86-02-20 !status work-item 86-02-20 !status failed letter ballot (3+1-6-2) 86-02-20 !status committee-approved (5-2-2) 85-11-20 (pending letter ballot) !status work-item 85-10-29 !status received 85-10-24 !references AI-00466, 83-00677, 83-00674, 83-00337, 83-00711, 83-00712, 83-00741 !topic Status of library tasks when the main program terminates | !summary 89-06-16 If a task depends on a library package, then after normal termination of the main (sub)program, the environment task must wait for all library tasks to | terminate; if all library tasks and all their dependent tasks are either terminated or waiting on an open terminate alternative of a select statement, the program as a whole terminates. If execution of the main program is abandoned, either because the main program raises an exception or because an exception is raised by the elaboration of a library unit, the effect on unterminated library tasks is implementation-dependent. In particular, such tasks can be aborted by the implementation. !question 88-10-03 Is an implementation allowed to terminate the execution of a task object declared in a library package when the main program terminates? 9.4(13, a note) says: The usual rules apply to the main program. Consequently, termination of the main program awaits termination of any dependent task even if the corresponding task type is declared in a library package. On the other hand, termination of the main program does not await termination of tasks that depend on library packages; THE LANGUAGE DOES NOT DEFINE WHETHER SUCH TASKS ARE REQUIRED TO TERMINATE. Does "the language does not define whether such tasks are required to terminate" mean that an implementation can terminate them? Status of library tasks when the main program terminates AI-00399/14 2 89-06-16 BI WA | !recommendation 89-06-16 If a task depends on a library package, then after normal termination of the main (sub)program, the environment task must wait for all library tasks to | terminate; if all library tasks and all their dependent tasks are either terminated or waiting on an open terminate alternative of a select statement, the program as a whole terminates. If execution of the main program is abandoned, either because the main program raises an exception or because an exception is raised by the elaboration of a library unit, the effect on unterminated library tasks is implementation-dependent. !discussion 88-08-23 9.4(13) is a note that points out the consequences of the rules of the language. The rules for termination of tasks say (9.4(6-10)): If a task has no dependent task, its termination takes place when it has completed its execution. ... If a task has dependent tasks, its termination takes place when the execution of the task is completed and all dependent tasks are terminated. Termination of a task otherwise takes place if and only if its execution has reached an open terminate alternative in a select statement (see 9.7.1), and the following conditions are satisfied: The task depends on some master whose execution is completed (hence not a library package). Each task that depends on the master considered is either already terminated or similarly waiting on an open terminate alternative of a select statement. When both conditions are satisfied, the task considered becomes terminated, together with all tasks that depend on the master considered. These are all the rules that define when tasks are terminated. (Note that if a task object is declared in a library package or if an access to a task type is declared in a library package specification, the library package serves as a master (9.4(1), see below). However, the term "completion" is never defined for a library package (see 9.4(5)), so a library task that is executing a select statement with an open terminate alternative can never, in principle, be terminated. This issue is discussed further below.) 10.1(8) states: Each main program acts as if called by some environment task; the means by which this execution is initiated are not prescribed by the language definition. Status of library tasks when the main program terminates AI-00399/14 3 89-06-16 BI WA Since the technical term "task" has been used, one can reasonably conclude that the rules of the Standard must apply to the environment task. Since the environment task calls the main program, and since elaboration of needed library packages must precede calling the main program (10.5(1)), it is reasonable for the environment task to be the elaborator of library packages (since no other execution entity of the Standard qualifies for this necessary role). 9.4(1-3) define the master of a task: Each task DEPENDS on at least one master. A MASTER is a construct that is either a task, a currently executing block statement or subprogram, or a library package (a package declared within another program unit is not a master). The dependence on a master is a direct dependence in the following two cases: (a) The task designated by a task object that is the object, or a subcomponent of the object, created by the evaluation of an allocator depends on the master that elaborates the corresponding access type definition. (b) The task designated by any other task object depends on the master whose execution creates the task object. Furthermore, if a task depends on a given master that is a block statement executed by another master, then the task depends also on this other master, in an indirect manner; the same holds if the given master is a subprogram called by another master, and if the given master is a task that depends (directly or indirectly) on another master. A general rule that can be derived from the rules of 9.4 is, "If the execution of a task elaborates a task object or access-to-task type definition, any corresponding designated tasks are dependent on the elaborating task." Also, the elaborating task cannot terminate until all dependent tasks terminate or reach a select statement with an open terminate alternative. Since the environment task elaborates the library packages, then from the above derived rules it follows that library tasks are dependent on the environment task and that the environment task cannot terminate until each library task has either terminated or reached a select statement with an open terminate alternative. Thus, although "the language does not define whether such [library] tasks are required to terminate," this does not mean an implementation can abort library tasks just because the main (sub)program has completed its execution, particularly when the main subprogram has completed normally, i.e., without raising an unhandled exception. The note in 9.4(13) merely states an innocuous consequence of the language rules and means only that library tasks are neither required to always terminate nor to never terminate; it is the user's entire program (including library units), executed according to the Status of library tasks when the main program terminates AI-00399/14 4 89-06-16 BI WA rules, that determines whether termination eventually occurs. (A similar statement could be made about subprograms: the language does not define whether a subprogram is required to complete its execution. Of course, such an observation is hardly worth making about subprograms, but non-terminating programs that use tasks have some real-world utility, and this fact is noted in 9.4(13).) Now suppose a main program terminates by raising an exception. Execution of the main program is abandoned (11.4.1(5)). The Standard does not specify that this exception is propagated to the environment task, but it does not forbid it either. If propagated to the environment task, one can imagine an exception handler that takes some action. Any action taken by such an exception handler is implementation defined (since the Standard is silent on this point); in particular, there is no rule that forbids the implementation from aborting all library tasks. Nor is there a rule that requires such tasks to be aborted. Hence, when the main program terminates abnormally (i.e., by raising an exception), an implementation is allowed (but not required) to abort all library tasks. Similar reasoning applies when an exception is raised while elaborating library units (prior to calling the main program). If an exception is raised in this case, execution of the main program is abandoned (11.4.2(4, 7)), and one can imagine that the exception is propagated to the environment task. If the exception is handled by the environment task, the handler is allowed to abort any activated library tasks. We distinguish between normal completion of the main program (after which library tasks cannot be aborted by the implementation) and abnormal completion because in the normal completion case, there is no reason to assume special action should be taken by the environment task, and moreover, it is useful to allow executing library tasks to continue their execution. In contrast, abandoning execution of the main program by raising an exception (either during library unit elaboration or during execution of the main program itself) almost certainly means the programmer has made a mistake; the environment task should therefore be allowed to terminate execution of the entire program by aborting any library tasks that are executing. Some objections have been raised to the above reasoning. One objection is: The rules for termination in 9.4(8) note that the execution of a library package is never completed, and hence, if the master of a task is a library package, a terminate alternative within the task can never be selected. There are two answers to this objection. First, if all library tasks are waiting at terminate alternatives and the main program is complete, no tasks can be called, so no activity can occur. From the viewpoint of the Ada code, it is impossible to tell if the terminate alternatives have been selected or not, because it makes no difference. (Note that, in general, even if an entry in a selective wait with terminate is an interrupt entry, the terminate alternative can be selected since the task that calls the interrupt entry does not exist in the Ada program and so is not dependent on any master in the Ada program.) Second, if one takes the view that the master of a library Status of library tasks when the main program terminates AI-00399/14 5 89-06-16 BI WA task is not just a library package but also the environment task (since the environment task caused the execution of the library package), then completing the execution of a portion of the environment task can allow selection of a terminate alternative in a library task. Another proposed objection is that the Standard does not specify any required behavior after execution of the main program is complete. For example, 14.1(7) says: The language does not define what happens to external files after the completion of the main program. An interpretation of this paragraph is given in AI-00466, namely, this statement merely recognizes that since external files created by Ada programs can be modified (or deleted) by means that are outside the control of an Ada program, the Standard cannot specify what happens to such files once the Ada program has fully completed its execution. Nonetheless, after the main program's execution is complete (in the sense of 9.4(5)), execution of library tasks can continue, and in particular, such tasks can continue to access external files according to the usual rules. Another objection is that the environment task is only mentioned once in the Standard, in 10.1(8): A subprogram that is a library unit can be used as a main program in the usual sense. Each main program acts as if called by some environment task; ... This paragraph does not state any relation between library packages and the environment task, and 10.5, which discusses the elaboration of library units, does not mention the environment task. The conclusion: there is no relation between the environment task and library packages, and in particular, there is no basis for concluding that the environment task is a master of library tasks. The environment task was mentioned in 10.1(8) in order that the effect of a delay statement in the main program would be defined. (Since a delay statement delays the execution of a task, and a main program is a subprogram, not a task, the Standard has to explain what task is delayed by delay statements that are executed by the main program.) Although not explicitly stated, delay statements in the bodies of library packages also must cause some task to be delayed, and the only task that is appropriate is the environment task, so library packages must be considered to be elaborated by the environment task. It is not unreasonable to consider the environment task to be the master of library tasks. It has also been argued that a user needing some tasks to run forever can easily create them by declaring only task types in the library packages; task objects of such types would then be declared in the main program. Although such a program structure is possible, why should it be forced on programmers? A very natural program structure for non-terminating embedded systems is for the main program to be null -- all the real work is performed by library Status of library tasks when the main program terminates AI-00399/14 6 89-06-16 BI WA tasks, and system start-up occurs when the library units are elaborated. There is no real benefit to Ada users to say that it is implementation-dependent whether such a program structure will have the desired effect. In short, although objections can be raised against the proposed interpretation, on balance, the objections are not persuasive. Equally, or even more important, there is no benefit in allowing some implementations to terminate library tasks when the main program completes. Such an effect only makes useful, well-structured programs for embedded applications non- portable. If for no other reason, making Ada programs less implementation-dependent would be sufficient reason for adopting the interpretation presented here.