<Query Kind="FSharpProgram" />

open System
open System.Threading 
open System.Threading.Tasks
open System.Diagnostics

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

let TID () = Thread.CurrentThread.ManagedThreadId

let simulatedJob id time = // int -> int -> Async<int>
    async {
       printfn "    [%d] job %d starts" (TID()) id
       
       let sw = Stopwatch.StartNew ()
       do! Async.Sleep time
       sw.Stop()
       let timespan = sw.ElapsedMilliseconds
       
       printfn "    [%d] job %d ends %d ms" (TID()) id timespan
       return id
    }

// ------------------------------
// child = simulatedJob id time
// ------------------------------

printfn "\r\nBad Parent : no parallelism"

let bad_parent = async {
        printfn "... [%d] run parent0" (TID())
        let! (res:int) = simulatedJob 1000 1000  // child
        printfn "... [%d] end parent0 & job %d" (TID()) res 
    }

Async.RunSynchronously bad_parent

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

printfn "\r\nGood parent1: task child"

let good_parent1 = async {
        printfn "... [%d] run parent1" (TID())
        let (task_child:Task<int>) = 
            simulatedJob 1500 1500  // child
                |> Async.StartAsTask 
        Thread.Sleep (100)
        printfn "... [%d] run parent1 & job" (TID())      
        let res = task_child.Result  // Wait()
        printfn "... [%d] end parent1 & job %d" (TID()) res 
    }

Async.RunSynchronously good_parent1

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

printfn "\r\nGood parent2: task child rewraped as async"

let good_parent2 = async {
        printfn "... [%d] run parent2" (TID())
        let (async_child:Async<int>) = 
            simulatedJob 2000 2000  // child
                |> Async.StartAsTask 
                |> Async.AwaitTask
        Thread.Sleep 100
        printfn "... [%d] run parent2 & job" (TID())      
        let! (res:int) = async_child
        printfn "... [%d] end parent2 & job %d" (TID()) res       
    }

Async.RunSynchronously good_parent2

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

printfn "\r\nGood parent3: async child"

let good_parent3 = async {
        printfn "... [%d] run parent3" (TID())
        let! (async_child:Async<int>) = 
            simulatedJob 3000 3000  // child
                |> Async.StartChild
        printfn "... [%d] run parent3 & job" (TID())      
        let! (res:int) = async_child
        printfn "... [%d] end parent3 & job %d" (TID()) res       
    }

Async.RunSynchronously good_parent3

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

printfn "\r\nTest: async child in main"

printfn "... [%d] run main0" (TID())

let async_async_child:Async<Async<int>> = 
    simulatedJob 1000 1000 |> Async.StartChild
    
printfn "... [%d] run main1 ..." (TID())      

let async_child:Async<int> = 
    async_async_child |> Async.RunSynchronously

printfn "... [%d] run main2 & job" (TID())      

let (res:int) = 
    async_child |> Async.RunSynchronously

printfn "... [%d] end main3 & job %d" (TID()) res       

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

printfn ""