Search code examples
clinuxmultithreadingpthread-join

can we define a variable before calling pthread-create() function


i try to test multithreading in c language and on Manjaro OS. i write a small code but i faced a strange problem

i execute below simple code but i didn't get expected result:

#include <stdio.h>
#include <string.h>
#include <pthread.h>
#include <semaphore.h>
#define THREADS 4

void *routine1(void * x)
{
    int result =2;
    pthread_exit((void *)result);
}

int main ()
{
    int sum=0;
    int retval=0;
   pthread_t threads[THREADS];
    for ( int i=0;i<THREADS;i++)
        pthread_create(&threads[i], NULL, routine1, (void *)i );
   for (int i=0; i<THREADS; i++)
   {
     pthread_join(threads[i],&retval);
     sum+=retval;
   }
   printf("%d\n",sum);
   return 0;

}

the result of above code is:

2

but i expected to see 8 value on output. after debugging for some hours i found that if i declare the sum variable after pthread_create() function, the code will run normally:

#include <stdio.h>
#include <string.h>
#include <pthread.h>
#include <semaphore.h>
#define THREADS 4

void *routine1(void * x)
{
    int result =2;
    pthread_exit((void *)result);
}

int main ()
{
    int retval=0;
   pthread_t threads[THREADS];
   for ( int i=0;i<THREADS;i++)
   pthread_create(&threads[i], NULL, routine1, (void *)i );
   int sum=0;
   for (int i=0; i<THREADS; i++)
   {
        pthread_join(threads[i],&retval);
        sum+=retval;
   }
   printf("%d\n",sum);
   return 0;
}

the output of code is:

 8

which is right answer.

i want to know why the first code is wrong? it is because of pthread_create() function?

NOTE: if you run this code don't care about warnings, they are all about casting


Solution

  • No, it's not the order. It's the type of retval. When, passing &retval to pthread_join, it's the wrong type/sizeof.

    When retval is an int, it's only 4 bytes, but [assuming 64 bit compile], pthread_join expects a pointer to void *, which is 8 bytes.

    This causes undefined behavior because the call will write 8 bytes into a variable that is only 4 bytes. The remaining 4 bytes go beyond the size of retval and may overwrite anything on the stack.

    There was the same undefined behavior in your second example, but we just "got lucky" and got the expected result [probably because the order of variables in the stack frame changed to avoid the UB from producing an unexpected result]. But, both still had UB.

    Also, when casting to/from a pointer, we should use long instead of int [to prevent compiler warnings].

    Here's the corrected code:

    #include <stdio.h>
    #include <string.h>
    #include <pthread.h>
    #include <semaphore.h>
    #define THREADS 4
    
    void *
    routine1(void *x)
    {
    #if 0
        int result = 2;
    #else
        long result = 2;
    #endif
    
        pthread_exit((void *) result);
    }
    
    int
    main()
    {
        int sum = 0;
    #if 0
        int retval = 0;
    #else
        void *retval;
    #endif
        pthread_t threads[THREADS];
    
    #if 0
        for (int i = 0; i < THREADS; i++)
            pthread_create(&threads[i], NULL, routine1, (void *) i);
    #else
        for (long i = 0; i < THREADS; i++)
            pthread_create(&threads[i], NULL, routine1, (void *) i);
    #endif
    
        for (int i = 0; i < THREADS; i++) {
            pthread_join(threads[i], &retval);
    #if 0
            sum += retval;
    #else
            sum += (long) retval;
    #endif
        }
    
        printf("%d\n", sum);
    
        return 0;
    
    }