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> +#+ +:+ +#+ */
|
/* By: ruiferna <ruiferna@student.42porto.com> +#+ +:+ +#+ */
|
||||||
/* +#+#+#+#+#+ +#+ */
|
/* +#+#+#+#+#+ +#+ */
|
||||||
/* Created: 2025/10/08 19:31:58 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;
|
t_shared *shared;
|
||||||
int student_id;
|
int student_id;
|
||||||
shared = (t_shared *) arg;
|
shared = (t_shared *) arg;
|
||||||
srand(time(NULL));
|
/* Use a reentrant RNG (rand_r) with a per-thread seed to avoid
|
||||||
pthread_mutex_lock(&shared->students_mutex);
|
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;
|
shared->students += 1;
|
||||||
student_id = shared->students;
|
student_id = shared->students;
|
||||||
printf("[%i] Student %i arrived at the library.\n", student_id, student_id);
|
printf("[%i] Student %i arrived at the library.\n", student_id, student_id);
|
||||||
@ -68,13 +70,13 @@ void *student_routine(void *arg)
|
|||||||
usleep(100);
|
usleep(100);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
int use_time = 2 + rand() % 4;
|
int use_time = 2 + (rand_r(&seed) % 4);
|
||||||
usleep(use_time * 1000000);
|
usleep(use_time * 1000000);
|
||||||
pthread_mutex_lock(&shared->pcs_available_mutex);
|
pthread_mutex_lock(&shared->pcs_available_mutex);
|
||||||
shared->pcs_available += 1;
|
shared->pcs_available += 1;
|
||||||
printf("[%i] Student %i finished using a computer.\n", student_id, student_id);
|
printf("[%i] Student %i finished using a computer.\n", student_id, student_id);
|
||||||
pthread_mutex_unlock(&shared->pcs_available_mutex);
|
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);
|
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
|
# Test 6: Each philosopher eats multiple times
|
||||||
echo -n "Test 6: All philosophers eat... "
|
echo -n "Test 6: All philosophers eat... "
|
||||||
PHILO_0_EATS=$(echo "$OUTPUT" | grep -ciE "(philosopher 0|philo 0).*eat")
|
# Extract philosopher IDs from eating lines. Support both formats like:
|
||||||
PHILO_1_EATS=$(echo "$OUTPUT" | grep -ciE "(philosopher 1|philo 1).*eat")
|
# "[123] Philosopher 1 is eating." and any line containing "eating" followed by a number.
|
||||||
PHILO_2_EATS=$(echo "$OUTPUT" | grep -ciE "(philosopher 2|philo 2).*eat")
|
# 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
|
# Fallback: some logs may format differently; take the 3rd whitespace field which is the id in
|
||||||
echo -e "${GREEN}✓ PASSED${NC} (P0:$PHILO_0_EATS, P1:$PHILO_1_EATS, P2:$PHILO_2_EATS)"
|
# 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++))
|
((PASSED++))
|
||||||
else
|
else
|
||||||
echo -e "${RED}✗ FAILED${NC}"
|
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++))
|
((FAILED++))
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user