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

Is pthread_create( id, 0, f, &something_on_my_stack ); safe?

30 views
Skip to first unread message

Johann 'Myrkraverk' Oskarsson

unread,
Nov 20, 2011, 10:29:05 PM11/20/11
to
Hi all,

Given

void *f( void *j )
{
int i = *((int *)j);
//..
}

void g( void )
{
pthread_t *id = ...;
int something_on_my_stack = 42; // Life, universe & everything
pthread_create( id, 0, f, &something_on_my_stack );

is there any guarantee the value has been copied to f()s stack by the
time pthread_create() returns?

Or, to be a little bit more explicit, is there anything I can use for
safe argument passing to newly created threads without allocating new
memory and (presumably) have the threads free it?

My current design calls for two integers passed to the newly created
threads. I want something both safe and clean. That is, minimum of
code clutter.


--
Johann Oskarsson http://www.2ndquadrant.com/ |[]
PostgreSQL Development, 24x7 Support, Training and Services --+--
|
Blog: http://my.opera.com/myrkraverk/blog/

Ian Collins

unread,
Nov 21, 2011, 2:00:35 AM11/21/11
to
On 11/21/11 04:29 PM, Johann 'Myrkraverk' Oskarsson wrote:
> Hi all,
>
> Given
>
> void *f( void *j )
> {
> int i = *((int *)j);
> //..
> }
>
> void g( void )
> {
> pthread_t *id = ...;
> int something_on_my_stack = 42; // Life, universe& everything
> pthread_create( id, 0, f,&something_on_my_stack );
>
> is there any guarantee the value has been copied to f()s stack by the
> time pthread_create() returns?

No.

> Or, to be a little bit more explicit, is there anything I can use for
> safe argument passing to newly created threads without allocating new
> memory and (presumably) have the threads free it?

I can't see any problem with

static int something_on_my_stack = 42;

--
Ian Collins

Marcel Müller

unread,
Nov 21, 2011, 2:48:58 AM11/21/11
to
On 21.11.2011 08:00, Ian Collins wrote:
> I can't see any problem with
>
> static int something_on_my_stack = 42;

Except that is no longer reentrant and you cannot run more than one
thread in parallel this way.

The common way is to allocate something on the heap.


Marcel

Johann 'Myrkraverk' Oskarsson

unread,
Nov 21, 2011, 2:56:50 AM11/21/11
to
Ian Collins <ian-...@hotmail.com> writes:

Thank you.
Unfortunately, the numbers are retrieved in a loop. To be extra ex-
plicit, file descriptors from accept(), pipe() and friends. I can't
see any way that does not involve either 1) memory allocation/freeing
or 2) synchronization. Both seem unnecessary code bloat for such a
simple task.

I did not see it at the first glance at the FAQ.

Ian Collins

unread,
Nov 21, 2011, 3:01:37 AM11/21/11
to
On 11/21/11 08:56 PM, Johann 'Myrkraverk' Oskarsson wrote:
> Ian Collins<ian-...@hotmail.com> writes:
>
> Thank you.
>
>> On 11/21/11 04:29 PM, Johann 'Myrkraverk' Oskarsson wrote:
>>> Or, to be a little bit more explicit, is there anything I can use
>>> for safe argument passing to newly created threads without
>>> allocating new memory and (presumably) have the threads free it?
>>
>> I can't see any problem with
>>
>> static int something_on_my_stack = 42;
>
> Unfortunately, the numbers are retrieved in a loop. To be extra ex-
> plicit, file descriptors from accept(), pipe() and friends. I can't
> see any way that does not involve either 1) memory allocation/freeing
> or 2) synchronization. Both seem unnecessary code bloat for such a
> simple task.

Dynamic allocation is the idiomatic solution for any non-trivial
situation. Sometimes extra complexity is unavoidable.

--
Ian Collins

Robert Wessel

unread,
Nov 21, 2011, 3:28:05 AM11/21/11
to
On Mon, 21 Nov 2011 03:29:05 +0000, Johann 'Myrkraverk' Oskarsson
<joh...@2ndquadrant.com> wrote:

