Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Copying pthread_t

712 views
Skip to first unread message

Hallvard B Furuseth

unread,
Jul 24, 2007, 11:55:23 AM7/24/07
to
The comp.programming.threads FAQ, Q15/Q70, says it may be unportable to
copy a pthread_t. This makes no sense to me, since pthread_t values
are copied (passed by value) by the pthread API itself: pthread_self(),
pthread_equal(), pthread_join(), pthread_cancel(), and some others.

Has pthread_t copying been observed to be a problem with some
implementation? I'm guessing the FAQ authors have just been a bit too
enthusiastic about not copying pthread_* types.

Q70 also mentions some other problems, pthread_destroy() on both copies
or synchronization while doing '=', but that plain bugs in the program,
no different from e.g. close() the same file descriptor twice or '=' on
something else which would need a mutex.

--
Regards,
Hallvard

David Schwartz

unread,
Jul 24, 2007, 2:16:06 PM7/24/07
to
On Jul 24, 8:55 am, Hallvard B Furuseth <h.b.furus...@usit.uio.no>
wrote:

> The comp.programming.threads FAQ, Q15/Q70, says it may be unportable to
> copy a pthread_t. This makes no sense to me, since pthread_t values
> are copied (passed by value) by the pthread API itself: pthread_self(),
> pthread_equal(), pthread_join(), pthread_cancel(), and some others.

Obviously, those types of 'copying' have to work. However, suppose a
'pthread_t' is actually a pointer. By copying a 'pthread_t', you may
wind up with two pointers to the same place. This could cause a
problem if one 'pthread_t' has its value changed.

However, if 'pthread_t' was a pointer, one would expect it to point to
a static structure associated with the thread. But nothing in the
standard prevents it from pointing to a dynamic structure associated
with that particular 'pthread_t'.

> Has pthread_t copying been observed to be a problem with some
> implementation? I'm guessing the FAQ authors have just been a bit too
> enthusiastic about not copying pthread_* types.

One would have to go out of one's way to create an implementation
where it broke. To my knowledge, nobody has done so. That said, I
would strongly caution you not to rely it working.

DS

Eric Sosman

unread,
Jul 24, 2007, 4:31:15 PM7/24/07
to
David Schwartz wrote On 07/24/07 14:16,:

> On Jul 24, 8:55 am, Hallvard B Furuseth <h.b.furus...@usit.uio.no>
> wrote:
>
>>The comp.programming.threads FAQ, Q15/Q70, says it may be unportable to
>>copy a pthread_t. This makes no sense to me, since pthread_t values
>>are copied (passed by value) by the pthread API itself: pthread_self(),
>>pthread_equal(), pthread_join(), pthread_cancel(), and some others.
>
>
> Obviously, those types of 'copying' have to work. However, suppose a
> 'pthread_t' is actually a pointer. By copying a 'pthread_t', you may
> wind up with two pointers to the same place. This could cause a
> problem if one 'pthread_t' has its value changed.

I don't understand this at all: How does the existence
of two -- or twenty -- pointers to something make trouble?

> However, if 'pthread_t' was a pointer, one would expect it to point to
> a static structure associated with the thread. But nothing in the
> standard prevents it from pointing to a dynamic structure associated
> with that particular 'pthread_t'.

Again, I don't understand what trouble you anticipate.
Note that the dynamic data (if any) cannot simply live at a
fixed offset from the pthread_t variable itself (e.g., just
after it), because it is possible to put pthread_t values
in arrays. Also, a function like pthread_join() receives
only the pthread_t value, not an indication of where the
value was stored before the caller put it in a register or
pushed in on the stack or whatever.

>>Has pthread_t copying been observed to be a problem with some
>>implementation? I'm guessing the FAQ authors have just been a bit too
>>enthusiastic about not copying pthread_* types.
>
>
> One would have to go out of one's way to create an implementation
> where it broke. To my knowledge, nobody has done so. That said, I
> would strongly caution you not to rely it working.

In view of C's call-by-value semantics, it seems to me
that a call to pthread_join() and the like cannot avoid
copying a pthread_t value. Similarly, pthread_self() must
deliver a value; storing it is copying it, and must be
allowed. Perhaps these "functions" might be implemented
as magical constructs that do not behave like actual C
functions, using call-by-reference or call-by-innuendo,
and return-by-subterfuge for pthread_self() -- but I don't
buy it, not for a minute.

If pthread_t is a legitimate C data type and if the
pthread_xxx functions are actual C functions, I don't see
how there can be any danger in copying them at will.

--
Eric....@sun.com

Hallvard B Furuseth

unread,
Jul 24, 2007, 3:39:31 PM7/24/07
to
David Schwartz writes:
> On Jul 24, 8:55 am, Hallvard B Furuseth <h.b.furus...@usit.uio.no>
> wrote:
>> The comp.programming.threads FAQ, Q15/Q70, says it may be unportable to
>> copy a pthread_t. This makes no sense to me, since pthread_t values
>> are copied (passed by value) by the pthread API itself: pthread_self(),
>> pthread_equal(), pthread_join(), pthread_cancel(), and some others.
>
> Obviously, those types of 'copying' have to work.

I see no reason to put that copying in quotes. C functions have call by
value, which means copying. Caller and callee will have the value in
different memory locations, and modifying one will not affect the other
(unless the compiler finds that it can safely optimize that it away).

> However, suppose a
> 'pthread_t' is actually a pointer. By copying a 'pthread_t', you may
> wind up with two pointers to the same place. This could cause a
> problem if one 'pthread_t' has its value changed.

Meaning the other pointer is now invalid? Then thread 1 doing
pthread_t tid = pthread_self();
can break if thread 2 changes the "original" pthread_t after
pthread_self() returned but before thread 1 gets to use "tid".

Similarly, the program can break if this happens:
- thread 1 calls pthread_whatever(original pthread_t, ...),
- pthread_t gets copied to the stack of pthread_whatever(),
- thread 2 changes original pthread_t,
- thread 1 breaks when pthread_whatever() uses the value on the stack.
though in this case a compiler which is aware of the pthread_t
weirdness might be able to prevent this.

> However, if 'pthread_t' was a pointer, one would expect it to point to
> a static structure associated with the thread. But nothing in the
> standard prevents it from pointing to a dynamic structure associated
> with that particular 'pthread_t'.

Well, the standard says the pthread API must work. If the internal
structure from pthread_create(&tid,...) contains the pointer &tid and
expects it to be valid, then a program using this function can break:

void foo() { pthread_t tid; (void) pthread_create(&tid, ...); }

After foo() returns, the internal thread structure points to an
out-of-scope variable, and unless the compiler uses a garbage collected
stack structure with closures, the next function call might reuse that
memory for something else.

> One would have to go out of one's way to create an implementation
> where it broke. To my knowledge, nobody has done so.

Great. Thanks.

> That said, I would strongly caution you not to rely it working.

That means I can't safely write a function which takes a pthread_t
argument. It must take a pthread_t pointer.

Which also means if I call pthread_self() and pass the result to such a
private function, I must keep the place where that pthread_t was stored
in scope until that private function is done with the pthread_t*.

--
Regards,
Hallvard

David Schwartz

unread,
Jul 24, 2007, 6:11:49 PM7/24/07
to
On Jul 24, 1:31 pm, Eric Sosman <Eric.Sos...@sun.com> wrote:

> I don't understand this at all: How does the existence
> of two -- or twenty -- pointers to something make trouble?

If the intention was that only one pointer ever be available, and you
create a case where there can be two, things can break when one
pointer goes away. For example, consider an implementation that rigs
things such that a special 'destructor function' is run when a
'pthread_t' goes out of scope or is reassigned.

> Again, I don't understand what trouble you anticipate.
> Note that the dynamic data (if any) cannot simply live at a
> fixed offset from the pthread_t variable itself (e.g., just
> after it), because it is possible to put pthread_t values
> in arrays. Also, a function like pthread_join() receives
> only the pthread_t value, not an indication of where the
> value was stored before the caller put it in a register or
> pushed in on the stack or whatever.

The value of the 'pthread_t' could be a pointer. It could be a pointer
to a structure that is freed when the 'pthread_t' goes out of scope.

> > One would have to go out of one's way to create an implementation
> > where it broke. To my knowledge, nobody has done so. That said, I
> > would strongly caution you not to rely it working.

