diff --git a/rendu/death_monitor.c b/rendu/death_monitor.c index ce19dfb..c222e86 100644 --- a/rendu/death_monitor.c +++ b/rendu/death_monitor.c @@ -6,37 +6,29 @@ /* By: ruiferna +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2025/10/09 20:16:37 by ruiferna #+# #+# */ -/* Updated: 2025/10/09 21:23:37 by ruiferna ### ########.fr */ +/* Updated: 2025/10/10 20:51:41 by ruiferna ### ########.fr */ /* */ /* ************************************************************************** */ #include "simple_philosophers.h" -#define WORKERS 4 +#define WORKERS 900 static pthread_mutex_t random_mutex = PTHREAD_MUTEX_INITIALIZER; int get_random_sleep_duration() { - // Seed variable to ensure unique seed per thread unsigned int seed; - // Acquire mutex to ensure thread-safe random number generation pthread_mutex_lock(&random_mutex); - - // Use thread-safe method to generate seed seed = time(NULL) ^ pthread_self(); - - // Generate random number between 1000-4000 milliseconds - int duration = 1000 + rand_r(&seed) % 3001; - - // Release mutex + /* produce a duration between 1000 and 4000 ms (1-4 seconds) */ + int duration = 1000 + rand_r(&seed) % 6001; pthread_mutex_unlock(&random_mutex); - return duration; } - -typedef struct { +typedef struct +{ pthread_mutex_t mutex; struct timeval last_activity_time; int alive; // 1 if alive, 0 if dead @@ -60,6 +52,7 @@ void *worker_routine(void *arg) int sleep_time; shared = (t_shared *) arg; + /* assign a unique id in range [0, WORKERS-1] */ pthread_mutex_lock(&shared->mutex_id); shared->id += 1; worker_id = shared->id; @@ -76,9 +69,39 @@ void *worker_routine(void *arg) return (NULL); } + +void *monitor_routine(void *arg) +{ + t_shared *shared; + long long time_diff; + int i; + + shared = (t_shared *) arg; + while (1) + { + i = 0; + while (i < WORKERS) + { + pthread_mutex_lock(&shared->workers_mutexes[i]); + time_diff = get_current_time() - shared->last_updated[i]; + pthread_mutex_unlock(&shared->workers_mutexes[i]); + if (time_diff > 3000) + { + printf("Worker %i died.\n", i); + return (NULL); + } + i++; + } + /* sleep once per full scan to keep checks frequent and non-blocking */ + precise_sleep(50); + } + + return (NULL); +} int main() { t_shared shared; + pthread_t monitor; int i; i = 0; @@ -101,12 +124,15 @@ int main() i++; } - i = 0; - while (i < WORKERS) - { - pthread_join(shared.workers[i], NULL); - i++; - } + pthread_create(&monitor, NULL, monitor_routine, &shared); + pthread_join(monitor, NULL); + + // i = 0; + // while (i < WORKERS) + // { + // pthread_join(shared.workers[i], NULL); + // i++; + // } return (0); -} \ No newline at end of file +} diff --git a/rendu/philosophers_args.c b/rendu/philosophers_args.c new file mode 100644 index 0000000..c9217b6 --- /dev/null +++ b/rendu/philosophers_args.c @@ -0,0 +1,383 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* philosophers_args.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: ruiferna +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/10/12 00:00:00 by ruiferna #+# #+# */ +/* Updated: 2025/10/12 01:23:53 by ruiferna ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include +#include +#include +#include +#include +#include + +typedef struct s_philo +{ + int id; + int meals_eaten; + long long last_meal_time; + pthread_mutex_t meal_mutex; + 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 must_eat_count; + int has_must_eat; + int simulation_stop; + long long start_time; + pthread_mutex_t *forks; + pthread_mutex_t printf_mutex; + pthread_mutex_t stop_mutex; + t_philo *philos; +} t_shared; + +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; + + pthread_mutex_lock(&shared->stop_mutex); + if (!shared->simulation_stop) + { + pthread_mutex_unlock(&shared->stop_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); + } + else + pthread_mutex_unlock(&shared->stop_mutex); +} + +int check_stop(t_shared *shared) +{ + int stop; + + pthread_mutex_lock(&shared->stop_mutex); + stop = shared->simulation_stop; + pthread_mutex_unlock(&shared->stop_mutex); + return (stop); +} + +void philo_eat(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; + } + 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"); + pthread_mutex_lock(&philo->meal_mutex); + philo->last_meal_time = get_current_time(); + pthread_mutex_unlock(&philo->meal_mutex); + print_message(philo->shared, philo->id, "is eating"); + precise_sleep(philo->shared->time_to_eat); + pthread_mutex_lock(&philo->meal_mutex); + philo->meals_eaten++; + pthread_mutex_unlock(&philo->meal_mutex); + pthread_mutex_unlock(&philo->shared->forks[second]); + pthread_mutex_unlock(&philo->shared->forks[first]); +} + +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) + { + pthread_mutex_lock(&shared->philos[i].meal_mutex); + current_time = get_current_time(); + time_since_meal = current_time - shared->philos[i].last_meal_time; + pthread_mutex_unlock(&shared->philos[i].meal_mutex); + if (time_since_meal > shared->time_to_die) + { + pthread_mutex_lock(&shared->stop_mutex); + shared->simulation_stop = 1; + pthread_mutex_unlock(&shared->stop_mutex); + pthread_mutex_lock(&shared->printf_mutex); + printf("%lld %d died\n", current_time - shared->start_time, + shared->philos[i].id); + pthread_mutex_unlock(&shared->printf_mutex); + return (1); + } + i++; + } + return (0); +} + +int check_all_ate(t_shared *shared) +{ + int i; + int all_done; + + if (!shared->has_must_eat) + return (0); + i = 0; + all_done = 1; + while (i < shared->num_philos) + { + pthread_mutex_lock(&shared->philos[i].meal_mutex); + if (shared->philos[i].meals_eaten < shared->must_eat_count) + all_done = 0; + pthread_mutex_unlock(&shared->philos[i].meal_mutex); + i++; + } + if (all_done) + { + pthread_mutex_lock(&shared->stop_mutex); + shared->simulation_stop = 1; + pthread_mutex_unlock(&shared->stop_mutex); + return (1); + } + return (0); +} + +void *monitor_routine(void *arg) +{ + t_shared *shared; + + shared = (t_shared *)arg; + while (!check_stop(shared)) + { + if (check_death(shared)) + break ; + if (check_all_ate(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) +{ + if (argc != 5 && argc != 6) + { + printf("Usage: %s number_of_philosophers time_to_die time_to_eat time_to_sleep [number_of_times_must_eat]\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); + if (argc == 6) + { + if (!is_valid_number(argv[5])) + return (0); + shared->must_eat_count = ft_atoi(argv[5]); + if (shared->must_eat_count <= 0) + return (0); + shared->has_must_eat = 1; + } + else + shared->has_must_eat = 0; + return (1); +} + +int init_shared(t_shared *shared) +{ + int i; + + shared->simulation_stop = 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++; + } + pthread_mutex_init(&shared->printf_mutex, NULL); + pthread_mutex_init(&shared->stop_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].shared = shared; + pthread_mutex_init(&shared->philos[i].meal_mutex, NULL); + i++; + } +} + +void cleanup(t_shared *shared, pthread_t *threads) +{ + int i; + + i = 0; + while (i < shared->num_philos) + { + pthread_mutex_destroy(&shared->forks[i]); + pthread_mutex_destroy(&shared->philos[i].meal_mutex); + i++; + } + pthread_mutex_destroy(&shared->printf_mutex); + pthread_mutex_destroy(&shared->stop_mutex); + free(shared->forks); + free(shared->philos); + free(threads); +} + +int main(int argc, char **argv) +{ + t_shared shared; + pthread_t *threads; + pthread_t monitor; + int i; + + memset(&shared, 0, sizeof(t_shared)); + if (!parse_arguments(argc, argv, &shared)) + { + printf("Error: Invalid arguments\n"); + return (1); + } + if (!init_shared(&shared)) + { + printf("Error: Initialization failed\n"); + return (1); + } + init_philosophers(&shared); + threads = malloc(sizeof(pthread_t) * shared.num_philos); + if (!threads) + { + cleanup(&shared, NULL); + return (1); + } + i = 0; + while (i < shared.num_philos) + { + pthread_create(&threads[i], NULL, philo_routine, &shared.philos[i]); + i++; + } + pthread_create(&monitor, NULL, monitor_routine, &shared); + i = 0; + while (i < shared.num_philos) + { + pthread_join(threads[i], NULL); + i++; + } + pthread_join(monitor, NULL); + cleanup(&shared, threads); + return (0); +} diff --git a/testers/test_10_philosophers_args.sh b/testers/test_10_philosophers_args.sh index c415c16..0b9e4b5 100755 --- a/testers/test_10_philosophers_args.sh +++ b/testers/test_10_philosophers_args.sh @@ -78,12 +78,13 @@ echo -e "${YELLOW}Using executable at: $EXE_PATH${NC}" # Test 1: Basic execution (no one should die) echo -n "Test 1: Basic case - 5 800 200 200... " -timeout 10 "$EXE_PATH" 5 800 200 200 > /tmp/philo_test1.txt 2>&1 +timeout 3 "$EXE_PATH" 5 800 200 200 > /tmp/philo_test1.txt 2>&1 EXIT_CODE=$? -if [ $EXIT_CODE -eq 0 ]; then +# Exit code 124 means timeout (which is expected for infinite simulation) +if [ $EXIT_CODE -eq 124 ] || [ $EXIT_CODE -eq 0 ]; then OUTPUT=$(cat /tmp/philo_test1.txt) - if ! echo "$OUTPUT" | grep -qiE "die"; then + if ! echo "$OUTPUT" | grep -qiE "died"; then echo -e "${GREEN}✓ PASSED${NC}" ((PASSED++)) else @@ -92,7 +93,7 @@ if [ $EXIT_CODE -eq 0 ]; then ((FAILED++)) fi else - echo -e "${RED}✗ FAILED (timeout or crash)${NC}" + echo -e "${RED}✗ FAILED (crash - exit code $EXIT_CODE)${NC}" ((FAILED++)) fi @@ -165,15 +166,21 @@ fi # Test 6: Check for memory leaks with valgrind if command -v valgrind &> /dev/null; then echo -n "Test 6: Memory leak check... " - VALGRIND_OUTPUT=$(valgrind --leak-check=full --error-exitcode=42 "$EXE_PATH" 4 410 200 200 2>&1) - if [ $? -ne 42 ]; then + # Use a limited run with meal count to ensure program terminates + VALGRIND_OUTPUT=$(timeout 10 valgrind --leak-check=full --error-exitcode=42 "$EXE_PATH" 4 410 200 200 5 2>&1) + VALGRIND_EXIT=$? + # Exit code 124 is timeout, 42 is memory leak + if [ $VALGRIND_EXIT -eq 0 ]; then echo -e "${GREEN}✓ PASSED${NC}" ((PASSED++)) - else + elif [ $VALGRIND_EXIT -eq 42 ]; then echo -e "${RED}✗ FAILED${NC}" echo "Memory leaks detected!" echo "$VALGRIND_OUTPUT" | grep -A 5 "LEAK SUMMARY" ((FAILED++)) + else + echo -e "${YELLOW}⚠ TIMEOUT/ERROR (exit code $VALGRIND_EXIT)${NC}" + echo "Skipping this test" fi else echo -e "${YELLOW}⊘ Test 6: Valgrind not installed, skipping memory test${NC}" @@ -194,7 +201,8 @@ if command -v gcc &> /dev/null; then TSAN_EXE="$PROJECT_ROOT/$(basename "$EXE_PATH")_tsan" gcc -Wall -Wextra -Werror -pthread -fsanitize=thread -g "$SOURCE_FILE" -o "$TSAN_EXE" 2>/dev/null if [ $? -eq 0 ]; then - TSAN_OUTPUT=$(timeout 15 "$TSAN_EXE" 4 410 200 200 2>&1) + # Use meal limit to ensure program terminates + TSAN_OUTPUT=$(timeout 10 "$TSAN_EXE" 4 410 200 200 5 2>&1) if echo "$TSAN_OUTPUT" | grep -q "WARNING: ThreadSanitizer: data race"; then echo -e "${RED}✗ FAILED${NC}" echo "Data race detected!"