Finished ex8 - simple_philosophers. Fixed tester to detect my solution output type.
This commit is contained in:
parent
b675fb4053
commit
1a007d0956
@ -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 */
|
||||
/* */
|
||||
/* ************************************************************************** */
|
||||
|
||||
@ -46,7 +46,9 @@ void *student_routine(void *arg)
|
||||
t_shared *shared;
|
||||
int student_id;
|
||||
shared = (t_shared *) arg;
|
||||
srand(time(NULL));
|
||||
/* 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;
|
||||
@ -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
166
rendu/simple_philosophers.c
Normal 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);
|
||||
|
||||
|
||||
}
|
||||
51
rendu/simple_philosophers.h
Normal file
51
rendu/simple_philosophers.h
Normal 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
|
||||
@ -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
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user