#r @"../bin/FSharpx.Extras.dll"

open System
open System.Threading
open FSharpx.Stm

let check p =
    match p with
    | true -> stm.Return(())
    | false -> retry ()

let forkIO (f : unit -> unit) = (new Thread(f)).Start()

let rec forever act : unit = act (); forever act

let take fork = 
    stm {
        let! ref_fork = readTVar (fork)
        do! check ref_fork
        do! writeTVar (fork) false

let adquire left_fork right_fork  =  
    stm { 
        do! take left_fork
        do! take right_fork
    } |> atomically    

let release left_fork right_fork = 
    stm { 
        do! writeTVar left_fork true
        do! writeTVar right_fork true
    } |> atomically    
let rng = new Random()

let n = 7 ;

let thinking = Array.zeroCreate n
let eating   = Array.zeroCreate n

let randomDelay () = 
    let waitTime = rng.Next(1000)
    Thread.Sleep waitTime
    uint64 waitTime

let eatOrThink i left_fork right_fork = 
    if (rng.Next(100)) > 50
        adquire left_fork right_fork
        printf "philosopher [%d] is eating.\n" i
        eating.[i] <- eating.[i] + randomDelay ()
        release left_fork right_fork
        printf "philosopher [%d] is thinking.\n" i
        thinking.[i] <- thinking.[i] + randomDelay ()

let philosofer i leftfork rightfork = 
    fun () -> forever (fun () -> eatOrThink i leftfork rightfork)

let timer = new System.Diagnostics.Stopwatch()


let rec main () = 
    let forks = Array.init n (fun _ -> newTVar(true))
    for i in 0..n-1 do
        forkIO (philosofer i forks.[i] forks.[(i + 1) % n])
let onInterrupt _ _ =
    printf "\ndone.\n"
    let total = float timer.ElapsedMilliseconds
    let p x = ((float x) * 100.0) / (float total)
    for i in 0..n-1 do
        printf "philosopher [%d] - percents: %.2f eating, %.2f thinking, %.2f obtaining forks\n"
            i (p eating.[i]) (p thinking.[i]) (100. - (p eating.[i] + p thinking.[i]))
Console.CancelKeyPress.AddHandler( new ConsoleCancelEventHandler( onInterrupt ))

namespace System
namespace System.Threading
val check: p: bool -> 'a
val p: bool
val forkIO: f: (unit -> unit) -> unit
val f: (unit -> unit)
type unit = Unit
Multiple items
type Thread = inherit CriticalFinalizerObject new: start: ParameterizedThreadStart -> unit + 3 overloads member Abort: unit -> unit + 1 overload member DisableComObjectEagerCleanup: unit -> unit member GetApartmentState: unit -> ApartmentState member GetCompressedStack: unit -> CompressedStack member GetHashCode: unit -> int member Interrupt: unit -> unit member Join: unit -> unit + 2 overloads member Resume: unit -> unit ...
<summary>Creates and controls a thread, sets its priority, and gets its status.</summary>

Thread(start: ParameterizedThreadStart) : Thread
Thread(start: ThreadStart) : Thread
Thread(start: ParameterizedThreadStart, maxStackSize: int) : Thread
Thread(start: ThreadStart, maxStackSize: int) : Thread
val forever: act: (unit -> unit) -> unit
val act: (unit -> unit)
val take: fork: 'a -> 'b
val fork: 'a
val adquire: left_fork: 'a -> right_fork: 'b -> 'c
val left_fork: 'a
val right_fork: 'b
val release: left_fork: 'a -> right_fork: 'b -> 'c
val rng: Random
Multiple items
type Random = new: unit -> unit + 1 overload member Next: unit -> int + 2 overloads member NextBytes: buffer: byte array -> unit + 1 overload member NextDouble: unit -> float member NextInt64: unit -> int64 + 2 overloads member NextSingle: unit -> float32 static member Shared: Random
<summary>Represents a pseudo-random number generator, which is an algorithm that produces a sequence of numbers that meet certain statistical requirements for randomness.</summary>

Random() : Random
Random(Seed: int) : Random
val n: int
val thinking: uint64 array
type Array = interface ICollection interface IEnumerable interface IList interface IStructuralComparable interface IStructuralEquatable interface ICloneable member Clone: unit -> obj member CopyTo: array: Array * index: int -> unit + 1 overload member GetEnumerator: unit -> IEnumerator member GetLength: dimension: int -> int ...
<summary>Provides methods for creating, manipulating, searching, and sorting arrays, thereby serving as the base class for all arrays in the common language runtime.</summary>
val zeroCreate: count: int -> 'T array
val eating: uint64 array
val randomDelay: unit -> uint64
val waitTime: int
Random.Next() : int
Random.Next(maxValue: int) : int
Random.Next(minValue: int, maxValue: int) : int
Thread.Sleep(timeout: TimeSpan) : unit
Thread.Sleep(millisecondsTimeout: int) : unit
Multiple items
val uint64: value: 'T -> uint64 (requires member op_Explicit)

type uint64 = UInt64

type uint64<'Measure> = uint64
val eatOrThink: i: int -> left_fork: 'a -> right_fork: 'b -> unit
val i: int
val printf: format: Printf.TextWriterFormat<'T> -> 'T
val philosofer: i: int -> leftfork: 'a -> rightfork: 'b -> unit -> unit
val leftfork: 'a
val rightfork: 'b
val timer: Diagnostics.Stopwatch
namespace System.Diagnostics
Multiple items
type Stopwatch = new: unit -> unit member Reset: unit -> unit member Restart: unit -> unit member Start: unit -> unit member Stop: unit -> unit static member GetTimestamp: unit -> int64 static member StartNew: unit -> Stopwatch static val Frequency: int64 static val IsHighResolution: bool member Elapsed: TimeSpan ...
<summary>Provides a set of methods and properties that you can use to accurately measure elapsed time.</summary>

Diagnostics.Stopwatch() : Diagnostics.Stopwatch
Diagnostics.Stopwatch.Start() : unit
val main: unit -> unit
val forks: obj array
val init: count: int -> initializer: (int -> 'T) -> 'T array
val i: int32
val onInterrupt: 'a -> 'b -> unit
Diagnostics.Stopwatch.Stop() : unit
val total: float
Multiple items
val float: value: 'T -> float (requires member op_Explicit)

type float = Double

type float<'Measure> = float
property Diagnostics.Stopwatch.ElapsedMilliseconds: int64 with get
<summary>Gets the total elapsed time measured by the current instance, in milliseconds.</summary>
<returns>A read-only long integer representing the total number of milliseconds measured by the current instance.</returns>
val p: x: uint64 -> float
val x: uint64
type Console = static member Beep: unit -> unit + 1 overload static member Clear: unit -> unit static member GetCursorPosition: unit -> struct (int * int) static member MoveBufferArea: sourceLeft: int * sourceTop: int * sourceWidth: int * sourceHeight: int * targetLeft: int * targetTop: int -> unit + 1 overload static member OpenStandardError: unit -> Stream + 1 overload static member OpenStandardInput: unit -> Stream + 1 overload static member OpenStandardOutput: unit -> Stream + 1 overload static member Read: unit -> int static member ReadKey: unit -> ConsoleKeyInfo + 1 overload static member ReadLine: unit -> string ...
<summary>Represents the standard input, output, and error streams for console applications. This class cannot be inherited.</summary>
event Console.CancelKeyPress: IEvent<ConsoleCancelEventHandler,ConsoleCancelEventArgs>
abstract IDelegateEvent.AddHandler: handler: 'Delegate -> unit
type ConsoleCancelEventHandler = new: object: obj * method: nativeint -> unit member BeginInvoke: sender: obj * e: ConsoleCancelEventArgs * callback: AsyncCallback * object: obj -> IAsyncResult member EndInvoke: result: IAsyncResult -> unit member Invoke: sender: obj * e: ConsoleCancelEventArgs -> unit
<summary>Represents the method that will handle the <see cref="E:System.Console.CancelKeyPress" /> event of a <see cref="T:System.Console" />.</summary>
<param name="sender">The source of the event.</param>
<param name="e">A <see cref="T:System.ConsoleCancelEventArgs" /> object that contains the event data.</param>