This example shows an implementation of the dining philosophers problem
implemented using joinads. The sample has been written by Nick Palladinos (see F# Snippets).
The description of the dining philosophers problem from WikiPedia looks like this:
Five silent philosophers sit at a table around a bowl of spaghetti. A fork is placed between each pair of adjacent philosophers.
Each philosopher must alternately think and eat. Eating is not limited by the amount of spaghetti left: assume an infinite supply. However, a philosopher can only eat while holding both the fork to the left and the fork to the right (an alternative problem formulation uses rice and chopsticks instead of spaghetti and forks).
Each philosopher can pick up an adjacent fork, when available, and put it down, when holding it. These are separate actions: forks must be picked up and put down one by one.
The problem is how to design a discipline of behavior (a concurrent algorithm) such that each philosopher won't starve, i.e. can forever continue to alternate between eating and thinking.
The solution using joinads represents each philosopher and a fork (or a chopstick) using
a single channel. The initialization code looks like this:
openSystemopenFSharp.Extensions.Joinads// Initialize channel for every philosopher and every chopstickletn=5letchopsticks= [| fori=1tondoyieldnewChannel<unit>() |]
lethungry= [| fori=1tondoyieldnewChannel<unit>() |]
letphilosophers= [| "Plato"; "Konfuzius"; "Socrates"; "Voltaire"; "Descartes" |]
letrandomDelay (r:Random) =System.Threading.Thread.Sleep(r.Next(1, 10) *1000)
The situation when a philosopher can start eating is captured by
a join pattern that matches on channels representing the philosopher and a fork on
the left and on the right:
Full name: TryJoinads.n type: int inherits: ValueType
val chopsticks : Channel<unit> []
Full name: TryJoinads.chopsticks type: Channel<unit> [] inherits: Array
val i : int type: int inherits: ValueType
Multiple items module Channel
from FSharp.Extensions.Joinads
-------------------- type Channel<'T> = class interface IChannel<'T> new : unit -> Channel<'T> member Call : message:'T -> unit member Put : message:'T -> unit end
Full name: FSharp.Extensions.Joinads.Channel<_> type: Channel<'T>
type unit = Unit
Full name: Microsoft.FSharp.Core.unit type: unit
val hungry : Channel<unit> []
Full name: TryJoinads.hungry type: Channel<unit> [] inherits: Array
val philosophers : string []
Full name: TryJoinads.philosophers type: string [] inherits: Array
val randomDelay : Random -> unit
Full name: TryJoinads.randomDelay
val r : Random
type Random = class new : unit -> System.Random new : int -> System.Random member Next : unit -> int member Next : int -> int member Next : int * int -> int member NextBytes : System.Byte [] -> unit member NextDouble : unit -> float end
Full name: System.Random
namespace System.Threading
type Thread = class inherit System.Runtime.ConstrainedExecution.CriticalFinalizerObject new : System.Threading.ThreadStart -> System.Threading.Thread new : System.Threading.ParameterizedThreadStart -> System.Threading.Thread member Abort : unit -> unit member CurrentCulture : System.Globalization.CultureInfo with get, set member CurrentUICulture : System.Globalization.CultureInfo with get, set member GetHashCode : unit -> int member IsAlive : bool member IsBackground : bool with get, set member Join : unit -> unit member Join : int -> bool member ManagedThreadId : int member Name : string with get, set member Start : unit -> unit member Start : obj -> unit member ThreadState : System.Threading.ThreadState static member CurrentThread : System.Threading.Thread static member GetDomain : unit -> System.AppDomain static member MemoryBarrier : unit -> unit static member Sleep : int -> unit static member Sleep : System.TimeSpan -> unit static member SpinWait : int -> unit end
Full name: System.Threading.Thread type: Threading.Thread inherits: Runtime.ConstrainedExecution.CriticalFinalizerObject
Threading.Thread.Sleep(timeout: TimeSpan) : unit Threading.Thread.Sleep(millisecondsTimeout: int) : unit
Random.Next() : int Random.Next(maxValue: int) : int Random.Next(minValue: int, maxValue: int) : int
val left : Channel<unit> type: Channel<unit>
val right : Channel<unit> type: Channel<unit>
val random : Random
val join : JoinBuilder
Full name: FSharp.Extensions.Joinads.Builders.join
val printfn : Printf.TextWriterFormat<'T> -> 'T
Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.printfn