> In view of C's call-by-value semantics, it seems to me
> that a call to pthread_join() and the like cannot avoid
> copying a pthread_t value.

NO, NO, NO! This is an argument type that I have to reject at least
once a week. You cannot "synthesize" guarantees across standards
because nothing in the pthreads standards requires that pthreads be
implement only by C semantics. In fact, such is impossible.

There is no rule that says a 'pthread_t' has to follow C's call-by-
value semantics. The code just has to work. If an implementation wants
to put special magic around pthread_t operations, it most certainly is
legal for it to do so.

> Similarly, pthread_self() must
> deliver a value; storing it is copying it, and must be
> allowed. Perhaps these "functions" might be implemented
> as magical constructs that do not behave like actual C
> functions, using call-by-reference or call-by-innuendo,
> and return-by-subterfuge for pthread_self() -- but I don't
> buy it, not for a minute.

Exactly. The standards tell you what has to work. Everything else
might or might not work. If the standards explicitly tell you that
something does not have to work, then it does not have to.

However, there are certainly cases where it's hard to imagine how
something could possibly not work unless someone intentionally worked
hard to make it not work. This is one such case.

> If pthread_t is a legitimate C data type and if the
> pthread_xxx functions are actual C functions, I don't see
> how there can be any danger in copying them at will.

There is no rule that pthread_t must follow the normal C type
semantics. In fact, there is explicitly a warning that it may not.

DS

David Schwartz

unread,
Jul 24, 2007, 6:19:36 PM7/24/07
to
On Jul 24, 12:39 pm, Hallvard B Furuseth <h.b.furus...@usit.uio.no>
wrote:

> I see no reason to put that copying in quotes. C functions have call by


> value, which means copying. Caller and callee will have the value in
> different memory locations, and modifying one will not affect the other
> (unless the compiler finds that it can safely optimize that it away).

You mean *normal* C functions. But there is no rule that says that the
pthread_* functions must be normal C functions. And, as you pointed
out, the compiler can optimize things so long as compliant code is not
broken. Copying pthread_t's is not permitted, so code that does so is
not compliant. The compiler is allowed to make optimizations that
breaks such code.

> > However, suppose a
> > 'pthread_t' is actually a pointer. By copying a 'pthread_t', you may
> > wind up with two pointers to the same place. This could cause a
> > problem if one 'pthread_t' has its value changed.

> Meaning the other pointer is now invalid? Then thread 1 doing
> pthread_t tid = pthread_self();
> can break if thread 2 changes the "original" pthread_t after
> pthread_self() returned but before thread 1 gets to use "tid".

Right. 'tid=pthread_self()' might somehow damage whatever 'tid'
contained before such that a copy of it is also changed.

This can certainly happen if 'tid' is a pointer to a structure that is
modified by the 'tid=pthread_self' operation.

> > However, if 'pthread_t' was a pointer, one would expect it to point to
> > a static structure associated with the thread. But nothing in the
> > standard prevents it from pointing to a dynamic structure associated
> > with that particular 'pthread_t'.

> Well, the standard says the pthread API must work. If the internal
> structure from pthread_create(&tid,...) contains the pointer &tid and
> expects it to be valid, then a program using this function can break:
>
> void foo() { pthread_t tid; (void) pthread_create(&tid, ...); }
>
> After foo() returns, the internal thread structure points to an
> out-of-scope variable, and unless the compiler uses a garbage collected
> stack structure with closures, the next function call might reuse that
> memory for something else.

In this case, it doesn't matter what 'tid' points to since 'tid' will
never be accessed again. I am not suggesting that 'tid' point to an
internal thread structure, I am suggesting that 'tid' point to a
structure whose sole purpose is to identify a thread.

Consider:

void get_id(pthread_t *j)
{
pthread_t tid; // this could allocate memory for the structure
tid=pthread_self(); // this could fill in the structure
(*j)=tid; // this could copy a pointer to the structure
return; // this could release the memory for 'tid' since it went out
of scope
}

The net result would be filling in 'j' with a pointer to memory that
has been freed.

> > One would have to go out of one's way to create an implementation
> > where it broke. To my knowledge, nobody has done so.

> Great. Thanks.

> > That said, I would strongly caution you not to rely it working.

> That means I can't safely write a function which takes a pthread_t
> argument. It must take a pthread_t pointer.

That's probably, strictly speaking, true. But it's almost
inconceivable that you'd get into trouble in case where the two copies
don't go their separate ways. Even my crazy hypothetical won't break
in just that case.

> Which also means if I call pthread_self() and pass the result to such a
> private function, I must keep the place where that pthread_t was stored
> in scope until that private function is done with the pthread_t*.

You have no choice. If you call a function, everything that called it
will stay in scope until the function returns. I don't think you have
to worry about passing a pthread_t by value. You just have to avoid
copying that value into another pthread_t.

Even I won't go so far as to say it's not safe to *pass* a 'pthread_t'
by value.

DS

Eric Sosman

unread,
Jul 24, 2007, 9:19:39 PM7/24/07
to
David Schwartz wrote:
> On Jul 24, 1:31 pm, Eric Sosman <Eric.Sos...@sun.com> wrote:
>
>> I don't understand this at all: How does the existence
>> of two -- or twenty -- pointers to something make trouble?
>
> If the intention was that only one pointer ever be available, and you
> create a case where there can be two, things can break when one
> pointer goes away. For example, consider an implementation that rigs
> things such that a special 'destructor function' is run when a
> 'pthread_t' goes out of scope or is reassigned.

Such an implementation would contravene the C Standard,
and would not be C.

>> Again, I don't understand what trouble you anticipate.
>> Note that the dynamic data (if any) cannot simply live at a
>> fixed offset from the pthread_t variable itself (e.g., just
>> after it), because it is possible to put pthread_t values
>> in arrays. Also, a function like pthread_join() receives
>> only the pthread_t value, not an indication of where the
>> value was stored before the caller put it in a register or
>> pushed in on the stack or whatever.
>
> The value of the 'pthread_t' could be a pointer. It could be a pointer
> to a structure that is freed when the 'pthread_t' goes out of scope.

Such an implementation would contravene the C Standard,
and would not be C.

Well, then, how about

pthread_t t1, t2;
t1 = t2 = pthread_self();

? My argument boils down to the notion that pthread_self(),
like any other C function, returns a *value* and not a magic
cookie. You can store that value in any (compatible) object,
and you can retrieve the same value from that object later
(to hand to pthread_cancel(), for instance). If you can
retrieve "the same" value, you can store it in a second, third,
fourth, ... object similarly, and you can retrieve it from any
of those objects; it doesn't matter whether the value was "newly
minted" from a function call or was found by evaluating a
previously-stored object.

>> If pthread_t is a legitimate C data type and if the
>> pthread_xxx functions are actual C functions, I don't see
>> how there can be any danger in copying them at will.
>
> There is no rule that pthread_t must follow the normal C type
> semantics. In fact, there is explicitly a warning that it may not.

Well, you've got me there, because I don't know which of
the myriad of standards contains the warning. (The nice part
about standards is that there are enough of them to let everyone
have his own.) As a favor, could you cite or quote the warning?

--
Eric Sosman
eso...@ieee-dot-org.invalid

Hallvard B Furuseth

unread,
Jul 25, 2007, 1:10:51 AM7/25/07
to
David Schwartz writes:
> On Jul 24, 12:39 pm, Hallvard B Furuseth <h.b.furus...@usit.uio.no>
> wrote:
>> I see no reason to put that copying in quotes. C functions have call by
>> value, which means copying. Caller and callee will have the value in
>> different memory locations, and modifying one will not affect the other
>> (unless the compiler finds that it can safely optimize that it away).
>
> You mean *normal* C functions. But there is no rule that says that the
> pthread_* functions must be normal C functions.

The Posix standard just calls them functions, as far as I can tell.
Where is this different from all the other functions in the standard?
Or can't other Posix functions be relied on to be normal functions
either.

> And, as you pointed
> out, the compiler can optimize things so long as compliant code is not
> broken.

Yes...

> Copying pthread_t's is not permitted, so code that does so is not
> compliant. The compiler is allowed to make optimizations that
> breaks such code.

