!standard 03.10.02 (02) 00-05-16 AI95-00237/00 !class confirmation 00-05-16 !status received 00-05-16 !priority Low !difficulty Medium !subject Finalization of task attributes. !summary !question C.7.2(17) says: "When a task terminates, the implementation shall finalize all attributes of the task, and reclaim any other storage associated with the attributes." Does this mean *after* a task terminates, or at some time in the near vicinity? And what behavior is allowed if this termination occurs concurrently with the interrogation of attributes of the task? !response !ACATS test This issue is not testable unless a deterministic semantics is required. [I hope that doesn't happen - ED.] !appendix From: Michael Yoder Sent: Monday, May 15, 2000 4:34 PM C.7.2(17) says: "When a task terminates, the implementation shall finalize all attributes of the task, and reclaim any other storage associated with the attributes." Does this mean *after* a task terminates, or at some time in the near vicinity? And what behavior is allowed if this termination occurs concurrently with the interrogation of attributes of the task? Any answer other than that the finalization occurs after termination (and that termination is atomic with respect to operations involving task attributes) has unpleasant consequences. These take the form of race conditions when interrogating the attributes (they can be used after being finalized in such a case, or even simultaneously with being finalized). If it occurs after finalization, what is permissible behavior for the Ada.Task_Identification.Current_Task function during this finalization? (The natural-seeming answer, that it is the task whose attributes are being finalized, would have the consequence that Current_Task returns a terminated task.) ************************************************************* From: Ted Baker Sent: Monday, May 15, 2000 6:29 PM The processing involved in task termination is sufficiently complicated that it cannot be considered to be atomic in any strong sense. There is an interval of time during which a task is in the process of terminating, but not yet terminated. Some time during this process the task becomes terminated, after which the value of the 'Terminated attribute will start returning the value True. (I don't think the Ada language needs to define this point precisely.) After this point, if a task is in some way manages to call Current_Task'Terminated it will see the value True, just as any other task would see that attribute of the same task. I don't see this behavior as being a problem. I do think that anyone who expects to do complicated processing in a finalizer (in particular, behavior that requires the current task to not be terminated) is asking for trouble. ************************************************************* From: Tucker Taft Sent: Monday, May 15, 2000 5:55 PM > Any answer other than that the finalization occurs after termination (and > that termination is atomic with respect to operations involving task > attributes) has unpleasant consequences. Good point. So (of course ;-) the answer is that task termination is atomic with respect to task attribute interrogation, and that before finalizing task attributes, the task is (marked) terminated. It is a bit surprising that you can set and get attributes of a task after it is completed, normally or abnormally. I.e. task'callable can be false, but you can still fiddle with its attributes (I just noticed that our RTS got this wrong -- we raise Tasking_Error if the task is uncallable -- looks like yet another ACATS test might be needed...). > If it occurs after finalization, what is permissible behavior for the > Ada.Task_Identification.Current_Task function during this finalization? > (The natural-seeming answer, that it is the task whose attributes are being > finalized, would have the consequence that Current_Task returns a > terminated task.) Yes, I would say that Is_Terminated(Current_Task) should be true during attribute finalization -- admittedly weird. ************************************************************* From: Randy Brukardt Sent: Monday, May 15, 2000 8:01 PM > Yes, I would say that Is_Terminated(Current_Task) should be true > during attribute finalization -- admittedly weird. Say what? You want us to rewrite our runtime systems so that tasks can execute after termination just for task attributes? That sounds as silly as returning terminated tasks outside of their master was in Ada 83. In Janus/Ada at least, Terminated=True is equivalent to all data structures destroyed. It is set just previous to exiting to the scheduler to let another task run, and there is no possibility of coming back. I suppose we could somehow add a TCB flag "Tuckers_Bogus_Terminate", and set it during the finalization of the task (returning it rather than the real "Terminated" to queries of 'Terminated), but that would require adding at least one supervisor call or a "magic" finalization chain marker. I think Ted has the right answer (termination is not atomic, because it involves finalization). The ability of task attributes to be finalized can be useful, because it allows user to insure resources are freed when the task terminates. But using that capability does not need to depend on anything in the task itself, so tightly defining this is not necessary for the primary use. Some certified compilers limit task attributes to a single word (using the permission of C.7.2(29)), thus sidestepping the issue (a tagged type can fit into a single word). If we insist on a tightly defined semantics, I think we will end up seeing more compilers adopting similar restrictions. ************************************************************* From: Michael Yoder Sent: Tuesday, May 16, 2000 9:39 AM Randy Brukardt wrote, responding to Tucker Taft: >> Yes, I would say that Is_Terminated(Current_Task) should be true >> during attribute finalization -- admittedly weird. > >Say what? You want us to rewrite our runtime systems so that tasks can >execute after termination just for task attributes? That sounds as silly as >returning terminated tasks outside of their master was in Ada 83. Actually, I like Tucker's answer better than the one I had originally hoped for (namely, that interrogating Current_Task during attribute finalization was a bounded error). >In Janus/Ada at least, Terminated=True is equivalent to all data structures >destroyed. It is set just previous to exiting to the scheduler to let >another task run, and there is no possibility of coming back. That is a pleasant property, but I wouldn't consider this relevant to the issue unless it was a condition required by the present or future LRM. Is there a consensus that it ought to become so? >I think Ted has the right answer (termination is not atomic, because it >involves finalization). The ability of task attributes to be finalized can >be useful, because it allows user to insure resources are freed when the >task terminates. But using that capability does not need to depend on >anything in the task itself, so tightly defining this is not necessary for >the primary use. I don't understand the last part of this paragraph. What do you consider to be the primary use? Termination must be atomic in this sense: at some point in time, interrogating T'Terminated (explicitly or implicitly) must become true and remain so. Making this attribute mushy in *any* sense is asking for much trouble. Let us not mix the two meanings of the word "termination" here: "termination" as the process of tearing down a task isn't relevant. What matters is when the task becomes terminated, as a binary state change. >Some certified compilers limit task attributes to a single word (using the >permission of C.7.2(29)), thus sidestepping the issue (a tagged type can fit >into a single word). If we insist on a tightly defined semantics, I think we >will end up seeing more compilers adopting similar restrictions. But not having tightly defined semantics makes the feature much harder to use correctly. There isn't any implementation difficulty here: insuring termination is atomic to uses of attributes is as simple as having a termination flag of an atomic boolean type. ************************************************************* From: Ted Baker Sent: Tuesday, May 16, 2000 10:27 AM | So (of course ;-) the answer is that task termination is | atomic with respect to task attribute interrogation, and that | before finalizing task attributes, the task is (marked) terminated. That is possible. On the other hand, I don't think the language of the ARM (below) is so tight as to require that the task be marked terminated before the finalization starts, not do I think it needs to be tighter. | 17 When a task terminates, the implementation shall finalize all | attributes of the task, and reclaim any other storage associated | with the attributes. Termination is really a process, and marking the task terminated simply means it is committed to this process ... not that the process of task termination has completed. With finalization of task attributes being part of this process, and the finalization code possibly taking an unbounded amount of time (a bad usage) to run, the termination process could potentially be very protracted. By the way, there is no reason to assume the finalization of task attributes of a task T is done by the same task T. I just looked at the GNARL, and noticed that the finalization of task attributes for a task T is generally done by some other task T, generally one of the master tasks of T. I think this is entirely consistent with the ARM and with the way in which the attributes were created (which could be by the task itself or any other task). I also noticed in GNARL that the finalization of task attributes is done late in the termination process, just before the task control block is deallocated. That is, when the task state becomes terminated the task may continue executing in the runtime system for a while, and may continue to exist as a referenceable entity for a while after it stops executing. Finally, when there can be no further reference to the task, the attributes are finalized and the control block is deallocated. This postponement of finalization seems consistent with the ARM, since it is still part of the termination process. I originally thought it was friendlier to users, since my original intent for user-defined task attributes in Ada9X was that they continue to be useable throughout the same scope as the language-defined task attributes (like T'Terminated). However, at some point the clause was put in saying that Tasking_Error should be raised for referenced to attributes of terminated tasks, so the postponement of finalization does not have any benefit. ************************************************************* From: Randy Brukardt Sent: Tuesday, May 16, 2000 11:47 AM > Termination must be atomic in this sense: at some point in time, > interrogating T'Terminated (explicitly or implicitly) must > become true and remain so. Making this attribute mushy in *any* > sense is asking for much trouble. Let us not mix the two meanings > of the word "termination" here: "termination" as the process of > tearing down a task isn't relevant. What matters is when the task > becomes terminated, as a binary state change. OK, but why do you care? That is, how can you write a useful program that cares whether 'Terminated changes before or after the finalization of task attributes? (One that simply prints out the value of 'Terminated is not useful!) I can't do it without using a task attribute from the Finalize of another attribute, but that is likely to have triggered the bounded error for C.7.2(13.1/1). Moreover, C.7.2(16/1) allows an implementation to use a per-task lock to insure atomic operations, and that would deadlock. So using task attributes in Finalize should be avoided. > But not having tightly defined semantics makes the feature much harder to > use correctly. Again, I don't see this. Please provide an example that doesn't use task attributes in a Finalize. > There isn't any implementation difficulty here: insuring > termination is atomic to uses of attributes is as simple as having a > termination flag of an atomic boolean type. By adding precision where none was present before, you are forcing (some) implementors to take an effort to modify their task supervisors. This is a particularly difficult area to work on, and I think we need to see that there is some *real* benefit to users before we require work from implementors. The change is certainly more than "having a termination flag". Janus/Ada does not allow the program direct access to the TCB, so each entity visible to the program requires the addition of supervisor calls. This would take at least two new supervisor calls (one to query the "real" termination flag, and one to set the "fake" termination flag before doing attribute finalization). Moreover, we would have to carefully examine every use of the current termination flag to see if was used for "real" termination or to return the value of 'Terminated. There is a very real chance that the change would break something subtle, potentially causing days of debugging. So, I think that in the absence of a real benefit to users, this question should not be answered. ************************************************************* From: Ted Baker Sent: Tuesday, May 16, 2000 1:17 PM | Termination must be atomic in this sense: at some point in time, | interrogating T'Terminated (explicitly or implicitly) must become true and | remain so. The property you state above is not atomicity, but monotonicity. No one has suggested that the change in value of T'Terminated is not monotonic. | But not having tightly defined semantics makes the feature much harder to | use correctly. There isn't any implementation difficulty here: insuring | termination is atomic to uses of attributes is as simple as having a | termination flag of an atomic boolean type. If you attempt to access a user-defined task attribute of a terminated task you will get Program_Error, unless you have disabled this check, or you are using the pointer method and have a reference to the attribute via a dangling pointer. In the latter cases you are programming erroneously. Again, you cannot make any assumption about what task is executing the finalization code for the attributes of a task. Therefore, it would be erroneous to use the value returned by Current_Task. ************************************************************* From: Michael Yoder Sent: Tuesday, May 16, 2000 1:19 PM The case I care about is that mentioned in my original message: when other tasks use attributes of a task which is in the process of becoming terminated. (That is why I included implicit use of 'Terminated above.) These are the "race conditions" I spoke of. No use of task attributes in Finalize is needed, only the usual, expected kind of uses by other tasks. In fact, is isn't necessary to export task IDs out of their scope. *Any* use of task attributes counts if this use could occur either before or after the task terminates. (For example, a monitor task sampling the attribute periodically.) The example was given in my original message: "[task attributes] can be used after being finalized in this case, or even simultaneously with being finalized." I'm asserting that the language of C.7.2(17) should be interpreted so as to prevent uses of task attributes after they are finalized, or (worse) concurrently with being finalized. More precisely, attempted use should raise Tasking_Error in accordance with C.7.2(13). >By adding precision where none was present before, you are forcing (some) >implementors to take an effort to modify their task supervisors. This is a >particularly difficult area to work on, and I think we need to see that >there is some *real* benefit to users before we require work from >implementors. The benefit is that an entire *class* of usage becomes possible. I would say that any use which doesn't involve exporting task IDs out of scope (so erroneous execution doesn't enter into the picture) should be considered normal usage. The alternative is to say that correct use of task attributes must allow for the possibility that the task could terminate, and the finalization procedure could be run, concurrently with independent use of the attribute. ************************************************************* From: Randy Brukardt Sent: Tuesday, May 16, 2000 3:51 PM > The case I care about is that mentioned in my original message: when other > tasks use attributes of a task which is in the process of becoming > terminated. (That is why I included implicit use of 'Terminated above.) > These are the "race conditions" I spoke of. No use of task attributes in > Finalize is needed, only the usual, expected kind of uses by other tasks. > In fact, is isn't necessary to export task IDs out of their scope. OK, thanks for explaining this. I had read your original message before sending the last message, and I could not decipher what caused the race conditions you were talking about. Unfortunately, this discussion has been filled with irrelevant details, like the value of 'Terminated and returning terminated tasks, none of which has anything to do with the problem at hand. It is completely clear that any reference to an attribute that is being finalized (or already has been finalized) should raise Tasking_Error. (If that isn't true, than any reference to a task attribute is potentially erroneous, which clearly isn't acceptable). However, it certainly isn't necessary to require compilers to support running terminated tasks in order to get that behavior. The 'Terminated value is of no use in this case anyway (because testing the attribute and then doing an operation is a potential race condition), so it seems to me that the real problem is that C.7.2(13) is tied too tightly to termination. I would suggest making access to task attributes during the process of termination a bounded error, such that they either work as expected or raise Tasking_Error. Alternatively, we could extend/revise C.7.2(13) to say that Tasking_Error is raised if the finalization of the attribute has begun. (Indeed, that could completely replace the existing rule.) Changing the meaning of 'Terminated here is distributed pain for at least some run-time systems, and has no user value (at least, it doesn't have anything to do with the example you stated). Tweaking the rules for attributes alone localizes any needed changes to the attribute code itself; and I suspect that most implementations get this right somehow. ************************************************************* From: Michael Yoder Sent: Tuesday, May 16, 2000 3:44 PM Ted Baker wrote: >| Termination must be atomic in this sense: at some point in time, >| interrogating T'Terminated (explicitly or implicitly) must become true and >| remain so. > >The property you state above is not atomicity, but monotonicity. >No one has suggested that the change in value of T'Terminated >is not monotonic. Well, yes, but this change must in effect be atomic by virtue of the rules in 9.10(2-10), plus a lot of citations I didn't want to run down. Now that I look at it more closely, I think these rules plus C.7.2(17) should suffice in this particular instance. (Taking a strict interpretation of the latter, as I have advocated in other messages.) I believe it is provable that termination is effectively atomic with respect to nearly any tasking operation, but I'd hate to have to construct all the proofs. :-) >| But not having tightly defined semantics makes the feature much harder to >| use correctly. There isn't any implementation difficulty here: insuring >| termination is atomic to uses of attributes is as simple as having a >| termination flag of an atomic boolean type. > >If you attempt to access a user-defined task attribute of a >terminated task you will get Program_Error, unless you have >disabled this check, or you are using the pointer method and have >a reference to the attribute via a dangling pointer. In the >latter cases you are programming erroneously. This only applies to the specific case of dereferencing a value supplied by Reference. The cases of using Value, Set_Value, or Reinitialize remain problems with respect to the synchronization issues regarding finalization. (Sorry if the phrase "use of task attributes" was vague on this point.) >Again, you cannot make any assumption about what task is executing >the finalization code for the attributes of a task. Therefore, >it would be erroneous to use the value returned by Current_Task. This is an entirely reasonable answer to the final question of my first message, assuming you are using "erroneous" colloquially here. If not I'd argue it is preferable to make it a bounded error, and rule as to what behavior is allowed. ************************************************************* From: Jean-Pierre Rosen Sent: Tuesday, May 16, 2000 4:03 PM I was surprised in this discussion that everybody talked only about *terminated* tasks. For variables declared in the task body, finalization happens when the task is *completed*, but not yet *terminated* - see 9.3(5). Is there any reason to believe that task attributes do not behave the same? After all, they are quite like variables declared directly within the task. Admitedly, the phrasing "when the task terminates" is vague, but it would seem logical to admit this describes the termination process in 9.3(5). ************************************************************* From: Tucker Taft Sent: Tuesday, May 16, 2000 9:27 PM Randy Brukardt wrote: > ... > > It is completely clear that any reference to an attribute that is being > finalized (or already has been finalized) should raise Tasking_Error. (If > that isn't true, than any reference to a task attribute is potentially > erroneous, which clearly isn't acceptable). > > However, it certainly isn't necessary to require compilers to support > running terminated tasks in order to get that behavior. The 'Terminated > value is of no use in this case anyway (because testing the attribute and > then doing an operation is a potential race condition), so it seems to me > that the real problem is that C.7.2(13) is tied too tightly to termination. > > I would suggest making access to task attributes during the process of > termination a bounded error, such that they either work as expected or raise > Tasking_Error. Alternatively, we could extend/revise C.7.2(13) to say that > Tasking_Error is raised if the finalization of the attribute has begun. > (Indeed, that could completely replace the existing rule.) I would prefer to see us change this so that you get Tasking_Error if a task is *completed* or abnormal and you reference its task attributes. That is the usual boundary between normal and Tasking_Error propagation. I wonder why we made task attributes different? Is it perhaps that a task wants to refer to its own attributes during finalization? Is that added capability useful/worth the trouble? ************************************************************* From: Tucker Taft Sent: Tuesday, May 16, 2000 7:54 PM Michael Yoder wrote: > ... I would > say that any use which doesn't involve exporting task IDs out of scope (so > erroneous execution doesn't enter into the picture) should be considered > normal usage. The alternative is to say that correct use of task > attributes must allow for the possibility that the task could terminate, > and the finalization procedure could be run, concurrently with independent > use of the attribute. It seems the critical requirement is that no access to a task attribute is permitted after it has begun to be finalized. I.e., Tasking_Error is raised on reference to a task attribute that is being, or has been finalized. It would seem "nice" that 'Terminated return True before you start getting these kinds of Tasking_Errors, but that seems somewhat less critical. I agree with Mike that it should *not* be erroneous to reference task attributes of a task concurrent with its terminating, and it should *not* be possible to successfully get/set an attribute after it has been finalized. ************************************************************* From: Randy Brukardt Sent: Wednesday, May 17, 2000 11:25 AM Tucker Taft wrote: > I would prefer to see us change this so that you get Tasking_Error if > a task is *completed* or abnormal and you reference its task > attributes. That is the usual boundary between normal and Tasking_Error > propagation. I agree. The only reason I didn't suggest it was that it seemed to be too large a change - someone might care. > I wonder why we made task attributes different? Is it perhaps > that a task wants to refer to its own attributes during finalization? > Is that added capability useful/worth the trouble? The AARM offers no enlightenment here. ************************************************************* From: Michael Yoder Sent: Wednesday, May 17, 2000 11:48 AM Tucker wrote: >I would prefer to see us change this so that you get Tasking_Error if >a task is *completed* or abnormal and you reference its task attributes. >That is the usual boundary between normal and Tasking_Error propagation. This would have the strong virtue that my original second question would need no answer: during finalization of task attributes, Current_Task would always be the task whose attributes are being finalized, and it wouldn't be terminated yet. Or rather, it would enable a change along such lines. I would ask Ted Baker et al. to weigh in as to how painful such a change would be. >I wonder why we made task attributes different? Is it perhaps >that a task wants to refer to its own attributes during finalization? >Is that added capability useful/worth the trouble? I don't think you lose anything here: currently the task is terminated in at least some existing implementations, so attributes aren't available anyway. Also, any scheme that allowed this would probably have to include precise rules about the order of finalization of the attributes, or it wouldn't be usable in any event. ************************************************************* From: Ted Baker Sent: Wednesday, May 17, 2000 11:33 AM | This only applies to the specific case of dereferencing a value | supplied by Reference. The cases of using Value, Set_Value, or | Reinitialize remain problems with respect to the synchronization | issues regarding finalization. (Sorry if the phrase "use of | task attributes" was vague on this point.) That is pretty clearly specified as safe. ************************************************************* From: Ted Baker Sent: Wednesday, May 17, 2000 11:35 AM | I was surprised in this discussion that everybody talked only | about *terminated* tasks. For variables declared in the task | body, finalization happens when the task is *completed*, but not | yet *terminated* - see 9.3(5). | Is there any reason to believe that task attributes do not behave | the same ? After all, they are quite like variables declared | directly within the task. Admitedly, the phrasing "when the task | terminates" is vague, but it would seem logical to admit this | describes the termination process in 9.3(5). There can be a very long (maybe forever) delay between completion and termination of a task. Finalization of task attributes before termination is illegal. ************************************************************* From: Ted Baker Sent: Wednesday, May 17, 2000 12:42 PM | I would prefer to see us change this so that you get | Tasking_Error if a task is *completed* or abnormal and you | reference its task attributes. That is the usual boundary | between normal and Tasking_Error propagation. I wonder why we | made task attributes different? Is it perhaps that a task wants | to refer to its own attributes during finalization? Is that | added capability useful/worth the trouble? I think this would be a very bad idea. Anybody who uses task attributes (e.g., for user-defined scheduling, resource management) is likely to continue to need the attribute until the task stops executing. Remember that with finalization the meaning of "complete" is pretty much gone. It effectively just means the task can no longer make rendezvous. After a task completes it can still have active children and still do meaningful execution, through finalization actions. 1) We should always avoid unnecessary language changes. 2) This change would gain nothing. 3) This change would cost users functionality and increase the risk of user race conditions. 4) This change would require changes to implementations, and possibly break existing code. ************************************************************* From: Ted Baker Sent: Wednesday, May 17, 2000 12:52 PM | This would have the strong virtue that my original second question would | need no answer: during finalization of task attributes, Current_Task would | always be the task whose attributes are being finalized, and it wouldn't be | terminated yet. Or rather, it would enable a change along such lines. I | would ask Ted Baker et al. to weigh in as to how painful such a change | would be. WHOA!!! (Isn't anybody reading?) I mentioned earlier that there are situations where this is impossible, i.e., the task whose attributes are being finalized never even activated, but we still have to finalize the attribute values. Remember the purpose of these attributes, which is to allow a user to keep track of user-defined resources associated with tasks, and recover the resources when they can no longer be used. If we change anything here, it should be to allow access to the attributes of terminated tasks (without Tasking_Error) up the point where the task can no longer be referenced, i.e. until the point where the system resources of the task are recovered by the implementation. ************************************************************* From: Randy Brukardt Sent: Wednesday, May 17, 2000 3:32 PM Ted said: > If we change anything here, it should be to allow access to the > attributes of terminated tasks (without Tasking_Error) up the > point where the task can no longer be referenced, i.e. until the > point where the system resources of the task are recovered by the > implementation. We *have* to change something here, because the current situation allows access to finalized attributes on some implementations. We need to at least tighten up the language of this section to prevent that. But certainly your idea is at least as bad as Tucker's: it requires proxy finalizations, which may not be possible on all implementations of finalization. And it certainly would require most implementations to change. I still think my idea was the best, because it eliminated the direct connection between termination (i.e. 'Terminated) and attribute access, without substantially changing anything for users. Probably most implementations would already compily: Replace C.7.2(13) by: For all of the operations defined in this package, Tasking_Error is raised if the finalization of the attribute has begun. Combined with C.7.2(17), this is similar to the existing rule, but it eliminates worrying about whether a terminated task (as opposed to completed task) can run. (Remember that objects of *all* types are finalized, not just tagged types.) I think this has the minimal change, and avoids the grey area. But I'm no expert in real-time programming, so I might have missed something. Randy. ************************************************************* From: Ted Giering Sent: Wednesday, May 17, 2000 3:23 PM Ted Baker wrote: > If we change anything here, it should be to allow access to the > attributes of terminated tasks (without Tasking_Error) up the > point where the task can no longer be referenced, i.e. until the > point where the system resources of the task are recovered by the > implementation. As you point out in a previous message, that would require changes to some implementations, in particular Apex. Our runtime has a terminated task mark itself as terminated and then finalize its attributes. The terminated flag itself is used to determine if Tasking_Error should be raised on attribute reference. Once the attributes are finalized the task never runs again. Deferring attribute finalization further would require having another task do the work, which I suspect would be a nontrivial change. I wonder if the original idea of saying that the attributes are finalized on task termination is just to indicate that their finalization needs to come after the rest of the task body finalization, that is, the finalization of objects declared in the task body, just to preserve LIFO ordering. Finalizing the attributes after marking the task as terminated obviates the need to further specify the order of attribute finalization, as Mike pointed out, which simplifies implementation. *************************************************************