Implement mandatory part of philosophers
authorLukas Jiriste <ljiriste@student.42prague.com>
Thu, 7 Mar 2024 16:52:47 +0000 (17:52 +0100)
committerLukas Jiriste <ljiriste@student.42prague.com>
Thu, 7 Mar 2024 16:52:47 +0000 (17:52 +0100)
Sorry for big commit, I wrote it in one session and didn't encounter
an apropriate point for partial commit.
This needs to be tested and refactored to pass the 42 Norm.

philo/Makefile [new file with mode: 0644]
philo/main.c [new file with mode: 0644]

diff --git a/philo/Makefile b/philo/Makefile
new file mode 100644 (file)
index 0000000..1a6a033
--- /dev/null
@@ -0,0 +1,30 @@
+CC := gcc
+
+CFLAGS := -Wall -Wextra -Werror -Wpedantic
+#-std=c99 
+NAME := philo
+
+SRCDIR := .
+SRCS := main.c
+SRCS := $(addprefix $(SRCDIR)/, $(SRCS))
+OBJS := $(SRCS:%.c=%.o)
+
+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)
diff --git a/philo/main.c b/philo/main.c
new file mode 100644 (file)
index 0000000..31fb432
--- /dev/null
@@ -0,0 +1,353 @@
+/* ************************************************************************** */
+/*                                                                            */
+/*                                                        :::      ::::::::   */
+/*   main.c                                             :+:      :+:    :+:   */
+/*                                                    +:+ +:+         +:+     */
+/*   By: ljiriste <marvin@42.fr>                    +#+  +:+       +#+        */
+/*                                                +#+#+#+#+#+   +#+           */
+/*   Created: 2024/03/05 12:49:40 by ljiriste          #+#    #+#             */
+/*   Updated: 2024/03/07 17:51:19 by ljiriste         ###   ########.fr       */
+/*                                                                            */
+/* ************************************************************************** */
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <pthread.h>
+#include <sys/time.h>
+#include <stdio.h>
+#include <unistd.h>
+
+//     unsigned long would be more apropriate for min_eats_num
+//     but for the ease of parsing I'm using size_t
+typedef struct s_settings
+{
+       int                             end;
+       size_t                  min_eats_num;
+       size_t                  philo_count;
+       suseconds_t             time_to_die;
+       suseconds_t             time_to_eat;
+       suseconds_t             time_to_sleep;
+       suseconds_t             program_start;
+       pthread_mutex_t terminal;
+}                                      t_settings;
+
+typedef struct s_philo
+{
+       size_t                  times_eaten;
+       size_t                  id;
+       suseconds_t             last_eaten;
+       pthread_mutex_t *forks[2];
+       t_settings              *settings;
+}                                      t_philo;
+
+suseconds_t    cur_time_sus(void)
+{
+       struct timeval  tv;
+
+       gettimeofday(&tv, NULL);
+       return (tv.tv_sec * 1000000 + tv.tv_usec);
+}
+
+void   thread_print(t_settings *set, size_t id, const char *message)
+{
+       pthread_mutex_lock(&set->terminal);
+       if (set->end)
+       {
+               pthread_mutex_unlock(&set->terminal);
+               return ;
+       }
+       printf("%li %li %s\n", (cur_time_sus() - set->program_start) / 1000, id, message);
+       pthread_mutex_unlock(&set->terminal);
+       return ;
+}
+
+void   philo_sleep(t_settings *set, size_t id)
+{
+       thread_print(set, id, "is sleeping");
+       usleep(set->time_to_sleep);
+       return ;
+}
+
+void   philo_think(t_settings *set, size_t id)
+{
+       thread_print(set, id, "is thinking");
+       return ;
+}
+
+int    philo_eat(t_philo *philo)
+{
+       pthread_mutex_lock(philo->forks[0]);
+       thread_print(philo->settings, philo->id, "has taken a fork");
+       pthread_mutex_lock(philo->forks[1]);
+       thread_print(philo->settings, philo->id, "has taken a fork");
+       thread_print(philo->settings, philo->id, "is eating");
+       philo->last_eaten = cur_time_sus();
+       usleep(philo->settings->time_to_eat);
+       pthread_mutex_unlock(philo->forks[0]);
+       pthread_mutex_unlock(philo->forks[1]);
+       return (0);
+}
+
+void   *be_a_philosopher(void *inp)
+{
+       t_philo *philo;
+
+       philo = (t_philo *)inp;
+       philo->last_eaten = philo->settings->program_start;
+       while (!philo->settings->end)
+       {
+               philo_think(philo->settings, philo->id);
+               if (philo_eat(philo) == 1)
+                       break ;
+               ++philo->times_eaten;
+               if (philo->times_eaten == philo->settings->min_eats_num)
+                       return (NULL);
+               philo_sleep(philo->settings, philo->id);
+       }
+       return (NULL);
+}
+
+pthread_mutex_t        *init_forks(size_t count)
+{
+       pthread_mutex_t *forks;
+
+       forks = malloc(sizeof(pthread_mutex_t) * count);
+       if (!forks)
+               return (NULL);
+       while (count > 0)
+       {
+               pthread_mutex_init(&forks[--count], NULL);
+       }
+       return (forks);
+}
+
+t_philo        *build_philos(pthread_mutex_t *forks, t_settings *set)
+{
+       t_philo *philos;
+       size_t  count;
+
+       if (!forks)
+               return (NULL);
+       count = set->philo_count;
+       philos = malloc(sizeof(t_philo) * count);
+       if (!philos)
+               return (NULL);
+       philos[0].forks[1] = forks + count - 1;
+       while (count > 0)
+       {
+               --count;
+               philos[count].id = count + 1;
+               philos->times_eaten = 0;
+               philos[count].settings = set;
+               philos[count].forks[0] = forks + count - count % 2;
+               if (count > 0)
+                       philos[count].forks[1] = forks + count - (count + 1) % 2;
+       }
+       return philos;
+}
+
+size_t ft_atos(const char *str)
+{
+       size_t  res;
+
+       res = 0;
+       while (*str == ' ')
+               ++str;
+       if (*str == '+')
+               ++str;
+       while ('0' <= *str && *str <= '9')
+       {
+               res *= 10;
+               res += *str - '0';
+               ++str;
+       }
+       return (res);
+}
+
+static size_t  size_needed(int n)
+{
+       size_t  res;
+
+       res = 1;
+       if (n == 0)
+               return (2);
+       if (n < 0)
+               ++res;
+       while (n != 0)
+       {
+               ++res;
+               n /= 10;
+       }
+       return (res);
+}
+
+char   *ft_stoa(int n)
+{
+       size_t  size;
+       char    *res;
+
+       size = size_needed(n);
+       res = malloc(size * sizeof(char));
+       if (res == NULL)
+               return (res);
+       res[--size] = '\0';
+       if (n == 0 && res)
+               res[0] = '0';
+       else if (n < 0 && res)
+       {
+               res[0] = '-';
+               res[--size] = '0' - (n % 10);
+               n = -(n / 10);
+       }
+       while (n > 0 && res)
+       {
+               res[--size] = '0' + (n % 10);
+               n /= 10;
+       }
+       return (res);
+}
+
+int    ft_strcmp(const char *s1, const char *s2)
+{
+       size_t  i;
+
+       i = 0;
+       while (s1[i] == s2[i] && s1[i] && s2[i])
+               ++i;
+       return ((unsigned char)s1[i] - (unsigned char)s2[i]);
+}
+
+int    parse_args(const char *arg, size_t *val)
+{
+       int             res;
+       char    *check_str;
+
+       *val = ft_atos(arg);
+       check_str = ft_stoa(*val);
+       while (*arg == ' ' || *arg == '+')
+               ++arg;
+       res = ft_strcmp(check_str, arg);
+       free(check_str);
+       return (res);
+}
+
+int    parse_arg(const char *arg, suseconds_t *val)
+{
+       size_t  tmp;
+
+       if (parse_args(arg, &tmp))
+               return (1);
+       *val = tmp;
+       if (*val != ((*val * 1000) / 1000))
+               return (1);
+       *val *= 1000;
+       return (0);
+}
+
+int    parse_input(int argc, char **argv, t_settings *set)
+{
+       int     res;
+
+       if (argc != 5 && argc != 6)
+               return (0);
+       res = 0;
+       res = res || parse_args(argv[1], &set->philo_count);
+       res = res || parse_arg(argv[2], &set->time_to_die);
+       res = res || parse_arg(argv[3], &set->time_to_eat);
+       res = res || parse_arg(argv[4], &set->time_to_sleep);
+       if (argc == 6)
+               res = res || parse_args(argv[5], &set->min_eats_num);
+       else
+               set->min_eats_num = 0;
+       return (res);
+}
+
+int    init_settings(int argc, char **argv, t_settings *set)
+{
+       pthread_mutex_init(&set->terminal, NULL);
+       set->end = 0;
+       return (parse_input(argc, argv, set));
+}
+
+
+void   cleanup(pthread_mutex_t *forks, t_philo *philos, pthread_t *threads, t_settings *set)
+{
+       size_t  count;
+
+       count = set->philo_count;
+       while (count > 0)
+       {
+               --count;
+               pthread_mutex_destroy(&forks[count]);
+       }
+       pthread_mutex_destroy(&set->terminal);
+       free(forks);
+       free(philos);
+       free(threads);
+       return ;
+}
+
+void   create_philos(t_philo *philos, pthread_t *threads, size_t count)
+{
+       while (count > 0)
+       {
+               --count;
+               pthread_create(threads + count, NULL, be_a_philosopher, philos + count);
+       }
+       return ;
+}
+
+void   check_death(t_philo *philos, t_settings *set)
+{
+       suseconds_t     oldest;
+       suseconds_t     now;
+       size_t          i;
+
+       while (1)
+       {
+               i = 0;
+               now = cur_time_sus();
+               while (i < set->philo_count)
+               {
+                       oldest = philos[0].last_eaten;
+                       if((now - philos[i].last_eaten) > set->time_to_die)
+                       {
+                               pthread_mutex_lock(&set->terminal);
+                               printf("%li %lu %s\n", (cur_time_sus() - set->program_start) / 1000, i + 1, "died");
+                               set->end = 1;
+                               pthread_mutex_unlock(&set->terminal);
+                               return ;
+                       }
+                       if (oldest > philos[i].last_eaten)
+                               oldest = philos[i].last_eaten;
+                       ++i;
+               }
+               now = cur_time_sus();
+               //usleep(now - oldest);
+       }
+}
+
+int    main(int argc, char **argv)
+{
+       t_settings              settings;
+       pthread_mutex_t *forks;
+       pthread_t               *threads;
+       t_philo                 *philos;
+
+       if (init_settings(argc, argv, &settings))
+               return (1);
+       threads = malloc(sizeof(pthread_t) * settings.philo_count);
+       forks = init_forks(settings.philo_count);
+       philos = build_philos(forks, &settings);
+       if (!forks || !threads || !philos)
+       {
+               cleanup(forks, philos, threads, &settings);
+               return (2);
+       }
+       settings.program_start = cur_time_sus();
+       create_philos(philos, threads, settings.philo_count);
+       usleep(settings.time_to_die / 10);
+       check_death(philos, &settings);
+       cleanup(forks, philos, threads, &settings);
+       return (0);
+}