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
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;
}