{"id":3521,"date":"2015-04-08T13:00:55","date_gmt":"2015-04-08T13:00:55","guid":{"rendered":"http:\/\/writeasync.net\/?p=3521"},"modified":"2015-04-08T02:47:54","modified_gmt":"2015-04-08T02:47:54","slug":"laziness-is-a-virtue","status":"publish","type":"post","link":"http:\/\/writeasync.net\/?p=3521","title":{"rendered":"Laziness is a virtue"},"content":{"rendered":"<p>Sometimes you want lazy initialization but your initialization function is asynchronous. <a href=\"https:\/\/github.com\/stephentoub\">Stephen Toub<\/a> wrote about this conundrum <a href=\"http:\/\/blogs.msdn.com\/b\/pfxteam\/archive\/2011\/01\/15\/10116210.aspx\">years ago on the pfxteam blog<\/a>. The solution as he describes it is fairly straightforward &#8212; use <code>Lazy&lt;T&gt;<\/code> combined with <code>Task&lt;T&gt;<\/code> and you&#8217;re 90% there. <a href=\"http:\/\/blog.stephencleary.com\/2012\/08\/asynchronous-lazy-initialization.html\">Stephen Cleary<\/a> later packaged this up as <a href=\"https:\/\/github.com\/StephenCleary\/AsyncEx\/wiki\/AsyncLazy\">AsyncLazy<\/a> within his popular <a href=\"http:\/\/www.nuget.org\/packages\/Nito.AsyncEx\">Nito.AsyncEx library<\/a>.<\/p>\n<p>One quibble I have with these implementations is that they force a thread switch due to the <code>Task.Factory.StartNew<\/code> call wrapping the initialization function. If you have a well-behaved asynchronous routine that is guaranteed nonblocking (and you should!), you don&#8217;t need this extra layer. So we ostensibly know the solution, but let&#8217;s apply the rigor of TDD to show that it meets our specifications.<\/p>\n<p>Here is the starting code:<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\r\npublic sealed class AsyncLazy&lt;T&gt;\r\n{\r\n    private readonly Lazy&lt;Task&lt;T&gt;&gt; lazy;\r\n\r\n    public AsyncLazy(Func&lt;Task&lt;T&gt;&gt; initializeAsync)\r\n    {\r\n        this.lazy = new Lazy&lt;Task&lt;T&gt;&gt;(initializeAsync, LazyThreadSafetyMode.ExecutionAndPublication);\r\n    }\r\n\r\n    public Task&lt;T&gt; GetAsync()\r\n    {\r\n        return this.lazy.Value;\r\n    }\r\n}\r\n<\/pre>\n<p>The first few tests show that the initialization and cached value semantics are correct:<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\r\n&#x5B;TestMethod]\r\npublic void ShouldInitializeOnFirstCallCompleteSync()\r\n{\r\n    int count = 0;\r\n    AsyncLazy&lt;int&gt; lazy = new AsyncLazy&lt;int&gt;(() =&gt; Task.FromResult(++count));\r\n\r\n    Task&lt;int&gt; task = lazy.GetAsync();\r\n\r\n    Assert.IsTrue(task.IsCompleted);\r\n    Assert.AreEqual(1, task.Result);\r\n}\r\n\r\n&#x5B;TestMethod]\r\npublic void ShouldReturnCachedValueAfterInitialization()\r\n{\r\n    int count = 0;\r\n    AsyncLazy&lt;int&gt; lazy = new AsyncLazy&lt;int&gt;(() =&gt; Task.FromResult(++count));\r\n\r\n    lazy.GetAsync();\r\n    Task&lt;int&gt; task = lazy.GetAsync();\r\n\r\n    Assert.IsTrue(task.IsCompleted);\r\n    Assert.AreEqual(1, task.Result);\r\n}\r\n\r\n&#x5B;TestMethod]\r\npublic void ShouldLeaveTaskPendingUntilInitializationCompletes()\r\n{\r\n    TaskCompletionSource&lt;int&gt; tcs = new TaskCompletionSource&lt;int&gt;();\r\n    AsyncLazy&lt;int&gt; lazy = new AsyncLazy&lt;int&gt;(() =&gt; tcs.Task);\r\n\r\n    Task&lt;int&gt; task = lazy.GetAsync();\r\n\r\n    Assert.IsFalse(task.IsCompleted);\r\n\r\n    tcs.SetResult(123);\r\n\r\n    Assert.IsTrue(task.IsCompleted);\r\n    Assert.AreEqual(123, task.Result);\r\n}\r\n\r\n&#x5B;TestMethod]\r\npublic void ShouldCompleteAllPendingTasksInOrderAfterInitializationCompletes()\r\n{\r\n    List&lt;string&gt; steps = new List&lt;string&gt;();\r\n    TaskCompletionSource&lt;int&gt; tcs = new TaskCompletionSource&lt;int&gt;();\r\n    AsyncLazy&lt;int&gt; lazy = new AsyncLazy&lt;int&gt;(() =&gt; tcs.Task);\r\n\r\n    lazy.GetAsync().ContinueWith(t =&gt; steps.Add(&quot;first:&quot; + t.Result), TaskContinuationOptions.ExecuteSynchronously);\r\n    lazy.GetAsync().ContinueWith(t =&gt; steps.Add(&quot;second:&quot; + t.Result), TaskContinuationOptions.ExecuteSynchronously);\r\n\r\n    tcs.SetResult(321);\r\n\r\n    CollectionAssert.AreEqual(new string&#x5B;] { &quot;first:321&quot;, &quot;second:321&quot; }, steps);\r\n}\r\n<\/pre>\n<p>Now we move on to the exceptional cases. Let&#8217;s review the behavior of the core <code>Lazy&lt;T&gt;<\/code> class according to <a href=\"https:\/\/msdn.microsoft.com\/en-us\/library\/dd642331(v=vs.110).aspx\">the MSDN documentation<\/a>:<\/p>\n<blockquote><p>&#8230;if the factory method throws an exception the first time a thread tries to access the Value property of the Lazy&lt;T&gt; object, the same exception is thrown on every subsequent attempt.<\/p><\/blockquote>\n<p>The following tests (shown post-refactoring) should prove the same about our async implementation. Note that they cover exception variations for both synchronous (i.e. thrown immediately from the factory method) and asynchronous (i.e. from the completion of a faulted <code>Task&lt;T&gt;<\/code>):<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\r\n&#x5B;TestMethod]\r\npublic void ShouldThrowExceptionOnGetIfInitializationFailsSync()\r\n{\r\n    ShouldThrowExceptionOnGetIfInitializationFails(true);\r\n}\r\n\r\n&#x5B;TestMethod]\r\npublic void ShouldThrowCachedExceptionOnSecondGetIfInitializationFailsSync()        \r\n{\r\n    ShouldThrowCachedExceptionOnSecondGetIfInitializationFails(true);\r\n}\r\n\r\n&#x5B;TestMethod]\r\npublic void ShouldThrowExceptionOnGetIfInitializationFailsAsync()\r\n{\r\n    ShouldThrowExceptionOnGetIfInitializationFails(false);\r\n}\r\n\r\n&#x5B;TestMethod]\r\npublic void ShouldThrowCachedExceptionOnSecondGetIfInitializationFailsAsync()\r\n{\r\n    ShouldThrowCachedExceptionOnSecondGetIfInitializationFails(false);\r\n}\r\n\r\nprivate static void ShouldThrowExceptionOnGetIfInitializationFails(bool isSync)\r\n{\r\n    Exception expected = new InvalidOperationException(&quot;Expected exception.&quot;);\r\n    AsyncLazy&lt;int&gt; lazy = new AsyncLazy&lt;int&gt;(() =&gt; Throw(0, expected, isSync));\r\n\r\n    Task&lt;int&gt; task = lazy.GetAsync();\r\n\r\n    AssertTaskCompletedWithException(expected, task);\r\n}\r\n\r\nprivate static void ShouldThrowCachedExceptionOnSecondGetIfInitializationFails(bool isSync)\r\n{\r\n    int count = 0;\r\n    Exception expected = new InvalidOperationException(&quot;Expected exception.&quot;);\r\n    AsyncLazy&lt;int&gt; lazy = new AsyncLazy&lt;int&gt;(() =&gt; Throw(++count, expected, isSync));\r\n\r\n    lazy.GetAsync();\r\n    Task&lt;int&gt; task = lazy.GetAsync();\r\n\r\n    Assert.AreEqual(1, count);\r\n    AssertTaskCompletedWithException(expected, task);\r\n}\r\n\r\nprivate static void AssertTaskCompletedWithException&lt;TException&gt;(TException expected, Task task) where TException : Exception\r\n{\r\n    Assert.IsTrue(task.IsCompleted);\r\n    Assert.IsTrue(task.IsFaulted);\r\n    Assert.IsNotNull(task.Exception);\r\n    Assert.AreEqual(1, task.Exception.InnerExceptions.Count);\r\n    Assert.AreSame(expected, task.Exception.InnerException);\r\n}\r\n\r\nprivate static Task&lt;T&gt; Throw&lt;T, TException&gt;(T value, TException exception, bool isSync) where TException : Exception\r\n{\r\n    if (isSync)\r\n    {\r\n        throw exception;\r\n    }\r\n    else\r\n    {\r\n        TaskCompletionSource&lt;T&gt; tcs = new TaskCompletionSource&lt;T&gt;();\r\n        tcs.SetException(exception);\r\n        return tcs.Task;\r\n    }\r\n}\r\n<\/pre>\n<p>But this is where we run into trouble &#8212; the synchronous tests fail because the exception does not translate to a faulted task in our current implementation. Rather it is thrown directly from <code>GetValueAsync<\/code>. I have deemed this undesirable and fixed the implementation to follow the test specification:<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\r\npublic sealed class AsyncLazy&lt;T&gt;\r\n{\r\n    private readonly Lazy&lt;Task&lt;T&gt;&gt; lazy;\r\n    public AsyncLazy(Func&lt;Task&lt;T&gt;&gt; initializeAsync)\r\n    {\r\n        this.lazy = new Lazy&lt;Task&lt;T&gt;&gt;(WrapException(initializeAsync), LazyThreadSafetyMode.ExecutionAndPublication);\r\n    }\r\n\r\n    public Task&lt;T&gt; GetAsync()\r\n    {\r\n        return this.lazy.Value;\r\n    }\r\n\r\n    private static Func&lt;Task&lt;T&gt;&gt; WrapException(Func&lt;Task&lt;T&gt;&gt; initializeAsync)\r\n    {\r\n        return delegate\r\n        {\r\n            try\r\n            {\r\n                return initializeAsync();\r\n            }\r\n            catch (Exception e)\r\n            {\r\n                TaskCompletionSource&lt;T&gt; tcs = new TaskCompletionSource&lt;T&gt;();\r\n                tcs.SetException(e);\r\n                return tcs.Task;\r\n            }\r\n        };\r\n    }\r\n}\r\n<\/pre>\n<p>The key to the fix is the WrapException method. This allows us to catch synchronous exceptions and translate them to asynchronous ones (wrapped inside a Task).<\/p>\n<p>And there you have it &#8212; a slightly new twist on an old pattern.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Sometimes you want lazy initialization but your initialization function is asynchronous. Stephen Toub wrote about this conundrum years ago on the pfxteam blog. The solution as he describes it is fairly straightforward &#8212; use Lazy&lt;T&gt; combined with Task&lt;T&gt; and you&#8217;re&hellip; <\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[21,61,41],"tags":[],"class_list":["post-3521","post","type-post","status-publish","format-standard","hentry","category-async","category-concurrency","category-tdd"],"_links":{"self":[{"href":"http:\/\/writeasync.net\/index.php?rest_route=\/wp\/v2\/posts\/3521","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/writeasync.net\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/writeasync.net\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/writeasync.net\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/writeasync.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=3521"}],"version-history":[{"count":0,"href":"http:\/\/writeasync.net\/index.php?rest_route=\/wp\/v2\/posts\/3521\/revisions"}],"wp:attachment":[{"href":"http:\/\/writeasync.net\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=3521"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/writeasync.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=3521"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/writeasync.net\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=3521"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}