/* ************************************************************************** */ /* */ /* ::: :::::::: */ /* 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); }