๐Ÿด 2 Philosophers (Minimal 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

2 philosophers sit at a round table with 2 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_1->>philosopher_1: {:start_hungry, :fork_0, :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_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_1->>fork_0: {:request_fork, :philosopher_1} Note over philosopher_1,fork_0: t=150ms philosopher_0->>fork_0: {:request_fork, :philosopher_0} Note over philosopher_0,fork_0: t=150ms fork_0->>philosopher_1: {:fork_granted, :fork_0} Note over fork_0,philosopher_1: t=150ms fork_0->>philosopher_0: {:fork_denied, :fork_0} Note over fork_0,philosopher_0: t=150ms philosopher_1->>fork_1: {:request_fork, :philosopher_1} Note over philosopher_1,fork_1: t=150ms fork_1->>philosopher_1: {:fork_granted, :fork_1} Note over fork_1,philosopher_1: t=150ms philosopher_1->>philosopher_1: {:mumble, "I'm full!"} Note over philosopher_1,philosopher_1: t=225ms philosopher_1->>fork_0: {:release_fork, :philosopher_1} Note over philosopher_1,fork_0: t=225ms philosopher_1->>fork_1: {:release_fork, :philosopher_1} Note over philosopher_1,fork_1: t=225ms philosopher_0->>philosopher_0: {:start_hungry, :fork_0, :fork_1} Note over philosopher_0,philosopher_0: t=300ms philosopher_1->>philosopher_1: {:start_hungry, :fork_0, :fork_1} Note over philosopher_1,philosopher_1: t=300ms philosopher_0->>fork_0: {:request_fork, :philosopher_0} Note over philosopher_0,fork_0: t=300ms philosopher_1->>fork_0: {:request_fork, :philosopher_1} Note over philosopher_1,fork_0: t=300ms fork_0->>philosopher_0: {:fork_granted, :fork_0} Note over fork_0,philosopher_0: t=300ms fork_0->>philosopher_1: {:fork_denied, :fork_0} Note over fork_0,philosopher_1: t=300ms philosopher_0->>fork_1: {:request_fork, :philosopher_0} Note over philosopher_0,fork_1: t=300ms fork_1->>philosopher_0: {:fork_granted, :fork_1} Note over fork_1,philosopher_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_0->>fork_1: {:release_fork, :philosopher_0} Note over philosopher_0,fork_1: t=375ms philosopher_1->>philosopher_1: {:start_hungry, :fork_0, :fork_1} Note over philosopher_1,philosopher_1: t=450ms philosopher_0->>philosopher_0: {:start_hungry, :fork_0, :fork_1} Note over philosopher_0,philosopher_0: t=450ms philosopher_1->>fork_0: {:request_fork, :philosopher_1} Note over philosopher_1,fork_0: t=450ms philosopher_0->>fork_0: {:request_fork, :philosopher_0} Note over philosopher_0,fork_0: t=450ms fork_0->>philosopher_1: {:fork_granted, :fork_0} Note over fork_0,philosopher_1: t=450ms fork_0->>philosopher_0: {:fork_denied, :fork_0} Note over fork_0,philosopher_0: t=450ms philosopher_1->>fork_1: {:request_fork, :philosopher_1} Note over philosopher_1,fork_1: t=450ms fork_1->>philosopher_1: {:fork_granted, :fork_1} Note over fork_1,philosopher_1: t=450ms philosopher_1->>philosopher_1: {:mumble, "I'm full!"} Note over philosopher_1,philosopher_1: t=525ms philosopher_1->>fork_0: {:release_fork, :philosopher_1} Note over philosopher_1,fork_0: t=525ms philosopher_1->>fork_1: {:release_fork, :philosopher_1} Note over philosopher_1,fork_1: t=525ms philosopher_0->>philosopher_0: {:start_hungry, :fork_0, :fork_1} Note over philosopher_0,philosopher_0: t=600ms philosopher_1->>philosopher_1: {:start_hungry, :fork_0, :fork_1} Note over philosopher_1,philosopher_1: t=600ms philosopher_0->>fork_0: {:request_fork, :philosopher_0} Note over philosopher_0,fork_0: t=600ms philosopher_1->>fork_0: {:request_fork, :philosopher_1} Note over philosopher_1,fork_0: t=600ms fork_0->>philosopher_0: {:fork_granted, :fork_0} Note over fork_0,philosopher_0: t=600ms fork_0->>philosopher_1: {:fork_denied, :fork_0} Note over fork_0,philosopher_1: t=600ms philosopher_0->>fork_1: {:request_fork, :philosopher_0} Note over philosopher_0,fork_1: t=600ms fork_1->>philosopher_0: {:fork_granted, :fork_1} Note over fork_1,philosopher_0: t=600ms philosopher_0->>philosopher_0: {:mumble, "I'm full!"} Note over philosopher_0,philosopher_0: 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_1->>philosopher_1: {:start_hungry, :fork_0, :fork_1} Note over philosopher_1,philosopher_1: t=750ms philosopher_0->>philosopher_0: {:start_hungry, :fork_0, :fork_1} Note over philosopher_0,philosopher_0: t=750ms philosopher_1->>fork_0: {:request_fork, :philosopher_1} Note over philosopher_1,fork_0: t=750ms philosopher_0->>fork_0: {:request_fork, :philosopher_0} Note over philosopher_0,fork_0: t=750ms fork_0->>philosopher_1: {:fork_granted, :fork_0} Note over fork_0,philosopher_1: t=750ms fork_0->>philosopher_0: {:fork_denied, :fork_0} Note over fork_0,philosopher_0: t=750ms philosopher_1->>fork_1: {:request_fork, :philosopher_1} Note over philosopher_1,fork_1: t=750ms fork_1->>philosopher_1: {:fork_granted, :fork_1} Note over fork_1,philosopher_1: t=750ms

Simulation Source Code

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

What the Diagram Shows