Where does the standard say that? (Same place as the warning you
mentioned that it may need not follow normal C type semantics, I guess,
so maybe the answer fits better in the other thread.)

>>> However, suppose a
>>> 'pthread_t' is actually a pointer. By copying a 'pthread_t', you may
>>> wind up with two pointers to the same place. This could cause a
>>> problem if one 'pthread_t' has its value changed.
>
>> Meaning the other pointer is now invalid? Then thread 1 doing
>> pthread_t tid = pthread_self();
>> can break if thread 2 changes the "original" pthread_t after
>> pthread_self() returned but before thread 1 gets to use "tid".
>
> Right. 'tid=pthread_self()' might somehow damage whatever 'tid'
> contained before such that a copy of it is also changed.

If you are right about the rest, yes. But I was not referring to
changing that tid, but the tid from phread_create(). So read the above
as
pthread_t tid = pthread_self();

where there is no previous value to destroy.

Or are you saying that one cannot even _store_ the result from
pthread_self(), even though there is no warning about that in the
pthread_self() definition? Or has there been a warning about that in a
draft, but not in the final version?

> (...)


>>> However, if 'pthread_t' was a pointer, one would expect it to point to
>>> a static structure associated with the thread. But nothing in the
>>> standard prevents it from pointing to a dynamic structure associated
>>> with that particular 'pthread_t'.
>>
>> Well, the standard says the pthread API must work. If the internal
>> structure from pthread_create(&tid,...) contains the pointer &tid and
>> expects it to be valid, then a program using this function can break:
>>
>> void foo() { pthread_t tid; (void) pthread_create(&tid, ...); }
>>
>> After foo() returns, the internal thread structure points to an
>> out-of-scope variable, and unless the compiler uses a garbage collected
>> stack structure with closures, the next function call might reuse that
>> memory for something else.
>
> In this case, it doesn't matter what 'tid' points to since 'tid' will
> never be accessed again. I am not suggesting that 'tid' point to an
> internal thread structure, I am suggesting that 'tid' point to a
> structure whose sole purpose is to identify a thread.

If you are not even talking about it referring back to itself, I'm
_really_ at loss about how this could be different from having two
pointers to some other object.

> Consider:
>
> void get_id(pthread_t *j)
> {
> pthread_t tid; // this could allocate memory for the structure
> tid=pthread_self(); // this could fill in the structure
> (*j)=tid; // this could copy a pointer to the structure
> return; // this could release the memory for 'tid' since it went out
> of scope
> }
>
> The net result would be filling in 'j' with a pointer to memory that
> has been freed.

No it would not. Your 'tid' here is a pointer, remember? It _points_
to the allocated memory you refer to. *j points to that structure, not
to tid. After the return from get_id(), the structure is still
allocated, and *j points to it, only the tid pointer is out of scope.

>> That means I can't safely write a function which takes a pthread_t
>> argument. It must take a pthread_t pointer.

>> (...)


>> Which also means if I call pthread_self() and pass the result to such a
>> private function, I must keep the place where that pthread_t was stored
>> in scope until that private function is done with the pthread_t*.
>
> You have no choice. If you call a function, everything that called it
> will stay in scope until the function returns.

Oops, I should have said that memory must be kept valid. E.g if I put
the pthread_t in malloced memory.

> I don't think you have to worry about passing a pthread_t by
> value. You just have to avoid copying that value into another
> pthread_t. Even I won't go so far as to say it's not safe to *pass* a
> 'pthread_t' by value.

Are you only talking about not overwriting an initialized pthread_t with
another pthread_t, then? While it's OK to initialize a pthread_t with
another one? That's what passing by values does - it initializes the
new one. But then, what if you return from that function with the
initialized copy and call it again so the same memory area gets
initialized with another pthread_t value?

--
Regards,
Hallvard

David Schwartz

unread,
Jul 25, 2007, 3:56:04 AM7/25/07
to
On Jul 24, 10:10 pm, Hallvard B Furuseth <h.b.furus...@usit.uio.no>
wrote:

> > You mean *normal* C functions. But there is no rule that says that the


> > pthread_* functions must be normal C functions.

> The Posix standard just calls them functions, as far as I can tell.
> Where is this different from all the other functions in the standard?
> Or can't other Posix functions be relied on to be normal functions
> either.

Any apparent function that has specific caveats cannot be relied upon
to act like a normal function. For example, some compiler 'magic'
might be needed around the mutex functions to inhibit inter-procedure
optimization. This may make those functions different from every
other functions.

What you cannot do is combine implementation limits from one standard
to get shoehorn guarantees out of another. Nothing in the POSIX
standard requires it to be implement by pure standards-conformant C
code, and in fact it would be impossible to do so.

> > And, as you pointed
> > out, the compiler can optimize things so long as compliant code is not
> > broken.
>
> Yes...
>
> > Copying pthread_t's is not permitted, so code that does so is not
> > compliant. The compiler is allowed to make optimizations that
> > breaks such code.

> Where does the standard say that? (Same place as the warning you
> mentioned that it may need not follow normal C type semantics, I guess,
> so maybe the answer fits better in the other thread.)

I don't have the standard handy, but it's the same section that warns
about copying mutexes, condition variables, and the other pthread_*
types.

> Or are you saying that one cannot even _store_ the result from
> pthread_self(), even though there is no warning about that in the
> pthread_self() definition? Or has there been a warning about that in a
> draft, but not in the final version?

No, you can store the result of pthread_self in a pthread_t. However,
assigning a new value to the same pthread_t might destroy the original
value such that a copy of it now points to something that was
destroyed.

> >> After foo() returns, the internal thread structure points to an
> >> out-of-scope variable, and unless the compiler uses a garbage collected
> >> stack structure with closures, the next function call might reuse that
> >> memory for something else.

> > In this case, it doesn't matter what 'tid' points to since 'tid' will
> > never be accessed again. I am not suggesting that 'tid' point to an
> > internal thread structure, I am suggesting that 'tid' point to a
> > structure whose sole purpose is to identify a thread.

> If you are not even talking about it referring back to itself, I'm
> _really_ at loss about how this could be different from having two
> pointers to some other object.

Two pointers to any object can cause confusion and problems if the
object is modified through one pointer while the other pointer is
expected to refer to the same thing all along. Imagine if a pthread_t
worked like many string classes do. If you copy a pthread_t, they may
both winding up pointing to the same place, causing a reassignment of
one to change the value of the other.

Consider:
int *i, *j;
i=malloc(sizeof(void *));
j=malloc(sizeof(void *));

i=GetSomeValue();
SomeFunction(i);
j=i;
i=GetSomeOtherValue(); // oops, we just broke j

Under the hood, each 'pthread_t' could have some memory allocated to
it to copy the information needed to identify a thread. Setting one
pthread_t equal to another could cause them to point to the same
structure, causing a subsequent chance of one to change the other.

> > Consider:
>
> > void get_id(pthread_t *j)
> > {
> > pthread_t tid; // this could allocate memory for the structure
> > tid=pthread_self(); // this could fill in the structure
> > (*j)=tid; // this could copy a pointer to the structure
> > return; // this could release the memory for 'tid' since it went out
> > of scope
> > }
>
> > The net result would be filling in 'j' with a pointer to memory that
> > has been freed.

> No it would not. Your 'tid' here is a pointer, remember? It _points_
> to the allocated memory you refer to. *j points to that structure, not
> to tid. After the return from get_id(), the structure is still
> allocated, and *j points to it, only the tid pointer is out of scope.

I am assuming the implementation uses destructors. When a pthread_t
goes out of scope, the object has to be freed for this to work or
there will be memory leaks.

> >> That means I can't safely write a function which takes a pthread_t
> >> argument. It must take a pthread_t pointer.
> >> (...)
> >> Which also means if I call pthread_self() and pass the result to such a
> >> private function, I must keep the place where that pthread_t was stored
> >> in scope until that private function is done with the pthread_t*.
>
> > You have no choice. If you call a function, everything that called it
> > will stay in scope until the function returns.

> Oops, I should have said that memory must be kept valid. E.g if I put
> the pthread_t in malloced memory.

I think this has to work.

> > I don't think you have to worry about passing a pthread_t by
> > value. You just have to avoid copying that value into another
> > pthread_t. Even I won't go so far as to say it's not safe to *pass* a
> > 'pthread_t' by value.

