is there a way to create a thread that will check other thread in C, dinning philosophers implementation

90 views Asked by At

I have a question regarding threads in C, I know that to create a thread the function pthread_create is needed and I'm currently working on the dining philosopher problem and in this implementation of that problem I have to look if a philosopher has died of starvation.

I tested my programs and it works well, but to look if a philosopher has died I create another thread that'll always run and check in it if a philosoper has died. a philosopher will die of starvation if he has not eat during a certain amount of time since his last meal.

Defining the general structure of the program and headers.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <string.h>
#include <sys/time.h>
struct t_phil;
typedef struct t_info t_info;
typedef struct t_phil t_phil;

typedef struct t_info
{
    int num_of_phil;
    t_phil *philo;
    int min_dinner;
    pthread_mutex_t *mutex;
    int plate_eaten;
    int num_of_dead_phil;
    int time_to_die;
    int time_to_sleep;
    int time_to_eat;
    pthread_mutex_t t_mut;
} t_info;

typedef struct t_phil
{
    int number;
    int has_eaten_all;
    int finished_meal;
    int is_dead;
    t_info *data;
    pthread_mutex_t *right;
    pthread_mutex_t *left;
    struct timeval last_dinner;
    pthread_t thread;
} t_phil;
int count = 0;

Here is the function, that'll simulate the dinner, this one works as intended but I am open to possible error or improvement.

void *routine(void *args)
{
    t_phil *philo = (t_phil *)(args);
    int i = philo->number;

    if ((philo->number % 2))
        sleep(1);
    gettimeofday(&philo->last_dinner, 0);
    while (!philo->is_dead)
    {
        pthread_mutex_lock(philo->left);
        printf("Philosopher : %i has take left fork\n", philo->number + 1);
        pthread_mutex_lock(philo->right);
        printf("Philosopher : %i has take right fork\n", philo->number + 1);
        gettimeofday(&philo->last_dinner, 0);
        printf("Philosopher :%i is eating in at %li\n", philo->number + 1, philo->last_dinner.tv_sec * 1000);
        pthread_mutex_lock(&philo->data->t_mut);
        // if (philo->data->num_of_dead_phil && !philo->data->min_dinner)
        //     break;
        if (philo->is_dead)
            break;
        gettimeofday(&philo->last_dinner, NULL);
        philo->finished_meal++;
        if (philo->finished_meal == philo->data->min_dinner)
        {
            philo->data->plate_eaten++;
            philo->has_eaten_all = 1;
        }
        sleep(philo->data->time_to_eat);
        pthread_mutex_unlock(&philo->data->t_mut);
        pthread_mutex_unlock(philo->left);
        pthread_mutex_unlock(philo->right);
        if (philo->has_eaten_all)
            break;
        printf("Philosopher : %i is now sleeping at %li\n", philo->number + 1, philo->last_dinner.tv_sec * 1000);
        sleep(philo->data->time_to_sleep);
        printf("Philosopher : %i is now thinking at %li\n", philo->number + 1, philo->last_dinner.tv_sec * 1000);
    }
    return (NULL);
}

This one function is the one not working as intended, and I don't know why is this happening right now, as my if statement seems to have the right condition but I am never entering in the if statement meaning that condition is never met while it should be. I tested a lot of value and the same result happen each time

