{"action":"reopened","issue":{"url":"https://api.github.com/repos/louthy/language-ext/issues/317","repository_url":"https://api.github.com/repos/louthy/language-ext","labels_url":"https://api.github.com/repos/louthy/language-ext/issues/317/labels{/name}","comments_url":"https://api.github.com/repos/louthy/language-ext/issues/317/comments","events_url":"https://api.github.com/repos/louthy/language-ext/issues/317/events","html_url":"https://github.com/louthy/language-ext/issues/317","id":283051425,"number":317,"title":"Deadlock from calling Task.Result","user":{"login":"bender2k14","id":34664007,"avatar_url":"https://avatars1.githubusercontent.com/u/34664007?v=4","gravatar_id":"","url":"https://api.github.com/users/bender2k14","html_url":"https://github.com/bender2k14","followers_url":"https://api.github.com/users/bender2k14/followers","following_url":"https://api.github.com/users/bender2k14/following{/other_user}","gists_url":"https://api.github.com/users/bender2k14/gists{/gist_id}","starred_url":"https://api.github.com/users/bender2k14/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/bender2k14/subscriptions","organizations_url":"https://api.github.com/users/bender2k14/orgs","repos_url":"https://api.github.com/users/bender2k14/repos","events_url":"https://api.github.com/users/bender2k14/events{/privacy}","received_events_url":"https://api.github.com/users/bender2k14/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":15,"created_at":"2017-12-18T23:08:10Z","updated_at":"2018-02-07T16:08:40Z","closed_at":null,"author_association":"CONTRIBUTOR","body":"# Example Code\r\nHere is a test that exhibits a deadlock.\r\n```\r\n[Fact]\r\npublic void MTaskFold_InAsyncContextWithTaskWaitingForActivation_Halts() =>\r\n  AsyncContext.Run(() => MTaskFold_WithTaskWaitingForActivation_Halts());\r\n\r\nprivate static void MTaskFold_WithTaskWaitingForActivation_Halts() {\r\n  var intTask = TimeSpan\r\n    .FromMilliseconds(100)\r\n    .Apply(Task.Delay)\r\n    .ContinueWith(_ => 0);\r\n\r\n  var actual = default(MTask<int>)\r\n    .Fold(intTask, 0, (x, y) => 0)\r\n    (Unit.Default);\r\n\r\n  // execution terminates by reaching here\r\n}\r\n```\r\nThe type `AsyncContext` is from the NuGet package [Nito.AsyncEx](https://github.com/StephenCleary/AsyncEx).\r\n\r\n# Expected Behavior\r\nAs stated in the test, the expected behavior is that the test terminates.\r\n\r\n# Actual Behavior\r\nThe test does not terminate.  Here is the source code under test.\r\n```\r\n[Pure]\r\npublic Func<Unit, S> Fold<S>(Task<A> ma, S state, Func<S, A, S> f) => _ =>\r\n{\r\n  if (ma.IsFaulted) return state;\r\n  return f(state, ma.Result);\r\n};\r\n```\r\n\r\nThe test does not terminate because of a deadlock that is caused when calling `ma.Result`.\r\n\r\n# Possible Fix\r\nStephen Cleary, an expert on this topic and author of the aforementioned NuGet package Nito.AsyncEx, [explains](http://blog.stephencleary.com/2012/07/dont-block-on-async-code.html) why this deadlock occurs.  To summarize, my understanding is that it is a best practice to not call `Task.Result` unless `Task.Status == TaskStatus.RanToCompletion`.  This is _not_ true in the example that I have provided.  Instead it is in the state `TaskStatus.WaitingForActivation`.\r\n\r\nOtherwise, the best practice is to `await` the task after calling `Task.ConfigureAwait(false)`.  I don't see how you can do this without changing the return type of `Fold`.  If I may adjust the return type slightly from `Func<Unit, S>` to `Func<Unit, Task<S>>`, then I would implement Stephen's best practice suggestion like this.\r\n```\r\n[Pure]\r\npublic Func<Unit, Task<S>> Fold<S>(Task<A> ma, S state, Func<S, A, S> f) => async _ =>\r\n{\r\n    if (ma.IsFaulted) return state;\r\n    return f(state, await ma.ConfigureAwait(false));\r\n};\r\n```\r\n\r\nThis new return type seems more natural to me.  My understanding of monads is that you are not allowed to just extract the underlying value of the monad.  Unfortunately, `Fold` is doing just that when it calls `Task.Result`.  However, I have only recently started using monads and your library, so I know that I still have a great deal to learn and that it is very possible that I am misunderstanding something here.\r\n\r\nThank you for considering my bug report.  I am interested to hear your thoughts about it."}}
{"actor":{"display_login":"bender2k14"}}