>Hi all,
>
>Given
>
> void *f( void *j )
> {
> int i = *((int *)j);
> //..
> }
>
> void g( void )
> {
> pthread_t *id = ...;
> int something_on_my_stack = 42; // Life, universe & everything
> pthread_create( id, 0, f, &something_on_my_stack );
>
>is there any guarantee the value has been copied to f()s stack by the
>time pthread_create() returns?
>
>Or, to be a little bit more explicit, is there anything I can use for
>safe argument passing to newly created threads without allocating new
>memory and (presumably) have the threads free it?
>
>My current design calls for two integers passed to the newly created
>threads. I want something both safe and clean. That is, minimum of
>code clutter.


Nothing really, in general. Just allocate the memory and pass a
pointer to that. It's simple, it works.

On occasion, it's reasonable to have the created thread post a
semaphore that the creator is waiting on, when the referenced items
are no longer needed. That usually goes with needing to wait until
the created thread has started successfully (and is now ready to do
what ever it is it does), and having a large amount of startup data.
Of course in this case, you're now stuck passing the semaphore plus
the other data to the created thread, and that's going to be more work
than passing a couple of ints.

David Schwartz

unread,
Nov 21, 2011, 3:06:24 PM11/21/11
to
On Nov 20, 11:56 pm, Johann 'Myrkraverk' Oskarsson
<joh...@2ndquadrant.com> wrote:

> Unfortunately, the numbers are retrieved in a loop.  To be extra ex-
> plicit, file descriptors from accept(), pipe() and friends.  I can't
> see any way that does not involve either 1) memory allocation/freeing
> or 2) synchronization.  Both seem unnecessary code bloat for such a
> simple task.

Here are the choices you have rejected:

1) Holding the value around until you are sure the new thread is done
with it (synchronization).

2) Giving the new thread its own copy so it can use it at its leisure
(memory allocating).

Even at a high level of generality, what could a third alternative
look like?

Your son needs a car. Either you share yours with him (option 1) or
you get him his own (option 2). What's the third choice?

DS

Johann 'Myrkraverk' Oskarsson

unread,
Nov 21, 2011, 3:30:45 PM11/21/11
to
David Schwartz <dav...@webmaster.com> writes:

> On Nov 20, 11:56 pm, Johann 'Myrkraverk' Oskarsson
> <joh...@2ndquadrant.com> wrote:
>
>> Unfortunately, the numbers are retrieved in a loop.  To be extra
>> ex- plicit, file descriptors from accept(), pipe() and friends.  I
>> can't see any way that does not involve either 1) memory
>> allocation/freeing or 2) synchronization.  Both seem unnecessary
>> code bloat for such a simple task.
>
> Here are the choices you have rejected:

I did not reject them. Both apply code overhead (as in, I write more
code) I have to maintain. The more code I can get rid of the better.

> Your son needs a car. Either you share yours with him (option 1) or
> you get him his own (option 2). What's the third choice?

He buys his own.

Casper H.S. Dik

unread,
Nov 21, 2011, 6:07:50 PM11/21/11
to
Johann 'Myrkraverk' Oskarsson <joh...@2ndquadrant.com> writes:

>David Schwartz <dav...@webmaster.com> writes:

>> On Nov 20, 11:56 pm, Johann 'Myrkraverk' Oskarsson
>> <joh...@2ndquadrant.com> wrote:
>>
>>> Unfortunately, the numbers are retrieved in a loop.  To be extra
>>> ex- plicit, file descriptors from accept(), pipe() and friends.  I
>>> can't see any way that does not involve either 1) memory
>>> allocation/freeing or 2) synchronization.  Both seem unnecessary
>>> code bloat for such a simple task.
>>
>> Here are the choices you have rejected:

>I did not reject them. Both apply code overhead (as in, I write more
>code) I have to maintain. The more code I can get rid of the better.


If it's only one fd, cast it to a pointer and use the pointer as an
integer; there is really no reason to interpret the pointer as a
pointer, right?

Casper
--

Johann 'Myrkraverk' Oskarsson

unread,
Nov 22, 2011, 12:31:57 AM11/22/11
to
Casper H.S. Dik <Caspe...@OrSPaMcle.COM> writes:

