๐Ÿด 5 Philosophers (Classic Table)

Dining Philosophers Problem simulated with virtual time
๐ŸŽฌ GenServerVirtualTime Actor Simulation
This diagram shows the interactions between philosophers and forks over virtual time. All synchronous fork requests, grants, and releases are captured.

The Problem

5 philosophers sit at a round table with 5 forks between them. Each philosopher alternates between thinking and eating. To eat, a philosopher needs both adjacent forks.

Challenge: How to prevent deadlock when all philosophers simultaneously grab their left fork?

Solution: Asymmetric fork acquisition - odd philosophers grab right fork first, even philosophers grab left fork first.

โ†’โ†’ Solid Arrow
Synchronous call (request/release fork)
Activation Box
Fork is processing a request
Timestamps
Virtual time progression
sequenceDiagram philosopher_4->>philosopher_4: {:start_hungry, :fork_4, :fork_0} Note over philosopher_4,philosopher_4: t=150ms philosopher_3->>philosopher_3: {:start_hungry, :fork_4, :fork_3} Note over philosopher_3,philosopher_3: t=150ms philosopher_2->>philosopher_2: {:start_hungry, :fork_2, :fork_3} Note over philosopher_2,philosopher_2: t=150ms philosopher_1->>philosopher_1: {:start_hungry, :fork_2, :fork_1} Note over philosopher_1,philosopher_1: t=150ms philosopher_0->>philosopher_0: {:start_hungry, :fork_0, :fork_1} Note over philosopher_0,philosopher_0: t=150ms philosopher_4->>philosopher_4: {:mumble, "I'm hungry!"} Note over philosopher_4,philosopher_4: t=150ms philosopher_3->>philosopher_3: {:mumble, "I'm hungry!"} Note over philosopher_3,philosopher_3: t=150ms philosopher_2->>philosopher_2: {:mumble, "I'm hungry!"} Note over philosopher_2,philosopher_2: t=150ms philosopher_1->>philosopher_1: {:mumble, "I'm hungry!"} Note over philosopher_1,philosopher_1: t=150ms philosopher_0->>philosopher_0: {:mumble, "I'm hungry!"} Note over philosopher_0,philosopher_0: t=150ms philosopher_4->>fork_4: {:request_fork, :philosopher_4} Note over philosopher_4,fork_4: t=150ms philosopher_3->>fork_4: {:request_fork, :philosopher_3} Note over philosopher_3,fork_4: t=150ms philosopher_2->>fork_2: {:request_fork, :philosopher_2} Note over philosopher_2,fork_2: t=150ms philosopher_1->>fork_2: {:request_fork, :philosopher_1} Note over philosopher_1,fork_2: t=150ms philosopher_0->>fork_0: {:request_fork, :philosopher_0} Note over philosopher_0,fork_0: t=300ms philosopher_4->>philosopher_4: {:start_hungry, :fork_4, :fork_0} Note over philosopher_4,philosopher_4: t=300ms philosopher_3->>philosopher_3: {:start_hungry, :fork_4, :fork_3} Note over philosopher_3,philosopher_3: t=300ms philosopher_2->>philosopher_2: {:start_hungry, :fork_2, :fork_3} Note over philosopher_2,philosopher_2: t=300ms philosopher_1->>philosopher_1: {:start_hungry, :fork_2, :fork_1} Note over philosopher_1,philosopher_1: t=300ms philosopher_0->>philosopher_0: {:start_hungry, :fork_0, :fork_1} Note over philosopher_0,philosopher_0: t=300ms fork_4->>philosopher_4: {:fork_granted, :fork_4} Note over fork_4,philosopher_4: t=300ms fork_2->>philosopher_2: {:fork_granted, :fork_2} Note over fork_2,philosopher_2: t=300ms fork_0->>philosopher_0: {:fork_granted, :fork_0} Note over fork_0,philosopher_0: t=300ms fork_4->>philosopher_3: {:fork_denied, :fork_4} Note over fork_4,philosopher_3: t=300ms fork_2->>philosopher_1: {:fork_denied, :fork_2} Note over fork_2,philosopher_1: t=300ms philosopher_4->>fork_4: {:request_fork, :philosopher_4} Note over philosopher_4,fork_4: t=300ms philosopher_3->>fork_4: {:request_fork, :philosopher_3} Note over philosopher_3,fork_4: t=300ms philosopher_2->>fork_2: {:request_fork, :philosopher_2} Note over philosopher_2,fork_2: t=300ms philosopher_1->>fork_2: {:request_fork, :philosopher_1} Note over philosopher_1,fork_2: t=300ms philosopher_0->>fork_0: {:request_fork, :philosopher_0} Note over philosopher_0,fork_0: t=300ms philosopher_4->>fork_0: {:request_fork, :philosopher_4} Note over philosopher_4,fork_0: t=300ms philosopher_2->>fork_3: {:request_fork, :philosopher_2} Note over philosopher_2,fork_3: t=300ms philosopher_0->>fork_1: {:request_fork, :philosopher_0} Note over philosopher_0,fork_1: t=300ms fork_4->>philosopher_4: {:fork_denied, :fork_4} Note over fork_4,philosopher_4: t=300ms fork_2->>philosopher_2: {:fork_denied, :fork_2} Note over fork_2,philosopher_2: t=300ms fork_0->>philosopher_0: {:fork_denied, :fork_0} Note over fork_0,philosopher_0: t=300ms fork_3->>philosopher_2: {:fork_granted, :fork_3} Note over fork_3,philosopher_2: t=300ms fork_4->>philosopher_3: {:fork_denied, :fork_4} Note over fork_4,philosopher_3: t=300ms fork_0->>philosopher_4: {:fork_denied, :fork_0} Note over fork_0,philosopher_4: t=300ms fork_1->>philosopher_0: {:fork_granted, :fork_1} Note over fork_1,philosopher_0: t=300ms fork_2->>philosopher_1: {:fork_denied, :fork_2} Note over fork_2,philosopher_1: t=300ms philosopher_2->>fork_2: {:release_fork, :philosopher_2} Note over philosopher_2,fork_2: t=300ms philosopher_4->>fork_4: {:release_fork, :philosopher_4} Note over philosopher_4,fork_4: t=300ms philosopher_0->>fork_0: {:release_fork, :philosopher_0} Note over philosopher_0,fork_0: t=300ms philosopher_0->>philosopher_0: {:mumble, "I'm full!"} Note over philosopher_0,philosopher_0: t=375ms philosopher_0->>fork_0: {:release_fork, :philosopher_0} Note over philosopher_0,fork_0: t=375ms philosopher_2->>philosopher_2: {:mumble, "I'm full!"} Note over philosopher_2,philosopher_2: t=375ms philosopher_0->>fork_1: {:release_fork, :philosopher_0} Note over philosopher_0,fork_1: t=375ms philosopher_2->>fork_2: {:release_fork, :philosopher_2} Note over philosopher_2,fork_2: t=375ms philosopher_2->>fork_3: {:release_fork, :philosopher_2} Note over philosopher_2,fork_3: t=375ms philosopher_0->>philosopher_0: {:start_hungry, :fork_0, :fork_1} Note over philosopher_0,philosopher_0: t=450ms philosopher_1->>philosopher_1: {:start_hungry, :fork_2, :fork_1} Note over philosopher_1,philosopher_1: t=450ms philosopher_2->>philosopher_2: {:start_hungry, :fork_2, :fork_3} Note over philosopher_2,philosopher_2: t=450ms philosopher_3->>philosopher_3: {:start_hungry, :fork_4, :fork_3} Note over philosopher_3,philosopher_3: t=450ms philosopher_4->>philosopher_4: {:start_hungry, :fork_4, :fork_0} Note over philosopher_4,philosopher_4: t=450ms philosopher_1->>fork_2: {:request_fork, :philosopher_1} Note over philosopher_1,fork_2: t=450ms philosopher_0->>fork_0: {:request_fork, :philosopher_0} Note over philosopher_0,fork_0: t=450ms philosopher_2->>fork_2: {:request_fork, :philosopher_2} Note over philosopher_2,fork_2: t=450ms philosopher_3->>fork_4: {:request_fork, :philosopher_3} Note over philosopher_3,fork_4: t=450ms philosopher_4->>fork_4: {:request_fork, :philosopher_4} Note over philosopher_4,fork_4: t=450ms fork_2->>philosopher_1: {:fork_granted, :fork_2} Note over fork_2,philosopher_1: t=450ms fork_0->>philosopher_0: {:fork_granted, :fork_0} Note over fork_0,philosopher_0: t=450ms fork_4->>philosopher_3: {:fork_granted, :fork_4} Note over fork_4,philosopher_3: t=450ms fork_2->>philosopher_2: {:fork_denied, :fork_2} Note over fork_2,philosopher_2: t=450ms philosopher_0->>fork_1: {:request_fork, :philosopher_0} Note over philosopher_0,fork_1: t=450ms fork_4->>philosopher_4: {:fork_denied, :fork_4} Note over fork_4,philosopher_4: t=450ms philosopher_1->>fork_1: {:request_fork, :philosopher_1} Note over philosopher_1,fork_1: t=450ms philosopher_3->>fork_3: {:request_fork, :philosopher_3} Note over philosopher_3,fork_3: t=450ms fork_1->>philosopher_0: {:fork_granted, :fork_1} Note over fork_1,philosopher_0: t=450ms fork_1->>philosopher_1: {:fork_denied, :fork_1} Note over fork_1,philosopher_1: t=450ms fork_3->>philosopher_3: {:fork_granted, :fork_3} Note over fork_3,philosopher_3: t=450ms philosopher_1->>fork_2: {:release_fork, :philosopher_1} Note over philosopher_1,fork_2: t=450ms philosopher_3->>philosopher_3: {:mumble, "I'm full!"} Note over philosopher_3,philosopher_3: t=525ms philosopher_0->>philosopher_0: {:mumble, "I'm full!"} Note over philosopher_0,philosopher_0: t=525ms philosopher_3->>fork_4: {:release_fork, :philosopher_3} Note over philosopher_3,fork_4: t=525ms philosopher_0->>fork_0: {:release_fork, :philosopher_0} Note over philosopher_0,fork_0: t=525ms philosopher_0->>fork_1: {:release_fork, :philosopher_0} Note over philosopher_0,fork_1: t=525ms philosopher_3->>fork_3: {:release_fork, :philosopher_3} Note over philosopher_3,fork_3: t=525ms philosopher_0->>philosopher_0: {:start_hungry, :fork_0, :fork_1} Note over philosopher_0,philosopher_0: t=600ms philosopher_4->>philosopher_4: {:start_hungry, :fork_4, :fork_0} Note over philosopher_4,philosopher_4: t=600ms philosopher_3->>philosopher_3: {:start_hungry, :fork_4, :fork_3} Note over philosopher_3,philosopher_3: t=600ms philosopher_2->>philosopher_2: {:start_hungry, :fork_2, :fork_3} Note over philosopher_2,philosopher_2: t=600ms philosopher_1->>philosopher_1: {:start_hungry, :fork_2, :fork_1} Note over philosopher_1,philosopher_1: t=600ms philosopher_4->>fork_4: {:request_fork, :philosopher_4} Note over philosopher_4,fork_4: t=600ms philosopher_2->>fork_2: {:request_fork, :philosopher_2} Note over philosopher_2,fork_2: t=600ms philosopher_0->>fork_0: {:request_fork, :philosopher_0} Note over philosopher_0,fork_0: t=600ms fork_4->>philosopher_4: {:fork_granted, :fork_4} Note over fork_4,philosopher_4: t=600ms philosopher_3->>fork_4: {:request_fork, :philosopher_3} Note over philosopher_3,fork_4: t=600ms philosopher_1->>fork_2: {:request_fork, :philosopher_1} Note over philosopher_1,fork_2: t=600ms fork_2->>philosopher_2: {:fork_granted, :fork_2} Note over fork_2,philosopher_2: t=600ms fork_0->>philosopher_0: {:fork_granted, :fork_0} Note over fork_0,philosopher_0: t=600ms fork_2->>philosopher_1: {:fork_denied, :fork_2} Note over fork_2,philosopher_1: t=600ms philosopher_4->>fork_0: {:request_fork, :philosopher_4} Note over philosopher_4,fork_0: t=600ms fork_4->>philosopher_3: {:fork_denied, :fork_4} Note over fork_4,philosopher_3: t=600ms philosopher_0->>fork_1: {:request_fork, :philosopher_0} Note over philosopher_0,fork_1: t=600ms philosopher_2->>fork_3: {:request_fork, :philosopher_2} Note over philosopher_2,fork_3: t=600ms fork_0->>philosopher_4: {:fork_denied, :fork_0} Note over fork_0,philosopher_4: t=600ms fork_3->>philosopher_2: {:fork_granted, :fork_3} Note over fork_3,philosopher_2: t=600ms philosopher_4->>fork_4: {:release_fork, :philosopher_4} Note over philosopher_4,fork_4: t=600ms fork_1->>philosopher_0: {:fork_granted, :fork_1} Note over fork_1,philosopher_0: t=600ms philosopher_2->>philosopher_2: {:mumble, "I'm full!"} Note over philosopher_2,philosopher_2: t=675ms philosopher_2->>fork_2: {:release_fork, :philosopher_2} Note over philosopher_2,fork_2: t=675ms philosopher_0->>philosopher_0: {:mumble, "I'm full!"} Note over philosopher_0,philosopher_0: t=675ms philosopher_2->>fork_3: {:release_fork, :philosopher_2} Note over philosopher_2,fork_3: t=675ms philosopher_0->>fork_0: {:release_fork, :philosopher_0} Note over philosopher_0,fork_0: t=675ms philosopher_0->>fork_1: {:release_fork, :philosopher_0} Note over philosopher_0,fork_1: t=675ms philosopher_4->>philosopher_4: {:start_hungry, :fork_4, :fork_0} Note over philosopher_4,philosopher_4: t=750ms philosopher_0->>philosopher_0: {:start_hungry, :fork_0, :fork_1} Note over philosopher_0,philosopher_0: t=750ms philosopher_1->>philosopher_1: {:start_hungry, :fork_2, :fork_1} Note over philosopher_1,philosopher_1: t=750ms philosopher_2->>philosopher_2: {:start_hungry, :fork_2, :fork_3} Note over philosopher_2,philosopher_2: t=750ms philosopher_3->>philosopher_3: {:start_hungry, :fork_4, :fork_3} Note over philosopher_3,philosopher_3: t=750ms philosopher_0->>fork_0: {:request_fork, :philosopher_0} Note over philosopher_0,fork_0: t=750ms philosopher_2->>fork_2: {:request_fork, :philosopher_2} Note over philosopher_2,fork_2: t=750ms philosopher_4->>fork_4: {:request_fork, :philosopher_4} Note over philosopher_4,fork_4: t=750ms philosopher_1->>fork_2: {:request_fork, :philosopher_1} Note over philosopher_1,fork_2: t=750ms philosopher_3->>fork_4: {:request_fork, :philosopher_3} Note over philosopher_3,fork_4: t=750ms fork_0->>philosopher_0: {:fork_granted, :fork_0} Note over fork_0,philosopher_0: t=750ms fork_4->>philosopher_4: {:fork_granted, :fork_4} Note over fork_4,philosopher_4: t=750ms fork_2->>philosopher_2: {:fork_granted, :fork_2} Note over fork_2,philosopher_2: t=750ms fork_4->>philosopher_3: {:fork_denied, :fork_4} Note over fork_4,philosopher_3: t=750ms philosopher_4->>fork_0: {:request_fork, :philosopher_4} Note over philosopher_4,fork_0: t=750ms philosopher_0->>fork_1: {:request_fork, :philosopher_0} Note over philosopher_0,fork_1: t=750ms fork_2->>philosopher_1: {:fork_denied, :fork_2} Note over fork_2,philosopher_1: t=750ms philosopher_2->>fork_3: {:request_fork, :philosopher_2} Note over philosopher_2,fork_3: t=750ms fork_0->>philosopher_4: {:fork_denied, :fork_0} Note over fork_0,philosopher_4: t=750ms fork_3->>philosopher_2: {:fork_granted, :fork_3} Note over fork_3,philosopher_2: t=750ms fork_1->>philosopher_0: {:fork_granted, :fork_1} Note over fork_1,philosopher_0: t=750ms philosopher_4->>fork_4: {:release_fork, :philosopher_4} Note over philosopher_4,fork_4: t=750ms philosopher_0->>philosopher_0: {:mumble, "I'm full!"} Note over philosopher_0,philosopher_0: t=825ms philosopher_0->>fork_0: {:release_fork, :philosopher_0} Note over philosopher_0,fork_0: t=825ms philosopher_2->>philosopher_2: {:mumble, "I'm full!"} Note over philosopher_2,philosopher_2: t=825ms philosopher_2->>fork_2: {:release_fork, :philosopher_2} Note over philosopher_2,fork_2: t=825ms philosopher_0->>fork_1: {:release_fork, :philosopher_0} Note over philosopher_0,fork_1: t=825ms philosopher_2->>fork_3: {:release_fork, :philosopher_2} Note over philosopher_2,fork_3: t=825ms philosopher_2->>philosopher_2: {:start_hungry, :fork_2, :fork_3} Note over philosopher_2,philosopher_2: t=900ms philosopher_4->>philosopher_4: {:start_hungry, :fork_4, :fork_0} Note over philosopher_4,philosopher_4: t=900ms philosopher_3->>philosopher_3: {:start_hungry, :fork_4, :fork_3} Note over philosopher_3,philosopher_3: t=900ms philosopher_1->>philosopher_1: {:start_hungry, :fork_2, :fork_1} Note over philosopher_1,philosopher_1: t=900ms philosopher_0->>philosopher_0: {:start_hungry, :fork_0, :fork_1} Note over philosopher_0,philosopher_0: t=900ms philosopher_2->>fork_2: {:request_fork, :philosopher_2} Note over philosopher_2,fork_2: t=900ms philosopher_4->>fork_4: {:request_fork, :philosopher_4} Note over philosopher_4,fork_4: t=900ms philosopher_0->>fork_0: {:request_fork, :philosopher_0} Note over philosopher_0,fork_0: t=900ms philosopher_3->>fork_4: {:request_fork, :philosopher_3} Note over philosopher_3,fork_4: t=900ms philosopher_1->>fork_2: {:request_fork, :philosopher_1} Note over philosopher_1,fork_2: t=900ms fork_2->>philosopher_2: {:fork_granted, :fork_2} Note over fork_2,philosopher_2: t=900ms fork_4->>philosopher_4: {:fork_granted, :fork_4} Note over fork_4,philosopher_4: t=900ms philosopher_2->>fork_3: {:request_fork, :philosopher_2} Note over philosopher_2,fork_3: t=900ms fork_2->>philosopher_1: {:fork_denied, :fork_2} Note over fork_2,philosopher_1: t=900ms fork_0->>philosopher_0: {:fork_granted, :fork_0} Note over fork_0,philosopher_0: t=900ms fork_4->>philosopher_3: {:fork_denied, :fork_4} Note over fork_4,philosopher_3: t=900ms philosopher_4->>fork_0: {:request_fork, :philosopher_4} Note over philosopher_4,fork_0: t=900ms philosopher_0->>fork_1: {:request_fork, :philosopher_0} Note over philosopher_0,fork_1: t=900ms fork_3->>philosopher_2: {:fork_granted, :fork_3} Note over fork_3,philosopher_2: t=900ms fork_0->>philosopher_4: {:fork_denied, :fork_0} Note over fork_0,philosopher_4: t=900ms fork_1->>philosopher_0: {:fork_granted, :fork_1} Note over fork_1,philosopher_0: t=900ms philosopher_4->>fork_4: {:release_fork, :philosopher_4} Note over philosopher_4,fork_4: t=900ms philosopher_2->>philosopher_2: {:mumble, "I'm full!"} Note over philosopher_2,philosopher_2: t=975ms philosopher_0->>philosopher_0: {:mumble, "I'm full!"} Note over philosopher_0,philosopher_0: t=975ms philosopher_2->>fork_2: {:release_fork, :philosopher_2} Note over philosopher_2,fork_2: t=975ms philosopher_0->>fork_0: {:release_fork, :philosopher_0} Note over philosopher_0,fork_0: t=975ms philosopher_2->>fork_3: {:release_fork, :philosopher_2} Note over philosopher_2,fork_3: t=975ms philosopher_0->>fork_1: {:release_fork, :philosopher_0} Note over philosopher_0,fork_1: t=975ms

Simulation Source Code

simulation =
DiningPhilosophers.create_simulation(
num_philosophers: 5,
think_time: 150,
eat_time: 75,
trace: true
)
|> ActorSimulation.run(duration: 800)

What the Diagram Shows