Added race_detector exercice
This commit is contained in:
parent
cae865cd92
commit
f8f51f8cc2
575
rendu/race_detector.c
Normal file
575
rendu/race_detector.c
Normal file
@ -0,0 +1,575 @@
|
||||
/* ************************************************************************** */
|
||||
/* */
|
||||
/* ::: :::::::: */
|
||||
/* race_detector.c :+: :+: :+: */
|
||||
/* +:+ +:+ +:+ */
|
||||
/* By: ruiferna <ruiferna@student.42porto.com> +#+ +:+ +#+ */
|
||||
/* +#+#+#+#+#+ +#+ */
|
||||
/* Created: 2025/10/14 00:00:00 by ruiferna #+# #+# */
|
||||
/* Updated: 2025/10/14 15:38:47 by ruiferna ### ########.fr */
|
||||
/* */
|
||||
/* ************************************************************************** */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <pthread.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
|
||||
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);
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user