๐Ÿด 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? This would create a circular wait condition.

Solution: Asymmetric fork acquisition strategy - odd-numbered philosophers grab their right fork first, even-numbered philosophers grab their left fork first. This breaks the symmetry and prevents circular deadlock.

โ†’ Solid Arrow
Synchronous call (request/release fork)
Activation Box
Fork is processing a request
Timestamps
Virtual time progression
Self-loops
Philosopher internal state changes
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_0->>philosopher_0: {:mumble, "I'm hungry!"} 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->>fork_0: {:request_fork, :philosopher_0} Note over philosopher_0,fork_0: t=150ms philosopher_1->>fork_0: {:request_fork, :philosopher_1} Note over philosopher_1,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_1->>fork_0: {:request_fork, :philosopher_1} Note over philosopher_1,fork_0: t=300ms philosopher_0->>fork_0: {:request_fork, :philosopher_0} Note over philosopher_0,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_0->>fork_0: {:request_fork, :philosopher_0} Note over philosopher_0,fork_0: t=450ms philosopher_1->>fork_0: {:request_fork, :philosopher_1} Note over philosopher_1,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_1->>fork_0: {:request_fork, :philosopher_1} Note over philosopher_1,fork_0: t=600ms philosopher_0->>fork_0: {:request_fork, :philosopher_0} Note over philosopher_0,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_0->>fork_0: {:request_fork, :philosopher_0} Note over philosopher_0,fork_0: t=750ms philosopher_1->>fork_0: {:request_fork, :philosopher_1} Note over philosopher_1,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