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

Re: Rejected XSH ERN 101

To: Alexander Terekhov <yyyyyyyy@xxxxxxxxxx>
Subject: Re: Rejected XSH ERN 101
From: Dave Butenhof <yyyyyyyyyyyyyy@xxxxxx>
Date: Thu, 04 Sep 2003 10:34:02 -0400
Cc: yyyyyyyyyyyyyy@xxxxxxxxxxxxx
Organization: Hewlett-Packard Company
References: <OFBC995A77.048F675A-ONC1256D96.00741F29-C1256D96.0074E4DB@de.ibm.com>
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>