> Are you only talking about not overwriting an initialized pthread_t with
> another pthread_t, then? While it's OK to initialize a pthread_t with
> another one? That's what passing by values does - it initializes the
> new one. But then, what if you return from that function with the
> initialized copy and call it again so the same memory area gets
> initialized with another pthread_t value?

You cannot assume that copying a pthread_t does anything but a shallow
copy. It violates the standard to assume the two pthread_t's can then
go their separate ways without breaking. The only legal way to
initialize a pthread_t is by one of the pthread_* functions like
pthread_self or pthread_create.

That said, since there is no pthread_copy function, I'm not sure you
have much choice. It would be fair to consider any implementation that
did this legal but brain damaged. You don't have to work on every
possible legal implementation. An implementation that gave each thread
one CPU cycle every decade is legal.

DS

David Schwartz

unread,
Jul 25, 2007, 5:16:10 AM7/25/07
to
On Jul 24, 6:19 pm, Eric Sosman <esos...@ieee-dot-org.invalid> wrote:

> David Schwartz wrote:

> > If the intention was that only one pointer ever be available, and you
> > create a case where there can be two, things can break when one
> > pointer goes away. For example, consider an implementation that rigs
> > things such that a special 'destructor function' is run when a
> > 'pthread_t' goes out of scope or is reassigned.

> Such an implementation would contravene the C Standard,
> and would not be C.

Creating multiple threads contravenes the C standard. Pthreads code
will not strictly conform to the C standard, so what? Clearly you
cannot implement pthreads with "just C". There is no reason a
destructor couldn't be one of the extensions a particular pthreads
implementations uses/requires.

> > The value of the 'pthread_t' could be a pointer. It could be a pointer
> > to a structure that is freed when the 'pthread_t' goes out of scope.

> Such an implementation would contravene the C Standard,
> and would not be C.

Of course. So would *any* implementation. What's your point?

You cannot synthesize guarantees from one standard based on
limitations in another standard. Any pthreads limitation will have to
rely on platform-specific things that are not in the C standard.


> > However, there are certainly cases where it's hard to imagine how
> > something could possibly not work unless someone intentionally worked
> > hard to make it not work. This is one such case.
>
> Well, then, how about
>
> pthread_t t1, t2;
> t1 = t2 = pthread_self();

If there was a function that returned an initialized mutex, would you
expect this to work? I would think not. I do agree in this case that
it's much more reasonable to expect this to work. But I don't think
the standard requires it.

Of course, just because the standard doesn't require something to work
doesn't mean that an implementation that breaks it is brain damaged.

> ? My argument boils down to the notion that pthread_self(),
> like any other C function, returns a *value* and not a magic
> cookie.

Nothing requires pthread_self to be like any other C function. There
is no reason it can't use some kind of special compiler magic and many
reasons that it might wish to. The only restriction standard is that
compliant code work. Obviously, there is also a QofI issue.

> You can store that value in any (compatible) object,
> and you can retrieve the same value from that object later
> (to hand to pthread_cancel(), for instance). If you can
> retrieve "the same" value, you can store it in a second, third,
> fourth, ... object similarly, and you can retrieve it from any
> of those objects; it doesn't matter whether the value was "newly
> minted" from a function call or was found by evaluating a
> previously-stored object.

You can if and only if the standard doesn't say you can't. You clearly
can't do this with mutexes and expect it to work. You can't do it with
condition variables.

I don't have the standard in front of me, but my recollection is that
it says that the same rules apply to pthread_s. However, there is a
'pthread_equal' function and there is no 'pthread_copy' function. And
there are cases where it makes an awful lot of sense to copy a
pthread_t (versus no cases where it makes sense to copy a mutex or
condition variable).

> >> If pthread_t is a legitimate C data type and if the
> >> pthread_xxx functions are actual C functions, I don't see
> >> how there can be any danger in copying them at will.
>
> > There is no rule that pthread_t must follow the normal C type
> > semantics. In fact, there is explicitly a warning that it may not.

> Well, you've got me there, because I don't know which of
> the myriad of standards contains the warning. (The nice part
> about standards is that there are enough of them to let everyone
> have his own.) As a favor, could you cite or quote the warning?

I don't have it handy. I'll try to dig it up for you. But it's in the
same place that warns you that you cannot compare or copy other
pthread_* types such as mutexes.

DS

Ian Collins

unread,
Jul 25, 2007, 5:26:48 AM7/25/07
to
David Schwartz wrote:
> On Jul 24, 6:19 pm, Eric Sosman <esos...@ieee-dot-org.invalid> wrote:
>
>> David Schwartz wrote:
>
>>> If the intention was that only one pointer ever be available, and you
>>> create a case where there can be two, things can break when one
>>> pointer goes away. For example, consider an implementation that rigs
>>> things such that a special 'destructor function' is run when a
>>> 'pthread_t' goes out of scope or is reassigned.
>
>> Such an implementation would contravene the C Standard,
>> and would not be C.
>
> Creating multiple threads contravenes the C standard. Pthreads code
> will not strictly conform to the C standard, so what? Clearly you
> cannot implement pthreads with "just C". There is no reason a
> destructor couldn't be one of the extensions a particular pthreads
> implementations uses/requires.
>
Such an extension would have to be documented in order for every
compiler for the system to implement it. Rather unlikely don't you think?

--
Ian Collins.

Hallvard B Furuseth

unread,
Jul 25, 2007, 6:48:56 AM7/25/07
to
David Schwartz writes:
> On Jul 24, 6:19 pm, Eric Sosman <esos...@ieee-dot-org.invalid> wrote:
>> David Schwartz wrote:
>
>>> If the intention was that only one pointer ever be available, and you
>>> create a case where there can be two, things can break when one
>>> pointer goes away. For example, consider an implementation that rigs
>>> things such that a special 'destructor function' is run when a
>>> 'pthread_t' goes out of scope or is reassigned.
>
>> Such an implementation would contravene the C Standard,
>> and would not be C.
>
> Creating multiple threads contravenes the C standard. Pthreads code
> will not strictly conform to the C standard, so what?

So the POSIX standard says how it differs from the C standard, that's
what. And if there is a place which says pthread_t can't be copied,
that's indeed the end of it no matter how strange it would be.

But I can't find such a place. For pthread_t to "take action" when it
goes out of scope I think what would be need to be documented is that
pthread_t is not a proper C type and does not follow the C type rules.


[Rearranging your message a bit]

>> You can store that value in any (compatible) object, (...)


>
> You can if and only if the standard doesn't say you can't. You clearly
> can't do this with mutexes and expect it to work. You can't do it with
> condition variables.

Right. Which is also why the mutex and condition functions take
pointers to such variables, so this fits with how entirely normal C
functions can work. Not so with functions taking pthread_t.

> I don't have the standard in front of me, but my recollection is that
> it says that the same rules apply to pthread_s.

Looking at <http://www.opengroup.org/onlinepubs/000095399/> (IEEE Std
1003.1, 2004 Edition, I hope that's the right standard:-) -

The pthread_mutex_init() description, functions/pthread_mutex_init.html,
says
"Only mutex itself may be used for performing synchronization. The
result of referring to copies of mutex in calls to
pthread_mutex_lock(), pthread_mutex_trylock(), pthread_mutex_unlock(),
and pthread_mutex_destroy() is undefined."
and also
"Attempting to initialize an already initialized mutex results in
undefined behavior."
which would correspond to your objections to overwrite an existing
pthread_t object. pthread_cond_init.html has similar language.

There is no such language in pthread_create.html and pthread_self.html.

basedefs/sys/types.h.html, issue 6, notes that pthread_t need not be an
_arithmetic_ type, so it can be a structure. It does not go on to
mention it might be a magical non-C-like type.

> However, there is a
> 'pthread_equal' function and there is no 'pthread_copy' function.

Yes, the rationale in susv3/functions/pthread_equal.html explains that

"Implementations may choose to define a thread ID as a structure. (...)
Since the C language does not support comparison on structure types, the
pthread_equal() function is provided to compare thread IDs."

The C language does support struct assignment, so pthread_copy() is
not needed.

> And there are cases where it makes an awful lot of sense to copy a
> pthread_t (versus no cases where it makes sense to copy a mutex or
> condition variable).

Quite so, that's why I started this thread.


