It may be easier to repurpose the code for the bonus.
--- /dev/null
+CC := gcc
+
+CFLAGS := -Wall -Wextra -Werror -Wpedantic
+-std=c99
+NAME := philo
+
+INCDIR := .
+SRCDIR := .
+SRCS := main.c \
+ philo.c \
+ helpers.c \
+ parsing.c \
+ parsing_misc.c \
+ pars_arg.c \
+ inner_pars_arg.c \
+ mem_management.c \
+ mutex.c \
+
+SRCS := $(addprefix $(SRCDIR)/, $(SRCS))
+OBJS := $(SRCS:%.c=%.o)
+CFLAGS += $(addprefix -I, $(INCDIR))
+
+all : $(NAME)
+
+debug : CFLAGS += -g
+debug : $(NAME)
+
+$(NAME) : $(OBJS)
+ $(CC) $(CFLAGS) $(OBJS) -o $@
+
+%.o : %.c
+ $(CC) $(CFLAGS) -c $< -o $@
+
+clean :
+ $(RM) $(OBJS)
+
+fclean :
+ $(RM) $(OBJS) $(NAME)
+
+re : fclean
+ $(MAKE)
--- /dev/null
+/* ************************************************************************** */
+/* */
+/* ::: :::::::: */
+/* helpers.c :+: :+: :+: */
+/* +:+ +:+ +:+ */
+/* By: ljiriste <marvin@42.fr> +#+ +:+ +#+ */
+/* +#+#+#+#+#+ +#+ */
+/* Created: 2024/05/09 09:13:40 by ljiriste #+# #+# */
+/* Updated: 2024/05/09 11:59:36 by ljiriste ### ########.fr */
+/* */
+/* ************************************************************************** */
+
+#include "philo.h"
+#include <sys/time.h>
+#include <stdio.h>
+
+int end(t_settings *set)
+{
+ int res;
+
+ mutex_lock(&set->end_lock);
+ res = set->end;
+ mutex_unlock(&set->end_lock);
+ return (res);
+}
+
+useconds_t usecs_since_start(struct timeval start)
+{
+ struct timeval time;
+
+ gettimeofday(&time, NULL);
+ return ((time.tv_sec - start.tv_sec) * 1000000
+ + time.tv_usec - start.tv_usec);
+}
+
+void print_timestamp(t_philo *philo, const char *str)
+{
+ mutex_lock(&philo->settings->terminal_lock);
+ printf("%u %lu %s\n", usecs_since_start(philo->settings->start)
+ / 1000, philo->id, str);
+ mutex_unlock(&philo->settings->terminal_lock);
+ return ;
+}
+
+int report(t_philo *philo, const char *str)
+{
+ if (!end(philo->settings))
+ {
+ print_timestamp(philo, str);
+ return (0);
+ }
+ return (1);
+}
--- /dev/null
+/* ************************************************************************** */
+/* */
+/* ::: :::::::: */
+/* inner_pars_arg.c :+: :+: :+: */
+/* +:+ +:+ +:+ */
+/* By: ljiriste <marvin@42.fr> +#+ +:+ +#+ */
+/* +#+#+#+#+#+ +#+ */
+/* Created: 2024/03/28 11:35:59 by ljiriste #+# #+# */
+/* Updated: 2024/03/28 11:36:24 by ljiriste ### ########.fr */
+/* */
+/* ************************************************************************** */
+
+#include "parsing.h"
+#include <stdlib.h>
+
+// Maybe this should be changed so that it only changes
+// res when the parse is successful?
+int inner_parse_arg(t_big_uint *res, const char *arg)
+{
+ char *tmp;
+
+ *res = ft_atobui(arg);
+ tmp = ft_buitoa(*res);
+ if (!same_number(tmp, arg))
+ {
+ free(tmp);
+ return (1);
+ }
+ free(tmp);
+ return (0);
+}
--- /dev/null
+/* ************************************************************************** */
+/* */
+/* ::: :::::::: */
+/* main.c :+: :+: :+: */
+/* +:+ +:+ +:+ */
+/* By: ljiriste <marvin@42.fr> +#+ +:+ +#+ */
+/* +#+#+#+#+#+ +#+ */
+/* Created: 2024/03/22 11:19:48 by ljiriste #+# #+# */
+/* Updated: 2024/05/09 12:31:31 by ljiriste ### ########.fr */
+/* */
+/* ************************************************************************** */
+
+#include "philo.h"
+#include <stddef.h>
+#include <pthread.h>
+#include <limits.h>
+#include <stdio.h>
+
+static int seat_philosophers(t_diner *diner, pthread_t *threads)
+{
+ size_t i;
+
+ gettimeofday(&diner->setting.start, NULL);
+ i = 0;
+ while (i < diner->setting.philo_count)
+ {
+ if (pthread_create(threads + i, NULL,
+ be_a_philosopher, diner->philos + i))
+ {
+ mutex_lock(&diner->setting.end_lock);
+ diner->setting.end = 1;
+ mutex_unlock(&diner->setting.end_lock);
+ while (i > 0)
+ {
+ pthread_join(threads[--i], NULL);
+ }
+ return (1);
+ }
+ ++i;
+ }
+ return (0);
+}
+
+static int is_dead(t_philo *philo)
+{
+ mutex_lock(&philo->philo_lock);
+ if (usecs_since_start(philo->settings->start) - philo->last_eaten
+ > philo->settings->time_to_die)
+ {
+ mutex_unlock(&philo->philo_lock);
+ return (1);
+ }
+ mutex_unlock(&philo->philo_lock);
+ return (0);
+}
+
+static int all_full(t_diner *diner)
+{
+ size_t i;
+ unsigned int min_eaten;
+
+ if (!diner->setting.should_check_hunger)
+ return (0);
+ i = 0;
+ min_eaten = UINT_MAX;
+ while (i < diner->setting.philo_count)
+ {
+ mutex_lock(&diner->philos[i].philo_lock);
+ if (min_eaten > diner->philos[i].num_eaten)
+ min_eaten = diner->philos[i].num_eaten;
+ mutex_unlock(&diner->philos[i].philo_lock);
+ ++i;
+ }
+ return (min_eaten >= diner->setting.min_eat_num);
+}
+
+static enum e_end watch_philosophers(t_diner *diner)
+{
+ size_t i;
+
+ while (1)
+ {
+ i = 0;
+ while (i < diner->setting.philo_count)
+ {
+ if (is_dead(diner->philos + i))
+ {
+ mutex_lock(&diner->setting.end_lock);
+ diner->setting.end = 1;
+ mutex_unlock(&diner->setting.end_lock);
+ print_timestamp(diner->philos + i, "died");
+ return (death);
+ }
+ ++i;
+ }
+ if (all_full(diner))
+ {
+ mutex_lock(&diner->setting.end_lock);
+ diner->setting.end = 1;
+ mutex_unlock(&diner->setting.end_lock);
+ return (well_fed);
+ }
+ }
+}
+
+int main(int argc, char **argv)
+{
+ t_diner diner;
+ pthread_t *threads;
+ enum e_end res;
+
+ if (parse_input(&diner.setting, argc, argv))
+ {
+ return (1);
+ }
+ if (init(&diner, &threads))
+ {
+ cleanup(&diner, threads);
+ return (2);
+ }
+ if (seat_philosophers(&diner, threads))
+ {
+ cleanup(&diner, threads);
+ return (3);
+ }
+ res = watch_philosophers(&diner);
+ cleanup(&diner, threads);
+ if (res == well_fed)
+ printf("All philosophers are well fed.\n");
+ return (0);
+}
--- /dev/null
+/* ************************************************************************** */
+/* */
+/* ::: :::::::: */
+/* mem_management.c :+: :+: :+: */
+/* +:+ +:+ +:+ */
+/* By: ljiriste <marvin@42.fr> +#+ +:+ +#+ */
+/* +#+#+#+#+#+ +#+ */
+/* Created: 2024/03/28 09:39:55 by ljiriste #+# #+# */
+/* Updated: 2024/05/09 12:41:33 by ljiriste ### ########.fr */
+/* */
+/* ************************************************************************** */
+
+#include "philo.h"
+#include <pthread.h>
+#include <stddef.h>
+#include <stdlib.h>
+
+// Shortcircuiting does no harm here as when any mutex_init fails
+// the program will end anyway.
+int init(t_diner *diner, pthread_t **threads)
+{
+ const size_t count = diner->setting.philo_count;
+ size_t i;
+
+ init_mutex(&diner->setting.terminal_lock, NULL);
+ init_mutex(&diner->setting.end_lock, NULL);
+ diner->philos = malloc(sizeof(*diner->philos) * count);
+ diner->forks = malloc(sizeof(*diner->forks) * count);
+ *threads = malloc(sizeof(**threads) * count);
+ if (!diner->philos || !diner->forks || !*threads)
+ return (1);
+ i = 0;
+ while (i < count)
+ {
+ init_mutex(diner->forks + i, NULL);
+ init_mutex(&diner->philos[i].philo_lock, NULL);
+ diner->philos[i].forks[i % 2] = diner->forks + i;
+ diner->philos[i].forks[(i + 1) % 2] = diner->forks
+ + (i + 1) % count;
+ diner->philos[i].settings = &diner->setting;
+ diner->philos[i].last_eaten = 0;
+ diner->philos[i].id = i + 1;
+ diner->philos[i].num_eaten = 0;
+ ++i;
+ }
+ return (!all_mutexes_initialized(diner));
+}
+
+void cleanup(t_diner *diner, pthread_t *threads)
+{
+ const size_t count = diner->setting.philo_count;
+ size_t i;
+
+ i = 0;
+ if (diner->philos)
+ {
+ while (i < count)
+ {
+ pthread_join(threads[i], NULL);
+ destroy_mutex(&diner->philos[i++].philo_lock);
+ }
+ }
+ i = 0;
+ if (diner->forks)
+ while (i < count)
+ destroy_mutex(diner->forks + (i++));
+ destroy_mutex(&diner->setting.terminal_lock);
+ destroy_mutex(&diner->setting.end_lock);
+ free(diner->philos);
+ free(diner->forks);
+ free(threads);
+ return ;
+}
--- /dev/null
+/* ************************************************************************** */
+/* */
+/* ::: :::::::: */
+/* mutex.c :+: :+: :+: */
+/* +:+ +:+ +:+ */
+/* By: ljiriste <marvin@42.fr> +#+ +:+ +#+ */
+/* +#+#+#+#+#+ +#+ */
+/* Created: 2024/03/28 10:35:16 by ljiriste #+# #+# */
+/* Updated: 2024/03/28 15:12:37 by ljiriste ### ########.fr */
+/* */
+/* ************************************************************************** */
+
+#include "philo.h"
+#include <pthread.h>
+
+int init_mutex(t_mutex *mutex, pthread_mutexattr_t *attr)
+{
+ mutex->is_init = !(pthread_mutex_init(&mutex->mutex, attr));
+ return (mutex->is_init);
+}
+
+void destroy_mutex(t_mutex *mutex)
+{
+ if (mutex->is_init)
+ pthread_mutex_destroy(&mutex->mutex);
+ mutex->is_init = 0;
+}
+
+int mutex_lock(t_mutex *mutex)
+{
+ if (!mutex->is_init)
+ return (1);
+ return (pthread_mutex_lock(&mutex->mutex));
+}
+
+int mutex_unlock(t_mutex *mutex)
+{
+ if (!mutex->is_init)
+ return (1);
+ return (pthread_mutex_unlock(&mutex->mutex));
+}
+
+int all_mutexes_initialized(t_diner *diner)
+{
+ int res;
+ size_t i;
+
+ res = 1;
+ res = res && diner->setting.terminal_lock.is_init;
+ res = res && diner->setting.end_lock.is_init;
+ i = 0;
+ while (i < diner->setting.philo_count)
+ {
+ res = res && diner->forks[i].is_init;
+ res = res && diner->philos[i].philo_lock.is_init;
+ ++i;
+ }
+ return (res);
+}
--- /dev/null
+/* ************************************************************************** */
+/* */
+/* ::: :::::::: */
+/* pars_arg.c :+: :+: :+: */
+/* +:+ +:+ +:+ */
+/* By: ljiriste <marvin@42.fr> +#+ +:+ +#+ */
+/* +#+#+#+#+#+ +#+ */
+/* Created: 2024/03/28 11:29:47 by ljiriste #+# #+# */
+/* Updated: 2024/05/09 10:38:14 by ljiriste ### ########.fr */
+/* */
+/* ************************************************************************** */
+
+#include "parsing.h"
+#include <stddef.h>
+#include <stdlib.h>
+
+int parse_arg_usec(useconds_t *res, const char *arg)
+{
+ t_big_uint tmp;
+
+ if (inner_parse_arg(&tmp, arg))
+ return (1);
+ tmp *= 1000;
+ if (tmp > (useconds_t)-1)
+ return (1);
+ *res = (useconds_t)tmp;
+ return (0);
+}
+
+#if SIZE_MAX > UINT_MAX
+
+int parse_arg_size(size_t *res, const char *arg)
+{
+ t_big_uint tmp;
+
+ if (inner_parse_arg(&tmp, arg))
+ return (1);
+ if (tmp > (size_t)-1)
+ return (1);
+ *res = (size_t)tmp;
+ return (0);
+}
+
+#else
+
+int parse_arg_size(size_t *res, const char *arg)
+{
+ t_big_uint tmp;
+
+ if (inner_parse_arg(&tmp, arg))
+ return (1);
+ *res = (size_t)tmp;
+ return (0);
+}
+
+#endif //SIZE_MAX > UINT_MAX
+
+#if UINT_MAX > SIZE_MAX
+
+int parse_arg_uint(unsigned int *res, const char *arg)
+{
+ t_big_uint tmp;
+
+ if (inner_parse_arg(&tmp, arg))
+ return (1);
+ if (tmp > (unsigned int)-1)
+ return (1);
+ *res = (unsigned int)tmp;
+ return (0);
+}
+
+#else
+
+int parse_arg_uint(unsigned int *res, const char *arg)
+{
+ t_big_uint tmp;
+
+ if (inner_parse_arg(&tmp, arg))
+ return (1);
+ *res = (unsigned int)tmp;
+ return (0);
+}
+
+#endif //UINT_MAX > SIZE_MAX
--- /dev/null
+/* ************************************************************************** */
+/* */
+/* ::: :::::::: */
+/* parsing.c :+: :+: :+: */
+/* +:+ +:+ +:+ */
+/* By: ljiriste <marvin@42.fr> +#+ +:+ +#+ */
+/* +#+#+#+#+#+ +#+ */
+/* Created: 2024/03/26 09:31:35 by ljiriste #+# #+# */
+/* Updated: 2024/05/09 12:35:58 by ljiriste ### ########.fr */
+/* */
+/* ************************************************************************** */
+
+#include "parsing.h"
+#include "philo.h"
+#include <stddef.h>
+#include <stdlib.h>
+
+int is_sane(t_settings *settings)
+{
+ return (settings->philo_count > 0);
+}
+
+int parse_input(t_settings *settings, int argc, char **argv)
+{
+ if (argc != 5 && argc != 6)
+ return (1);
+ settings->end = 0;
+ settings->should_check_hunger = 0;
+ if (parse_arg_size(&settings->philo_count, argv[1])
+ || parse_arg_usec(&settings->time_to_die, argv[2])
+ || parse_arg_usec(&settings->time_to_eat, argv[3])
+ || parse_arg_usec(&settings->time_to_sleep, argv[4]))
+ {
+ return (1);
+ }
+ if (argc == 6)
+ {
+ if (parse_arg_uint(&settings->min_eat_num, argv[5]))
+ return (1);
+ settings->should_check_hunger = 1;
+ }
+ return (!is_sane(settings));
+}
--- /dev/null
+/* ************************************************************************** */
+/* */
+/* ::: :::::::: */
+/* parsing.h :+: :+: :+: */
+/* +:+ +:+ +:+ */
+/* By: ljiriste <marvin@42.fr> +#+ +:+ +#+ */
+/* +#+#+#+#+#+ +#+ */
+/* Created: 2024/03/28 11:24:41 by ljiriste #+# #+# */
+/* Updated: 2024/03/28 11:40:04 by ljiriste ### ########.fr */
+/* */
+/* ************************************************************************** */
+
+#ifndef PARSING_H
+# define PARSING_H
+
+# include <limits.h>
+# include <stddef.h>
+# include <unistd.h>
+
+// This done to make parsing easier/non-repetitive
+// but it cannot be a part of parsing.c as more files depend on it
+//
+// This assumes useconds_t is not the biggest type
+// The assumption is made because it is not easy to check
+# if SIZE_MAX > UINT_MAX
+
+typedef size_t t_big_uint;
+
+# else
+
+typedef unsigned int t_big_uint;
+
+# endif //SIZE_MAX > UINT_MAX
+
+t_big_uint ft_atobui(const char *str);
+char *ft_buitoa(t_big_uint num);
+int same_number(const char *num1, const char *num2);
+
+int inner_parse_arg(t_big_uint *res, const char *arg);
+
+int parse_arg_usec(useconds_t *res, const char *arg);
+int parse_arg_size(size_t *res, const char *arg);
+int parse_arg_uint(unsigned int *res, const char *arg);
+
+#endif //PARSING_H
--- /dev/null
+/* ************************************************************************** */
+/* */
+/* ::: :::::::: */
+/* parsing_misc.c :+: :+: :+: */
+/* +:+ +:+ +:+ */
+/* By: ljiriste <marvin@42.fr> +#+ +:+ +#+ */
+/* +#+#+#+#+#+ +#+ */
+/* Created: 2024/03/26 10:42:02 by ljiriste #+# #+# */
+/* Updated: 2024/03/28 11:53:49 by ljiriste ### ########.fr */
+/* */
+/* ************************************************************************** */
+
+#include "parsing.h"
+#include <stdlib.h>
+
+t_big_uint ft_atobui(const char *str)
+{
+ t_big_uint res;
+
+ res = 0;
+ if (!str)
+ return (res);
+ while (*str == ' ' || *str == '+')
+ ++str;
+ while ('0' <= *str && *str <= '9')
+ {
+ res *= 10;
+ res += *str - '0';
+ ++str;
+ }
+ return (res);
+}
+
+char *ft_buitoa(t_big_uint num)
+{
+ char *res;
+ size_t size;
+ t_big_uint tmp;
+
+ tmp = num;
+ size = 0;
+ while (tmp > 0)
+ {
+ ++size;
+ tmp /= 10;
+ }
+ if (size == 0)
+ size = 1;
+ res = malloc(size + 1);
+ if (!res)
+ return (NULL);
+ res[size--] = '\0';
+ if (num == 0)
+ res[0] = '0';
+ while (num > 0)
+ {
+ res[size--] = '0' + num % 10;
+ num /= 10;
+ }
+ return (res);
+}
+
+int same_number(const char *num1, const char *num2)
+{
+ while (*num1 == ' ' || *num1 == '+' || *num1 == '0')
+ ++num1;
+ while (*num2 == ' ' || *num2 == '+' || *num2 == '0')
+ ++num2;
+ while (*num1 && *num2 && *num1 == *num2)
+ {
+ ++num1;
+ ++num2;
+ }
+ return (!((int)*num1 - (int)*num2));
+}
--- /dev/null
+/* ************************************************************************** */
+/* */
+/* ::: :::::::: */
+/* philo.c :+: :+: :+: */
+/* +:+ +:+ +:+ */
+/* By: ljiriste <marvin@42.fr> +#+ +:+ +#+ */
+/* +#+#+#+#+#+ +#+ */
+/* Created: 2024/05/09 08:45:21 by ljiriste #+# #+# */
+/* Updated: 2024/05/09 10:25:46 by ljiriste ### ########.fr */
+/* */
+/* ************************************************************************** */
+
+#include "philo.h"
+#include <unistd.h>
+
+static void philo_think(t_philo *philo)
+{
+ report(philo, "is thinking");
+ return ;
+}
+
+static void philo_eat(t_philo *philo)
+{
+ mutex_lock(philo->forks[0]);
+ if (report(philo, "has taken a fork"))
+ {
+ mutex_unlock(philo->forks[0]);
+ return ;
+ }
+ mutex_lock(philo->forks[1]);
+ report(philo, "has taken a fork");
+ mutex_lock(&philo->philo_lock);
+ philo->last_eaten = usecs_since_start(philo->settings->start);
+ ++philo->num_eaten;
+ mutex_unlock(&philo->philo_lock);
+ if (!report(philo, "is eating"))
+ usleep(philo->settings->time_to_eat);
+ mutex_unlock(philo->forks[0]);
+ mutex_unlock(philo->forks[1]);
+ return ;
+}
+
+static void philo_sleep(t_philo *philo)
+{
+ if (!report(philo, "is sleeping"))
+ usleep(philo->settings->time_to_sleep);
+ return ;
+}
+
+void *be_a_philosopher(void *void_philo)
+{
+ t_philo *const philo = void_philo;
+
+ while (!end(philo->settings))
+ {
+ philo_think(philo);
+ philo_eat(philo);
+ philo_sleep(philo);
+ }
+ return (NULL);
+}
--- /dev/null
+/* ************************************************************************** */
+/* */
+/* ::: :::::::: */
+/* philo.h :+: :+: :+: */
+/* +:+ +:+ +:+ */
+/* By: ljiriste <marvin@42.fr> +#+ +:+ +#+ */
+/* +#+#+#+#+#+ +#+ */
+/* Created: 2024/03/22 11:10:17 by ljiriste #+# #+# */
+/* Updated: 2024/05/09 12:21:57 by ljiriste ### ########.fr */
+/* */
+/* ************************************************************************** */
+
+#ifndef PHILO_H
+# define PHILO_H
+
+# include <stddef.h>
+# include <pthread.h>
+# include <sys/time.h>
+# include <unistd.h>
+
+typedef struct s_mutex
+{
+ int is_init;
+ pthread_mutex_t mutex;
+} t_mutex;
+
+// The .start member holds the result of gettimeofday
+// Every other member that holds time info counts
+// microseconds from that point (in other structures also)
+typedef struct s_settings
+{
+ int end;
+ int should_check_hunger;
+ unsigned int min_eat_num;
+ useconds_t time_to_die;
+ useconds_t time_to_eat;
+ useconds_t time_to_sleep;
+ size_t philo_count;
+ struct timeval start;
+ t_mutex terminal_lock;
+ t_mutex end_lock;
+} t_settings;
+
+typedef struct s_philosopher
+{
+ unsigned int num_eaten;
+ size_t id;
+ useconds_t last_eaten;
+ t_settings *settings;
+ t_mutex *forks[2];
+ t_mutex philo_lock;
+} t_philo;
+
+typedef struct s_diner
+{
+ t_settings setting;
+ t_philo *philos;
+ t_mutex *forks;
+} t_diner;
+
+enum e_end
+{
+ death,
+ well_fed,
+};
+
+int parse_input(t_settings *settings, int argc, char **argv);
+
+int init(t_diner *diner, pthread_t **threads);
+void cleanup(t_diner *diner, pthread_t *threads);
+
+int init_mutex(t_mutex *mutex, pthread_mutexattr_t *attr);
+void destroy_mutex(t_mutex *mutex);
+int mutex_lock(t_mutex *mutex);
+int mutex_unlock(t_mutex *mutex);
+int all_mutexes_initialized(t_diner *diner);
+
+int end(t_settings *set);
+useconds_t usecs_since_start(struct timeval start);
+int report(t_philo *philo, const char *str);
+void print_timestamp(t_philo *philo, const char *str);
+
+void *be_a_philosopher(void *philo);
+
+#endif //PHILO_H