From f8f51f8cc2f8c4291b94de9a683de97cbddc856a Mon Sep 17 00:00:00 2001 From: Rui Ribeiro <42305006+ruiribeiro04@users.noreply.github.com> Date: Tue, 14 Oct 2025 16:04:33 +0100 Subject: [PATCH] Added `race_detector` exercice --- rendu/race_detector.c | 575 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 575 insertions(+) create mode 100644 rendu/race_detector.c diff --git a/rendu/race_detector.c b/rendu/race_detector.c new file mode 100644 index 0000000..a82c290 --- /dev/null +++ b/rendu/race_detector.c @@ -0,0 +1,575 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* race_detector.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: ruiferna +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/10/14 00:00:00 by ruiferna #+# #+# */ +/* Updated: 2025/10/14 15:38:47 by ruiferna ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include +#include +#include +#include +#include +#include + +typedef enum e_strategy +{ + STRATEGY_A, // Global mutex for all operations + STRATEGY_B, // Mutex per fork + printf mutex + STRATEGY_C // Minimize lock time +} t_strategy; + +typedef struct s_philo +{ + int id; + int meals_eaten; + long long last_meal_time; + int data_race_count; + struct s_shared *shared; +} t_philo; + +typedef struct s_shared +{ + int num_philos; + int time_to_die; + int time_to_eat; + int time_to_sleep; + int simulation_stop; + int total_data_races; + int total_deaths; + long long start_time; + long long total_time; + t_strategy strategy; + pthread_mutex_t *forks; + pthread_mutex_t global_mutex; // Strategy A + pthread_mutex_t printf_mutex; // Strategy B + t_philo *philos; +} t_shared; + +typedef struct s_test_result +{ + t_strategy strategy; + int data_races; + int deaths; + long long time_ms; +} t_test_result; + +long long get_current_time(void) +{ + struct timeval tv; + + gettimeofday(&tv, NULL); + return ((tv.tv_sec * 1000LL) + (tv.tv_usec / 1000LL)); +} + +void precise_sleep(int ms) +{ + long long start_time; + + start_time = get_current_time(); + while ((get_current_time() - start_time) < ms) + usleep(100); +} + +void print_message(t_shared *shared, int philo_id, char *message) +{ + long long timestamp; + + if (shared->strategy == STRATEGY_A) + { + // Strategy A: Already have global lock + timestamp = get_current_time() - shared->start_time; + printf("%lld %d %s\n", timestamp, philo_id, message); + } + else + { + // Strategy B & C: Use printf mutex + pthread_mutex_lock(&shared->printf_mutex); + timestamp = get_current_time() - shared->start_time; + printf("%lld %d %s\n", timestamp, philo_id, message); + pthread_mutex_unlock(&shared->printf_mutex); + } +} + +int check_stop(t_shared *shared) +{ + int stop; + + if (shared->strategy == STRATEGY_A) + { + // Strategy A: Already have global lock + stop = shared->simulation_stop; + } + else + { + // Strategy B & C: Need to check atomically + stop = shared->simulation_stop; + } + return (stop); +} + +void detect_data_race(t_philo *philo, char *operation) +{ + // Simple data race detection: count concurrent access to shared data + philo->data_race_count++; + philo->shared->total_data_races++; +} + +void philo_eat_strategy_a(t_philo *philo) +{ + int left_fork; + int right_fork; + int first; + int second; + + left_fork = philo->id - 1; + right_fork = philo->id % philo->shared->num_philos; + first = left_fork; + second = right_fork; + if (first > second) + { + first = right_fork; + second = left_fork; + } + + // Strategy A: Global lock for all operations + pthread_mutex_lock(&philo->shared->global_mutex); + print_message(philo->shared, philo->id, "has taken a fork"); + if (philo->shared->num_philos == 1) + { + pthread_mutex_unlock(&philo->shared->global_mutex); + return ; + } + print_message(philo->shared, philo->id, "has taken a fork"); + philo->last_meal_time = get_current_time(); + detect_data_race(philo, "meal_time_update"); + print_message(philo->shared, philo->id, "is eating"); + precise_sleep(philo->shared->time_to_eat); + philo->meals_eaten++; + detect_data_race(philo, "meals_eaten_update"); + pthread_mutex_unlock(&philo->shared->global_mutex); +} + +void philo_eat_strategy_b(t_philo *philo) +{ + int left_fork; + int right_fork; + int first; + int second; + + left_fork = philo->id - 1; + right_fork = philo->id % philo->shared->num_philos; + first = left_fork; + second = right_fork; + if (first > second) + { + first = right_fork; + second = left_fork; + } + + // Strategy B: Mutex per fork + printf mutex + pthread_mutex_lock(&philo->shared->forks[first]); + print_message(philo->shared, philo->id, "has taken a fork"); + if (philo->shared->num_philos == 1) + { + pthread_mutex_unlock(&philo->shared->forks[first]); + return ; + } + pthread_mutex_lock(&philo->shared->forks[second]); + print_message(philo->shared, philo->id, "has taken a fork"); + philo->last_meal_time = get_current_time(); + detect_data_race(philo, "meal_time_update"); + print_message(philo->shared, philo->id, "is eating"); + precise_sleep(philo->shared->time_to_eat); + philo->meals_eaten++; + detect_data_race(philo, "meals_eaten_update"); + pthread_mutex_unlock(&philo->shared->forks[second]); + pthread_mutex_unlock(&philo->shared->forks[first]); +} + +void philo_eat_strategy_c(t_philo *philo) +{ + int left_fork; + int right_fork; + int first; + int second; + + left_fork = philo->id - 1; + right_fork = philo->id % philo->shared->num_philos; + first = left_fork; + second = right_fork; + if (first > second) + { + first = right_fork; + second = left_fork; + } + + // Strategy C: Minimize lock time - lock only when necessary + pthread_mutex_lock(&philo->shared->forks[first]); + print_message(philo->shared, philo->id, "has taken a fork"); + if (philo->shared->num_philos == 1) + { + pthread_mutex_unlock(&philo->shared->forks[first]); + return ; + } + if (pthread_mutex_trylock(&philo->shared->forks[second]) != 0) + { + // Couldn't get second fork, release first and retry + pthread_mutex_unlock(&philo->shared->forks[first]); + usleep(1000); // Brief pause + return ; + } + print_message(philo->shared, philo->id, "has taken a fork"); + + // Update meal time with minimal lock time + long long current_time = get_current_time(); + philo->last_meal_time = current_time; + detect_data_race(philo, "meal_time_update"); + + print_message(philo->shared, philo->id, "is eating"); + precise_sleep(philo->shared->time_to_eat); + + // Update meals eaten + philo->meals_eaten++; + detect_data_race(philo, "meals_eaten_update"); + + pthread_mutex_unlock(&philo->shared->forks[second]); + pthread_mutex_unlock(&philo->shared->forks[first]); +} + +void philo_eat(t_philo *philo) +{ + if (philo->shared->strategy == STRATEGY_A) + philo_eat_strategy_a(philo); + else if (philo->shared->strategy == STRATEGY_B) + philo_eat_strategy_b(philo); + else + philo_eat_strategy_c(philo); +} + +void *philo_routine(void *arg) +{ + t_philo *philo; + + philo = (t_philo *)arg; + if (philo->id % 2 == 0) + precise_sleep(50); + while (!check_stop(philo->shared)) + { + print_message(philo->shared, philo->id, "is thinking"); + philo_eat(philo); + if (check_stop(philo->shared)) + break ; + print_message(philo->shared, philo->id, "is sleeping"); + precise_sleep(philo->shared->time_to_sleep); + } + return (NULL); +} + +int check_death(t_shared *shared) +{ + int i; + long long current_time; + long long time_since_meal; + + i = 0; + while (i < shared->num_philos) + { + current_time = get_current_time(); + time_since_meal = current_time - shared->philos[i].last_meal_time; + if (time_since_meal > shared->time_to_die) + { + shared->simulation_stop = 1; + shared->total_deaths++; + if (shared->strategy == STRATEGY_A) + pthread_mutex_lock(&shared->global_mutex); + print_message(shared, shared->philos[i].id, "died"); + if (shared->strategy == STRATEGY_A) + pthread_mutex_unlock(&shared->global_mutex); + return (1); + } + i++; + } + return (0); +} + +void *monitor_routine(void *arg) +{ + t_shared *shared; + + shared = (t_shared *)arg; + while (!check_stop(shared)) + { + if (check_death(shared)) + break ; + usleep(1000); + } + return (NULL); +} + +int ft_atoi(const char *str) +{ + int result; + int i; + + result = 0; + i = 0; + while (str[i] >= '0' && str[i] <= '9') + { + result = result * 10 + (str[i] - '0'); + i++; + } + return (result); +} + +int is_valid_number(const char *str) +{ + int i; + + i = 0; + if (!str || str[0] == '\0') + return (0); + while (str[i]) + { + if (str[i] < '0' || str[i] > '9') + return (0); + i++; + } + return (1); +} + +int parse_arguments(int argc, char **argv, t_shared *shared, int *iterations) +{ + if (argc != 5 && argc != 6) + { + printf("Usage: %s number_of_philosophers time_to_die time_to_eat time_to_sleep [iterations]\n", + argv[0]); + return (0); + } + if (!is_valid_number(argv[1]) || !is_valid_number(argv[2]) + || !is_valid_number(argv[3]) || !is_valid_number(argv[4])) + return (0); + shared->num_philos = ft_atoi(argv[1]); + shared->time_to_die = ft_atoi(argv[2]); + shared->time_to_eat = ft_atoi(argv[3]); + shared->time_to_sleep = ft_atoi(argv[4]); + if (shared->num_philos <= 0 || shared->time_to_die <= 0 + || shared->time_to_eat <= 0 || shared->time_to_sleep <= 0) + return (0); + *iterations = 10; // Default for testing + if (argc == 6) + { + if (!is_valid_number(argv[5])) + return (0); + *iterations = ft_atoi(argv[5]); + if (*iterations <= 0) + *iterations = 10; + } + return (1); +} + +int init_shared(t_shared *shared) +{ + int i; + + shared->simulation_stop = 0; + shared->total_data_races = 0; + shared->total_deaths = 0; + shared->forks = malloc(sizeof(pthread_mutex_t) * shared->num_philos); + if (!shared->forks) + return (0); + shared->philos = malloc(sizeof(t_philo) * shared->num_philos); + if (!shared->philos) + { + free(shared->forks); + return (0); + } + i = 0; + while (i < shared->num_philos) + { + pthread_mutex_init(&shared->forks[i], NULL); + i++; + } + if (shared->strategy == STRATEGY_A) + pthread_mutex_init(&shared->global_mutex, NULL); + else + pthread_mutex_init(&shared->printf_mutex, NULL); + return (1); +} + +void init_philosophers(t_shared *shared) +{ + int i; + long long current_time; + + current_time = get_current_time(); + shared->start_time = current_time; + i = 0; + while (i < shared->num_philos) + { + shared->philos[i].id = i + 1; + shared->philos[i].meals_eaten = 0; + shared->philos[i].last_meal_time = current_time; + shared->philos[i].data_race_count = 0; + shared->philos[i].shared = shared; + i++; + } +} + +void cleanup(t_shared *shared, pthread_t *threads) +{ + int i; + + i = 0; + while (i < shared->num_philos) + { + pthread_mutex_destroy(&shared->forks[i]); + i++; + } + if (shared->strategy == STRATEGY_A) + pthread_mutex_destroy(&shared->global_mutex); + else + pthread_mutex_destroy(&shared->printf_mutex); + free(shared->forks); + free(shared->philos); + free(threads); +} + +t_test_result run_simulation(t_strategy strategy, int num_philos, int time_to_die, + int time_to_eat, int time_to_sleep) +{ + t_shared shared; + pthread_t *threads; + pthread_t monitor; + int i; + long long start_time; + long long end_time; + + // Initialize shared data + memset(&shared, 0, sizeof(t_shared)); + shared.num_philos = num_philos; + shared.time_to_die = time_to_die; + shared.time_to_eat = time_to_eat; + shared.time_to_sleep = time_to_sleep; + shared.strategy = strategy; + + if (!init_shared(&shared)) + return ((t_test_result){strategy, -1, -1, -1}); + + init_philosophers(&shared); + threads = malloc(sizeof(pthread_t) * shared.num_philos); + if (!threads) + { + cleanup(&shared, NULL); + return ((t_test_result){strategy, -1, -1, -1}); + } + + start_time = get_current_time(); + + // Create philosopher threads + i = 0; + while (i < shared.num_philos) + { + pthread_create(&threads[i], NULL, philo_routine, &shared.philos[i]); + i++; + } + + // Create monitor thread + pthread_create(&monitor, NULL, monitor_routine, &shared); + + // Wait for threads + i = 0; + while (i < shared.num_philos) + { + pthread_join(threads[i], NULL); + i++; + } + pthread_join(monitor, NULL); + + end_time = get_current_time(); + + t_test_result result = { + strategy, + shared.total_data_races, + shared.total_deaths, + end_time - start_time + }; + + cleanup(&shared, threads); + return (result); +} + +void print_strategy_name(t_strategy strategy) +{ + if (strategy == STRATEGY_A) + printf("Strategy A (Global Mutex)"); + else if (strategy == STRATEGY_B) + printf("Strategy B (Fork Mutexes + Print)"); + else + printf("Strategy C (Minimize Locks)"); +} + +void run_stress_test(int num_philos, int time_to_die, int time_to_eat, int time_to_sleep, int iterations) +{ + t_test_result results[3]; + long long total_times[3] = {0, 0, 0}; + int total_races[3] = {0, 0, 0}; + int total_deaths[3] = {0, 0, 0}; + int i; + + printf("Running stress test with %d iterations...\n", iterations); + printf("Configuration: %d philosophers, die=%dms, eat=%dms, sleep=%dms\n\n", + num_philos, time_to_die, time_to_eat, time_to_sleep); + + for (i = 0; i < iterations; i++) + { + int strategy; + for (strategy = 0; strategy < 3; strategy++) + { + results[strategy] = run_simulation(strategy, num_philos, time_to_die, + time_to_eat, time_to_sleep); + total_times[strategy] += results[strategy].time_ms; + total_races[strategy] += results[strategy].data_races; + total_deaths[strategy] += results[strategy].deaths; + } + if ((i + 1) % 10 == 0) + printf("Completed %d/%d iterations\n", i + 1, iterations); + } + + printf("\n=== STRESS TEST RESULTS ===\n"); + for (i = 0; i < 3; i++) + { + printf("\n"); + print_strategy_name(i); + printf(":\n"); + printf(" Average time: %.2f ms\n", (double)total_times[i] / iterations); + printf(" Total data races: %d (avg: %.2f per run)\n", + total_races[i], (double)total_races[i] / iterations); + printf(" Total deaths: %d (avg: %.2f per run)\n", + total_deaths[i], (double)total_deaths[i] / iterations); + } +} + +int main(int argc, char **argv) +{ + t_shared shared; + int iterations; + + memset(&shared, 0, sizeof(t_shared)); + if (!parse_arguments(argc, argv, &shared, &iterations)) + { + printf("Error: Invalid arguments\n"); + return (1); + } + + // Run stress test with the provided configuration + run_stress_test(shared.num_philos, shared.time_to_die, + shared.time_to_eat, shared.time_to_sleep, iterations); + + return (0); +} \ No newline at end of file