Email List: Xaustin-group-lX
[All Lists]

Re: Rejected XSH ERN 101

To: yyyyyyyyyyyyyy@xxxxxxxxxxxxx
Subject: Re: Rejected XSH ERN 101
From: "Alexander Terekhov" <yyyyyyyy@xxxxxxxxxx>
Date: Thu, 4 Sep 2003 19:02:54 +0200
Dave Butenhof wrote:
[...]
> clearing/ignoring the existing values might "contribute 
> to" an application memory leak -- but if the application 
> fails to clean up abandoned TSD values it's got a memory 
> leak ANYWAY

Not at all. Clean up "abandoned" TSD data can be done without
clearing the existing values in the TSD slots. You just need
"delete static_cast<type *>(tsd_get(key))". Clearing of value 
in the TSD slot is only needed if you have defined a key with 
a destructor... but you're not sure that thead won't terminate 
resulting in another clean up as part of thread termination.
IOW, you'd "probably" define a key without destructor if you 
can and want to take care of data clean up (if any is needed) 
on your own.

In the case of keys without destructors, an application either
doesn't need any data clean up (see DCSI-TLS) or it has some
means to perform the clean up of "abandoned" keys (might even
have some means to "intercept" thread termination and perform 
data cleanup without using TSD dtor... heck, Win32 zealots do 
it all the time ;-) ). In this case, the current wording even 
kinda encourages application programmers to NOT bother with 
clearing the slots, I'd say.

In my example (I mean stuff not working under current True64), 
I have defined a key without destructor and THAT program would 
continue to be conforming even under the "proposed action" to 
impose an extra UB. The situation would be different if I'd 
wrote a similar program that would defined a key with "a dummy"
destructor. There would be NO "leaks" but such appl would cease 
to be conforming. I really doubt that you can find some similar 
appls(s) that would become broken under the "proposed action"
(a change in the normative section for _key_delete()), though.

[...]
> Look, if we're going to asynchronously clear TSD values (or 
> "behave as if"), what's the point in restricting it to keys 
> with no destructor? 

Versioning is a bad thing. I don't like it. I'd prefer to pay 
the price only when implementation chooses to reuse a key with 
no destructor (zapping slots eagerly in pthread_key_delete()
aside for a moment). There shouldn't be that many such keys... 
nor should we need to traverse many zillions of threads in a 
"typical application". That's the entire point. "Economy". ;-)

> Any cost of versioning or traversal will be the same. 

I don't think so. 

> Furthermore, it means remembering the previous definition 
> of the key (because the definition depends on whether THAT 
> definition had a destructor); something that's otherwise 
> not necessary.

Yeah, apart of doing the zapping (traversing and clearing) 
eagerly -- you probably won't need to remember the definition, 
then. OTOH, with NPTL-like versioning, you really need to 
maintain version number, stop counting on its MAX... and slow 
down "a bit" all get and set operations. Not good. (NPTL folks 
might want to fix finally all {reported} msync bugs in "0.57+" 
first, however ;-) ;-) )

regards,
alexander.

To:     Alexander yyyyyyyyyyyyyyyyyyyy@xxxxx
cc:     yyyyyyyyyyyyyy@xxxxxxxxxxxxx 
Subject:        Re: Rejected XSH ERN 101


Alexander Terekhov wrote:

>Dave Butenhof wrote:
>[...]
> 
>
>>Perhaps you intended to suggest that the existing requirement on 
>>pthread_key_create() of presenting NULL values to the subsequent 
>>pthread_getspecific() in all threads be restricted only to keys 
>>that at last incarnation (before pthread_key_delete) defined keys 
>>with no destructor? 
>> 
>>
>Sort of. The intent is to 'force' applications to clear all slots 
>for a key with a destructor (non-NULL dtor ptr) -- not doing so 
>would trigger UB at pthread_key_delete() invocation... optional 
>EBUSY checking aside for a moment.
>
My point was more that making it "undefined" doesn't actually help 
anyone...

>>You didn't say anything remotely suggesting that.
>> 
>>
>Yeah, apart from the text in the Action. ;-)
>
>      "For a thread-specific data key with a NULL destructor
>       pointer, the thread-specific data values associated with
>       key need not be NULL at the time pthread_key_delete() is
>       called; otherwise (for a thread-specific data key with a
>       non-NULL destructor pointer), application must ensure that
>       the thread-specific data values are NULL in all threads
>       prior to key destruction."
>
Yes, but that's not what you claim you want to do. You're making 
behavior undefined if the application fails to do something it probably 
cannot in general accomplish, but you're NOT changing the text that 
currently unconditionally requires that the value of a newly created key 
(whether "fresh" or "recycled") shall be NULL in all threads. That's an 
IMPLEMENTATION requirement, regardless of what the application does, 
must do, or can do.

One can certainly INFER your desired change from your actual change, but 
it's not stated and therefore would not be a formal requirement of the 
standard.

For example, let's say we have an implementation that doesn't detect a 
"dangling value" on deletion of a TSD key. One facility in the process 
deletes a TSD key, leaving a non-NULL value somewhere. The "application" 
is broken. Undefined behavior is invoked. In some vague sense, it's now 
"OK" that a TSD key created later, reusing this key value, gives a 
non-NULL value in one or more threads. The "application" has broken its 
contract with the implementation. OK, fine. But that's a technicality. I 
wasn't trying to say this doesn't accomplish anything; but what it 
accomplishes is academic standardese of essentially no practical value 
to most real-world applications.

