!standard C.07.02 (17) 01-09-11 AI95-00237/05 !class binding interpretation 00-05-31 !status work item 01-10-05 !status ARG approved 8-0-0 01-05-20 !status work item 00-06-01 !status received 00-05-16 !qualifier Clarification !priority Low !difficulty Medium !subject Finalization of task attributes !summary The master of task attributes is the master of the instantiation of Ada.Task_Attributes that creates them. Task attributes can be finalized at any point after their task is terminated and no later than the completion of the master of the instantiation of Ada.Task_Attributes that created them. It is a bounded error to call Ada.Task_Identification.Current_Task during the finalization of a task attribute. !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? (Yes.) Can the termination occur concurrently with the interrogation of attributes of the task? (No.) This wording also appears to allow an attribute to outlive its type. Is this intended? (No.) !recommendation (See summary.) !wording Add the following after C.7.2(13): When the master of an instantiation of Ada.Task_Attributes is finalized, the corresponding attribute of each task is finalized. Delete C.7.2(17). Add the following after C.7.2(28): If the task terminates before the master of the instantiation of Task_Attributes is finalized, an implementation may finalize the object corresponding to the attribute of the task at this time (instead of when the master of the instantiation is finalized), and reclaim any other storage associated with the attribute. Add "finalization of a task attribute" to C.7.1(17). !discussion Although it is not clear from the the standard, it is not possible in general for a task to finalize its own attributes. This is because a task that is never activated may have attributes that require finalization. However, this begs the question: if a task owning attributes cannot finalize them, what task can? One way to attempt to answer the question is to determine the master of the attributes. (After all, for ordinary objects, it is the master that determines when they are finalized.) However, the standard does not address this at all. It is clear that the task attributes should not exist any longer than the instantiation that created them. If they do exist past that point, there is the risk that their type is also no longer accessible, and that the finalization may depend on entities that no longer exist. Therefore, we declare that the master of the instantiation is the master of the task attributes. This gives a clear point by which the finalization of attributes must have completed: the completion of the master which contains the instantiation. Once we have this clear point, we no longer need the (now) conflicting rule that says that the attributes are finalized when their task terminates. Moreover, this rule complicates implementation of task attributes, requiring an interaction between the attributes package and the task supervisor upon task termination. This can be difficult to accomplish when Ada tasking is built on top of an existing operating systems threading mechanisms. Therefore, we convert this rule to an implementation permission, allowing finalization at any point after a task terminates. We leave it as an implementation permission (rather than deleting it outright) for three reasons: * So that existing implementations need not be changed; * So that storage can be recovered when a task terminates. For long-running applications which dynamically create tasks, this storage can be significant; * So that attributes can be implemented as a property of a task (say, as space in a task control block [TCB]). In order to support that, we have to be able to finalize attributes when task data structures are freed. An implementation taking advantage of this permission must insure that only a single task finalizes the attributes. (It is possible for a task to terminate at the same time that the master containing the instantation is left. It must never be the case that both tasks finalize the attributes.) It is important that attributes are not finalized before task termination, because they can be accessed until T'Terminated becomes True. We do not want to be able to access finalized task attributes. Since we do not define which task finalizes the attributes (or even if it is a real task), we make the use of Current_Task in the Finalize routine of a task attribute a bounded error. !corrigendum C.7.1(17) @drepl It is a bounded error to call the Current_Task function from an entry body or an interrupt handler. Program_Error is raised, or an implementation-defined value of the type Task_ID is returned. @dby It is a bounded error to call the Current_Task function from an entry body, interrupt handler, or finalization of a task attribute. Program_Error is raised, or an implementation-defined value of the type Task_ID is returned. !corrigendum C.7.2(13) @dinsa For all the operations declared in this package, Tasking_Error is raised if the task identified by T is terminated. Program_Error is raised if the value of T is Null_Task_ID. @dinst When the master of an instantiation of Ada.Task_Attributes is finalized, the corresponding attribute of each task is finalized. !corrigendum C.7.2(17) @ddel When a task terminates, an implementation shall finalize all attributes of the task, and reclaim any other storage associated with the attributes. !corrigendum C.7.2(28) @dinsa An implementation need not actually create the object corresponding to a task attribute until its value is set to something other than that of Initial_Value, or until Reference is called for the task attribute. Similarly, when the value of the attribute is to be reinitialized to that of Initial_Value, the object may instead be finalized and its storage reclaimed, to be recreated when needed later. While the object does not exist, the function Value may simply return Initial_Value, rather than implicitly creating the object. @dinst If the task terminates before the master of the instantiation of Task_Attributes is finalized, an implementation may finalize the object corresponding to the attribute of the task at this time (instead of when the master of the instantiation is finalized), and reclaim any other storage associated with the attribute. !ACATS test It would be possible to create a test to insure that task attributes are finalized at some point between the termination of the task and the leaving of the instantiation's master, but such a test would not add much value to CXC7003.A (which already tests finalization of attributes, but not this specific case). !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. ************************************************************* From: Tucker Taft Sent: Wednesday, May 17, 2000 9:57 PM Randy Brukardt wrote: > 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. I presume you only meant to replace the first sentence C.7.2(13) I would rather see : For all of the operations defined in this package, Tasking_Error is raised if the task identified by T is terminated, or the implementation has begun finalization of the attribute. > 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. I think we should preserve the fact that once 'Terminated is true, you get Tasking_Error. You may start getting it a bit earlier than that, but no later. ************************************************************* From: Robert A Duff Sent: Thursday, May 18, 2000 10:31 AM > For all of the operations defined in this package, Tasking_Error is raised if the > task identified by T is terminated, or the implementation has begun > finalization of the attribute. Don't we want to allow the implementation to raise T_E if it has started finalizing *some* attribute, and some other task accesses some *other* attribute? Otherwise, the implementation has to keep track of which attributes it has finalized, individually. It seems easier to implement if it can simply keep track of the fact that it has started finalizing the attributes. It seems like this would not affect programs -- how can you write a program that cares about the difference between "we have started finalizing attribute X" and "we are about to start finalizing attribute X real soon -- as soon as we get done finalizing attribute Y"? Such a program would get T_E anyway, if the other task were just a little bit slower. Am I making sense? ************************************************************* From: Michael Yoder Sent: Thursday, May 18, 2000 12:32 PM Ted Baker wrote: >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. I cannot find this in your earlier messages. Could you provide a pointer? If I have missed a message I'd like to read it. ************************************************************* From: Jean-Pierre Rosen Sent: Thursday, May 18, 2000 2:35 AM To: Ada-Comment List Subject: Re: [Ada-Comment] finalization of task attributes; value of Current_Task > | 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. > Of course. I just meant that: - during finalization, the task should be seen as completed, but not terminated. - finalization of task attributes should be considered part of "finalization of the task body", as defined in 9.3(5). I agree that accessing the attribute after finalization has started should raise Tasking_Error, which is a bit earlier than when the task is terminated. (Note however that there are other places in the language where you can access a variable after it has been finalized, but that would be unfortunate in this case, since it would mean accessing released memory). ************************************************************* From: Ted Baker Sent: Thursday, May 18, 2000 12:30 PM | 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 am not pushing for a change. I would prefer leaving the language as it is. I just wanted to point out in opposition to the proposal for moving finalization of attributes earlier (task completion) that if we are ready to break implementations and applications, it would be better to gain something useful for the user. I think the later finalization would do that. | 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, ... | 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. That is what I have always thought. That is, the finalization of task attributes must happen after the finalization of all the objects within the task body. In that way those finalizations can safely use the task attributes. | ...just to preserve LIFO ordering. I don't think LIFO fits here, since the values of the task attributes can be changed dynamically during the lifetime of the task. Also, a new task attribute can be created after the creation of a task. There is just no connection between the lifetimes of the attribute values and the tasks, other than it makes sense toe force cleanup at the end of the task to avoid storage leakage. ************************************************************* From: Ted Baker Sent: Thursday, May 18, 2000 12:15 PM | 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. I don't see any such gap. The language currently requires that Tasking_Error be raised if the user attempts to access one of these attributes of a task that has reached the "terminated" state. I think we all agree that the task is marked as "terminated" when it begins the termination process, so that during finalization the task is viewed as terminated. | 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. 1) The concept of "proxy" here has no relevance, as these objects that are being finalized do not "belong" to any one task in the sense that local objects do. In general, the attribute objects will have already been initialized via what you are calling a "proxy". Moreover, if another task changes the value of an attribute, the finalization will be done by the task that changes the attribute value. In general, these attributes can be initialized by any task and finalized by any task. 2) You have no choice but to use another task in the case where the task was never activated. Everything I said above applys to the current semantics, and I can accept leaving things alone since they are not really broken. I think no change is called for. On the other hand, if we are to make a change, I think the argument is in favor of requiring that finalization be delayed further, for the reasons stated in my earlier e-mail. ************************************************************* From: Randy Brukardt Sent: Thursday, May 18, 2000 2:51 PM > I don't see any such gap. The language currently requires that > Tasking_Error be raised if the user attempts to access one of > these attributes of a task that has reached the "terminated" > state. I think we all agree that the task is marked as > "terminated" when it begins the termination process, so that > during finalization the task is viewed as terminated. WHOA! (Deja-vu:-) Apparently, your messages aren't the only ones that aren't being read. I certainly don't agree with the above statement - I believe that any model where a terminated task can execute is flawed. That is why we have the "completed" state - it applies to a task which has finished executing but still has finalization to do. Which is why I proposed rewriting C.7.2(13) to eliminate the problem. But you seem to have refused to even comment on it - thus we seem to have two parallel conversations. In any case, C.7.2(17) does not say what you stated; if there really is a consensus that your statement is true, then we need to rewrite C.7.2(17) to say so (and to provide a red flag that the Ada 'Terminated has to be carried as a separate flag - it would then have nothing to do with the *real* status of the task). > | 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. > > 1) The concept of "proxy" here has no relevance, as these objects > that are being finalized do not "belong" to any one task in the > sense that local objects do. In general, the attribute objects > will have already been initialized via what you are calling a > "proxy". Moreover, if another task changes the value of an > attribute, the finalization will be done by the task that changes > the attribute value. In general, these attributes can be > initialized by any task and finalized by any task. There is something very wrong with the model you explain here. The lifetime of the attribute has to be at least as long as the task that it is an attribute of. However, the task that sets the attribute may have terminated long before the attribute is finalized; there is no way in general for that task to finalize the attribute. The only possible ways to implement these things are either that they "belong" to the task containing the attribute (possibly requiring a proxy operation), or that they "belong" to a pseudo-task that surrounds the environment task. In either case, setting them requires a assigning them so that belong to the appropriate scope (possibly with some sort of proxy); they cannot remain owned by the caller. > 2) You have no choice but to use another task in the case where the > task was never activated. And since no such task is available in general, it can only be done at the appropriate point by launching an extra task to do it. (The task supervisor itself can't call user code, as that might call back into the task supervisor, which could deadlock or worse.) You have convinced me that it is impossible to correctly implement these things (at least in Janus/Ada). Or, at least that it is unbearably expensive. And all for a feature of very little benefit (tasks don't terminate very often in real Ada programs, as they usually are either library level or declared by an access to task at the library level). (The memory deallocation is not a problem, as it can occur along with deallocation of other task structures). The problem, of course, is the use of controlled types as task attributes. The only solution I see is prohibiting them altogether. I doubt I could get enough support for that in RM, but an implementation can do it with creative use of C.7.2(18). I recommend that to all of the implementors. (For me personally, a better solution would be to burn all of my Ada books and go bag groceries -- I'd make more money that way...) Randy. ************************************************************* From: Michael Yoder Sent: Thursday, May 18, 2000 1:13 PM At 11:31 AM 5/18/00 -0400, you wrote: > > For all of the operations defined in this package, Tasking_Error is > raised if the > > task identified by T is terminated, or the implementation has begun > > finalization of the attribute. > >Don't we want to allow the implementation to raise T_E if it has started >finalizing *some* attribute, and some other task accesses some *other* >attribute? Otherwise, the implementation has to keep track of which >attributes it has finalized, individually. It seems easier to implement >if it can simply keep track of the fact that it has started finalizing >the attributes. It seems like this would not affect programs -- how can >you write a program that cares about the difference between "we have >started finalizing attribute X" and "we are about to start finalizing >attribute X real soon -- as soon as we get done finalizing attribute Y"? >Such a program would get T_E anyway, if the other task were just a >little bit slower. Yes, but an implementation that went down this path wouldn't need the new flexibility. (This is equivalent to what Randy called bogus termination earlier.) It could rename the property "attribute finalization started" to "terminated," and rename "terminated" to 'truly and sincerely terminated" when the RTL internally used the property that all of a task's data structures were gone. ************************************************************* From: Ted Baker Sent: Thursday, May 18, 2000 4:22 PM >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. | I cannot find this in your earlier messages. Could you provide a | pointer? If I have missed a message I'd like to read it. Sorry. Since I don't see the echo-back copy of the message in my mail compendium, it may have one of those messages I occasionally lose when somebody interrupts me and my system goes down before I finish composing and hit the send key, or maybe it was one of those cases where I decided I was over-elaborating the point and decided to delete part of the message before hitting the send key. Here is the point: There is a period of time between creation of a task and its activation, during which the task object exists but there is no associated active thread of control. If an exception is raised in the creator/activator task the newly created task will never be activated. However, it may have task attributes and the attributes still need to be finalized. This cannot be done by the task itself, since that task was never activated and has no thread of control. Hence, it is absolutely necessary that there be a provision for what has been (I believe incorrectly, but memorably) called "proxy" finalization of the attributes. Given this necessity, there is no benefit to an implemenation in insisting in other cases that the finalization of attribute values ever be done by the task with which they are associated. | 17 When a task terminates, the implementation shall finalize all attributes | of the task, and reclaim any other storage associated with the attributes. I think I can speak with some authority on this issue, since the only reason we have this clause is that I argued for it back when I was on the 9X MRT. Tucker and Offer did not want it originally, until I convinced them that users need this capability to prevent storage leakage and clean up dangling references. The word "when" is used in the above clause in the sense it is commonly used in sentence such "When you finish eating, please wash the dishes." The clause does not say the task shall finalize the attributes. It says "the implementation". That was intentional, because there are situations where the task has never started execution, as mentioned above. The finalization of attributes is connected in this sentence with the reclamation of storage. That is intentional, and is consistent with the treatment of unchecked deallocation of objects, which also requires finalization of attributes. The finalization of the task attributes would normally be done by whatever thread of control relaims the storage of the task. ************************************************************* From: Michael Yoder Sent: Thursday, May 18, 2000 3:53 PM To: Ada-Comment List Subject: Re: [Ada-Comment] finalization of task attributes; value of Current_Task Randy Brukardt wrote: >The only possible ways to implement these things are either that they >"belong" to the task containing the attribute (possibly requiring a proxy >operation), or that they "belong" to a pseudo-task that surrounds the >environment task. In either case, setting them requires a assigning them so >that belong to the appropriate scope (possibly with some sort of proxy); >they cannot remain owned by the caller. Actually, you can have them belong to the environment task itself. This seems to raise the question "Who finalizes the environment task's attributes?". However, if you examine the process closely, you find that the attributes get finalized as their scopes are exited, so that when the time comes to finalize the environment task's attributes after its termination, the number left to finalize must be zero. So you escape by a whisker. ************************************************************* From: Ted Baker Sent: Thursday, May 18, 2000 3:53 PM To: ada-comment@ada-auth.org Subject: Re: [Ada-Comment] finalization of task attributes; value of Current_Task | Don't we want to allow the implementation to raise T_E if it has started | finalizing *some* attribute, and some other task accesses some *other* | attribute? Otherwise, the implementation has to keep track of which | attributes it has finalized, individually. That is what the current semantics say, i.e., you mark the task as Terminated at some point, and start finalization after that point. The task attribute operations just check raise Tasking error if T'Terminated is true. It is all very simple. WHAT IS THE PROBLEM PEOPLE ARE TRYING TO SOLVE HERE? This whole thread seems to be talking about making unnecessary languages changes. I have not yet seen any problem that requires a language change. ************************************************************* From: Randy Brukardt Sent: Thursday, May 18, 2000 6:10 PM Mike Yoder wrote: > Actually, you can have them belong to the environment task itself. This > seems to raise the question "Who finalizes the environment task's > attributes?". However, if you examine the process closely, you find that > the attributes get finalized as their scopes are exited, so that when the > time comes to finalize the environment task's attributes after its > termination, the number left to finalize must be zero. So you escape by a > whisker. You are either being unclear here, or one of us completely misunderstands the model, because the attributes certainly survive their (original) scopes exiting. Set_Value clearly is an assignment (including the call to Adjust) [C.7.2(11)]. The original scope of the item is irrelevant. This assignment would presumably arrange to have the item in the appropriate scope. (Ted seems to have this misunderstanding.) But in that case, the attributes of the environment task certainly still exist when it terminates - because nothing else could finalize them. Whether it matters (or whether they simply can be finalized "early"), is an open question. In Janus/Ada, at least, the environment task is special (in order that programs that don't use any tasking can avoid the substantial overhead of the task supervisor), and when it's finalization is complete, the only possible thing to do is an immediate exit of the program. It's not practical to change that. Doing finalization of these entities is clearly a problem. We're using a special finalization item tag to do this when the task terminates, but as Ted points out, that doesn't work for unactivated tasks. Our task supervisor cannot call user code (it's not reentrant when a context switch is required, as there is only a single save area); it has to use another task to finalize things. And forcing a task to finalize something is a one-way trip: it's not possible to return to what it was previously doing (because the saved registers, etc, would be killed if it swapped out). So the only way to handle unactivated tasks would be a spawn a task specifically to call the finalize. I find this whole thing quite dubious, because in practice, Ada tasks don't terminate until the program does. The only time I've seen tasks terminate before the end of the program is in validation tests and in a couple of test programs I constructed to make sure that the resources are recovered at that point. But end users tend to call and complain that the resources (mainly memory) aren't recovered when the task is completed: which isn't possible in Ada 95 because the task has to run to execute finalizations. They mistake completion for termination. That distinction is confusing enough (and that is why I object to any semantics that implies that terminated tasks can run, even for a short period). Anyway, when these things are finalized is unlikely to matter to most programs, simply because all of the tasks terminate along with the application. Similarly storage leakage of attributes is nearly irrelevant. It is interesting to note that the only reason that this package even has *anything* to do with the task supervisor is C.7.2(17). Otherwise, it is simple stand-alone library package. Perhaps something on the line of Ted's idea is right: change C.7.2(17) into an implementation permission. Then there would be no requirement to finalize these things at an particular point, and the problems would go away -- they could be finalized as late as the ending of the application. As it is, the only viable alternative for Janus/Ada is to completely prohibit the use of controlled types in Ada.Task_Attributes -- because the work involved in getting this to work right far exceeds any possible benefit. ************************************************************* From: Randy Brukardt Sent: Thursday, May 18, 2000 6:29 PM > WHAT IS THE PROBLEM PEOPLE ARE TRYING TO SOLVE HERE? I think that everybody but you thought it was possible, even expected, for the task itself to finalize the attributes. Thus all of the discussion about when the 'Terminated flag. You are clearly right about that, but I think it is too expensive to force implementors to do, and this implementor, at least, has no intention of spending a couple of weeks to redo all of our task supervisor to handle a case that hardly ever appears in practice. And based on our experience with environment-level finalization in Claw, compiler writers have a lot more important things to get right before worrying about this one. If it was up to me, I'd simply repeal C.7.2(17), since it no longer serves any real purpose (C.7.2(13) and the generic instantiation rules seem to prevent all of the problem cases.) But banning controlled types as attributes works as well. I suspect that any attempt to test this would have the effect of causing a lot of compilers to do exactly that. For what it's worth, Janus/Ada has never made any attempt to implement C.7.2(17), and based on this discussion, its clear it should never try to do so. ************************************************************* From: Tucker Taft Sent: Thursday, May 18, 2000 5:57 PM Ted Baker wrote: > WHAT IS THE PROBLEM PEOPLE ARE TRYING TO SOLVE HERE? The problem is that the RM is not clear what is the value of 'Terminated while attribute finalization is occurring. You think it is clear that 'Terminate is true before finalization starts, and I agree that solves Mike's problem. However, Randy indicates that making 'Terminated appear true before finalization of attributes is finished is a hardship in his compiler. So one solution is to allow Tasking_Error to be raised either when the task is terminated, or when it has reached the stage of finalizing task attributes, whichever comes first. The other solution is to require that 'Terminated be true before finalization starts. I believe Randy would prefer the first solution. You seem to believe that the latter is clearly the intent of the RM, but at least Randy doesn't agree. ************************************************************* From: Ted Baker Sent: Friday, May 19, 2000 5:42 PM To: ada-comment@ada-auth.org Subject: Re: [Ada-Comment] finalization of task attributes; value of Current_Task I did not get that from his comments, but I don't see leaving this detail unspecified as being a problem. ************************************************************* From: Tucker Taft Sent: Thursday, May 18, 2000 8:56 PM Randy Brukardt wrote: > > Mike Yoder wrote: > > > Actually, you can have them belong to the environment task itself. This > > seems to raise the question "Who finalizes the environment task's > > attributes?". However, if you examine the process closely, you find that > > the attributes get finalized as their scopes are exited, so that when the > > time comes to finalize the environment task's attributes after its > > termination, the number left to finalize must be zero. So you escape by a > > whisker. > > You are either being unclear here, or one of us completely misunderstands > the model, because the attributes certainly survive their (original) scopes > exiting. > ... When the scope of an instantiation of Ada.Task_Attributes ends, it makes sense to finalize the corresponding attribute in all tasks. This is particularly important if the instantiation is not library level, because the attribute type might also be going away soon after the instantiation goes away. So I believe what Mike is saying is that by the time the environment task terminates, all of the instantiations of Ada.Task_Attributes have been finalized, and hence there are no attributes left on it. It is unfortunate that the RM doesn't talk explicitly about what happens when an instantiation of Ada.Task_Attributes is finalized. Our implementation happens to follow the same approach as Mike suggests, namely finalizing the attribute in all tasks when the instantiation is finalized, but it is hard to find a requirement for that in the RM (other than the usual underlying reasonableness requirement that Robert Dewar mentions now and then ;-). ************************************************************* From: Michael Yoder Sent: Friday, May 19, 2000 10:39 AM Ted Baker wrote: >WHAT IS THE PROBLEM PEOPLE ARE TRYING TO SOLVE HERE? C.7.2(17) says "When a task terminates,...". This is ambiguous; if it had said "After a task terminates" there would be no ambiguity. I phrased my original query conditionally exactly because I *feared* the ambiguity was intentional, and *hoped* that pointing out the consequences would lead to tightening the language (or interpreting it tightly). Here is a trial balloon. Randy could clearly use C.7.2(29) to restrict attributes to those too small to contain controlled components or task components. It seems reasonable that any restriction looser than that should be permitted while still claiming to implement Annex C. So, for example, he could say that his implementation forbids attributes requiring non-null finalization. Then there would be no obstacle to taking the strict interpretation, unless there are others here with lurking objections, as it were. My preference would be to change the word "when" to "after" in C.7.2(17). ************************************************************* From: Ted Baker Sent: Friday, May 19, 2000 5:54 PM | C.7.2(17) says "When a task terminates,...". This is ambiguous; if it had | said "After a task terminates" there would be no ambiguity. I phrased my | original query conditionally exactly because I *feared* the ambiguity was | intentional, and *hoped* that pointing out the consequences would lead to | tightening the language (or interpreting it tightly). In my recollection, the ambiguity was intentional, and I think it is useful. | Here is a trial balloon. Randy could clearly use C.7.2(29) to restrict | attributes to those too small to contain controlled components or task | components. It seems reasonable that any restriction looser than that | should be permitted while still claiming to implement Annex C. So, for | example, he could say that his implementation forbids attributes requiring | non-null finalization. Then there would be no obstacle to taking the | strict interpretation, unless there are others here with lurking | objections, as it were. This is a reasonable approach. ************************************************************* From: Michael Yoder Sent: Friday, May 19, 2000 9:33 AM Randy Brukardt wrote: >Mike Yoder wrote: > > > Actually, you can have them belong to the environment task itself. This > > seems to raise the question "Who finalizes the environment task's > > attributes?". However, if you examine the process closely, you find that > > the attributes get finalized as their scopes are exited, so that when the > > time comes to finalize the environment task's attributes after its > > termination, the number left to finalize must be zero. So you escape by a > > whisker. > >You are either being unclear here, or one of us completely misunderstands >the model, because the attributes certainly survive their (original) scopes >exiting. You are right that I misunderstand the model if this is true, and upon reading C.7.2 I find no support for my belief, which dismays me. My belief comes from this paragraph of section C.6.2 of the Ada 95 Rationale: "The generic package Task_Attributes can be instantiated locally in a scope deeper than the library level. The effect of such an instantiation is to define a new attribute (for all tasks) for the lifetime of that scope. When the scope is left, the corresponding attribute no longer exists and cannot be referenced anymore. An implementation may therefore deallocate all such attribute objects when the scope is left." Such deallocation would presumably require that finalization be done first. Failing to allow this (indeed, failing to *require* this) would have very serious consequences: you could create objects whose finalization used variables and types that no longer existed, for example. This is certainly a turn of affairs I didn't anticipate. :-) I confess I took the above paragraph on faith without looking for explicit support in the LRM. ************************************************************* From: Ted Baker Sent: Friday, May 19, 2000 5:56 PM I believe it was intended that when the scope of an instantiation of Task_Attributes is left, the attribute values be finalized and the associated storage recovered. That is how we implemented it in GNARL. ************************************************************* From: Randy Brukardt Sent: Friday, May 19, 2000 1:32 PM Well, I doubt that anyone would seriously argue that we don't want to require finalization at this point. So, I think we need at add something like "When the master of an instantiation of Ada.Task_Attributes is finalized, the associated attribute for all tasks is finalized." after C.7.2(13). This has the advantage of making it clear what the master for task attribute objects is, which makes the semantics much more deterministic. (The master matters, of course, for types with controlled subcomponents). I would argue that adding this rule makes C.7.2(17) unnecessary, so I would suggest turning it into an implementation permission (so that existing implementations would not have to change). "When a task terminates, an implementation may finalize all attributes of the task, and reclaim any other storage associated with the attributes." The second change would eliminate all of my objections/problems, and would have very little effect on real programs (since tasks rarely terminate before the entire program). The need to spawn a task to finalize attributes when a task terminates is far to heavyweight for a feature that will be rarely used (that is, the combination of controlled task attributes and tasks that actually terminate). I don't think that the expense to every tasking program (in size, at least) and especially to every task that uses task attributes (even of none of them are controlled) is justified. ************************************************************* From: Robert A Duff Sent: Friday, May 19, 2000 1:13 PM J-P says: > (Note however that there are other places in the language where you can > access a variable after it has been finalized, but that would be > unfortunate in this case, since it would mean accessing released > memory). Yes, there are other places in the language where you can reference finalized objects. However, there is no place in the language where a task can reference an object that is simultaneously being finalized by a different task. At least, there *should* be no such place, which was Mike's original point. ************************************************************* From: Robert A Duff Sent: Friday, May 19, 2000 12:52 PM Mike Yoder says: > You are right that I misunderstand the model if this is true, and upon > reading C.6.7 I find no support for my belief, which dismays me. My belief > comes from this paragraph of section C.6.2 of the Ada 95 Rationale: > > "The generic package Task_Attributes can be instantiated locally in a scope > deeper than the library level. The effect of such an instantiation is to > define a new attribute (for all tasks) for the lifetime of that > scope. When the scope is left, the corresponding attribute no longer > exists and cannot be referenced anymore. An implementation may therefore > deallocate all such attribute objects when the scope is left." > > Such deallocation would presumably require that finalization be done first. > > Failing to allow this (indeed, failing to *require* this) would have very > serious consequences: you could create objects whose finalization used > variables and types that no longer existed, for example. Yes, that does seem like a hole. So are we saying that an attribute can be finalized at any time after 'Terminated becomes true, and before the instantiation goes away? In particular, for a library-level instantiation, all task attributes can be finalized just before the program is done. In fact, this implies that an implementation that does the finalization *only* when the instantiation is finalized is correct -- the implementation can completely ignore task termination, and just let the attributes stay around until much later. If "when" means "after", then it could be "long after". That implementation would seem to make Randy happy. True? Is this what you and Ted believe the rules should be (or what the rules already are, in Ted's case)? - Bob P.S. Our implementation has some problems in this area. I'm waiting for this discussion to settle down before I fix them! ************************************************************* From: Michael Yoder Sent: Friday, May 19, 2000 11:34 AM I wrote: ... Randy could clearly use C.7.2(29) to restrict attributes to those too small to contain controlled components or task components. Dummy! Task attributes can't be limited, so task components can't occur here. ************************************************************* From: Ted Baker Sent: Friday, May 19, 2000 1:31 PM | I agree that accessing the attribute after finalization has | started should raise Tasking_Error, which is a bit earlier than | when the task is terminated. No. The above remark implies that termination is an instantaneous transition. The task needs to marked as Terminated before the finalization of attributes begins. --Ted ************************************************************* From: Ted Baker Sent: Friday, May 19, 2000 1:34 PM | ... and rename "terminated" to 'truly and sincerely terminated" | when the RTL internally used the property that all of a task's data | structures were gone. No. If you did this there would be no place where T'Terminated could ever return False. That is because the earliest point you can delete the last of the task's data structures is when there can be no further reference to the task. Moreover, if you did this change I believe it would break lots of applications and guess it would break quite a few validation tests, since I recall quite a few of them using T'Terminated at points and expecting it to return False. ************************************************************* From: Michael Yoder Sent: Friday, May 19, 2000 2:16 PM To: Ada-Comment List Subject: Re: [Ada-Comment] finalization of task attributes; value of Current_Task You misunderstand what I was saying. What I said had bearing only on implementations that wanted to take advantage of the change being discussed which would allow raising Tasking_Error when an attribute's finalization started, this finalization being started *before* the task became terminated. Bob's suggestion was that this exception might be permitted when *any* attribute had started finalization. My point was: if the implementation was recording this point in time for the sake of the attributes package, it could simply redefine when 'Terminated became true to be identical with that point in time. If the RTS had previously been using the property that termination implied all data structures were gone, it could rename its old "terminated" property to be something else (e.g. finalized, or fully terminated, or whatever) when it needed this stronger property to hold for the sake of its internal algorithms. The point, then, was that such an implementation would not need the additional implementation flexibility, since it could by a simple transformation comply with the stricter requirement that 'Terminated be true before attribute finalization started. ************************************************************* From: Michael Yoder Sent: Friday, May 19, 2000 3:25 PM Randy Brukardt wrote: >The need to spawn a task to finalize attributes when a task terminates is >far to heavyweight for a feature that will be rarely used (that is, the >combination of controlled task attributes and tasks that actually >terminate). I don't think that the expense to every tasking program (in >size, at least) and especially to every task that uses task attributes (even >of none of them are controlled) is justified. I think you need only one global task to get early attribute finalization (in your implementation). I could tolerate the overhead of one task for the sake of having task attributes, but you might judge differently. I like the suggestion you and Bob have independently proposed. ************************************************************* From: Ted Baker Sent: Friday, May 19, 2000 5:27 PM | ... any model where a terminated task can execute is flawed. You should have objected more strongly during the Ada 9X processs, then. This is not something new. It is a change that has been there in the language definition all along. | That is why we have the "completed" state - it applies to a task | which has finished executing but still has finalization to do. There are other reasons for Completed, including waiting for dependent tasks to terminate. Your comment about finalization only applies to finalization of objects declared within the task. | Which is why I proposed rewriting C.7.2(13) to eliminate the problem. But | you seem to have refused to even comment on it - thus we seem to have two | parallel conversations. Sorry. I saw that and did not comment on the specifics because I am completely oppoesed to making any change. There is no need for change, and this is a case where change breaks implementations and user programs. Hence. | In any case, C.7.2(17) does not say what you stated; if there really is a | consensus that your statement is true, then we need to rewrite C.7.2(17) to | say so (and to provide a red flag that the Ada 'Terminated has to be carried | as a separate flag - it would then have nothing to do with the *real* status | of the task). No. C.7.2(17) does say what I stated. | 17 When a task terminates, the implementation shall finalize all | attributes of the task, and reclaim any other storage associated | with the attributes. Interpreting this as ambiguously as it was intended, i.e., to cover the range of meanings from "when a person finishes eating the person shall wash up the dishes" through "when a person eats the person shall not talk with his mouth full", this means that finalization should be done some time during or after termination. Interpreting this as ambiguously as it was intended, the implementation may cause the task itself to do the finalization, or do it via some other thread of control, as appropriate. Interpreting this in the context of stroage reclamation, as it was intended, the finalization of attributes should be done as part of the deallocation of the storage associated with the task implementation. | There is something very wrong with the model you explain here. The lifetime | of the attribute has to be at least as long as the task that it is an | attribute of. However, the task that sets the attribute may have terminated | long before the attribute is finalized; there is no way in general for that | task to finalize the attribute. I did not say the task that created the attribute needs to finalize it. I just argued that there is no need for a special connection between a given task and who finalizes the attributes associated with the given task for the last time. | The only possible ways to implement these things are either that they | "belong" to the task containing the attribute (possibly requiring a proxy | operation), or that they "belong" to a pseudo-task that surrounds the | environment task. In either case, setting them requires a assigning them so | that belong to the appropriate scope (possibly with some sort of proxy); | they cannot remain owned by the caller. There is no need for the attributes to be "owned" by any particular task. The usual concept of "scope" does not apply to task attributes in the sense of objects that are declared within a master. The storage for an attribute value may be allocated (and initialized) when (a) an instance of the Task_Attributes package is elaborated at run time -- at which point the elaboration of the package creates default attribute values for all existing tasks, (b) a new task is created -- at which point the creation of the task creates defacult values for the existing attributes of the task, (c) a call to Set_Value or Reinitialize changes the value from the default to a non-default value (if the implementation chooses to optimize the default values in a way that eliminates the need for storage until a nondefault value is assigned). If you need to think in terms of scope, the scope of the task attributes is the same as that of the "task control block" object in the Ada runtime system. | > 2) You have no choice but to use another task in the case where the | > task was never activated. | And since no such task is available in general, it can only be done at the | appropriate point by launching an extra task to do it. (The task supervisor | itself can't call user code, as that might call back into the task | supervisor, which could deadlock or worse.) Well.... you have hit a tender spot. (By the way, what exactly is the "task supervisor"? We don't have one in GNARL.) The notion of finalization is already inherently risky, given that it can take place after a task has been aborted, and during finalization abort is deferred. This means that if a finalizer gets stuck in a loop or such, there is no way to kill the task. This problem was known from the beginning, and it was accepted that finalizers are critical code, that must be very tightly verified and kept simple. Finalization of a task attribute may be a tiny bit riskier, if there is more than one task who can do it, since there must be some kind of lock that prevents concurrent finalizations of the same object, and that lock could cause a deadlock if the finalizer runs amok. Does this seem bad? I don't see it as any worse than any other way a program can run amok, since all Ada tasks are in the same logical address space (due to stack and pointer sharing), and so may easily trash both each other's states and that of the Ada runtime system if they run badly amok. | You have convinced me that it is impossible to correctly implement these | things (at least in Janus/Ada). Or, at least that it is unbearably | expensive. And all for a feature of very little benefit (tasks don't | terminate very often in real Ada programs, as they usually are either | library level or declared by an access to task at the library level). (The | memory deallocation is not a problem, as it can occur along with | deallocation of other task structures). | The problem, of course, is the use of controlled types as task attributes. | The only solution I see is prohibiting them altogether. I doubt I could get | enough support for that in RM, but an implementation can do it with creative | use of C.7.2(18). I recommend that to all of the implementors. I do agree that allowing task attributes of controlled types turned out to be more costly and riskier than I had expected when it went into Ada 9X. At this point I would not personally object to a langauge change that dropped this feature completely, but I expect there would be users who would object. Therefore, I think the best solution is to leave it as is, and don't tighten the semantics or validation tests. Any such tightening would just hurt implementors, and by extension users. | (For me personally, a better solution would be to burn all of my Ada books | and go bag groceries -- I'd make more money that way...) That is too painful. ************************************************************* From: Ted Baker Sent: Friday, May 19, 2000 5:39 PM | Our task supervisor cannot call user code (it's not reentrant when a context | switch is required, as there is only a single save area); it has to use | another task to finalize things. And forcing a task to finalize something is | a one-way trip: it's not possible to return to what it was previously doing | (because the saved registers, etc, would be killed if it swapped out). So | the only way to handle unactivated tasks would be a spawn a task | specifically to call the finalize. In GNARL the whole runtime system is layered over a thread implementation, and so if there is anything like a "supervisor" it is in the thread layer. The finalization of task attributes is done by the master of the task when it leaves the scope of the task or task access types declaration, or by the task that does an unchecked deallocation (if any) on the task object. I agree that this call-out to execute the finalizers makes me feel as if walking on very thin ice, since the call is made with the runtime system lock of the task held locked. A bad finalizer could deadlock. | I find this whole thing quite dubious, because in practice, Ada tasks don't | terminate until the program does. The only time I've seen tasks terminate | before the end of the program is in validation tests and in a couple of test | programs I constructed to make sure that the resources are recovered at that | point. But end users tend to call and complain that the resources (mainly | memory) aren't recovered when the task is completed: which isn't possible in | Ada 95 because the task has to run to execute finalizations. They mistake | completion for termination. That distinction is confusing enough (and that | is why I object to any semantics that implies that terminated tasks can run, | even for a short period). Anyway, when these things are finalized is | unlikely to matter to most programs, simply because all of the tasks | terminate along with the application. Similarly storage leakage of | attributes is nearly irrelevant. | It is interesting to note that the only reason that this package even has | *anything* to do with the task supervisor is C.7.2(17). Otherwise, it is | simple stand-alone library package. Right. The hook back into the tasking runtime system is very awkward. | Perhaps something on the line of Ted's idea is right: change C.7.2(17) into | an implementation permission. Then there would be no requirement to finalize | these things at an particular point, and the problems would go away -- they | could be finalized as late as the ending of the application. As it is, the | only viable alternative for Janus/Ada is to completely prohibit the use of | controlled types in Ada.Task_Attributes -- because the work involved in | getting this to work right far exceeds any possible benefit. I would not object to that resolution. ************************************************************* From: Ted Baker Sent: Friday, May 19, 2000 5:49 PM | It is unfortunate that the RM doesn't talk explicitly about what | happens when an instantiation of Ada.Task_Attributes is | finalized. Our implementation happens to follow the same | approach as Mike suggests, namely finalizing the attribute in | all tasks when the instantiation is finalized, but it is hard to | find a requirement for that in the RM (other than the usual | underlying reasonableness requirement that Robert Dewar mentions | now and then ;-). GNARL also finalizes all the instances of a given attribute when the scope of the package instance is left. This is the only sensible thing to do. My own feeling at this point in the disucssion is that an implementation might do well to finagle an excuse to omit support for task attributes of a controlled type. The only serious loss is the ability to stop storage leakage. It would remove what may be a too-tempting "hook" for users to hang overly complex task finalization code, that could end up being very unreliable. ************************************************************* From: Ted Baker Sent: Friday, May 19, 2000 6:07 PM | Well, I doubt that anyone would seriously argue that we don't want to | require finalization at this point. So, I think we need at add something | like | "When the master of an instantiation of Ada.Task_Attributes is finalized, | the associated attribute for all tasks is finalized." | after C.7.2(13). | This has the advantage of making it clear what the master for task attribute | objects is, which makes the semantics much more deterministic. (The master | matters, of course, for types with controlled subcomponents). | I would argue that adding this rule makes C.7.2(17) unnecessary, so I would | suggest turning it into an implementation permission (so that existing | implementations would not have to change). | "When a task terminates, an implementation may finalize all attributes of | the task, and reclaim any other storage associated with the attributes." | The second change would eliminate all of my objections/problems, and would | have very little effect on real programs (since tasks rarely terminate | before the entire program). | The need to spawn a task to finalize attributes when a task terminates is | far to heavyweight for a feature that will be rarely used (that is, the | combination of controlled task attributes and tasks that actually | terminate). I don't think that the expense to every tasking program (in | size, at least) and especially to every task that uses task attributes (even | of none of them are controlled) is justified. I would not object to this change, since it seems to be neutral or friendly to both vendors and users. ************************************************************* From: Tucker Taft Sent: Thursday, June 01, 2000 8:28 AM Randy Brukardt wrote: > > Here is the writeup for AI-237, based on the conclusions of the discussion > on Ada comment. (I've omitted the lengthy E-Mail discussion from this mail; > please download the AI from the web site (www.ada-auth.org/~acats/arg) if > you're interested.) > > Randy. This looks pretty good to me. My only complaint is that we are still somewhat ambiguous in the proposed wording that finalization may only happen *after* the task terminates. Just saying it may occur "when" the task terminates might be misconstrued, in the same way the current wording has been misconstrued. -Tuck -- -Tucker Taft stt@averstar.com http://www.averstar.com/~stt/ Technical Director, Commercial Division, AverStar (formerly Intermetrics) (http://www.averstar.com/services/IT_consulting.html) Burlington, MA USA ************************************************************* From: Randy Brukardt Sent: Thursday, June 01, 2000 12:12 PM Tuck said: > This looks pretty good to me. My only complaint is that we are still > somewhat ambiguous in the proposed wording that finalization may only > happen *after* the task terminates. Just saying it may occur "when" the > task terminates might be misconstrued, in the same way the current > wording has been misconstrued. You are correct here. I meant to fix that; I changed the wording of the permission to "After the task terminates, ...". I'm not sure that reads well, but it was easy. :-) ************************************************************* From: Randy Brukardt (Editor) The following points were raised at the November 2000 ARG meeting: It is possible to have a memory leak if you don't finalize attribute when the task is terminated. It is possible that the instantiation to go away at the same time that a task is going away. Thus, two threads could try to finalize the same object at the time. An implementation must prevent this. The current task is undefined in the finalization (it might be a terminated task). These points were added to the discussion, and the use oc Current_Task during the finalization of a task attribute was made into a bounded error. *************************************************************