!standard 9.2(6) 07-06-13 AI05-0045-1/02 !class binding interpretation 07-04-04 !status work item 07-04-04 !status received 07-04-02 !priority Medium !difficulty Medium !qualifier Omission !subject Termination of unactivated tasks !summary Tasks that are created but never activated become terminated when their master is finalized. !question 9.2(6) says Should the task that created the new tasks never reach the point where it would initiate the activations (due to an abort or the raising of an exception), the newly created tasks become terminated and are never activated. A return statement for a type containing tasks that is left without being completed is going to leave the master of those tasks. But the activation point (at the site of the function call) could very well still be executed, for instance if there is a local handler. Moreover, an extended_return can be left by an exit or goto statement (not one of these cases listed in this sentence), and such exits will occur mainly in cases where the activation point will in fact be reached. How can the master being left wait for tasks that have not yet been activated, but are not terminated because this paragraph does not apply? !recommendation (See Summary.) !wording Replace 9.2(6) by: If a master includes (directly or indirectly) the creation of a task, and it begins finalization prior to either initiating the activation of the new task or returning the task to a caller as part or coextension of a return object, the new task is never activated and becomes terminated. AARM Note: A master could indirectly include the creation of a task if the task is returned to it from a function call. This rule covers such tasks that aren't activated. A created task might not be activated because of an exception being raised, because of an abort, or because of a transfer of control out of an extended_return_statement. !discussion What is important here is what happens if the master of unactivated tasks is finalized (as then we would need to wait for them). We don't care why the tasks were never activated. Thus, the original wording of 9.2(6) says too much. It also has a race condition, in that if the task is aborted when it reaches the activation point but before the tasks actually are activated, this rule doesn't seem to apply. The wording also is not clear about precisely when the tasks become terminated. The tasks being terminated on creation would meet the requirements of this rule. Thus, the value of T'Terminated is not well-defined for such tasks. That seems bad. Because of all of these reasons, we've rewritten the rule in terms of finalization rather than "reaching the point of activation". !corrigendum 9.2(6/2) @drepl Should the task that created the new tasks never reach the point where it would initiate the activations (due to an abort or the raising of an exception), the newly created tasks become terminated and are never activated. @dby If a master includes (directly or indirectly) the creation of a task, and it begins finalization prior to either initiating the activation of the new task or returning the task to a caller as part or coextension of a return object, the new task is never activated and becomes terminated. !ACATS test An ACATS C-Test should be created to check the extended_return case with a transfer of control: return A : Type_with_a_Task do ... goto Oops; ... end return; <> return B : Type_with_a_Task do ... end return; !appendix From: Stephen W Baird Date: Monday, April 2, 2007 11:48 AM 9.2(6) says Should the task that created the new tasks never reach the point where it would initiate the activations (due to an abort or the raising of an exception), the newly created tasks become terminated and are never activated. The parenthesized part needs updating for Ada05 to handle the case where an extended return statement for a function whose result type contains tasks is exited via either an exit statement or a goto statement. **************************************************************** From: Randy Brukardt Date: Monday, April 2, 2007 9:40 PM I'm begining to wonder if this isn't a real hole. Consider something like: return A : Type_with_a_Task do ... goto Oops; ... end return; <> return B : Type_with_a_Task do ... end return; In a case like this, the rule you note doesn't apply, as the "point at which the tasks are activated" is after the call in the caller of the function. That surely WILL be executed. But we still don't want the first set of tasks activated. Indeed, the rule is perfectly OK as originally written, as any case where the activation point isn't executed will be due to an exception (Program_Error). So I think we may need a new rule (or a reconsideration of the master rules, or something), which means a new AI. Sigh. **************************************************************** From: Stephen W Baird Date: Tuesday, April 3, 2007 3:55 PM I don't think there is a new problem here. The wording of 9.2(6) is terrible, but we've lived with it for years. Taken literally, the wording is ridiculous: "at the point where something will never happen, do something", or something like that. If, strictly speaking, this is defined at all (a point I am not willing to concede), it certainly requires a crystal ball - do or do not do something now depending on what is going to happen in the future. This reminds me of the old definition of the Max_Size_In_Storage_Units attribute ("the maximum value ... that will be requested ..."). What this wording really means is "at the point where the task activation point becomes unreachable, terminate the new tasks". I think this interpretation makes as much sense in Ada05 as it did in Ada95. In your example, the set of tasks associated with a different extended return statement (or even a different elaboration of the same extended return statement) is a different set. Thus, the task activation point for the original set of tasks is no longer reachable after the goto statement and 9.2(6) does apply. **************************************************************** From: Randy Brukardt Date: Monday, April 2, 2007 4:57 PM > If, strictly speaking, this is defined at all (a point I am not willing to > concede), it certainly requires a crystal ball - do or do not do something now > depending on what is going to happen in the future. This reminds me of the old > definition of the Max_Size_In_Storage_Units attribute ("the maximum value ... > that will be requested ..."). This is not the way I read this paragraph (with one small exception). It says that if you never reach the activation point, the tasks are never activated. That doesn't require a crystal ball. The part about "become terminated" is does have the problem you are talking about, but it is irrelevant until you reach the task waiting point. Here, you *are* going to reach the task activation point, but you *still* don't want to activate the tasks. That is completely different. > What this wording really means is "at the point where the task activation > point becomes unreachable, terminate the new tasks". Maybe. I'm not sure; I always thought it meant "if you reach the task waiting point (master destruction point), then any unactivated tasks are terminated". Maybe it doesn't matter in Ada 95 tasks. > I think this interpretation makes as much sense in Ada05 as it did in Ada95. For Ada 95, you may be right. But I don't see this for extended returns. > In your example, the set of tasks associated with a different extended return statement > (or even a different elaboration of the same extended return statement) is a > different set. So what? They're both activated at the same point. And, as a practical matter, they're going to be on the same ring (list): we're only going to pass one in to take the tasks! > Thus, the task activation point for the original set of tasks > is no longer reachable after the goto statement and 9.2(6)does apply. Huh? It hasn't changed (its the point of the call), and it is surely still going to be executed. Anyway, the object is finalized when its (local) master goes away (3.10.2(10.1/2) says that the master is the return statement until that statement completes). But that makes a totally new situation: one where the activation point of the tasks is after the master of the tasks is finalized. Keep in mind that there isn't any wording like 3.10.2(10.1/2) that applies to the activation point: it never changes its location! So it is initially outside of the master of the object; then when the return happens, the master changes so that the activation point is inside as usual. In an example like this, it's clear that the tasks are gone before the activation point is reached, but it surely will be reached. In any case, my concerns and your statement that the original wording is bad means this is *not* a minor wording change. I don't want that AI derailed because people are arguing about the meaning of rules, or are just plain confused. So we need an AI. And given that we're rewording this paragraph anyway, we might as well fix your concern. I'd dump all of the verbiage and simply say what is important: "if a task has not been activated when the master on which it depends is waiting for the termination of dependent tasks, the task becomes terminated and is never activated." This probably wasn't done because it is a forward reference (and it might make the most sense in 9.3) [although I note that "terminated" is also a forward reference]. That would drop all of the rot about why that might happen, which is a certain maintenance problem. If we moved this to 9.3 (after paragraph 5), then it might make sense to make the current wording a note in 9.2; then it doesn't have to be exactly right. **************************************************************** From: Stephen W Baird Date: Wednesday, April 4, 2007 3:24 PM This might be ok, but it makes me nervous because it doesn't quite correspond to how most implementations would want to implement this. The "typical" implementation that I envision involves terminating these tasks while still still in the callee. The caller is unaware that these tasks were ever created and is certainly not responsible for terminating them. In this example, function F return R is begin return R'(Some_Task_Component => <>, Some_Natural_Component => Ident_Int (-1), Some_Other_Task_Component => <>); exception when Constraint_Error => Do_A_Bunch_Of_Stuff; return ... ; end F; , any tasks that were created as part of the aggregate evaluation before Constraint_Error was raised would be terminated before, not after, the call to Do_A_Bunch_Of_Stuff. According to the wording you suggest, they would be terminated after. This would be ok if we are really sure that there is no way that anyone could tell the difference. I can't think of any way that anyone could tell the difference, but that is a much weaker statement. This is my only objection. I like the simplicity of the wording you propose. Perhaps it could be augmented with an explicit implementation permission to terminate the tasks earlier; or, at the very least, an AARM note indicating that this was the intent. What do you think? **************************************************************** From: Randy Brukardt Date: Monday, April 2, 2007 9:40 PM > This might be ok, but it makes me nervous because it doesn't quite > correspond to how most implementations would want to implement this. That's possible; I have no idea how a "typical" implementation works. Indeed, I don't think such a thing exists; implementations seem to vary widely here. > The "typical" implementation that I envision involves terminating these > tasks while still still in the callee. The caller is unaware that these > tasks were ever created and is certainly not responsible for terminating > them. In this example, > > function F return R is > begin > return R'(Some_Task_Component => <>, > Some_Natural_Component => Ident_Int (-1), > Some_Other_Task_Component => <>); > exception > when Constraint_Error => > Do_A_Bunch_Of_Stuff; > return ... ; > end F; > > , any tasks that were created as part of the aggregate evaluation before > Constraint_Error was raised would be terminated before, not after, > the call to Do_A_Bunch_Of_Stuff. > > According to the wording you suggest, they would be terminated after. No, that's wrong. The master of a return statement is the statement itself (see 3.10.2(10.1/2), which defines the accessibility which then defines the master). If the return statement is successfully completed, then the master is changed to that of the caller of the function. It's never the function itself. So, in this case, when the exception is raised, the return statement is not completed, and the master that is the return statement is left. That means that the created but unactivated tasks will be awaited, and that will trigger the new rule and make them terminated. Now, I realize that you may not actually implement a "real" master for a return statement, and simply have the incomplete exit of the return statement finalize and terminate the tasks. And that should be equivalent. But that's not how RM wording works. I realize that remembering that return statements are temporary masters is hard to do (even though it comes from a model that you originated), but it's the rule. Note that controlled finalization happens here, too, not after the exception handler. (Which is very ugly for us, but whatever.) > This would be ok if we are really sure that there is no way that anyone > could tell the difference. There is no difference, so I don't think you can tell it. ;-) > I can't think of any way that anyone could tell the difference, but that > is a much weaker statement. True, but I don't see how you could get a different result. > This is my only objection. I like the simplicity of the wording you > propose. Perhaps it could be augmented with an explicit implementation > permission to terminate the tasks earlier; or, at the very least, an > AARM note indicating that this was the intent. > > What do you think? I don't think you need to terminate the tasks earlier (or can, for that matter), because the moment when you start awaiting the master is essentially the same moment when you know that the activation point won't be executed/the return statement will not be completed. The only thing that I think might be incompatible is the precise instant when T'Terminated becomes true. Your reading of the wording seems to make that retroactive(!) - I think we're better off with the wording clear that isn't ever the case. So I think you've convinced me that I'm on the right track. It certainly is simpler than talking about executing the point of task activation. I'll write it up this way and then the full ARG can tear it to bits... ****************************************************************