> If it's only one fd, cast it to a pointer and use the pointer as an
> integer; there is really no reason to interpret the pointer as a
> pointer, right?

Alas, it's two or I'd probably do this. I *might* restrict myself to
64bit compilations and encode the two integers in one pointer but
that's just silly and gains me nothing in maintainability.

Or I can "assume" the file descriptors are low and encode them both in
the same integer. But that's even more silly and a maintenance
nightmare.

Ersek, Laszlo

unread,
Nov 22, 2011, 3:06:10 PM11/22/11
to
On Mon, 21 Nov 2011, Johann 'Myrkraverk' Oskarsson wrote:

> Hi all,
>
> Given
>
> void *f( void *j )
> {
> int i = *((int *)j);
> //..
> }
>
> void g( void )
> {
> pthread_t *id = ...;
> int something_on_my_stack = 42; // Life, universe & everything
> pthread_create( id, 0, f, &something_on_my_stack );
>
> is there any guarantee the value has been copied to f()s stack by the
> time pthread_create() returns?
>
> Or, to be a little bit more explicit, is there anything I can use for
> safe argument passing to newly created threads without allocating new
> memory and (presumably) have the threads free it?
>
> My current design calls for two integers passed to the newly created
> threads. I want something both safe and clean. That is, minimum of
> code clutter.

If you don't need more than num_threads threads at any time:

static struct thread_arg
{
int fd1,
fd2;
} thread_arg[num_threads];

If you start a low fixed number of threads per core, this could work. (If
you start threads for code organization purposes, not so much.) Just
before (re-)starting thread #n (0 <= n < num_threads), fill in
thread_arg[n], and pass &thread_arg[n] (written differently, thread_arg +
n) to the start function. If necessary for other purposes, the thread can
derive its own number from the arg address: (struct thread_arg *)arg -
thread_arg.

If num_threads is not known at compile time, perhaps you could afford a
single malloc() for the full array (and store the address in a "global"
pointer). In C99 you might use a VLA that belongs to a block outliving all
worker threads. As in:

struct thread_arg
{
unsigned thread_nr;
int fd1,
fd2;
};

int
main(int argc, char **argv)
{
unsigned num_threads;

get_num_threads(&num_threads, argc, argv);

{
struct thread_arg thread_arg[num_threads];
pthread_t worker[num_threads];
unsigned u;

for (u = 0u; u < num_threads; ++u) {
struct thread_arg *cur;

cur = thread_arg + u;
cur->thread_nr = u;
cur->fd1 = ...;
cur->fd2 = ...;
pthread_create(worker + u, 0, start_func, cur);
}

/* do whatever the "main thread" does */

/* join all workers before thread_arg goes out of scope & life */
for (u = 0u; u < num_threads; ++u) {
pthread_join(worker[u], 0);
}
}

return 0;
}

Laszlo

David Schwartz

unread,
Nov 23, 2011, 3:53:14 PM11/23/11
to
On Nov 21, 12:30 pm, Johann 'Myrkraverk' Oskarsson
<joh...@2ndquadrant.com> wrote:

> > Your son needs a car. Either you share yours with him (option 1) or
> > you get him his own (option 2). What's the third choice?
>
> He buys his own.

Well, yeah, that works in the hypothetical. But that wouldn't work in
this case. He wouldn't know what car to buy unless you wait until he's
finished buying a car, which is the synchronization you rejected.

DS

blmblm.m...@gmail.com

unread,
Nov 25, 2011, 12:49:13 PM11/25/11
to
In article <4ecad9c6$0$6961$e4fe...@news2.news.xs4all.nl>,
There was a discussion of this topic in comp.lang.c recently:

http://groups.google.com/group/comp.lang.c/browse_thread/thread/9ce104bbf47d3fbc/c96cd19501ef372b?hl=en&q=#c96cd19501ef372b

I'm not sure it reached consensus, but the arguments against this
"use the pointer parameter to pass an integer" approach seemed
to boil down to "not guaranteed to work on all platforms" and
"likely to confuse other readers of the code". To me the latter
alone is a good reason to avoid it, but -- <shrug>.

--
B. L. Massingill
ObDisclaimer: I don't speak for my employers; they return the favor.
0 new messages