pthread_join((pthread_t)42, NULL);
The spec I have (SUSv3) says:
The pthread_join() function shall fail if:
[EINVAL] The implementation has detected that the value specified by
thread does not refer to a joinable thread.
[ESRCH] No thread could be found corresponding to that specified by the
given thread ID.
That "shall" seems to mean that an implementation must always be able to
determine that a value passed in actually came from pthread_create(). This
would seem to preclude using pointers to represent pthread_t's (based on
the "42" example...), since the check for valid pthread_t's would be
prohibitively expensive (a hashtable of pointers?) for all the API's that
returned ESRCH.
It also seems that an implementation using pointers for pthread_t's would
be binary incompatible with an updated version that used a structure or
scalar, due to, say, pthread_cleanup_push() assuming that it could
dereference the pthread_t. :-/
matt.
"(pthread_t)42" is kinda "undefined behavior"[1], AFAIK.
>
> The spec I have (SUSv3) says:
>
> The pthread_join() function shall fail if:
>
> [EINVAL] The implementation has detected that the value specified by
> thread does not refer to a joinable thread.
>
> [ESRCH] No thread could be found corresponding to that specified by the
> given thread ID.
Well, I think that you can't "rely" on these errors without invocing
some *undefined behaivior*[1]... so, to me, "shall fail" is kinda
"oxymoron" here. The spec should probably say "may fail" for these
errors. File an aardvark -- there will be a conf.call shortly. ;-) ;-)
regards,
alexander.
(*) http://www.opengroup.org/austin
"....
Straw Poll
I agree with Glen that we need to
redefine the terms undefined and
unspecified.
The total number of votes cast is 28
Yes 11 / 39%
No 12 / 43%
Don't Know 4 / 14%
Don't Care 1 / 4%
( ) Yes
( ) No
( ) Don't Know
( ) Don't Care
>>Vote Now!<<
...." ;-)
regards,
alexander.
On Fri, 18 Oct 2002, Alexander Terekhov wrote:
> Matt Watson wrote:
> [...]
> > pthread_join((pthread_t)42, NULL);
>
> "(pthread_t)42" is kinda "undefined behavior"[1], AFAIK.
Really? I honestly have scoured SUSv3, and no where can I see how a
pthread_join() implementation can behave undefinedly if passed in a value
that was not returned in the pthread_t pointer passed in to
pthread_create().
Of course it's perfectly reasonable to erase your hard drive or play
Moonlight Sonata through your speakers if you make multiple calls to
pthread_join() on the same pthread_t simultaneously...
I'm sticking with my "shall return an error" interpretation, until someone
out-cites me...
matt.
AFAIK, pthread_t is defined as an opaque type... so, why not: <kinda>
struct pthread_t {
pthread_t( int ) {
erase_your_hard_drive();
play_Moonlight_Sonata_through_your_speakers();
}
.
.
.
<?>
regards,
alexander.
> > Really? I honestly have scoured SUSv3, and no where can I see how a
> > pthread_join() implementation can behave undefinedly if passed in a
> > value that was not returned in the pthread_t pointer passed in to
> > pthread_create().
The issue is not pthread_join, the issue is the cast. The cast is
undefined behavior. What happens after you do something undefined is
also undefined.
DS
< 2 x Forward Inline >
-------- Original Message --------
From: Matt Watson <mwa...@apple.com>
Newsgroups: comp.programming.threads
Subject: pthread_join() on detached/exited/garbage thread?
Date: Thu, 17 Oct 2002 10:33:19 -0700
Organization: Apple Computer, Inc.
Lines: 33
Message-ID: <Pine.OSX.4.44.021017...@amelio.apple.com>
NNTP-Posting-Host: amelio.apple.com
Mime-Version: 1.0
Content-Type: TEXT/PLAIN; charset=US-ASCII
X-Trace: news.apple.com 1034875947 14608 17.213.102.49 (17 Oct 2002 17:32:27 GMT)
X-Complaints-To: ab...@news.apple.com
NNTP-Posting-Date: Thu, 17 Oct 2002 17:32:27 +0000 (UTC)
Path: uni-berlin.de!fu-berlin.de!enews.sgi.com!news.spies.com!forum.apple.com!news.apple.com!amelio.apple.com!mwatson
Xref: uni-berlin.de comp.programming.threads:34263
Would a conforming implementation be allowed to abort() if the value
passed in to pthread_join() were of an already-exited thread? What if the
value passed in is garbage? What if the garbage passed in just happens to
pass the "is or was a pthread_t" check, but actually was never filled in
by pthread_create()? What if the implementation uses pointers to represent
pthread_t's and the caller does:
pthread_join((pthread_t)42, NULL);
The spec I have (SUSv3) says:
The pthread_join() function shall fail if:
[EINVAL] The implementation has detected that the value specified by
thread does not refer to a joinable thread.
[ESRCH] No thread could be found corresponding to that specified by the
given thread ID.
That "shall" seems to mean that an implementation must always be able to
determine that a value passed in actually came from pthread_create(). This
would seem to preclude using pointers to represent pthread_t's (based on
the "42" example...), since the check for valid pthread_t's would be
prohibitively expensive (a hashtable of pointers?) for all the API's that
returned ESRCH.
It also seems that an implementation using pointers for pthread_t's would
be binary incompatible with an updated version that used a structure or
scalar, due to, say, pthread_cleanup_push() assuming that it could
dereference the pthread_t. :-/
matt.
-------- Original Message --------
Path: uni-berlin.de!fu-berlin.de!newsfeed.news2me.com!newsfeed2.easynews.com!newsfeed1.easynews.com!easynews.com!easynews!news-out.cwix.com!newsfeed.cwix.com!newsfeed1.sea.pnap.net!newsfeed.pnap.net!forum.apple.com!news.apple.com!amelio.apple.com!mwatson
From: Matt Watson <mwa...@apple.com>
Newsgroups: comp.programming.threads
Subject: Re: pthread_join() on detached/exited/garbage thread?
Date: Fri, 18 Oct 2002 11:21:00 -0700
Organization: Apple Computer, Inc.
Lines: 23
Message-ID: <Pine.OSX.4.44.021018...@amelio.apple.com>
References: <Pine.OSX.4.44.021017...@amelio.apple.com><3DAFE1A6...@web.de>
NNTP-Posting-Host: amelio.apple.com
Mime-Version: 1.0
Content-Type: TEXT/PLAIN; charset=US-ASCII
X-Trace: news.apple.com 1034965205 22137 17.213.102.49 (18 Oct 2002 18:20:05 GMT)
X-Complaints-To: ab...@news.apple.com
NNTP-Posting-Date: Fri, 18 Oct 2002 18:20:05 +0000 (UTC)
In-Reply-To: <3DAFE1A6...@web.de>
Xref: uni-berlin.de comp.programming.threads:34281
On Fri, 18 Oct 2002, Alexander Terekhov wrote:
> Matt Watson wrote:
> [...]
> > pthread_join((pthread_t)42, NULL);
>
> "(pthread_t)42" is kinda "undefined behavior"[1], AFAIK.
Really? I honestly have scoured SUSv3, and no where can I see how a
pthread_join() implementation can behave undefinedly if passed in a value
that was not returned in the pthread_t pointer passed in to
pthread_create().
Of course it's perfectly reasonable to erase your hard drive or play
No. It says the function is only required to fail with this error if
it detects that the value is invalid. It does not say that that the
implementation is required to detect the invalid value - merely the
action if such detection is provided. The implementation may fail to
detect that the value is invalid either because the value is valid or
because the implementation is not capable of detecting any (or this
partuicular) invalid values.
--
Eppur si muove
To me, this would be "may fail"... ala mutex stuff here:
http://www.opengroup.org/onlinepubs/007904975/functions/pthread_mutex_destroy.html
(see "ERRORS" and "Tradeoff Between Error Checks and Performance Supported")
"shall fail" for [EINVAL] above (and [ESRCH]) is a defect, I believe.
regards,
alexander.
> Charles Bryant wrote:
>>
>> In article <Pine.OSX.4.44.021017...@amelio.apple.com>,
>> Matt Watson <mwa...@apple.com> wrote:
>> ...
>> >The spec I have (SUSv3) says:
>> >
>> >The pthread_join() function shall fail if:
>> >
>> >[EINVAL] The implementation has detected that the value specified by
>> >thread does not refer to a joinable thread.
>> ...
>> >That "shall" seems to mean that an implementation must always be able to
>> >determine that a value passed in actually came from pthread_create().
>>
>> No. It says the function is only required to fail with this error if
>> it detects that the value is invalid. It does not say that that the
>> implementation is required to detect the invalid value - merely the
>> action if such detection is provided.
>
> To me, this would be "may fail"... ala mutex stuff here:
This is a little more complicated. In the original POSIX, the terminology
separating required and optional errors was different; "if occurs" (SUS
shall fail) and "if detected" (SUS may fail).
However, the translation from POSIX to SUS terminology here wasn't the
problem. The POSIX does indeed list EINVAL and ESRCH as "if occurs",
intending to imply that the implementation is required to expend whatever
effort is required in detecting the errors.
Despite this intent, though, the wording of the EINVAL error description is
as it remains in SUS: "The implementation has detected that". This phrase
seems clearly someone's confusing attempt at non-normative prose. We've
already specified that the implementation is required to detect the
condition if it occurs. However, it is not our intent to say that any call
to pthread_join() must return with EINVAL... it SHALL do so only when the
thread has detected the appropriate error condition. Therefore, exactly,
EINVAL means that the implementation has detected that... There is no
question possible about whether or not detection is required. The standard
makes that requirement quite clear.
The phrase isn't actually harmful, though it is redundant and potentially
confusing, and could be removed without changing the meaning.
The only meaningful interpretation is this: If the error occurs, the
implementation is required to detect it. ("Shall fail" or "If occurs".)
Having detected the error, (there's the loose prose, already implied by the
normal conventions of listing error conditions), the implementation is
required to report it by returning EINVAL.
--
/--------------------[ David.B...@hp.com ]--------------------\
| 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 ]---/
What's defective below... application? implementation? both?
lnxmuell:/usr/terekhov # gcc -v
Reading specs from /usr/lib/gcc-lib/s390-suse-linux/2.95.3/specs
gcc version 2.95.3 20010315 (SuSE)
lnxmuell:/usr/terekhov # rpm -q glibc
glibc-2.2.2-26
lnxmuell:/usr/terekhov # gcc -pthread -o join join.c
lnxmuell:/usr/terekhov # ldd ./join
libpthread.so.0 => /lib/libpthread.so.0 (0x40022000)
libc.so.6 => /lib/libc.so.6 (0x40038000)
/lib/ld.so.1 => /lib/ld.so.1 (0x40000000)
lnxmuell:/usr/terekhov # ./join
2nd join: 22
Guess what... : Invalid argument
lnxmuell:/usr/terekhov # ./join
2nd join: 0
lnxmuell:/usr/terekhov # ./join
2nd join: 0
lnxmuell:/usr/terekhov # ./join
2nd join: 22
Guess what... : Invalid argument
lnxmuell:/usr/terekhov # ./join
2nd join: 0
lnxmuell:/usr/terekhov # ./join
2nd join: 0
lnxmuell:/usr/terekhov # ./join
2nd join: 0
lnxmuell:/usr/terekhov # ./join
2nd join: 22
Guess what... : Invalid argument
lnxmuell:/usr/terekhov # ./join
2nd join: 22
Guess what... : Invalid argument
lnxmuell:/usr/terekhov # ./join
2nd join: 22
Guess what... : Invalid argument
lnxmuell:/usr/terekhov # ./join
2nd join: 0
lnxmuell:/usr/terekhov # ./join
2nd join: 22
Guess what... : Invalid argument
lnxmuell:/usr/terekhov # ./join
2nd join: 0
lnxmuell:/usr/terekhov # cat join.c
#include <errno.h>
#include <stdio.h>
#include <assert.h>
#include <pthread.h>
void* dummy( void* p ) { return 0; }
int main() {
int status;
pthread_t thid;
status = pthread_create( &thid, 0, &dummy, 0 );
assert( !status );
status = pthread_join( thid, 0 );
assert( !status );
status = pthread_join( thid, 0 );
printf( "2nd join: %d\n", status );
if ( status ) {
errno = status;
perror( "Guess what... " );
}
return 0;
}
lnxmuell:/usr/terekhov #
<?>
regards,
alexander.
> We've already specified that the implementation is required to detect
> the condition if it occurs.
Ah, I see it now (re-reading the "Return Value" section clarified it for
me...)
How about this little tidbit:
"multiple simultaneous calls to pthread_join() specifying the same target
thread"
What does "simultaneous" mean?
matt.
>Well, I think that you can't "rely" on these errors without invocing
>some *undefined behaivior*[1]... so, to me, "shall fail" is kinda
>"oxymoron" here. The spec should probably say "may fail" for these
>errors. File an aardvark -- there will be a conf.call shortly. ;-) ;-)
Indeed; a "thread_t" might be a pointer; and the library may dereference
it.
Requiring such implementations to go through a linked list and check
whether it's a valid pointer is bad for many reasons.
I think we've had complaints about Solaris closedir() or somesuch
not working "properly" on bogus arguments; we may even have fixed
some of such bugs because nobody realized that burdening the C library
with such crap makes it bigger and slower and more fragile too.
If you pass garbage arguments to functions you can expect a SIGSEGV.
Casper
>Would a conforming implementation be allowed to abort() if the value
>passed in to pthread_join() were of an already-exited thread? What if the
>value passed in is garbage? What if the garbage passed in just happens to
>pass the "is or was a pthread_t" check, but actually was never filled in
>by pthread_create()? What if the implementation uses pointers to represent
>pthread_t's and the caller does:
>pthread_join((pthread_t)42, NULL);
This may fail to compile.
>The spec I have (SUSv3) says:
>The pthread_join() function shall fail if:
>[EINVAL] The implementation has detected that the value specified by
>thread does not refer to a joinable thread.
I'm not convinced by the arguments that garbage in should result in
EINVAL; as I read this the emphasis is on *joinable* thread.
I.e., the implementation does not need to detect that it is an invalid
thread id (SIGSEGV will do nicely, as far as I'm concerned), but
rather a thread id that cannot be joined.
If the thread id itself is invalid, pthread_join should return ESRCH for
"no such thread".
If my interpretation is not correct, I cannot reconcile these two errors:
[EINVAL] The implementation has detected that the value specified by
thread does not refer to a joinable thread.
[ESRCH] No thread could be found corresponding to that specified by the
given thread ID.
Casper
--
Expressed in this posting are my opinions. They are in no way related
to opinions held by my employer, Sun Microsystems.
Statements on Sun products included here are not gospel and may
be fiction rather than truth.
>What's defective below... application? implementation? both?
The implementation, I think.
>lnxmuell:/usr/terekhov # ./join
>2nd join: 22
>Guess what... : Invalid argument
>lnxmuell:/usr/terekhov # ./join
>2nd join: 0
I'm not sure what the standard wants to say about pthread_t;
but the error:
ESRCH No thread could be found corresponding to the given
thread ID.
strongly implies to me that *after* a pthread_t has terminated and was
successfully joined the "pthread_t" is still a valid thread identifier.
Solaris pthreads returns ESRCH which I think is the only correct
outcome of yoru sample program.
http://www.opengroup.org/sophocles/show_mail.tpl?source=L&listname=austin-review-l&id=1345
(Subject: Defect in XSH pthread_join())
regards,
alexander.
I don't quite understand this EINVAL-vs-ESRCH distinction myself. But that's
another aardvark... unless Mr. Butenhof can nicely clarify all this. ;-) ;-)
regards,
alexander.
Ulrich Drepper does this [it's from NPTLv04; don't miss "if (DEBUGGING_P"...]:
/* Copyright (C) 2002 Free Software Foundation, Inc.
This file is part of the GNU C Library.
Contributed by Ulrich Drepper <dre...@redhat.com>, 2002.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, write to the Free
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
02111-1307 USA. */
#include <errno.h>
#include <stdlib.h>
#include "atomic.h"
#include "pthreadP.h"
static void
cleanup (void *arg)
{
*(void **) arg = NULL;
}
int
pthread_join (threadid, thread_return)
pthread_t threadid;
void **thread_return;
{
struct pthread *self;
struct pthread *pd = (struct pthread *) threadid;
/* Make sure the descriptor is valid. */
if (DEBUGGING_P && __find_in_stack_list (pd) == NULL)
/* Not a valid thread handle. */
return ESRCH;
/* Is the thread joinable?. */
if (IS_DETACHED (pd))
/* We cannot wait for the thread. */
return EINVAL;
self = THREAD_SELF;
if (pd == self || self->joinid == pd)
/* This is a deadlock situation. The threads are waiting for each
other to finish. Note that this is a "may" error. To be 100%
sure we catch this error we would have to lock the data
structures but it is not necessary. In the unlikely case that
two threads are really caught in this situation they will
deadlock. It is the programmer's problem to figure this
out. */
return EDEADLK;
/* Wait for the thread to finish. If it is already locked something
is wrong. There can only be one waiter. */
if (atomic_compare_and_exchange_acq (&pd->joinid, self, NULL) != 0)
/* There is already somebody waiting for the thread. */
return EINVAL;
/* During the wait we change to asynchronous cancellation. If we
are cancelled the thread we are waiting for must be marked as
un-wait-ed for again. */
pthread_cleanup_push (cleanup, &pd->joinid);
/* Switch to asynchronous cancellation. */
int oldtype;
CANCEL_ASYNC (oldtype);
/* Wait for the child. */
lll_wait_tid (pd->tid);
/* Restore cancellation mode. */
CANCEL_RESET (oldtype);
/* Remove the handler. */
pthread_cleanup_pop (0);
/* Store the return value if the caller is interested. */
if (thread_return != NULL)
*thread_return = pd->result;
/* Free the TCB. */
__free_tcb (pd);
return 0;
}
>I don't quite understand this EINVAL-vs-ESRCH distinction myself. But that's
>another aardvark... unless Mr. Butenhof can nicely clarify all this. ;-) ;-)
I think at least one thing is quite clear:
EINVAL is returned for *detached* (unjoinable) threads.
ESRCH for non-existing ones.
as in the following example; note though, that this program
has a race condition: the pthread_join on the detached thread
may return EINVAL (thread exist but not joinable) or
ESRCH (thread has exited).
Attached program will sleep in the dummy function
if called with an argument; in main if not.
(In the hope that the sleeps determine the order
whether the detached thread finishes first or whether
join is called first.
In the first case the Solaris 9 results are:
./foo
2nd join: 3
Guess what... : No such process
detached join: 3
Guess what... : No such process
(sleep in main, thread exited: No such process as expected)
./foo foo
2nd join: 3
Guess what... : No such process
detached join: 22
Guess what... : Invalid argument
sleep in the dummy routine; the thread has not exited and
we're trying to join a detached thread; EINVAL as expected.
#include <errno.h>
#include <stdio.h>
#include <pthread.h>
/* sleep in main or in dummy */
int dummysleep = 0;
void *
dummy(void *p)
{
if (dummysleep)
sleep(1);
return 0;
}
int
main(int argc, char **argv)
{
int status;
pthread_t dthr, jthr;
pthread_attr_t dattr;
pthread_attr_init(&dattr);
pthread_attr_setdetachstate(&dattr, PTHREAD_CREATE_DETACHED);
status = pthread_create(&jthr, 0, &dummy, 0);
status = pthread_join(jthr, 0);
status = pthread_join(jthr, 0);
printf("2nd join: %d\n", status);
if (status) {
errno = status;
perror( "Guess what... " );
}
dummysleep = argc == 2;
status = pthread_create(&dthr, &dattr, &dummy, 0);
if (!dummysleep)
sleep(1);
status = pthread_join(dthr, 0);
printf("detached join: %d\n", status);
if (status) {
errno = status;
perror( "Guess what... " );
}
return 0;
}
--
Both, actually.
POSIX says it is an error to join a thread that's been joined or detached,
so the program cannot do so.
The implementation, however, is required to detect and report that error,
and it fails to do so. The second pthread_join() in each case must fail
with at least EINVAL. (It cannot succeed.)
The second pthread_join() call MAY also fail with ESRCH, depending on where
the implementation wakes joining threads during termination; timing between
the first join, termination, and the second join; and how the
implementation detects the error conditions.
That is, the target thread shall have always been detached/joined at the
time of the second pthread_join() call, and it cannot succeed. However,
given the ambiguity in definition of thread termination and join, the
target may either have completely terminated at the time of the second join
(in which case an ESRCH is appropriate), or it may still "exist", in which
case EINVAL would be appropriate.
There's nothing wrong with returning EINVAL even when the thread no longer
exists, presumably indicating that the pthread_t value HAD BEEN valid,
where ESRCH would mean the implementation knew it had never been valid, or
at least cannot determine whether it might have been valid. (A thread that
doesn't exist clearly "isn't joinable", after all.) In any case, the second
call to pthread_join() CANNOT succeed.
I presume that you see varying results because the implementation allows a
thread to remain "existing" after the return of pthread_join(), and it
sometimes continues to exist until the second call to pthread_join(). My
guess is that pthread_join() is treating EINVAL as an existence test, and
failing to distinguish that the thread is not joinable when it hasn't yet
terminated completely.
Note to everyone else: Alexander has instigated discussion of several issues
within the Open Group forum, one of which is tightening the definition of
pthread_join() to require that it return only when the target thread has
been fully terminated. If that discussion were to eventually lead to
changes in the standard, there could be no variability in the outcome of
the second call to pthread_join().
Such work need not necessarily tighten the distinction between EINVAL and
ESRCH such that a pthread_t value representing a thread that no longer
exists couldn't legitimately be considered either "not existing" or "not
joinable", though it might.
Well, concurrent might be a better word here, but everything other than "a
subsequent call to pthread_join() before the first has returned"
("simultaneous") is already covered by the rest of the description.
This clause requires that two threads both join the same third thread before
either join operation completes. This distinction is one of the important
changes from Draft 4, and allows (requires) pthread_join() to implicitly
detach (discard) the thread without a separate application call to
pthread_detach(). (The implicit detach of course also outlaws multiple
sequential calls, but that was incidental. The original standard had been
deliberately designed to allow multiple threads to join with a target, and
we eventually realized that added overhead with no value to reasonable
applications.)
> Matt Watson <mwa...@apple.com> writes:
>
>>Would a conforming implementation be allowed to abort() if the value
>>passed in to pthread_join() were of an already-exited thread? What if the
>>value passed in is garbage? What if the garbage passed in just happens to
>>pass the "is or was a pthread_t" check, but actually was never filled in
>>by pthread_create()? What if the implementation uses pointers to represent
>>pthread_t's and the caller does:
There's nothing "undefined" or even "unspecified" here, so abort() isn't
allowable. Any call to pthread_join() must either return success (the
pthread_t value represents a thread that is valid and joinable), or it must
fail with one of the two errors (though the distinction between those
errors is a bit loose).
>>pthread_join((pthread_t)42, NULL);
>
> This may fail to compile.
>
>>The spec I have (SUSv3) says:
>
>>The pthread_join() function shall fail if:
>
>>[EINVAL] The implementation has detected that the value specified by
>>thread does not refer to a joinable thread.
>
> I'm not convinced by the arguments that garbage in should result in
> EINVAL; as I read this the emphasis is on *joinable* thread.
>
> I.e., the implementation does not need to detect that it is an invalid
> thread id (SIGSEGV will do nicely, as far as I'm concerned), but
> rather a thread id that cannot be joined.
>
> If the thread id itself is invalid, pthread_join should return ESRCH for
> "no such thread".
>
> If my interpretation is not correct, I cannot reconcile these two errors:
>
>
> [EINVAL] The implementation has detected that the value specified by
> thread does not refer to a joinable thread.
>
> [ESRCH] No thread could be found corresponding to that specified by the
> given thread ID.
First off, (pthread_t)42 is an inherently erroneous construct. The pthread_t
type is opaque and unspecified, and if that even compiles on any given
platform it's "pure accident".
The interpretation of EINVAL and ESRCH in this case should be that
a) If the implementation can reliably detect that the pthread_t value
represents a valid thread that cannot be joined, it should return EINVAL.
b) If the pthread_t value does not represent a valid thread, it should
return ESRCH.
In the case of something extreme like (pthread_t)42, this may not be a value
that could ever possibly be a valid pthread_t -- for example, if pthread_t
is a pointer. In that case, EINVAL would be inappropriate, and the
implementation should return ESRCH.
However, if you could know that a pthread_t value represented a thread that
had existed, but has since been detached/joined and terminated, then either
value could be argued as "not entirely inappropriate". That is, the
pthread_t value indeed does not represent a valid thread... it's gone. On
the other hand, it did recently represent a valid thread, and the
implementation may still know that, and that the thread is (was) not
joinable. From the standard text it would be hard to argue that EINVAL
isn't allowed. It doesn't say "represents a VALID thread that cannot be
joined", or even "represents a thread that cannot be joined"... it says
"DOES NOT refer to a thread that can be joined".
> Note to everyone else: Alexander has instigated discussion of several issues
> within the Open Group forum, one of which is tightening the definition of
> pthread_join() to require that it return only when the target thread has
> been fully terminated.
If this were not specified, how is one to know when a
pthread_attr_setstackaddr()'d area is safe to free or reclaim?
pthread_join() seems to be the only portable way to do this.
matt.
> First off, (pthread_t)42 is an inherently erroneous construct. The pthread_t
> type is opaque and unspecified, and if that even compiles on any given
> platform it's "pure accident".
Of course, but if it compiles, the implementation had better be able
to handle it. Here's a more "portable" garbage example:
int
bad_pthread_join(void)
{
pthread_t an_uninitialized_pthread_t;
return pthread_join(an_uninitialized_pthread_t, NULL);
}
Based on the discussion, it seems to that pthread_join() forces the
implementation to keep track of all valid pthread_t's. (Darwin's
currently doesn't...).
I guess what I was hoping for was something to the effect of "if the
pthread_t passed in to pthread_*() is not a value returned in the
first argument of a successful pthread_create() call, the behavior is
undefined".
matt.
That is exactly the problem (well, okay, one of them) that caused this
to be brought up in the Open Group forum; pthread_join() is *not*
specified to do that, so there is no portable way to know when an
application-provided stack is no longer being used.
Dave Butenhof could probably fill in with war stories about how the
setstackaddr() stuff was only included at {someone's} insistence, was
originally made optional, and the ramifications of it on things like
pthread_join() weren't really considered at the time...
- Nathan
> David Butenhof <David.B...@compaq.com> wrote in message
> news:<AIQt9.7$dp2.2...@news.cpqcorp.net>...
>
>> First off, (pthread_t)42 is an inherently erroneous construct. The
>> pthread_t type is opaque and unspecified, and if that even compiles on
>> any given platform it's "pure accident".
>
> Of course, but if it compiles, the implementation had better be able
> to handle it. Here's a more "portable" garbage example:
"Portable garbage". I like that. Such an intriguing concept... ;-)
> int
> bad_pthread_join(void)
> {
> pthread_t an_uninitialized_pthread_t;
> return pthread_join(an_uninitialized_pthread_t, NULL);
> }
>
> Based on the discussion, it seems to that pthread_join() forces the
> implementation to keep track of all valid pthread_t's. (Darwin's
> currently doesn't...).
Yes, from a strict reading the standard does indeed require that. But then,
an implementation generally needs to track all currently valid IDs anyway.
(You don't just create a thread and forget about it, do you? How about
debugging?) The real requirement here is that you USE that information to
VALIDATE the thread values passed in.
> I guess what I was hoping for was something to the effect of "if the
> pthread_t passed in to pthread_*() is not a value returned in the
> first argument of a successful pthread_create() call, the behavior is
> undefined".
Convenient for the implementor, of course, but being "friendly" isn't all
that hard, or expensive, and in this case at least seems worthwhile.
David Butenhof wrote:
>>I guess what I was hoping for was something to the effect of "if the
>>pthread_t passed in to pthread_*() is not a value returned in the
>>first argument of a successful pthread_create() call, the behavior is
>>undefined".
>
>
> Convenient for the implementor, of course, but being "friendly" isn't all
> that hard, or expensive, and in this case at least seems worthwhile.
That's not true at all. It can be a very expensive operation. Just
have a few thousand threads running.
I definitely will stay with the garbage-in-garbage-out principle.
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.0.7 (GNU/Linux)
iD8DBQE9xWVr2ijCOnn/RHQRAtO5AKCf3GvhfGMg0hGWPVfDiiycCW7EigCgyX8T
HDHRWZi2LWEDfcYj0WTh3wE=
=WkIs
-----END PGP SIGNATURE-----
> -----BEGIN PGP SIGNED MESSAGE-----
> Hash: SHA1
>
> David Butenhof wrote:
>
>>>I guess what I was hoping for was something to the effect of "if the
>>>pthread_t passed in to pthread_*() is not a value returned in the
>>>first argument of a successful pthread_create() call, the behavior is
>>>undefined".
>>
>>
>> Convenient for the implementor, of course, but being "friendly" isn't all
>> that hard, or expensive, and in this case at least seems worthwhile.
>
> That's not true at all. It can be a very expensive operation. Just
> have a few thousand threads running.
"Expensive" is a relative term. It's not worth debating here.
> I definitely will stay with the garbage-in-garbage-out principle.
Right now, you have that option. Nobody's actually filed an interpretation
request or Aardvark to formally propose the change in wording that's been
discussed in the Open Group mail list. In other words, perhaps you'll be
able to continue indefinitely taking advantage of the ambiguity in wording
of the standard.
Uhmm, but what about this one:
http://www.opengroup.org/sophocles/show_mail.tpl?source=L&listname=austin-review-l&id=1345
(Subject: Defect in XSH pthread_join())
<?>
To me, if accepted, it will provide the legal basis to "stay with the
garbage-in-garbage-out principle."
> that's been
> discussed in the Open Group mail list. In other words, perhaps you'll be
> able to continue indefinitely taking advantage of the ambiguity in wording
> of the standard.
You probably mean the strengthened pthread_join() semantics and the
EINVAL-vs-ESRCH ambiguity, correct?
regards,
alexander.
>
> David Butenhof wrote:
> [...]
>> > I definitely will stay with the garbage-in-garbage-out principle.
>>
>> Right now, you have that option. Nobody's actually filed an
>> interpretation request or Aardvark to formally propose the change in
>> wording
>
> Uhmm, but what about this one:
>
>
http://www.opengroup.org/sophocles/show_mail.tpl?source=L&listname=austin-review-l&id=1345
> (Subject: Defect in XSH pthread_join())
>
> <?>
>
> To me, if accepted, it will provide the legal basis to "stay with the
> garbage-in-garbage-out principle."
Despite the lazy use of "The implementation has detected" in the description
of EINVAL, there's no ambiguity in the current specification of EINVAL. The
implementation is and always has been required to detect and report the use
of a pthread_t value that is not "a joinable thread". I don't believe this
should be changed.
There are some technical and trivial ambiguities regarding pthread_t
lifetimes that can be exploited to narrowly justify loose validation in
theoretically general circumstances. Those are outside pthread_join()
>> that's been
>> discussed in the Open Group mail list. In other words, perhaps you'll be
>> able to continue indefinitely taking advantage of the ambiguity in
>> wording of the standard.
>
> You probably mean the strengthened pthread_join() semantics and the
> EINVAL-vs-ESRCH ambiguity, correct?
No, not really.
Well, I'm now inclined to file two more aardvarks (please see A and B
below). I'd really appreciate if you can explain to me why I shall better
save my [and "everybody's"] time and NOT do it. ;-)
A) http://www.opengroup.org/onlinepubs/007904975/functions/pthread_detach.html
This one will be along the lines of "Defect in XSH pthread_join()"
above. Well, given that [ESRCH] is probably really meant to be
optional/"may fail", is there any really good reason(s) to keep
[EINVAL] mandatory/"shall fail" for pthread_detach()/_join()? To
me, it's "all" explained rather nicely in the article quoted below,
strengthened _join() aside for a moment(*).
B) http://www.opengroup.org/onlinepubs/007904975/functions/pthread_kill.html
I think that "shall fail" is wrong here too. The reasoning is as
follows:
http://groups.google.com/groups?selm=33D5F29E.3F54%40zko.dec.com
(Subject: Re: [Q.] How to validate a thread ID)
<Butenhof>
A call to pthread_kill with a signal number of 0 will check
whether the thread ID is valid. If it's not valid, pthread_kill
will return ESRCH, however, not -1. No pthread function, either
in POSIX threads or in the obsolete "draft 7" API currently
provided with AIX, will return -1.
A thread ID is "valid" from the time it's created (sometime
within the scope of the call to pthread_create that starts it)
until it has terminated and been detached (either via the
detachstate attribute having been set to PTHREAD_CREATE_DETACHED
or by a successful call to either pthread_detach or pthread_join).
There's no prohibition against using any function (except
pthread_detach and pthread_join) on a thread that's detached.
The catch is that you must ensure that the detached thread
can't have terminated. Although pthread_kill, unlike most
functions operating on a thread ID, is required to detect an
invalid thread ID, the system is allowed to "recycle" a thread
ID immediately upon termination of a detached thread -- so you
might be checking the wrong thread.
There isn't necessarily any good use for pthread_kill(<id>,0).
It's there mostly just because it mirrors the behavior of
kill(<pid>,0). You can't use it to verify whether a detached
thread has terminated unless you already know it CANNOT have
terminated (because the thread ID may have been recycled), and
you can't really use it for joinable threads, either. The ID is
valid until it's been joined/detached, even if it's terminated,
so the pthread_kill won't fail; and, as Patrick says, you
presumably can know whether you've joined with the thread
already. (And if you can't you're still in trouble since a
successful return from pthread_join detaches the thread and
allows the ID to be recycled.)
So the documented, supported, and fully portable behavior of
pthread_kill(<id>,0) is absolutely useless except as a
curiosity.
</Butenhof>
<XSH>
22057 In secure implementations, a process may be restricted from
sending a signal to a process having
22058 a different security label. In order to prevent the existence
or nonexistence of a process from
22059 being used as a covert channel, such processes should
appear nonexistent to the sender; that is,
22060 [ESRCH] should be returned, rather than [EPERM], if pid
refers only to such processes.
22061 Existing implementations vary on the result of a kill()
with pid indicating an inactive process (a
22062 terminated process that has not been waited for by its
parent). Some indicate success on such a
22063 call (subject to permission checking), while others give
an error of [ESRCH]. Since the definition
22064 of process lifetime in this volume of IEEE Std 1003.1-2001
covers inactive processes, the
22065 [ESRCH] error as described is inappropriate in this case.
In particular, this means that an
22066 application cannot have a parent process check for
termination of a particular child with kill().
22067 (Usually this is done with the null signal; this can be
done reliably with waitpid().)
.....
32001 The pthread_cancel() function may fail if:
^^^^^^^^
32002 [ESRCH] No thread could be found corresponding to that
specified by the given thread
32003 ID.
.....
33144 The pthread_getcpuclockid( ) function may fail if:
^^^^^^^^
33145 [ESRCH] The value specified by thread_id does not refer
to an existing thread.
.....
33206 The pthread_getschedparam() function may fail if:
^^^^^^^^
33207 [ESRCH] The value specified by thread does not refer
to an existing thread.
33208 The pthread_setschedparam() function may fail if:
^^^^^^^^
.....
33219 [ESRCH] The value specified by thread does not refer
to a existing thread.
.....
35346 The pthread_setschedprio() function may fail if:
^^^^^^^^
.....
35353 [ESRCH] The value specified by thread does not refer
to an existing thread.
</XSH>
regards,
alexander.
(*) Please note that you've also wrote this:
http://groups.google.com/groups?selm=RkQt9.5%24Rr2.256121%40news.cpqcorp.net
(Subject: Re: pthread_join() on detached/exited/garbage thread?)
"....
Note to everyone else: Alexander has instigated discussion of several issues
within the Open Group forum, one of which is tightening the definition of
pthread_join() to require that it return only when the target thread has
been fully terminated. If that discussion were to eventually lead to
changes in the standard, there could be no variability in the outcome of
the second call to pthread_join().
Such work need not necessarily tighten the distinction between EINVAL and
ESRCH such that a pthread_t value representing a thread that no longer
exists couldn't legitimately be considered either "not existing" or "not
joinable", though it might.
...."
lnxmuell:/usr/terekhov # gcc -v
Reading specs from /usr/lib/gcc-lib/s390-suse-linux/2.95.3/specs
gcc version 2.95.3 20010315 (SuSE)
lnxmuell:/usr/terekhov # rpm -q glibc
glibc-2.2.2-26
lnxmuell:/usr/terekhov # gcc -pthread -o detach detach.c
lnxmuell:/usr/terekhov # ldd ./detach
libpthread.so.0 => /lib/libpthread.so.0 (0x40022000)
libc.so.6 => /lib/libc.so.6 (0x40038000)
/lib/ld.so.1 => /lib/ld.so.1 (0x40000000)
lnxmuell:/usr/terekhov # ./detach 0
2nd detach: 22
Guess what... : Invalid argument
lnxmuell:/usr/terekhov # ./detach 1
2nd detach: 3
Guess what... : No such process
lnxmuell:/usr/terekhov # ./detach 1
2nd detach: 22
Guess what... : Invalid argument
lnxmuell:/usr/terekhov # ./detach 0
2nd detach: 22
Guess what... : Invalid argument
lnxmuell:/usr/terekhov # ./detach 1
2nd detach: 3
Guess what... : No such process
lnxmuell:/usr/terekhov # ./detach 1
2nd detach: 3
Guess what... : No such process
lnxmuell:/usr/terekhov # ./detach 1
2nd detach: 3
Guess what... : No such process
lnxmuell:/usr/terekhov # ./detach 100
2nd detach: 3
Guess what... : No such process
lnxmuell:/usr/terekhov # ./detach 0
2nd detach: 22
Guess what... : Invalid argument
lnxmuell:/usr/terekhov # ./detach 1000000
2nd detach: 3
Guess what... : No such process
lnxmuell:/usr/terekhov # ./detach 0
2nd detach: 22
Guess what... : Invalid argument
lnxmuell:/usr/terekhov # cat detach.c
#include <errno.h>
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
void* dummy( void* p ) { return 0; }
int main(int argc, char *argv[]) {
int status;
pthread_t thid;
assert( 2 == argc );
status = pthread_create( &thid, 0, &dummy, 0 );
assert( !status );
status = pthread_detach( thid );
assert( !status );
status = atoi(argv[1]);
if ( status ) usleep( status );
status = pthread_detach( thid );
printf( "2nd detach: %d\n", status );
if ( status ) {
errno = status;
perror( "Guess what... " );
}
return 0;
}
lnxmuell:/usr/terekhov #
[...]
> (*) Please note that you've also wrote this:
>
> http://groups.google.com/groups?selm=RkQt9.5%24Rr2.256121%40news.cpqcorp.net
> (Subject: Re: pthread_join() on detached/exited/garbage thread?)
[...]
regards,
alexander.
http://www.opengroup.org/sophocles/show_mail.tpl?source=L&listname=austin-review-l&id=1348
(Subject: Defect in XSH pthread_detach())
http://www.opengroup.org/sophocles/show_mail.tpl?source=L&listname=austin-review-l&id=1349
(Subject: Defect in XSH pthread_kill())
<TinyURL>
http://tinyurl.com/2hj6
(Subject: Defect in XSH pthread_detach())
http://tinyurl.com/2hj9
(Subject: Defect in XSH pthread_kill())
</TinyURL>
regards,
alexander.
> Alexander Terekhov wrote:
> [...]
> > > http://www.opengroup.org/sophocles/show_mail.tpl?source=L&listname=austin-review-l&id=1345
> > > > (Subject: Defect in XSH pthread_join())
> [...]
> > Well, I'm now inclined to file two more aardvarks ....
>
> http://www.opengroup.org/sophocles/show_mail.tpl?source=L&listname=austin-review-l&id=1348
> (Subject: Defect in XSH pthread_detach())
>
Alexander, I have seen that one and I don't really get the point. You give
as an example Linux program which returns ESEARCH or EINVAL, but then Linux
threads are not known for their 100% POSIX compatibility.
I have run the same program on Solaris 8 and I get EINVAL every time.
Does it prove anything? I don't know.
Besides, "may fail" could also mean that the call could succeed even though
the argument is not a valid detachable thread.
Bye, Dragan
--
Dragan Cvetkovic,
To be or not to be is true. G. Boole No it isn't. L. E. J. Brouwer
The point (behind all those "shall fail"-is-probably-wrong-here
aardvarks I've submitted recently) is that:
A) As Ulrich noted here, checking might be "expensive".
B) "expensiveness" aside, mandatory checking is pretty much
USELESS [debugging aside, of course] because...
C) It doesn't help in any matter whatsoever CORRECT programs,
unless I'm just missing something.
http://www.opengroup.org/onlinepubs/007904975/functions/pthread_mutex_destroy.html
<quote>
Tradeoff Between Error Checks and Performance Supported
Many of the error checks were made optional in order to let
implementations trade off performance versus degree of error
checking according to the needs of their specific applications
and execution environment. As a general rule, errors or
conditions caused by the system (such as insufficient memory)
always need to be reported, but errors due to an erroneously
coded application (such as failing to provide adequate
synchronization to prevent a mutex from being deleted while
in use) are made optional.
A wide range of implementations is thus made possible. For
example, an implementation intended for application debugging
may implement all of the error checks, but an implementation
running a single, provably correct application under very
tight performance constraints in an embedded computer might
implement minimal checks. An implementation might even be
provided in two versions, similar to the options that
compilers provide: a full-checking, but slower version;
and a limited-checking, but faster version. To forbid this
optionality would be a disservice to users.
By carefully limiting the use of "undefined behavior" only
to things that an erroneous (badly coded) application might
do, and by defining that resource-not-available errors are
mandatory, this volume of IEEE Std 1003.1-2001 ensures that
a fully-conforming application is portable across the full
range of implementations, while not forcing all
implementations to add overhead to check for numerous things
that a correct program never does.
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
</quote>
regards,
alexander.
> Dragan Cvetkovic wrote:
> [...]
> > Alexander, I have seen that one and I don't really get the point. You give
> > as an example Linux program which returns ESEARCH or EINVAL, but then Linux
> > threads are not known for their 100% POSIX compatibility.
>
> The point (behind all those "shall fail"-is-probably-wrong-here
> aardvarks I've submitted recently) is that:
>
> A) As Ulrich noted here, checking might be "expensive".
>
> B) "expensiveness" aside, mandatory checking is pretty much
> USELESS [debugging aside, of course] because...
>
> C) It doesn't help in any matter whatsoever CORRECT programs,
> unless I'm just missing something.
>
Yes, but your program calls pthread_detach() twice which, according to
POSIX documentation (same link as yours but ending in pthread_detach.html):
The effect of multiple pthread_detach() calls on the same target
thread is unspecified.
Heck ["unspecified" vs "undefined" aside for a moment], *THAT*
is the point. "SHALL fail" is nothing but ``oxymoron'' here,
really.
regards,
alexander.
--
"undefined --> might-format-the-disk
unspecified --> implementation-chosen
implementation-defined --> implementation-specified"
> Dragan Cvetkovic wrote:
> > Yes, but your program calls pthread_detach() twice which, according to
> > POSIX documentation (same link as yours but ending in pthread_detach.html):
> >
> > The effect of multiple pthread_detach() calls on the same target
> > thread is unspecified.
>
> Heck ["unspecified" vs "undefined" aside for a moment], *THAT*
> is the point. "SHALL fail" is nothing but ``oxymoron'' here,
> really.
>
My reading of the text is that these two errors ESEARCH and EINVAL are more
related to values that don't correspond to any thread in the system.
To quote a recent example, if you code something like
pthread_detach((pthread_t)42)
(where you don't have any thread that happens to have a representation
corresponding to (pthread_t)42), you should expect EINVAL or ESEARCH. If
you say "may fail", it could mean that pthread_detach((pthread_t)42) could
return without reporting an error which is obviously a bad thing.
Undefined behavior. And, BTW, any other ["portable"] silliness
along the lines of:
#include <pthread.h>
#include <iostream>
int main() {
std::cout << pthread_detach(*new pthread_t) << std::endl;
}
IS *ALSO* undefined behavior. ``End of Story.''
>
> (where you don't have any thread that happens to have a representation
> corresponding to (pthread_t)42), you should expect EINVAL or ESEARCH. If
> you say "may fail", it could mean that pthread_detach((pthread_t)42) could
> return without reporting an error which is obviously a bad thing.
Why the heck pthread_detach(*new pthread_t) should be treated any
"better" than pthread_mutex_destroy(new pthread_mutex_t)?
regards,
alexander.
Well, for one reason: pthread_detach() just sets some flag to detached
whereas pthread_mutex_destroy() destroys the mutex which could be locked by
some other thread.
E:\>where /r . /t pthread_detach.c
1795 9-19-102 10:57a E:\nptl03\nptl\pthread_detach.c
1795 9-19-102 10:57a E:\nptl04\nptl\pthread_detach.c
E:\>
<copy&paste>
E:\nptl04\nptl\pthread_detach.c DOS Line 1/52 Col 1
/* Copyright (C) 2002 Free Software Foundation, Inc.
This file is part of the GNU C Library.
Contributed by Ulrich Drepper <dre...@redhat.com>, 2002.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, write to the Free
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
02111-1307 USA. */
#include <errno.h>
#include "pthreadP.h"
#include <atomic.h>
int
pthread_detach (th)
pthread_t th;
{
struct pthread *pd = (struct pthread *) th;
int result = 0;
/* Mark the thread as detached. */
if (atomic_compare_and_exchange_acq (&pd->joinid, pd, NULL) != 0)
{
/* There are two possibilities here. First, the thread might
already be detached. In this case we return EINVAL.
Otherwise there might already be a wiater. The standard does
not mention what happens in this case. */
if (IS_DETACHED (pd))
result = EINVAL;
}
else
/* Check whether the thread terminated meanwhile. In this case we
will just free the TCB. */
if ((pd->cancelhandling & EXITING_BITMASK) != 0)
/* Note that the code in __free_tcb makes sure each thread
control block is freed only once. */
__free_tcb (pd);
return result;
}
regards,
alexander.
On 6 Nov 2002, Dragan Cvetkovic wrote:
> To quote a recent example, if you code something like
>
> pthread_detach((pthread_t)42)
>
> (where you don't have any thread that happens to have a representation
> corresponding to (pthread_t)42), you should expect EINVAL or ESEARCH. If
> you say "may fail", it could mean that pthread_detach((pthread_t)42) could
> return without reporting an error which is obviously a bad thing.
Even if you don't change this to "may fail", there seems to be a bit of
weasel-room here:
"The pthread_join() function shall fail if:
[EINVAL] The implementation has detected that the value specified by
thread does not refer to a joinable thread.
[ESRCH] No thread could be found corresponding to that specified by the
given thread ID."
To me, the leeway comes from "if the implementation has detected" and
"if no thread could be found".
What if the implementation is limited in its ability to detect or find?
If you really wanted to be precise, why not say:
"The pthread_join() function shall fail and return the indicated error
value when:
[EINVAL] The value specified by thread does not refer to a joinable
thread.
[ESRCH] No thread specified by the given thread ID exists."
(I'm sure there's something more appropriate than "exists": "is running",
perhaps?)
That would seem to force implementations to behave...
matt.
IANAL, but:
http://www.opengroup.org/onlinepubs/007904975/functions/xsh_chap01.html#tag_01_05_04
"....
may
Describes a feature or behavior that is optional for an implementation
that conforms to IEEE Std 1003.1-2001. An application should not rely
on the existence of the feature or behavior. An application that relies
on such a feature or behavior cannot be assured to be portable across
conforming implementations.
To avoid ambiguity, the opposite of may is expressed as need not,
instead of may not.
shall
For an implementation that conforms to IEEE Std 1003.1-2001, describes
a feature or behavior that is mandatory. An application can rely on the
existence of the feature or behavior.
For an application or user, describes a behavior that is mandatory.
...."
regards,
alexander.
>"The pthread_join() function shall fail and return the indicated error
>value when:
>[EINVAL] The value specified by thread does not refer to a joinable
>thread.
I think this really ought to be:
[EINVAL] The value specified by thread refers to a non-joinable thread.
Which clarifies an important point: this function is supposed to return
EINVAL only for non-detached threads; if not, what's the point of:
>[ESRCH] No thread specified by the given thread ID exists."
Casper