aboutsummaryrefslogtreecommitdiff
path: root/src/philo/philo.py
blob: afb6362b4fff597810e2833059dbe102c44b96f4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
# ############################################################################ #
#                                                                              #
#                                                         :::      ::::::::    #
#    philo.py                                           :+:      :+:    :+:    #
#                                                     +:+ +:+         +:+      #
#    By: cacharle <me@cacharle.xyz>                 +#+  +:+       +#+         #
#                                                 +#+#+#+#+#+   +#+            #
#    Created: 2020/10/01 10:52:56 by cacharle          #+#    #+#              #
#    Updated: 2020/10/01 11:19:23 by cacharle         ###   ########.fr        #
#                                                                              #
# ############################################################################ #

import itertools

from . import error
from philo.event import Event


class Philo:
    def __init__(
        self,
        id_:           int,
        timeout_die:   int,
        timeout_eat:   int,
        timeout_sleep: int,
        meal_num:      int
    ):
        self.logs          = []
        self.id             = id_
        self._timeout_die   = timeout_die
        self._timeout_eat   = timeout_eat
        self._timeout_sleep = timeout_sleep
        self._meal_num      = meal_num

    @property
    def used_forks(self):
        """ The number of forks currently used by the philosopher """
        if len(self.logs) < 1:
            return 0
        if self.logs[-1].event is Event.EAT:
            return 2
        if self.logs[-1].event is Event.FORK:
            if len(self.logs) > 1 and self.logs[-2].event is Event.FORK:
                return 2
            else:
                return 1
        return 0

    def check(self):
        """ Check log for errors

            - Must take 2 forks before eating
            - The delay between the taking of the second fork and eating should be almost 0
            - State switch should be
              thinking -> take fork -> take fork -> eat * meal_num -> sleep -> repeat
            - Should die when starving: last log timestamp - timeout_death > last_time_eat
        """

        if len(self.logs) == 0:
            return

    # def _check_fork_taking(self):
    #     for l1, l2, l3 in zip(self.logs, self.logs[1:], self.logs[2:]):
    #         if l1.event is Event.FORK and (l2.event is not Event.FORK or l2.event is not Event.EAT):
    #             self._raise("should take 2 forks then eat")
    #
    # def _check_meal(self):
    #     grouped = [(e, list(g)) for e, g in itertools.groupby(self.logs, (lambda x: x.event))]
    #     for e, g in grouped:
    #         if e is Event.EATING:
    #             if len(g) != self._meal_num:
    #                 raise error.Log(self.logs, "should eat {} times".format(self._meal_num))
    #         else:
    #             if len(g) != 1:
    #                 raise error.Log(self.logs, "event {} should occur 1 time".format(Event.to_string(e)))
    #
    # def _check_order(self):
    #     for l1, l2 in zip(self.logs, self.logs[1:]):
    #         if l2.event is Event.DIED:
    #             break
    #         if l1.event is Event.EATING and l2.event is Event.EATING:
    #             if l2.timestamp - l1.timestamp > self._timeout_eat:
    #                 raise ValueError
    #         second, timeout = {
    #             Event.THINKING: (Event.EATING, None),
    #             Event.EATING:   (Event.SLEEPING, self._timeout_eat),
    #             Event.SLEEPING: (Event.THINKING, self._timeout_sleep)
    #         }[l1.event]
    #         if l2.event is not second:
    #             raise error.Log(self.logs, "invalid switch {} -> {}".format(l1.event, l2.event))
    #         if timeout is not None and l2.timestamp - l1.timestamp > timeout:
    #             raise ValueError
    #
    # def _check_death_timeout(self):
    #     last_eat = None
    #     for log in reversed(self.logs):
    #         if log.event is Event.EATING:
    #             last_eat = log
    #             break
    #     last = self.logs[-1]
    #     if last_eat is not None and last_eat is not last:
    #         if last.timestamp - last_eat.timestamp > self._timeout_die + 10:
    #             raise error.Log(self.logs, "{} should be dead {} - {} > {}".format(
    #                 self.id, last.timestamp, last_eat.timestamp, self._timeout_die + 10))

    def _raise(self, msg):
        """ Helper to raise Log errrors"""
        raise error.Log(self.logs, msg)