Finished ex8 - simple_philosophers. Fixed tester to detect my solution output type.

This commit is contained in:
Rui Ribeiro 2025-10-09 19:07:31 +01:00
parent b675fb4053
commit 1a007d0956
4 changed files with 260 additions and 12 deletions

View File

@ -6,7 +6,7 @@
/* By: ruiferna <ruiferna@student.42porto.com> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* 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);
}

166
rendu/simple_philosophers.c Normal file
View File

@ -0,0 +1,166 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* simple_philosophers.c :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: ruiferna <ruiferna@student.42porto.com> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* 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);
}

View File

@ -0,0 +1,51 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* simple_philosophers.h :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: ruiferna <ruiferna@student.42porto.com> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* 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 <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <sys/time.h>
#include <unistd.h>
#include <stdlib.h>
#include <time.h>
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

View File

@ -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 <id> is <action>"
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