FSharpx.Extras


#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) = 
  let t = new Thread(f)
  t.Start()
  t

//----------------

//forever :: IO () -> IO ()
// Repeatedly perform the action
let rec forever act : unit = act (); forever act

//randomDelay :: IO ()
// Delay for a random time between 1 and 1000,000 microseconds
let rng = new Random()
let randomDelay () = 
  let waitTime = rng.Next(1000)
  Thread.Sleep(waitTime)

//choose :: [(STM a, a -> IO ())] -> IO ()
let choose choices : unit = 
  //stmActions :: [STM (IO ())]
  let stmActions = [ for (guard, rhs) in choices -> stm { let! v = guard in return (rhs v) } ]
  stmActions |> List.reduceBack orElse |> atomically

//---------------
type Gate = MkGate of int * TVar<int>

//newGate :: Int -> STM Gate
let newGate n = 
  stm { let tv = newTVar 0 in return MkGate (n, tv) }

//passGate :: Gate -> IO ()
let passGate (MkGate (n,tv)) =
  stm { let! n_left = readTVar tv
        do! check (n_left > 0)
        do! writeTVar tv (n_left-1)
        return () } |> atomically

//operateGate :: Gate -> IO ()
let operateGate (MkGate (n,tv)) =
   stm { return! writeTVar tv n } |> atomically
   stm { let! n_left = readTVar tv
         return! check (n_left = 0) } |> atomically

//---------------
type Group = MkGroup of int * TVar<int * Gate * Gate>

//newGroup :: Int -> IO Group
let newGroup n = 
  stm { let! g1 = newGate n
        let! g2 = newGate n
        let  tv = newTVar (n, g1, g2)
        return MkGroup (n, tv)  } |> atomically

//joinGroup :: Group -> IO (Gate,Gate)
let joinGroup (MkGroup (n,tv)) =
  stm { let! n_left, g1, g2 = readTVar tv
        do! check (n_left > 0) 
        do! writeTVar tv (n_left-1, g1, g2)
        return (g1,g2) } |> atomically

//awaitGroup :: Group -> STM (Gate,Gate)
let awaitGroup (MkGroup (n,tv)) =
  stm { let! n_left, g1, g2 = readTVar tv
        do! check (n_left = 0) 
        let! new_g1 = newGate n
        let! new_g2 = newGate n
        do! writeTVar tv (n,new_g1,new_g2)
        return (g1,g2) }

//---------------       
let rec main () = 
  let elf_gp = newGroup 3
  List.iter (fun n -> elf elf_gp n |> ignore) [1..10]
  let rein_gp = newGroup 9
  List.iter (fun n -> reindeer rein_gp n |> ignore) [1..9]
  forever (fun () -> santa elf_gp rein_gp)

and elf gp id = forkIO (fun () -> forever (fun () -> elf1 gp id; randomDelay ()))

and reindeer gp id = forkIO (fun () -> forever (fun () -> reindeer1 gp id; randomDelay ()))

//santa :: Group -> Group -> IO ()
and santa elf_group rein_group =
  Console.WriteLine("----------")
  choose [awaitGroup rein_group, run "deliver toys"; 
          awaitGroup elf_group,  run "meet in my study"]

//run :: String -> (Gate,Gate) -> IO ()
and run task (in_gate,out_gate) =
  Console.WriteLine("Ho! Ho! Ho! let's {0}", task)
  operateGate in_gate
  operateGate out_gate

//helper1 :: Group -> IO () -> IO ()
and helper1 group do_task =
  let in_gate, out_gate = joinGroup group
  passGate in_gate
  do_task ()
  passGate out_gate

//elf1, reindeer1 :: Group -> Int -> IO ()
and elf1 group id = helper1 group (fun () -> meetInStudy id)
and reindeer1 group id = helper1 group (fun () -> deliverToys id)

and meetInStudy id = Console.WriteLine("Elf {0} meeting in the study", id)
and deliverToys id = Console.WriteLine("Reindeer {0} delivering toys", id)

#if INTERACTIVE
main()
#else
[<EntryPoint>]
let entryPoint args = main(); 0
#endif
namespace System
namespace System.Threading
val check: p: bool -> 'a
val p: bool
val forkIO: f: (unit -> unit) -> Thread
val f: (unit -> unit)
type unit = Unit
val t: Thread
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
Thread.Start() : unit
Thread.Start(parameter: obj) : unit
val forever: act: (unit -> unit) -> unit
val act: (unit -> unit)
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 randomDelay: unit -> unit
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
val choose: choices: ('a * 'b) seq -> unit
val choices: ('a * 'b) seq
val stmActions: obj list
val guard: 'a
val rhs: 'b
Multiple items
module List from Microsoft.FSharp.Collections

--------------------
type List<'T> = | op_Nil | op_ColonColon of Head: 'T * Tail: 'T list interface IReadOnlyList<'T> interface IReadOnlyCollection<'T> interface IEnumerable interface IEnumerable<'T> member GetReverseIndex: rank: int * offset: int -> int member GetSlice: startIndex: int option * endIndex: int option -> 'T list static member Cons: head: 'T * tail: 'T list -> 'T list member Head: 'T member IsEmpty: bool member Item: index: int -> 'T with get ...
val reduceBack: reduction: ('T -> 'T -> 'T) -> list: 'T list -> 'T
type Gate = | MkGate of int * obj
union case Gate.MkGate: int * obj -> Gate
Multiple items
val int: value: 'T -> int (requires member op_Explicit)

--------------------
type int = int32

--------------------
type int<'Measure> = int
val newGate: n: 'a -> 'b
val n: 'a
val passGate: Gate -> 'a
val n: int
val tv: obj
val operateGate: Gate -> 'a
type Group = | MkGroup of int * obj
union case Group.MkGroup: int * obj -> Group
val newGroup: n: 'a -> 'b
val joinGroup: Group -> 'a
val awaitGroup: Group -> 'a
val main: unit -> unit
val elf_gp: Group
val iter: action: ('T -> unit) -> list: 'T list -> unit
val elf: gp: Group -> id: int -> Thread
val ignore: value: 'T -> unit
val rein_gp: Group
val reindeer: gp: Group -> id: int -> Thread
val santa: elf_group: Group -> rein_group: Group -> unit
val gp: Group
val id: int
val elf1: group: Group -> id: int -> unit
val reindeer1: group: Group -> id: int -> unit
val elf_group: Group
val rein_group: Group
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>
Console.WriteLine() : unit
   (+0 other overloads)
Console.WriteLine(value: uint64) : unit
   (+0 other overloads)
Console.WriteLine(value: uint32) : unit
   (+0 other overloads)
Console.WriteLine(value: string) : unit
   (+0 other overloads)
Console.WriteLine(value: float32) : unit
   (+0 other overloads)
Console.WriteLine(value: obj) : unit
   (+0 other overloads)
Console.WriteLine(value: int64) : unit
   (+0 other overloads)
Console.WriteLine(value: int) : unit
   (+0 other overloads)
Console.WriteLine(value: float) : unit
   (+0 other overloads)
Console.WriteLine(value: decimal) : unit
   (+0 other overloads)
val run: task: string -> in_gate: Gate * out_gate: Gate -> 'a
val task: string
val in_gate: Gate
val out_gate: Gate
val helper1: group: Group -> do_task: (unit -> unit) -> 'b
val group: Group
val do_task: (unit -> unit)
val meetInStudy: id: int -> unit
val deliverToys: id: int -> unit
Multiple items
type EntryPointAttribute = inherit Attribute new: unit -> EntryPointAttribute

--------------------
new: unit -> EntryPointAttribute