FSharpx.Task monad leaks memory when used to write long-running loops
rspeele opened this issue · 2 comments
Description
There is no reasonable way to write an infinite loop, such as one that polls looking for work to do, using FSharpx.Task.
Repro steps
The simplest repro is a console application:
open FSharpx.Task
let whileLoop() =
task {
while true do
do! Task.FromResult(())
}
let main argv =
whileLoop().Wait()
0
Expected behavior
The application runs indefinitely, doing nothing, with stable memory usage.
Actual behavior
The application runs for a minute or two, doing nothing, but steadily growing in memory usage until throwing an OutOfMemoryException
.
Known workarounds
As far as I know there is no clean functional way to chain tasks together that does not suffer from this problem. That is, implementing TaskBuilder.Bind
using ContinueWith
and Unwrap
inevitably causes this. One known workaround is to use System.Runtime.CompilerServices.AsyncMethodBuilder
to implement the loop (like the state machines that C#'s async/await construct compiles to).
I'm reporting this just as a heads-up after I realized it while trying to support constant-space tail recursion in my own TaskBuilder. My TaskBuilder is fine in imperative loops like while true
because it uses an AsyncMethodBuilder
, but its API is not 100% compatible with the one in FSharpx, so it's not a drop-in replacement. Its code is public domain though, so anybody who wishes to fix this issue in FSharpx is free to reference/copy it.
Related information
- Operating system: Windows 7, Windows 10
- Branch: master
- .NET Runtime, CoreCLR or Mono Version: .NET framework 4.6.2
We should remove this one in favor of https://github.com/rspeele/TaskBuilder.fs. Thanks, @rspeele
Closing as this will be fixed with #368