philosophers_prep/rendu/simple_philosophers.c

166 lines
5.0 KiB
C

/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* 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);
}