Home > Software design >  Not printing a formatted string affects the value of a variable in my multithreaded program
Not printing a formatted string affects the value of a variable in my multithreaded program

Time:05-12

Removing a printf statement affects the value of "i" in the loop where the program creates the child threads. I've tried printing a non-formatted string, and it breaks the program. Moving the printf above pthread_create() causes the same result. However, printing any formatted string after creating a thread will "fix" the program. Does anyone know what I've done wrong?

#include <stdio.h>
#include <pthread.h>

#define FALSE 0
#define TRUE !FALSE

pthread_mutex_t mutex;
pthread_cond_t cond1;
pthread_cond_t cond2;

int flag1 = FALSE;
int flag2 = FALSE;

void* someFunction(void* arg);
void anotherFunction(int threadNumber);

int main(int argc, char* argv[]) 
{
    pthread_t threads[6];

    pthread_mutex_init(&mutex, NULL);
    pthread_cond_init(&cond1, NULL);
    pthread_cond_init(&cond2, NULL);

    for (int i = 0; i < 6; i  )
    {
        // moving the printf to here will cause the program to hang
        pthread_create(&threads[i], NULL, &someFunction, &i);
        // not printing a formatted string here will cause the program to hang
        printf("Thread %d created\n", i);
    }
    
    pthread_mutex_lock(&mutex);

    for (int i = 0; i < 6; i  )
    {
        flag1 = TRUE;
        // go to child thread
        pthread_cond_signal(&cond1);

        // wait for child thread to change flag2
        while (flag2 == FALSE)
        {
            pthread_cond_wait(&cond2, &mutex);
        }
        
        flag2 = FALSE;
        pthread_mutex_unlock(&mutex);
    }

    pthread_mutex_destroy(&mutex);
    pthread_cond_destroy(&cond1);
    pthread_cond_destroy(&cond2);

    return 0;
}

void* someFunction(void* arg)
{
    int threadNumber = *(int*)arg;

    pthread_mutex_lock(&mutex);

    // wait for parent thread to change flag1
    while (flag1 == FALSE)
    {
        pthread_cond_wait(&cond1, &mutex);
    }

    anotherFunction(threadNumber);
    flag2 = TRUE;

    // go back to parent thread
    pthread_cond_signal(&cond2);
    pthread_mutex_unlock(&mutex);

    return NULL;
}

void anotherFunction(int threadNumber)
{
    if (threadNumber == 0)
    {
        printf("I'm thread %d!\n", threadNumber);
    }
    else if (threadNumber == 1)
    {        
        printf("I'm thread %d!\n", threadNumber);
    }
    else if (threadNumber == 2)
    {       
        printf("I'm thread %d!\n", threadNumber);
    }
    else if (threadNumber == 3)
    {        
        printf("I'm thread %d!\n", threadNumber);
    }
    else if (threadNumber == 4)
    {       
        printf("I'm thread %d!\n", threadNumber);
    }
    else if (threadNumber == 5)
    {        
        printf("I'm thread %d!\n", threadNumber);
    }
}

This is the output when printing a formatted string.

Thread 0 created
Thread 1 created
Thread 2 created
Thread 3 created
Thread 4 created
Thread 5 created
I'm thread 0!
I'm thread 1!
I'm thread 2!
I'm thread 3!
I'm thread 4!
I'm thread 5!

This is the output when removing the printf

I'm thread 1!
I'm thread 2!
I'm thread 3!
I'm thread 4!
I'm thread 5!

The program just hangs after printing "I'm thread 5!".

EDIT: Thanks for the replies guys! Here is the solution

    for (int i = 0; i < 6; i  )
    {
        int* copy = malloc(sizeof(int));
        *copy = i;
        // moving the printf to here will cause the program to hang
        pthread_create(&threads[i], NULL, &someFunction, copy);
        // not printing a formatted string here will cause the program to hang
        printf("Thread %d created\n", i);
    }

CodePudding user response:

Here you pass the address of a local variable i to the thread functions. This variable goes out of scope at least when the for loop ends (or maybe even at the end of each iteration). This means every access to the variable by the threads after the end of the loop (or after the end of the loop iteration) is undefined behavior.

    for (int i = 0; i < 6; i  )
    {
        // moving the printf to here will cause the program to hang
        pthread_create(&threads[i], NULL, &someFunction, &i);
        // not printing a formatted string here will cause the program to hang
        // printf("Thread %d created\n", i);
    }

Your program will report this error when you compile the program with GCC using options -fsanitize=address -fsanitize=undefined.

Even if you fix the scope of the variable, e.g. by moving it into function main's scope, you pass the address of the same variable to all threads, so it is undefined which value (0..6) every thread may see. If the thread creation is faster than actually starting the thread, all threads might see the same value 6.

(As noted in a comment, you might even see other values because of the data race. If the access to the variable is atomic on your platform or if the loop modifies only the least significant byte as in this example, you will typically get one of the values 0..6.)

CodePudding user response:

Thanks for the replies guys! The solution was to dynamically allocate some memory for a copy of i

for (int i = 0; i < 6; i  )
{
    int* copy = malloc(sizeof(int));
    *copy = i;
    // moving the printf to here will cause the program to hang
    pthread_create(&threads[i], NULL, &someFunction, copy);
    // not printing a formatted string here will cause the program to hang
    printf("Thread %d created\n", i);
}
  • Related