[Back to the rest of you message]

> Clearly you cannot implement pthreads with "just C".

So what? Nor can you implement the much else in the POSIX or C standard
with "just C". They just have to behave like C functions, except where
documented.

> There is no reason a
> destructor couldn't be one of the extensions a particular pthreads
> implementations uses/requires.

If the implementation manages to do it magically enough for the visible
behavior to follow the C standard, or if it documents that this aspect
does not follow the C standard, that's true.

> (...)


> You cannot synthesize guarantees from one standard based on
> limitations in another standard.

You can when the first standard is defined in terms of the second -
unless it says that it only looks like it is, but actually isn't.
What a brief grep (in a local copy of the HTML pages) found about that
is this from basedefs/xbd_chap01.html:

"With each function or header from the ISO C standard, a statement to
the effect that "any conflict is unintentional" is included. That is
intended to refer to a direct conflict. IEEE Std 1003.1-2001 acts in
part as a profile of the ISO C standard, and it may choose to further
constrain behaviors allowed to vary by the ISO C standard. Such
limitations are not considered conflicts.

Where additional semantics apply to a function or header, the material
is identified by use of the CX margin legend."

--
Hallvard

David Schwartz

unread,
Jul 25, 2007, 7:16:01 AM7/25/07
to
On Jul 25, 2:26 am, Ian Collins <ian-n...@hotmail.com> wrote:

> > Creating multiple threads contravenes the C standard. Pthreads code
> > will not strictly conform to the C standard, so what? Clearly you
> > cannot implement pthreads with "just C". There is no reason a
> > destructor couldn't be one of the extensions a particular pthreads
> > implementations uses/requires.

> Such an extension would have to be documented in order for every
> compiler for the system to implement it. Rather unlikely don't you think?

This assumes a system with more than one compiler and with the same
pthreads implementation for each. There can certainly be such systems,
but there don't have to be. If the compiler and thread library are
provided as a unit, this isn't an issue.

And, unfortunately, the answer to your question is no, it would not
have to be documented. For example, on a Linux system that comes with
NPTL, the NPTL library imposes all sorts of requirements on the
compiler. As far as I can tell, these are not documented anywhere.

DS

David Schwartz

unread,
Jul 25, 2007, 7:38:02 AM7/25/07
to
On Jul 25, 3:48 am, Hallvard B Furuseth <h.b.furus...@usit.uio.no>
wrote:

> David Schwartz writes:

> > You can if and only if the standard doesn't say you can't. You clearly
> > can't do this with mutexes and expect it to work. You can't do it with
> > condition variables.

> Right. Which is also why the mutex and condition functions take
> pointers to such variables, so this fits with how entirely normal C
> functions can work. Not so with functions taking pthread_t.

The argument that POSIX doesn't require an implementation to break
normal C conventions doesn't support the claim that POSIX doesn't
allow it.

> There is no such language in pthread_create.html and pthread_self.html.

I believe this was added to the standard pursuant to a defect report.
Opinions seem to differ about what the standard previously allowed/
required.

> basedefs/sys/types.h.html, issue 6, notes that pthread_t need not be an
> _arithmetic_ type, so it can be a structure. It does not go on to
> mention it might be a magical non-C-like type.

It did not need to note that. Nowhere does it say it has to be
anything in specific. The presence of one warning should not be taken
as an indication that things not warned about specifically cannot
happen.

> > However, there is a
> > 'pthread_equal' function and there is no 'pthread_copy' function.
>
> Yes, the rationale in susv3/functions/pthread_equal.html explains that
>
> "Implementations may choose to define a thread ID as a structure. (...)
> Since the C language does not support comparison on structure types, the
> pthread_equal() function is provided to compare thread IDs."
>
> The C language does support struct assignment, so pthread_copy() is
> not needed.

Right, POSIX certainly didn't want to require compiler extensions. But
it certainly didn't want to prohibit them either as they are required
on some platforms to support threads. (Try to do it on x86 without
inline assembly.)

> > There is no reason a
> > destructor couldn't be one of the extensions a particular pthreads
> > implementations uses/requires.

> If the implementation manages to do it magically enough for the visible
> behavior to follow the C standard, or if it documents that this aspect
> does not follow the C standard, that's true.

The visible behavior of pthreads does not have to follow the C
standard. In fact, it cannot.

> > (...)
> > You cannot synthesize guarantees from one standard based on
> > limitations in another standard.

> You can when the first standard is defined in terms of the second -
> unless it says that it only looks like it is, but actually isn't.
> What a brief grep (in a local copy of the HTML pages) found about that
> is this from basedefs/xbd_chap01.html:
>
> "With each function or header from the ISO C standard, a statement to
> the effect that "any conflict is unintentional" is included. That is
> intended to refer to a direct conflict. IEEE Std 1003.1-2001 acts in
> part as a profile of the ISO C standard, and it may choose to further
> constrain behaviors allowed to vary by the ISO C standard. Such
> limitations are not considered conflicts.
>
> Where additional semantics apply to a function or header, the material
> is identified by use of the CX margin legend."

I'm not sure what you think this buys you. Look, a pthreads
implementation may or may not be written in C, so it may or may not
follow C rules. All that's required to work is what the standard says
must work. You cannot infer from the C standard that other things must
work just because the C standard provides no way to make them not
work.

The standard states the guarantees that you have.

Consider:
int i;
i=1;
if(i==0) printf("foo\n");

You can probably synthesize from the C standard that this code will
never print 'foo'. Yet in pthreads code, it might. An access from
another thread renders the result of the comparison undefined.

You cannot "import" guarantees from one standard into the
implementation of another standard. You cannot argue that pthreads
functions must work a specific way just because the C standard
provides no way for them to work otherwise.

DS

David Schwartz

unread,
Jul 25, 2007, 7:42:25 AM7/25/07
to

For the record, it looks like the 2004 version of the standard made
the warnings in the FAQ obsolete. The warning about copying no longer
appears with respect to pthread_t's but only with respect to
semaphores and mutexes. I think it doesn't appear for condition
variables, which is really odd.

DS

Hallvard B Furuseth

unread,
Jul 25, 2007, 7:53:03 AM7/25/07
to
David Schwartz writes:
> On Jul 24, 10:10 pm, Hallvard B Furuseth <h.b.furus...@usit.uio.no>
> wrote:
>
>>> You mean *normal* C functions. But there is no rule that says that the
>>> pthread_* functions must be normal C functions.
>
>> The Posix standard just calls them functions, as far as I can tell.
>> Where is this different from all the other functions in the standard?
>> Or can't other Posix functions be relied on to be normal functions
>> either.
>
> Any apparent function that has specific caveats cannot be relied upon
> to act like a normal function.

True enough - about the documented caveats. I don't think there is such
a caveat for copying pthread_t, but see my other message for that.

(BTW, I found one general caveat - they can be macros. But these
evaluate each argument once and can be suppressed to get at the
equivalent function. IIRC the C standards says roughly the same thing
of its functions.)

> (...)


> What you cannot do is combine implementation limits from one standard
> to get shoehorn guarantees out of another.

I disagree, but I'll leave that to my other message too.

Please see the rest of this message in view of that. Hopefully we won't
get too many duplicated points that way.

>> Or are you saying that one cannot even _store_ the result from
>> pthread_self(), even though there is no warning about that in the
>> pthread_self() definition? Or has there been a warning about that in a
>> draft, but not in the final version?
>
> No, you can store the result of pthread_self in a pthread_t. However,
> assigning a new value to the same pthread_t might destroy the original
> value such that a copy of it now points to something that was
> destroyed.

If pthread_t can be as magical as you say, and the magic is very broken.
Also you need a compiler which maintains run-time type information, so
it will know to run destructors when you free() a memory area in which
you have put the pthread_t from pthread_self() or pthread_create().
Handling a union which contains a pthread_t would be fun too.

>> (...)


>> If you are not even talking about it referring back to itself, I'm
>> _really_ at loss about how this could be different from having two
>> pointers to some other object.
>
> Two pointers to any object can cause confusion and problems if the
> object is modified through one pointer while the other pointer is
> expected to refer to the same thing all along.

Sure. Nothing un-C-like about that.

> Imagine if a pthread_t worked like many string classes do.

C++ classes with destructors I take it.

