Search code examples
cmultithreadingprintfmutex

How to prevent race condition when multiple threads are writing in the same file descriptor in C?


I have the following function that will run in the thread:

void    *dinning_handler(void *arg)
{
    t_philo         *philo;
    struct timeval  start;

    philo = (t_philo *)arg;
    gettimeofday(&start, NULL);
    philo->last_meal_time = start;
    while (philo->max_eats == -1 || philo->eats < philo->max_eats)
    {
        print_blue("is thinking", philo->id, get_ts_in_ms());
        pthread_mutex_lock(philo->left_fork);
        pthread_mutex_lock(philo->right_fork);
        print_blue("has taken a fork", philo->id, get_ts_in_ms());
        print_green("is eating", philo->id, get_ts_in_ms());
        usleep(philo->time_to_eat * 1000);
        philo->eats++;
        gettimeofday(&philo->last_meal_time, NULL);
        pthread_mutex_unlock(philo->left_fork);
        pthread_mutex_unlock(philo->right_fork);
        print_blue("is sleeping", philo->id, get_ts_in_ms());
        usleep(philo->time_to_sleep * 1000);
    }
    return (NULL);
}

each of the print functions will have the following format:

void    print_red(char **msg, int id, long time)
{
    ft_printf("\033[1;31m");
    ft_printf("%u %d %s\n", time, id, msg);
    ft_printf("\033[0m");
}

This generate a race condition leading to write wrong values in the terminal. If I replace ft_print(which is a self implementation that suppose to work the same way of printf) for the original printf it works fine. Why? do printf use mutex before print? how can I fix my code?

EDIT:

Link for ft_printf implementation, it is too big to put it here


Solution

  • If I replace ft_print for the original printf it works fine. Why?

    POSIX requires printf to be thread-safe.

    But a sequence of printf statements is not atomic, so you also got lucky.

    You need to ensure the group of printf calls are executed atomically. In this case, the best solution is to combine them into one.

    printf(
       "\033[1;31m"
       "%u %d %s\n"
       "\033[0m",
       time, id, msg
    );
    

    how can I fix my code?

    As with printf, you need to make the group of calls to ft_printf atomic. Yes, you could use a mutex to achieve this.

    // Lock the mutex here.
    ft_printf( "\033[1;31m" );
    ft_printf( "%u %d %s\n", time, id, msg );
    ft_printf( "\033[0m" );
    // Unlock it here.