jack-pappas / ExtCore

An extended core library for F#.
Apache License 2.0
180 stars 32 forks source link

Unexpected TaskBuilder behaviour #23

Open polytypic opened 8 years ago

polytypic commented 8 years ago

Here is a simple program using TaskBuilder:

open System.Threading
open System.Threading.Tasks
open ExtCore.Control.Tasks

[<EntryPoint>]
let main _ =
  let completed = ref false
  let t = tasks {
    printfn "Started"
    let n = ref 10
    let s = ref 0
    printfn "Next while"
    while 0 < !n do
      printfn "Iteration"
      s := !n + !s
      n := !n - 1
    printfn "Sum %A" !s
    lock completed <| fun () ->
      completed := true
      Monitor.PulseAll completed
  }
  t.Start ()
  lock completed <| fun () ->
    while not !completed do
      Monitor.Wait completed |> ignore
  0

Running this program (Mono, but I don't expect different behavior under .Net) I get the following output:

Started
Next while

After which nothing happens. I would expect the computation to run to completion. Like when happens if you replace tasks with async and t.Start () with Async.Start t.

polytypic commented 8 years ago

BTW, this

    member this.While (guard, body : Task<_>)
        : Task<unit> =
        if guard () then
            this.Bind (body, (fun () -> this.While (guard, body)))
        else
            this.Zero ()

cannot possibly work. That is because tasks are fundamentally one-shot. You can create a lazy task using the task constructor as is done by delay

    member __.Delay (taskThunk : unit -> Task<'T>)
        : Task<'T> =
        new Task<'T>(fun () ->
            // Execute the task thunk to create the task.
            let task = taskThunk ()

            // Run the delayed task synchronously.
            task.RunSynchronously ()
            task.Wait ()

            // Return the result of the task.
            task.Result)

but, AFAICT, the resulting task can only be started once and it has to be started explicitly.

jack-pappas commented 7 years ago

Hi Vesa, thank you for reporting this.

I've been conservative about adding new features to ExtCore to ensure everything I do add serves some real-world need and can be well-tested and optimized. I added TaskBuilder as an experiment with the intention of finishing and testing it for correctness before release, but accidentally released it before I could get around to that.

I'll mark the type with [<Experimental>] now to indicate it shouldn't be used until I have a chance to fix the code and get it working correctly.

polytypic commented 7 years ago

BTW, I wrote a version of a Task builder

https://github.com/Hopac/Hopac/blob/master/Tests/AdHocTests/RunTask.fsi https://github.com/Hopac/Hopac/blob/master/Tests/AdHocTests/RunTask.fs

back around the time I reported the issue here. It is not thoroughly tested nor am I using it anywhere.