> If you copy a pthread_t, they may both winding up pointing to the same
> place, causing a reassignment of one to change the value of the other.

Well, it changes the value the other points to.

> Consider:
> int *i, *j;
> i=malloc(sizeof(void *));
> j=malloc(sizeof(void *));
>
> i=GetSomeValue();
> SomeFunction(i);
> j=i;
> i=GetSomeOtherValue(); // oops, we just broke j

Huh? We just made a memory leak with 'i=j;', that's all. The
equivalent memory leak with pthread_t is to overwrite the pthread_t from
pthread_create(), then keep the thread joinable and exit from it,
without saving a pthread_t value which can be pthread_join()ed.

And if you mean the program is now supposed believe it can do free(i);
free(j); and free the values from the original mallocs, that's a bug,
yes. Quite equivalent to the bug you get if you replace int* with
pthread_t, malloc with pthread_create, and free with pthread_join.
Nothing strange here.

> Under the hood, each 'pthread_t' could have some memory allocated to
> it to copy the information needed to identify a thread. Setting one
> pthread_t equal to another could cause them to point to the same
> structure, causing a subsequent chance of one to change the other.

I think you are talking about a broken non-C-like magical type, but if
that's valid then yes:

> (...)


> I am assuming the implementation uses destructors.

Take a look at the magic C++ needed to combine C-like pass-by-value with
destructors. Unless optimized away it runs a _constructor_ when passing
the value, matching the destructor which is run when leaving the
function. Similarly magic in assignment operators is neede do magic.
One sane implementation of what you describe would be for the
constructor and assignment operator to increment a reference count in
the structure the pthread_t points to, and the destructor and assignment
operator to decrement the old structure's refcount and only destroy when
it reaches zero.

Consider
pthread_t tid;
if (condition unpredictable to the compiler)
tid = pthread_self();
(void) pthread_self();

With the first statement, the implementation must initialize the
pthread_t to some value which prevents the destructor from destroying
anything. With the pthread_self() calls it needs to know that the
result from the first should be destroyed implicitly and the result
from the second should be saved so tid's destructor can destroy it.
So the compiler needs quite a bit of C++-like intelligence anyway.
Also, like I mentioned, it needs to know when to run destructors for
malloced memory and tids in unions.

--
Hallvard

Hallvard B Furuseth

unread,
Jul 25, 2007, 8:27:43 AM7/25/07
to
David Schwartz writes:
> On Jul 25, 3:48 am, Hallvard B Furuseth <h.b.furus...@usit.uio.no>
> wrote:
>> David Schwartz writes:
>>> You can if and only if the standard doesn't say you can't. You clearly
>>> can't do this with mutexes and expect it to work. You can't do it with
>>> condition variables.
>
>> Right. Which is also why the mutex and condition functions take
>> pointers to such variables, so this fits with how entirely normal C
>> functions can work. Not so with functions taking pthread_t.
>
> The argument that POSIX doesn't require an implementation to break
> normal C conventions doesn't support the claim that POSIX doesn't
> allow it.

I know. What I said above this addressed that. This point and much
else of my message boils down to, I couldn't find the reference you were
speaking of which makes pthread_t copying undefined. Lacking anything
definite, the places I did find where it would be natural to say
anything didn't, and the API does not reflect any such intent either.

>> There is no such language in pthread_create.html and pthread_self.html.
>
> I believe this was added to the standard pursuant to a defect report.
> Opinions seem to differ about what the standard previously allowed/
> required.

I'd like to see that and the older standard you mention in next message,
if anyone knows where to find them (and if it's free).

>>> However, there is a
>>> 'pthread_equal' function and there is no 'pthread_copy' function.
>>
>> Yes, the rationale in susv3/functions/pthread_equal.html explains that
>>
>> "Implementations may choose to define a thread ID as a structure. (...)
>> Since the C language does not support comparison on structure types, the
>> pthread_equal() function is provided to compare thread IDs."
>>
>> The C language does support struct assignment, so pthread_copy() is
>> not needed.
>
> Right, POSIX certainly didn't want to require compiler extensions. But
> it certainly didn't want to prohibit them either as they are required
> on some platforms to support threads. (Try to do it on x86 without
> inline assembly.)

I'm not sure what you mean by that, of course OS features may need
OS-specific stuff. Even Standard C's headers cannot be implemented
without compiler extensions. E.g. offsetof().

But my point was - speaking of inferring things from the API design -
you said the existence of pthread_equal() illustrated your point,
countrary to what its documented rationale says.

>>> There is no reason a
>>> destructor couldn't be one of the extensions a particular pthreads
>>> implementations uses/requires.
>
>> If the implementation manages to do it magically enough for the visible
>> behavior to follow the C standard, or if it documents that this aspect
>> does not follow the C standard, that's true.
>
> The visible behavior of pthreads does not have to follow the C
> standard. In fact, it cannot.

I know. And it documents that. Your other examples to illustrate this
are about how concurrency can break the C standard's guarantees. The
Posix standard documents that.

So I'm looking for where it says pthread_t copying is or was undefined.
And why pthread_self() need not work like C function, despite this not
being mentioned in the pthread_self() documentation (at least not before
the defect report you mentioned).

--
Hallvard

Dave Butenhof

unread,
Jul 25, 2007, 8:41:00 AM7/25/07
to

Yes, the warning does appear for condition variables:

Only cond itself may be used for performing synchronization. The
result of referring to copies of cond in calls to
pthread_cond_wait( ), pthread_cond_timedwait( ),
pthread_cond_signal( ), pthread_cond_broadcast( ), and
pthread_cond_destroy( ) is undefined.

Similar warnings appear for barriers, r/w locks, and spinlocks, as well
as mutexes and semaphores.

I do recall that the CV warning had been inadvertently omitted (or lost
during editing) on an earlier version, but it's in the latest.

SYNCHRONIZATION objects occupy a privileged niche. First, of course it's
too easy to get confused if you copy synchronization objects. Does the
copy retain the state of the source? Does that include, for example, a
queue of waiters? And what does that MEAN? (In practice, if a mutex or
CV contains a wait list queue, a C struct copy would copy the wait queue
head pointer(s), but of course doesn't actually copy the queue, meaning
that they copy's pointers will quickly become outdated and using it
could result in disaster.) Or imagine that the "lock flag" is in the
pthread_mutex_t structure, but the blocking state is in an externally
managed "blocking structure". (That's how the Tru64/VMS implementation
works.) In which case even a copy of a mutex that's unlocked and has no
waiters isn't meaningful. It's NOT the same mutex, because they have
different lock flags, so users of the two copies can't reliably
synchronize with each other; and yet they're also not independent
mutexes that can be used without collision because the copies will share
a single waiters queue.

A pthread_t isn't in the same class. It's an ID, or at best a handle,
representing a thread. It can't "be" the thread, because the standard
requires that there may be multiple coexistent instantiations of a
pthread_t value (pthread_create() and pthread_self(), or two calls to
pthread_self()). So pthread_t may, at best, be a POINTER to "the
thread", and a copy will simply duplicate that pointer.

There's no reason you can't copy a pthread_t, as long as you adhere to
the pthread_t lifetime rules when using the copies. What you CANNOT do
with pthread_t values (copied or otherwise) is to compare them using
C/C++ operators. They're OPAQUE objects and can be compared only using
pthread_equal(). There's no POSIX rule that a pthread_t value for a
given thread as returned by pthread_create() and a pthread_t value for
the same thread as returned by pthread_self() will have identical value;
or even that two successive values returned by pthread_self() for the
same thread are identical.

(While there are many arcane and impractical examples that can be
constructed for such a model, the simplest and most realistic is an
implementation where pthread_t is a structure that contains
"non-germane" fields. Those fields may be uninitialized during
construction, or might be reference counts, or any other bookkeeping
information. Any "straight C" mechanism for comparison, e.g. with
memcmp, would fail to recognize a set of "semantically identical"
pthread_t values.)

David Schwartz

unread,
Jul 25, 2007, 9:57:44 AM7/25/07
to
On Jul 25, 5:41 am, Dave Butenhof <david.buten...@hp.com> wrote:

> There's no reason you can't copy a pthread_t, as long as you adhere to
> the pthread_t lifetime rules when using the copies.

Except that the standard says you can't. At least, it used to, from
the 1996 edition:

"So that portable code can be written that is invariant to this
choice, the standard does not define assignment or equality for this
type."

In other words, unless the standard defines assignment or equality, it
does not exist for a type. It never does this for 'pthread_t'.

"[A]ssignment is not necessarily defined on a given opaque type[.]"

So assignment is not *necessarily* defined on pthread_t. And it is not
actually defined either.

Again, this seems to be fixed in the 2003 version.

DS

Dave Butenhof

unread,
Jul 25, 2007, 10:28:07 AM7/25/07
to
David Schwartz wrote:
> On Jul 25, 5:41 am, Dave Butenhof <david.buten...@hp.com> wrote:
>
>> There's no reason you can't copy a pthread_t, as long as you adhere to
>> the pthread_t lifetime rules when using the copies.
>
> Except that the standard says you can't. At least, it used to, from
> the 1996 edition:
>
> "So that portable code can be written that is invariant to this
> choice, the standard does not define assignment or equality for this
> type."

Maybe -- I'm at home and don't have my archives. But at one point many
of the pthread types were also (erroneously) required to be integers,
and that, too, was fixed.

> In other words, unless the standard defines assignment or equality, it
> does not exist for a type. It never does this for 'pthread_t'.

No; POSIX extends/profiles ISO C and where it doesn't specifically point
out an exception to standard C rules, there is no exception.

> "[A]ssignment is not necessarily defined on a given opaque type[.]"
>
> So assignment is not *necessarily* defined on pthread_t. And it is not
> actually defined either.

Without assignment, you cannot CALL a C function with a by-value
argument of that type, and pthread_t is routinely passed by value. The
"no assignment" types are always passed by reference, and that's critical.

> Again, this seems to be fixed in the 2003 version.

Specifically,

There are no defined comparison or assignment operators for the
following types:

pthread_attr_t
pthread_barrier_t
pthread_barrierattr_t
pthread_cond_t
pthread_condattr_t
pthread_mutex_t
pthread_mutexattr_t
pthread_rwlock_t
pthread_rwlockattr_t
pthread_spinlock_t
OB TRC trace_attr_t

Note that pthread_t does not appear on this list. There IS a defined
comparison operator, and no assignment operator is required.

Note also, specifically, that there is not (nor was there ever intended
to be) something like "only thread itself may be used for [thread
operations]" as specified for the synchronization functions.

