views:

94

answers:

4

I have a question about variable scope and memory management in C. I am writing a program that listens for a socket connection and then launches a new thread to handle that client. The main while() loop can launch many separate threads. My question is this:

If I don't use dynamic memory allocation [no malloc()], and instead have a variable locally declared as shown below, is it safe to use that variable in the called thread, even after the next iteration of the loop takes place?

while(1)
{
    // Accept new socket connection here
    // ...

    pthread_t pt;
    struct mypthreadargs args;

    rc = pthread_create(&pt, NULL, handle_client, &args);
    // The handle_client() function makes extensive use of the 'args' variable
}

What happens to the args variable (and the pt variable, too, for that matter) after the thread has been created. Is the memory lost once the while() loop starts over, or is it safe to use them as I have?

Thanks!

+2  A: 

Once every iteration is done, you have lost your ownership of pt and args. Accessing them is undefined behavior.

To address the comment, you could define an array of both types outside the loop, or make them global:

pthread_t pt[...];
struct mypthreadargs args[...];
while(1)
{
...
}

Of course, you have to make sure that you don't leave that scope before your threads are done.

AraK
Is there a way around this, other than using malloc()? I'd like to stick to variables on the stack if I can.
Tom
@Tom: Why? The stack isn't the proper place for data that's getting passed between threads.
Jherico
Make an array of structs, each thread having their own entry in that array. Before this function returns, make sure you `wait` for each thread to finish.
Anon.
+1  A: 

No, you don't want to do this.

It may or may not work, depending on exactly how this all gets invoked -- realistically, on most architectures, with most compilers, the memory pointed to by &args will continue to be valid, but you will most likely end up with multiple threads using the same myptreadargs struct, which you probably don't want (the actual behavior is undefined, this is only a likely outcome).

Instead, malloc( ) the struct mypthreadargs.

If you really want a way around this, and you're willing to live with a maximum number of threads in flight at once, you could set up an array of args structs and use a new one for each iteration, and mark them as available when the called threads are done with them. At that point, you're basically reimplementing a limited malloc( ), however, so I would question the wisdom of the exercise.

Stephen Canon
I guess I just don't type fast enough. Your point about 'on most architectures' isn't really valid though, imo. 9 times out of 10 in a tight loop, by the time he accesses the passed args object, it will be populated with data from some later iteration of the loop that is spawning threads. (or random data from the stack if the loop has already been exited.
Jherico
Agreed. I only intend to point out that the struct will probably continue to be a valid memory location that is readable by the thread, not that it will contain the data that he wants it to hold. In some ways, this is worse than if it were just invalidated and the program would crash -- then it would be totally clear that this is a bad idea. =)
Stephen Canon
A: 

This is incredibly unsafe. As soon as your thread is created and the next loop executes, then the mpthreadargs will likely be populated with data from the next iteration, not the data you expected to be passed to the thread.

If you don't want to use heap allocation, you need to create the variable in a scope that will outlive the thread. For instance, create an array of mpthreadargs object in a global scope sized to the number of threads you will need. If you don't know the maximum size of threads, then you pretty much have to use heap allocation.

Why are you averse to the use of malloc anyway?

Jherico
Thank you all for your answers! I was unsure of my coding practices and wanted some input.I was trying to avoid malloc() because I haven't been keeping track of the threads, so I don't (yet) have a proper place to free() the memory. By using malloc(), I need to either:a) Keep track of all threads created so that I can free() the pthread_t and mypthreadargs after the thread exits, orb) Set a limit on how many threads I will create and statically create an array for that many threads.I'll just have to keep track of threads, afterall.Thanks for the help!
Tom
A: 

When you pass the address of some data to a thread, that data's lifetime becomes unrelated to execution of the parent thread, so your only real choice is to allocate the memory for that data in way that decouples its lifetime from execution of the parent thread -- i.e. allocate with something like malloc or calloc.

Jerry Coffin