Is this kosher...

Is this kosher...

Post by Chris Thom » Mon, 22 Oct 2007 16:22:32


typedef struct foo_s foo;

struct foo_s {
pthread_mutex_t mutex;
[...];
};


foo* foo_create(void) {
foo* const _this = malloc(sizeof(*_this_));
if (_this) {
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
_this->mutex = mutex;
}
return _this;
}


?
 
 
 

Is this kosher...

Post by Gianni Mar » Mon, 22 Oct 2007 20:54:30


static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

is better.

 
 
 

Is this kosher...

Post by Logan Sha » Mon, 22 Oct 2007 23:40:53


I see your point, but it seems quite possible (likely?) that the
compiler will see that the local variable is used as a temporary
variable and eliminate it. The variable is written to in exactly
one place, is read in exactly one place, and goes out of scope
immediately afterwards. Constant propagation should be able to
take care of that pretty easily.

Of course, it seems equally clear to just write it as this:

_this->mutex = PTHREAD_MUTEX_INITIALIZER;

And that avoids the issue.

- Logan
 
 
 

Is this kosher...

Post by Eric Sosma » Tue, 23 Oct 2007 01:04:59


... if it compiles. On one system, the macro expands
(after a few intermediate stages) to

{{0, 0, 0, 0, 0x4d58}, {{{0}}}, 0}

... which is not valid as the r.h.s. of an assignment.

--
Eric Sosman
XXXX@XXXXX.COM
 
 
 

Is this kosher...

Post by Logan Sha » Tue, 23 Oct 2007 04:20:40


... but is valid as the value in an initialization, I guess?

C catches me off guard sometimes. I make assumptions like that
anything valid in an initializer would be valid in an assignment,
since in both cases the compiler has pretty much the same
information available (that is, the type that it should expect
the expression to be). But assumptions can cause trouble.

- Logan
 
 
 

Is this kosher...

Post by Eric Sosma » Tue, 23 Oct 2007 05:28:59


Not kosher, or at any rate not "strictly kosher." The
description of PTHREAD_MUTEX_INITIALIZER macro specifically
calls for "mutexes that are statically allocated" (Open Group
Base Specifications). You're trying to initialize an auto
mutex and then copy it to a dynamically-allocated mutex, and
that's not covered in the specification. It is conceivable
that PTHREAD_MUTEX_INITIALIZER could use some kind of compiler
magic to get the linker or loader to put the finishing touches
on the initialization, and that wouldn't work at run time after
the linker/loader had already come and gone.

On the other hand, it's "very likely to work," at least
up to a point. There's no reasonable[*] way for the Pthreads
functions to determine the C "storage class" of a mutex: auto
and dynamic and static all look the same. The first time
pthread_mutex_lock() encounters the mutex, it will see something
that looks and feels exactly like a statically-initialized mutex,
and will be happy with it. (Barring the aforementioned compiler
magic, of course.)

However, one need not resort to magic to think of reasons
why it might not be safe to free() such a mutex, and calling
pthread_mutex_destroy() on it would really be taking your life
in your hands. Considering the difficulty of getting rid of
this dynamic mutex after it's been created, and the odor of
un-sanctity hovering about the whole business, why not just
call pthread_mutex_init() and live in blessedness?

[*] An implementation might snoop through the address
mappings to discover whether the mutex was in a text, heap,
or stack segment -- but that's not 100% reliable, so I find
it hard to imagine that Pthreads would care to do the snooping
in the first place.

--
Eric Sosman
XXXX@XXXXX.COM
 
 
 

Is this kosher...

Post by Logan Sha » Tue, 23 Oct 2007 06:17:43


Well, if that's what the spec says, about all you can say is
"probably", because it would not be inconceivable that the
implementation could tell the difference.

For example, the pthread mutex struct could contain some code
that needs to be copied in from the initializer (maybe a jump
instruction) and you could be on a Harvard architecture machine
(by which I mean separate instruction and data caches). Then
the pthreads implementation could satisfy the spec by making
sure there is a cache flush sometime after all the static
variables are initialized but before they are used. That would
make the static mutexes behave nicely, but ones you allocated
yourself (either in stack or heap) would sometimes write stuff
into the data cache and it would sometimes not be visible to the
instruction cache (since it wouldn't have been flushed out and
there is no cache coherency guarantee between the two, totally-
separate caches), causing garbage to be treated as machine code.

I only mention this because I ran into a similar situation (not
with posix mutexes but with loading some code of my own) in the
past and there were many random, "unexplainable" crashes until
I finally figured out what was going on.

The m *** of the story is, don't do stuff like that. :-)

- Logan
 
 
 

Is this kosher...

Post by Dave Buten » Tue, 23 Oct 2007 18:39:50


The static initializer macro can only legally be used for statically
allocated storage. That's already been pointed out in a reply.

However, what I don't see pointed out is that COPYING a mutex is always
illegal. The mutex operations are specified to work only on an actual
mutex, not on a copy of a mutex. Specific quote:

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.

Most simply, a pthread_mutex_t may contain pointers to other storage;
and that storage might even have links back to the pthread_mutex_t. Your
"copy" might not work at all, or it might "appear to partly work" but
exhibit really *** failure modes.

If you need a dynamically allocated mutex, use pthread_mutex_init() to
initialize it.