Hallvard B Furuseth

unread,
Jul 25, 2007, 12:17:49 PM7/25/07
to
David Schwartz writes:
> And, unfortunately, the answer to your question is no, it would not
> have to be documented. For example, on a Linux system that comes with
> NPTL, the NPTL library imposes all sorts of requirements on the
> compiler. As far as I can tell, these are not documented anywhere.

"The compiler is gcc, without whatever optimizations they will think of
next year"?

--
Hallvard

David Schwartz

unread,
Jul 25, 2007, 5:06:02 PM7/25/07
to
On Jul 25, 7:28 am, Dave Butenhof <david.buten...@hp.com> wrote:

> There are no defined comparison or assignment operators for the
> following types:
>
> pthread_attr_t
> pthread_barrier_t
> pthread_barrierattr_t
> pthread_cond_t
> pthread_condattr_t
> pthread_mutex_t
> pthread_mutexattr_t
> pthread_rwlock_t
> pthread_rwlockattr_t
> pthread_spinlock_t
> OB TRC trace_attr_t

> Note that pthread_t does not appear on this list.

Clearly pthread_t can't be on this list because a comparison operator
is defined.

> There IS a defined
> comparison operator, and no assignment operator is required.

No assignment operator is defined. I don't know what you mean by "no
assignment operator is required". If you mean that regular assignment
has to work even though the standard doesn't say so, I would respond
that if that were true, why shouldn't regular assignment work for
pthread_spinlock_t even though no operator is defined?

DS

Ian Collins

unread,
Jul 25, 2007, 5:33:00 PM7/25/07
to
Isn't the intent of the standard clear form the interface? Any function
that has pthread_t as a parameter pass it by value, any function that
has an object from the above list as a parameter passes the object
through a pointer.

--
Ian Collins.

David Schwartz

unread,
Jul 25, 2007, 7:12:53 PM7/25/07
to
On Jul 25, 9:17 am, Hallvard B Furuseth <h.b.furus...@usit.uio.no>
wrote:

> David Schwartz writes:

The argument was that if a thread library relied on any behavior on
the part of the compiler that wasn't part of the language standard, it
would have to be documented. The fact is, it is *not* documented what
GCC behavior NPTL depends on. At least, I cannot find such
documentation, and it would be a very complex effort to work it out.
Simply put, nobody bothers.

DS

David Schwartz

unread,
Jul 25, 2007, 7:18:54 PM7/25/07
to
On Jul 25, 2:33 pm, Ian Collins <ian-n...@hotmail.com> wrote:

> Isn't the intent of the standard clear form the interface? Any
> function
> that has pthread_t as a parameter pass it by value, any function that
> has an object from the above list as a parameter passes the object
> through a pointer.

You are using the term "pass it by value" to mean two different
things. With respect to what the standard says, you mean that it
invokes the formal C syntax used to pass by value. With respect to
what you can infer from this, you mean that it invokes the formal C
behavior you get when you use that formal syntax.

It is quite clear that this does not have to be the case. See the many
examples earlier in this thread where I show cases where the pthreads
standards allows, often without explicitly saying so, code to have a
very different meaning from what you would normally infer from the C
standard.

For example, consider:

int a;
a=3;
if(a!=3) printf("foo");

The C standard allows you to synthesize the guarantee that this will
never call 'printf'. However, the pthreads standard makes it undefined
behavior if another thread is or might modify 'a' while this function
is taking place.

Similarly, the pthreads standard (the old one) says that when
assignment operators are not defined by the standard, the result of
such assignments is undefined. Assignment operators for 'pthread_t'
are not defined. So you cannot assume any guarantees you synthesize
from the C standard will apply.

I believe this was recognized as a defect in the standard and
repaired. It is now written such that assignment and comparison must
do what one normally expects except in cases where it is noted that
this is not the case. A warning appears for 'pthread_t' comparison but
not for assignment, so we may now safely assume it is safe.

DS

Ian Collins

unread,
Jul 25, 2007, 8:12:49 PM7/25/07
to
David Schwartz wrote:
> On Jul 25, 2:33 pm, Ian Collins <ian-n...@hotmail.com> wrote:
>
>> Isn't the intent of the standard clear form the interface? Any
>> function
>> that has pthread_t as a parameter pass it by value, any function that
>> has an object from the above list as a parameter passes the object
>> through a pointer.
>
> You are using the term "pass it by value" to mean two different
> things. With respect to what the standard says, you mean that it
> invokes the formal C syntax used to pass by value. With respect to
> what you can infer from this, you mean that it invokes the formal C
> behavior you get when you use that formal syntax.
>
That looks like one thing to me, POSIX does not add any exceptions to
the C standard value passing semantics.

I maintain that the intent is clear from the API, objects that can not
be copied are passed through a pointer, those that can are passed by value.

>
> I believe this was recognized as a defect in the standard and
> repaired. It is now written such that assignment and comparison must
> do what one normally expects except in cases where it is noted that
> this is not the case. A warning appears for 'pthread_t' comparison but
> not for assignment, so we may now safely assume it is safe.
>

So what's this thread arguing about?

--
Ian Collins.

Igmar Palsenberg

unread,
Jul 26, 2007, 3:02:43 AM7/26/07
to
Hallvard B Furuseth wrote:
> The comp.programming.threads FAQ, Q15/Q70, says it may be unportable to
> copy a pthread_t. This makes no sense to me, since pthread_t values
> are copied (passed by value) by the pthread API itself: pthread_self(),
> pthread_equal(), pthread_join(), pthread_cancel(), and some others.

The pthread_t structure may contain :

typedef struct pthread_t {
int i;
void *data;
char x;
} pthread_t

The pthread create function malloc() the structure and a seperate space
where *data points to.

You copy the struct, perform an operation, and the struct get's deleted,
along with the data *data points to.

That means your copy is still somewhere, which now points to unallocated
memory. The above can be extended to TLS storage, thread-specific data
in the pthread_t, etc. You can't tell what's in pthread_t, and hence
copying it isn't portable.

