Search code examples
cpthreadspthread-join

Value of the variable passed as reference gets changed in threads


I wrote this small program for understanding pthread_create and pthread_join but I dont understand why the value of the variable data gets altered after thread_join. Its printed as 0 after the call to pthread functions.

#include <pthread.h>
#include <stdio.h>
void* compute_prime (void* arg)
{
        int n = *((int*) arg);
        printf("Argument passed is %d\n",n);
        return (void *)n;
}
int main ()
{
        pthread_t thread;
        int data = 5000;
        int value=0;

        pthread_create (&thread, NULL, &compute_prime, &data);
        pthread_join (thread, (void*) &value);
        printf("The number is %d and return value is %d.\n", data, value);
        return 0;
}

And the output is

Argument passed is 5000
The number is 0 and return value is 5000.

Solution

  • This is because pthread_join has the prototype void **. It expects a pointer to an object of type void *; it modifies this void * object. Now it so happens that you're running on a 64-bit arch, where data and value are both 32 bits, and laid out in memory sequentially. Modifying a void * object that is laid out in memory starting from the address of the int object value clobbers the int object data too, as these are both 32 bits - thus you will see that the data was overwritten. However, this is undefined behaviour so anything might happen.


    Don't use casts as they will only serve to hide problems. Casting to a void * is especially dangerous as a void * can be converted to any other type implicitly, even to void **, even though usually it too would be incorrect. If you remove the cast, you most probably will get a warning or an error like

    thrtest.c: In function ‘main’:
    thrtest.c:16:31: error: passing argument 2 of ‘pthread_join’ from 
                    incompatible pointer type [-Werror=incompatible-pointer-types]
             pthread_join (thread, &value);
    

    if compiling with warnings enabled.


    If however you really want to do this, you must do it like this:

    void *value;
    pthread_join(thread, &value);
    intptr_t value_as_int = (intptr_t)value;
    

    Though it is not really portable either, as the conversion is implementation-defined.

    The most portable way to return an integer is to return a pointer to a mallocated object:

    int *return_value = malloc(sizeof (int));
    *return_value = 42;
    return return_value;
    
    ...
    
    void *value;
    pthread_join(thread, &value);
    int actual = *(int *)value;
    free(value);