If the EBUSY were a "shall fail", fine; this broken application facility 
would be unable to delete the key, and it therefore couldn't be reused. 
No problem. Except to accomplish that you need at least a count of 
threads with non-NULL values, which must be synchronized... and you 
might as well version the values and avoid the annoying error. Sure, 
clearing/ignoring the existing values might "contribute to" an 
application memory leak -- but if the application fails to clean up 
abandoned TSD values it's got a memory leak ANYWAY, of the same 
magnitude, whether the key is deleted or not. You've added aggravation 
and complication that's of no value to anyone. And in any case, the 
EBUSY is NOT going to be a "shall fail" so this line of reasoning is 
irrelevant.

>[...]
> 
>
>>I see no point in this.
>> 
>>
>
>Well, the point is that while I agree with you that 
>
>http://www.opengroup.org/austin/mailarchives/austin-group-l/msg04508.html
>(see "asynchronously zap the old value from all running threads")
>
>      "if I were to encounter an actual need to guarantee a "fresh 
>       value" for existing threads after the re-creation of a key, 
>       I would prefer this obnoxious mess, that costs "a lot" only 
>       when a key is actually reused, (which should occur rarely), 
>       over something like a versioning protocol that adds a 
>       smaller cost to every pthread_getspecific and 
>       pthread_setspecific call even if no key is ever reused."
>
>I would prefer to AVOID "this obnoxious mess" for keys WITH 
>destructors (non-NULL dtor ptrs). In the past, I was on your 
>side completely -- I though that applications should always 
>clear the slots prior to pthread_key_delete() invocation or 
>simply shall NOT delete a key if they don't want to have a 
>protocol that would ensure both tracking of key sharing and 
>cleanup of TSD data by each 'suspect thread' when it decides 
>that it 'won't use that key anymore':
>
>http://google.com/groups?selm=3C76897D.426BC98E%40web.de
>(Subject: Re: TSD key reusing issue)
>
>Later, I've run into 'example' that showed to me that Ulrich
>and Kaz were {partially} right and that, at least, for keys 
>with NO destructors (NULL dtors), the "requirement on 
>pthread_key_create() of presenting NULL values to the 
>subsequent pthread_getspecific() in all threads" (even if 
>key is reused and its slots were NOT cleared at the last 
>incarnation) does make sense. That example is nothing but 
>DCSI-TLS (in the scope of another "dynamic" shared object).
>
Look, if we're going to asynchronously clear TSD values (or "behave as 
if"), what's the point in restricting it to keys with no destructor? Any 
cost of versioning or traversal will be the same. Furthermore, it means 
remembering the previous definition of the key (because the definition 
depends on whether THAT definition had a destructor); something that's 
otherwise not necessary.

>[...]
> 
>
>>>Also, can someone point out an existing application that
>>>would be broken by what is being requested? Not a strong
>>>argument, I know, but I'm just curious.
>>> 
>>>
>Imposing UB (EBUSY aside for a moment) might be a problem.
>
>>You'd have to depend on seeing particular stale values from 
>> 
>>
>                         ^       ^^^^^^^^^^
>                         |       "non-NULL"
>NOT ---------------------+
>
Any non-NULL value is by definition "stale", because it persists from 
the previous definition of the key.

>>a previous incarnation of a TSD key in an existing thread. 
>> 
>>
>http://google.com/groups?selm=3C7666D8.542EE194%40web.de
>
>quoted...
>
>I wrote a THIRD[1] program that did it, I believe.
>
>And, yes, I would have NO problems with a requirement to 
>"manually" set TSD values to NULL in ALL threads prior to
>key destruction... BUT please spell this out *clearly* 
>in the STANDARD[2] then!
>
The point of TSD is that any library can use it to maintain per-thread 
values in any arbitrary thread that enters the code. Requiring that code 
to track and manage all those threads would defeat the basic intent. You 
could, practically speaking, delete a TSD key only if you could 
guarantee that only threads you created (and could control) would ever 
have instantiated values for that key.

Yes, we could say that, and there was a time that I would have agreed. 
But I've changed my mind. That would substantially reduce the usefulness 
and generality of an established standard function that currently has no 
such formal restrictions. Instead, I've decided we ought to mean what 
the standard says, and make this easy for the application.

>regards,
>alexander.
>
>[1] I think that it is FULLY conforming, 
>    please correct me if I am wrong:
>
Yes, it looks fine. And, as you point out, it currently fails on Tru64. 
That's because I was holding to the original working group intent that 
TSD impose no synchronization overhead, despite the accidental wording 
of the standard requiring otherwise. As we've already established, I've 
changed my mind, and "fixed" my code. (Although the fix hasn't yet 
propagated to the support pools; and if our "reaffirmation" of the 
existing text doesn't stand through discussion or balloting I could in 
theory decide to back it out.)

-- 
/--------------------[ yyyyyyyyyyyyyy@xxxxxx ]--------------------\
| Hewlett-Packard Company       Tru64 UNIX & VMS Thread Architect |
|     My book: http://www.awl.com/cseng/titles/0-201-63392-2/     |
\----[ http://homepage.mac.com/dbutenhof/Threads/Threads.html ]---/




<Prev in Thread] Current Thread [Next in Thread>