diff --git a/rendu/limited_resources.c b/rendu/limited_resources.c index 8ce6aa1..06794d7 100644 --- a/rendu/limited_resources.c +++ b/rendu/limited_resources.c @@ -6,7 +6,7 @@ /* By: ruiferna +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2025/10/08 19:31:58 by ruiferna #+# #+# */ -/* Updated: 2025/10/08 21:10:18 by ruiferna ### ########.fr */ +/* Updated: 2025/10/09 10:25:31 by ruiferna ### ########.fr */ /* */ /* ************************************************************************** */ @@ -45,9 +45,11 @@ void *student_routine(void *arg) */ t_shared *shared; int student_id; - shared = (t_shared *) arg; - srand(time(NULL)); - pthread_mutex_lock(&shared->students_mutex); + shared = (t_shared *) arg; + /* Use a reentrant RNG (rand_r) with a per-thread seed to avoid + data races when using rand()/srand() from multiple threads. */ + unsigned int seed = (unsigned int)time(NULL) ^ (unsigned int)(unsigned long)pthread_self(); + pthread_mutex_lock(&shared->students_mutex); shared->students += 1; student_id = shared->students; printf("[%i] Student %i arrived at the library.\n", student_id, student_id); @@ -68,13 +70,13 @@ void *student_routine(void *arg) usleep(100); } } - int use_time = 2 + rand() % 4; + int use_time = 2 + (rand_r(&seed) % 4); usleep(use_time * 1000000); pthread_mutex_lock(&shared->pcs_available_mutex); shared->pcs_available += 1; printf("[%i] Student %i finished using a computer.\n", student_id, student_id); pthread_mutex_unlock(&shared->pcs_available_mutex); - printf("[%i] Student %i leaved the library.\n", student_id, student_id); + printf("[%i] Student %i left the library.\n", student_id, student_id); return (NULL); } diff --git a/rendu/simple_philosophers.c b/rendu/simple_philosophers.c new file mode 100644 index 0000000..612f48d --- /dev/null +++ b/rendu/simple_philosophers.c @@ -0,0 +1,166 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* simple_philosophers.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: ruiferna +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/10/09 11:16:23 by ruiferna #+# #+# */ +/* Updated: 2025/10/09 18:58:45 by ruiferna ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "simple_philosophers.h" + +#define PHILOS 3 +#define FORKS 3 + +typedef struct s_shared +{ + int philos; + int alive; + pthread_mutex_t alive_mutex; + pthread_mutex_t philo_mutex; + pthread_mutex_t printf_lock; + pthread_mutex_t forks[FORKS]; +} t_shared; + +void message(long long timestamp, int id, + char *action, pthread_mutex_t *mutex) +{ + pthread_mutex_lock(mutex); + printf("[%lld] Philosopher %i is %s.\n", timestamp, id, action); + // printf("%lld %i %s\n", timestamp, id, action); + pthread_mutex_unlock(mutex); +} + +void *philo_routine(void *arg) +{ + t_shared *shared; + int philo_id; + long long timestamp; + + shared = (t_shared *) arg; + timestamp = get_current_time(); + pthread_mutex_lock(&shared->philo_mutex); + shared->philos += 1; + /* convert to 0-based id for indexing forks */ + philo_id = shared->philos - 1; + pthread_mutex_unlock(&shared->philo_mutex); + /* small stagger to reduce simultaneous contention: odd threads sleep briefly */ + if (philo_id % 2 == 1) + precise_sleep(50); + while (1) + { + /* check alive flag */ + pthread_mutex_lock(&shared->alive_mutex); + if (!shared->alive) + { + pthread_mutex_unlock(&shared->alive_mutex); + break; + } + pthread_mutex_unlock(&shared->alive_mutex); + + message(get_current_time()-timestamp, + philo_id + 1, "thinking", &shared->printf_lock); + precise_sleep(1000); + int left = philo_id; + int right = (philo_id + 1) % FORKS; + int first = left; + int second = right; + if (first > second) { + int tmp = first; + first = second; + second = tmp; + } + + pthread_mutex_lock(&shared->forks[first]); + message(get_current_time()-timestamp, + philo_id + 1, "grabbed first fork", &shared->printf_lock); + /* try to grab the second fork; if unavailable, release first and retry/check alive */ + if (pthread_mutex_trylock(&shared->forks[second]) != 0) + { + /* couldn't get second fork: release first and go back to loop */ + pthread_mutex_unlock(&shared->forks[first]); + message(get_current_time()-timestamp, + philo_id + 1, "released first fork", &shared->printf_lock); + /* brief pause with per-philosopher jitter to reduce contention */ + precise_sleep(10 + ((philo_id * 37) % 50)); + /* check alive before continuing */ + pthread_mutex_lock(&shared->alive_mutex); + if (!shared->alive) + { + pthread_mutex_unlock(&shared->alive_mutex); + break; + } + pthread_mutex_unlock(&shared->alive_mutex); + continue; + } + message(get_current_time()-timestamp, + philo_id + 1, "grabbed second fork", &shared->printf_lock); + message(get_current_time()-timestamp, + philo_id + 1, "eating", &shared->printf_lock); + precise_sleep(1000); + + pthread_mutex_unlock(&shared->forks[first]); + message(get_current_time()-timestamp, + philo_id + 1, "released first fork", &shared->printf_lock); + pthread_mutex_unlock(&shared->forks[second]); + message(get_current_time()-timestamp, + philo_id + 1, "released second fork", &shared->printf_lock); + } + return (NULL); +} + +void *monitor_routine(void *arg) +{ + t_shared *shared = (t_shared *)arg; + /* run for 10 seconds then flip alive to 0 */ + precise_sleep(10000); + pthread_mutex_lock(&shared->alive_mutex); + shared->alive = 0; + pthread_mutex_unlock(&shared->alive_mutex); + return NULL; +} + +int main() +{ + pthread_t *philos; + t_shared shared; + pthread_t monitor; + + pthread_mutex_init(&shared.philo_mutex, NULL); + pthread_mutex_init(&shared.printf_lock, NULL); + pthread_mutex_init(&shared.alive_mutex, NULL); + /* initialize fork mutexes */ + for (int i = 0; i < FORKS; ++i) + pthread_mutex_init(&shared.forks[i], NULL); + shared.philos = 0; + shared.alive = 1; + /* allocate correct size for pthread_t array */ + philos = malloc(sizeof(pthread_t) * PHILOS); + int i = 0; + while (i < PHILOS) + { + pthread_create(&philos[i++], NULL, philo_routine, &shared); + } + /* start monitor to stop after 10 seconds */ + pthread_create(&monitor, NULL, monitor_routine, &shared); + i = 0; + while (i < PHILOS) + { + pthread_join(philos[i++], NULL); + } + /* wait for monitor (it may have already finished) */ + pthread_join(monitor, NULL); + /* cleanup */ + for (int j = 0; j < FORKS; ++j) + pthread_mutex_destroy(&shared.forks[j]); + pthread_mutex_destroy(&shared.philo_mutex); + pthread_mutex_destroy(&shared.printf_lock); + pthread_mutex_destroy(&shared.alive_mutex); + free(philos); + return (0); + + +} \ No newline at end of file diff --git a/rendu/simple_philosophers.h b/rendu/simple_philosophers.h new file mode 100644 index 0000000..f1709a5 --- /dev/null +++ b/rendu/simple_philosophers.h @@ -0,0 +1,51 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* simple_philosophers.h :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: ruiferna +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/10/09 18:23:48 by ruiferna #+# #+# */ +/* Updated: 2025/10/09 18:24:44 by ruiferna ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#ifndef SIMPLE_PHILOSOPHERS_H +# define SIMPLE_PHILOSOPHERS_H + +#include +#include +#include +#include +#include +#include +#include + +long long get_current_time() +{ + struct timeval tv; + gettimeofday(&tv,NULL); + /* + A stuct tv tem tv_sec que sao os segundos desde a epoch e temos a tv_usec + que retorna os microsegundos ate completar 1 segundo (um milhão de microssegundos = 1 segundo). + Por isso, para conseguirmos realmente medir os milisegundos precisamos de juntar os dois + */ + return ((tv.tv_sec * 1000LL) + (tv.tv_usec / 1000LL)); +} + +long long time_diff(long long start, long long end) +{ + return (end - start); +} + +void precise_sleep(int ms) +{ + long long start_time; + + start_time = get_current_time(); + while ((get_current_time() - start_time) < ms) + { + usleep(100); + } +} +#endif \ No newline at end of file diff --git a/testers/test_8_simple_philosophers.sh b/testers/test_8_simple_philosophers.sh index 3aae748..a4e5307 100755 --- a/testers/test_8_simple_philosophers.sh +++ b/testers/test_8_simple_philosophers.sh @@ -147,16 +147,45 @@ fi # Test 6: Each philosopher eats multiple times echo -n "Test 6: All philosophers eat... " -PHILO_0_EATS=$(echo "$OUTPUT" | grep -ciE "(philosopher 0|philo 0).*eat") -PHILO_1_EATS=$(echo "$OUTPUT" | grep -ciE "(philosopher 1|philo 1).*eat") -PHILO_2_EATS=$(echo "$OUTPUT" | grep -ciE "(philosopher 2|philo 2).*eat") +# Extract philosopher IDs from eating lines. Support both formats like: +# "[123] Philosopher 1 is eating." and any line containing "eating" followed by a number. +# Extract philosopher IDs from eating lines by capturing the number after 'Philosopher' +# Supports lines like: [123] Philosopher 1 is eating. +EAT_IDS=$(echo "$OUTPUT" | grep -i "eating" | sed -n 's/.*Philosopher \([0-9]\+\).*/\1/p') -if [ "$PHILO_0_EATS" -gt 0 ] && [ "$PHILO_1_EATS" -gt 0 ] && [ "$PHILO_2_EATS" -gt 0 ]; then - echo -e "${GREEN}✓ PASSED${NC} (P0:$PHILO_0_EATS, P1:$PHILO_1_EATS, P2:$PHILO_2_EATS)" +# Fallback: some logs may format differently; take the 3rd whitespace field which is the id in +# the format "[ts] Philosopher is " +if [ -z "$EAT_IDS" ]; then + EAT_IDS=$(echo "$OUTPUT" | grep -i "eating" | awk '{print $3}' | sed 's/[^0-9]*//g') +fi + +# Normalize IDs to unique sorted list +UNIQUE_EATERS=$(echo "$EAT_IDS" | sort -n | uniq) + +# Determine expected philosopher ids from output (Philosopher X occurrences) +FOUND_PHILOS=$(echo "$OUTPUT" | grep -oE "Philosopher [0-9]+" | sed 's/Philosopher //g' | sort -n | uniq) + +ALL_ATE=true +MISSING_LIST="" +for ph in $FOUND_PHILOS; do + if ! echo "$UNIQUE_EATERS" | grep -qx "$ph"; then + ALL_ATE=false + MISSING_LIST="$MISSING_LIST $ph" + fi +done + +if [ "$ALL_ATE" = true ]; then + # Build counts per philosopher for reporting + REPORT="" + for ph in $FOUND_PHILOS; do + CNT=$(echo "$EAT_IDS" | grep -cx "$ph" || true) + REPORT="$REPORT P$ph:$CNT" + done + echo -e "${GREEN}✓ PASSED${NC} ($REPORT)" ((PASSED++)) else echo -e "${RED}✗ FAILED${NC}" - echo "Not all philosophers ate. Philo 0: $PHILO_0_EATS, Philo 1: $PHILO_1_EATS, Philo 2: $PHILO_2_EATS" + echo "Not all philosophers ate. Missing ids:$MISSING_LIST" ((FAILED++)) fi