166 lines
5.0 KiB
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);
|
|
|
|
|
|
} |