> Has pthread_t copying been observed to be a problem with some
> implementation? I'm guessing the FAQ authors have just been a bit too
> enthusiastic about not copying pthread_* types.
>

> Q70 also mentions some other problems, pthread_destroy() on both copies
> or synchronization while doing '=', but that plain bugs in the program,
> no different from e.g. close() the same file descriptor twice or '=' on
> something else which would need a mutex.

close() is close to a syscall, and closing an already closed fd is
defined behaviour, since it returns EBADFD. Since pthread_t can be a
complex structure, it isn't that simple.

Igmar

David Schwartz

unread,
Jul 26, 2007, 4:33:35 PM7/26/07
to
On Jul 25, 5:12 pm, Ian Collins <ian-n...@hotmail.com> wrote:

> That looks like one thing to me, POSIX does not add any exceptions to
> the C standard value passing semantics.

That doesn't mean you can synthesize guarantees. Nothing requires the
POSIX implementation to use standard C, and in fact, it almost has to
not do so. The types defined by the pthreads standard can be, and are
to at least some extent, "magic" as far as the C standard is
concerned.

> So what's this thread arguing about?

At the moment, whether you can synthesize guarantees in one standard
by assuming it must be implemented in accord to another standard just
because it is used in conjunction with that standard.

DS

Douglas Wells

unread,
Jul 26, 2007, 8:41:45 PM7/26/07
to
In article <f87mlr$d3$1...@usenet01.boi.hp.com>,

Dave Butenhof <david.b...@hp.com> writes:
> David Schwartz wrote:
> > On Jul 25, 5:41 am, Dave Butenhof <david.buten...@hp.com> wrote:
> >
> >> There's no reason you can't copy a pthread_t, as long as you adhere to
> >> the pthread_t lifetime rules when using the copies.
> >
> > Except that the standard says you can't. At least, it used to, from
> > the 1996 edition:
> >
> > "So that portable code can be written that is invariant to this
> > choice, the standard does not define assignment or equality for this
> > type."
>
> Maybe -- I'm at home and don't have my archives. But at one point many
> of the pthread types were also (erroneously) required to be integers,
> and that, too, was fixed.

DS, could you provide a reference for this quote, please? I can't find
it for pthread_t in my copy of the 1996 standard. In particular,
the similar quote in section 2.5 does not incorporate pthread_t.

> > In other words, unless the standard defines assignment or equality, it
> > does not exist for a type. It never does this for 'pthread_t'.

But my copy of the standard (9945-1: 1996 -- dated 1996-07-12) does
have a definition for pthread_equal -- in section 16.2.7. I'll
also note that pthread_equal was defined even in P1003.1c/D10
(Sep. 1994; section 16.1.7).

- dmw

--
. Douglas Wells . Connection Technologies .
. Internet: -sp9804- -at - contek.com- .

Marcin 'Qrczak' Kowalczyk

unread,
Jul 28, 2007, 6:22:05 AM7/28/07
to
Dnia 26-07-2007, Cz o godzinie 13:33 -0700, David Schwartz napisał(a):

> That doesn't mean you can synthesize guarantees. Nothing requires the
> POSIX implementation to use standard C, and in fact, it almost has to
> not do so. The types defined by the pthreads standard can be, and are
> to at least some extent, "magic" as far as the C standard is
> concerned.

No, there are not more magic than standard C guarantees unless
explicitly stated.

Copying pthread_t is perfectly legal, it works on all pthread
implementations, and there is no reason why it wouldn't work.

--
__("< Marcin Kowalczyk
\__/ qrc...@knm.org.pl
^^ http://qrnik.knm.org.pl/~qrczak/

Zeljko Vrba

unread,
Jul 28, 2007, 6:39:15 AM7/28/07
to
On 2007-07-28, Marcin 'Qrczak' Kowalczyk <qrc...@knm.org.pl> wrote:
>
> Copying pthread_t is perfectly legal,
>
Do you have a relevant reference?

>
>it works on all pthread implementations,

^^^
How many implementations of pthreads are out there? Dou you have the data for
every single one of them to be in position to make such claim?

>
> and there is no reason why it wouldn't work.
>

There is if the object internally implements techniques such as e.g. reference
counting (for whatever imaginable reason).

Ian Collins

unread,
Jul 28, 2007, 7:34:06 AM7/28/07
to
How can it? It's a C object. Are you claiming that a pthread_t can
never be passed by value?

--
Ian Collins.

Zeljko Vrba

unread,
Jul 28, 2007, 8:37:42 AM7/28/07
to
No, I'm claiming that if you do simple memcpy() on pthread it's internal
state may become inconsistent. You don't even need a reference count, it's
enough that it's a struct containing a pointer. Consider e.g. this case:

typedef struct {
...
void *p;
} pthread_t;

pthread_t orig, copy;

Now you do memcpy(&copy, &orig, sizeof(pthread_t)), and invoke some pthread_*
function afterwards on the original. That function might change p (e.g.
reallocate the storage) and invalidate your copy.

Dave Butenhof

unread,
Jul 28, 2007, 3:46:22 PM7/28/07
to

If you hold a pthread_t value past the thread's termination/detachment,
then, yes; using the pointer might be bad.

But think about this. pthread_self() returns a COPY of a pthread_t
value; it's returned by value, remember? It's a copy. You store that
copy somewhere, and then COPY it onto the stack or register to pass
(again by value) into pthread_join(). You're arguing this might not work
on all implementations. It has to work because C says it works, and
because POSIX doesn't say it won't work -- and POSIX states that ISO C
rules take precedence except where SPECIFIC exceptions are made by
POSIX. And of course in this case the standard depends on it working and
is hardly likely to say it doesn't.

It's perfectly fine to COPY the pthread_t value returned by
pthread_create(), or pthread_self(), somewhere else. In fact, this is
really common practice; e.g.,

int start_pipe(pipe_t *pipe)
{
pthread_t thd;
pthread_create (&thd, NULL, ...);
pipe->tid = thd;
}
int end_pipe(pipe_t *pipe)
{
void* val;
pthread_t thd = pipe->tid;
pthread_cancel(thd);
pthread_join(thd,&val);
}

You want to argue this might not work on some implementations, go ahead;
but I'm sure not going to. As far as I'm concerned, anywhere this
doesn't work isn't POSIX threads. Or maybe even "C". ;-)

It's been pointed out that there's a pthread_equal() but no
pthread_copy(). Yeah. That's because C is a bit odd in that it can COPY
a struct, but can't COMPARE a struct. Weird. But pthread_t must be
something that's copyable, by definition. For example, it can't be an
array type because C can't even COPY arrays. We had no interest in
allowing pthread_t to be an array (that doesn't even make much sense),
but we really did want to let it be a struct, and not (at least
entirely) because I'd already coded it that way for DCE threads.

pthread_t must be copy-able by standard C; especially for pass and
return by value function call semantics... but that means anywhere.

Chris Thomasson

unread,
Jul 28, 2007, 3:22:46 AM7/28/07
to
"Dave Butenhof" <david.b...@hp.com> wrote in message
news:f8g6ee$oe3$1...@usenet01.boi.hp.com...
[...]


> It's perfectly fine to COPY the pthread_t value returned by
> pthread_create(), or pthread_self(), somewhere else. In fact, this is
> really common practice; e.g.,
>
> int start_pipe(pipe_t *pipe)
> {
> pthread_t thd;
> pthread_create (&thd, NULL, ...);
> pipe->tid = thd;
> }
> int end_pipe(pipe_t *pipe)
> {
> void* val;
> pthread_t thd = pipe->tid;
> pthread_cancel(thd);
> pthread_join(thd,&val);
> }
>
> You want to argue this might not work on some implementations, go ahead;
> but I'm sure not going to. As far as I'm concerned, anywhere this doesn't
> work isn't POSIX threads. Or maybe even "C". ;-)

[...]

That code snippet pretty much says it all... :^)


Humm... For some reason I think the term _copy_ was being confused with
taking an actual reference to a pthread_t by incrementing a "counter"
somewhere. I think this discussion were confused that the lifetime of the
underlying pthread_t object is not effected when you making a simple C-style
copy of the struct. The rules are simple, you can't use any copy of a
pthread_t struct after the actual thread it really represents has been
completely joined.

0 new messages