void *watchers_phil(void *args)
{
    t_info *data = (t_info *)args;
    t_phil *phil = data->philo;
    int i = 0;
    struct timeval now;
    while (1)
    {
        if (data->plate_eaten == data->num_of_phil)
            break;
        while (i < data->num_of_phil)
        {
            if ((phil[i].last_dinner.tv_sec) >= ((phil[i].last_dinner.tv_sec) + (long int)data->time_to_die))
            {
                gettimeofday(&now, NULL);
                printf("Unfortunately Philosopher : %i, is dead because of starvation at %li....", phil[i].number, (now.tv_sec * 1000));
                phil[i].is_dead = 1;
            }
            i++;
        }
        i = 0;
    }
    return (NULL);
}
int main(int argc, char *argv[])
{
    t_info data;
    pthread_t watchers;

    memset(&data, 0, sizeof(t_info));
    data.num_of_phil = atoi(argv[1]);
    data.min_dinner = atoi(argv[2]);
    data.time_to_eat = atoi(argv[3]);
    data.time_to_die = atoi(argv[4]);
    data.time_to_sleep = atoi(argv[5]);
    t_phil *philo = malloc(sizeof(t_phil) * data.num_of_phil);
    if (!philo)
        return (1);
    pthread_mutex_t *mutex = malloc(sizeof(pthread_mutex_t) * data.num_of_phil);
    data.mutex = mutex;
    if (!mutex)
    {
        free(philo);
        return (1);
    }
    int i = 0;
    while (i < data.num_of_phil)
    {
        pthread_mutex_init(&data.mutex[i], NULL);
        i++;
    }
    printf("Number : %i\n", data.num_of_phil);
    pthread_mutex_init(&data.t_mut, NULL);
    i = 0;
    while (i < data.num_of_phil)
    {
        philo[i].number = i;
        philo[i].has_eaten_all = 0;
        philo[i].data = &data;
        philo[i].is_dead = 0;
        philo[i].right = &data.mutex[i];
        if (i == (data.num_of_phil - 1))
            philo[i].left = &data.mutex[0];
        else
            philo[i].left = &data.mutex[i + 1];
        i++;
    }
    data.philo = philo;
    i = 0;
    while (i < data.num_of_phil)
    {
        pthread_create(&data.philo[i].thread, NULL, routine, &data.philo[i]);
        i++;
    }
    pthread_create(&watchers, NULL, watchers_phil, &data);
    i = 0;
    while (i < data.num_of_phil)
    {
        pthread_join(data.philo[i].thread, NULL);
        i++;
    }
    pthread_join(watchers, NULL);
    printf("Dinner eaten : %i\n", data.plate_eaten);
    i = 0;
    while (i < data.num_of_phil)
    {
        pthread_mutex_destroy(&data.mutex[i]);
        i++;
    }
    pthread_mutex_destroy(&data.t_mut);
}
1

There are 1 answers

3
Aconcagua On

I recommend the following approach:

  • Have an array containing a timestamp for each philosopher thread.
  • Each philosopher thread updates its respective timestamp either on beginning or ending eating. To determine its own death it can use this timestamp as well. You could store the eating time or the time when starvation occurs, I personally would rather do the latter as it requires calculating this starvation time just once (and you don't need the eating time anywhere else) while the former would need to do it on every test for starvation.
  • The monitoring thread iterates over all these timestamps doing the following:
    • Determine if a thread has starved (current time – just get it once in front of the loop! – being after timestamp stored, if you follow my recommendation above). Flag the thread as starved by some suitable way so that you can ignore it next time.
    • From non-starved threads remember the minimum value.
  • After iteration sleep as long until this minimum time will be reached (maybe waking up minimally earlier for better precision). If a philosopher updates the timestamp corresponding to this minimum – never mind, the monitor will wake up in vain, just doing nothing, but that won't hurt apart from consuming a bit of CPU time.

Updating the timestamps: You need to be aware that – unless you can guarantee atomic timestamp update – you need to protect them against race conditions between philosopher threads and monitoring thread.

I see two options for:

  • One single mutex for the entire array. Easy to implement, but either of the involved threads might block another one (two philosophers, if both trying to update at the same time, a philosopher the monitor or the monitor one or more philosophers). If one thread is preempted right while holding this mutex the blocked one(s) need(s) to wait until this thread is scheduled again.

  • One mutex for each timestamp. The advantage is that blocking only can occur between the monitor and one single philosopher while all other philosophers can go on as usual. The implementation is more complex, though, and the monitor thread will run its tests a bit slower (you won't notice...) due to having to lock and unlock mutexes again and again (system calls involved, these are rather expensive).

Philosophers should hold the mutex over the entire time between reading the timestamp for checking own starvation until having written the updated starvation time. This assures the philosopher cannot starve right while eating, though in between you should do as little work as possible, especially if you opt for one single mutex (if you cannot avoid, then rather opt for multiple ones).