{"id":56057,"date":"2011-02-15T08:07:00","date_gmt":"2011-02-15T08:07:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/pfxteam\/2011\/02\/15\/spinwait-spinuntil-for-unit-testing\/"},"modified":"2011-02-15T08:07:00","modified_gmt":"2011-02-15T08:07:00","slug":"spinwait-spinuntil-for-unit-testing","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/dotnet\/spinwait-spinuntil-for-unit-testing\/","title":{"rendered":"SpinWait.SpinUntil for unit testing"},"content":{"rendered":"<p>One of the hidden gems in .NET 4 is the System.Threading.SpinWait type.&nbsp; This type is typically used for implementing lock-free solutions, and is used heavily throughout the rest of the threading and parallelism support in .NET 4.&nbsp; That&rsquo;s why I call it &ldquo;hidden&rdquo;, because most folks don&rsquo;t implement their own lock-free algorithms, and rightfully so.&nbsp; However, one very useful method on SpinWait that escapes the realm of lock-free programming is SpinUntil.<\/p>\n<p>In .NET 4, SpinWait exposes three overloads of the static SpinUntil method:<\/p>\n<blockquote>\n<p>public static void SpinUntil(Func&lt;bool&gt; condition); <br>public static bool SpinUntil(Func&lt;bool&gt; condition, TimeSpan timeout); <br>public static bool SpinUntil(Func&lt;bool&gt; condition, int millisecondsTimeout);<\/p>\n<\/blockquote>\n<p>You provide a Func&lt;bool&gt; to SpinUntil, and it&rsquo;ll invoke it over and over and over, with a nice mix of spinning, yielding, and sleeping in between invocations, until the condition returns true, or until a provided timeout occurs.&nbsp; This makes SpinUntil very useful in writing unit tests, in particular for testing asynchronous and otherwise concurrent components.&nbsp; <\/p>\n<p>Consider a component with an API as follows:<\/p>\n<blockquote>\n<p>public void AddAsync(T item); <br>public int Count { get; }<\/p>\n<\/blockquote>\n<p>You&rsquo;d like to verify in a unit test that added items end up increasing the count.&nbsp; However, since the add method stores the data asynchronously, you can&rsquo;t count on (no pun intended) the Count method being updated by the time AddAsync returns.&nbsp; There are a variety of solutions one might take here.&nbsp; For example, you might put the current thread to sleep for several seconds in hopes that the value will have changed by then:<\/p>\n<blockquote>\n<p>int prevCount = obj.Count; <br>AddAsync(data); <br>Thread.Sleep(4000); <br>Assert.AreEqual(prevCount + 1, obj.Count);<\/p>\n<\/blockquote>\n<p>However, in many such APIs, it&rsquo;s very likely that the effect of the operation will be almost immediately visible, and yet here we&rsquo;re sleeping for four seconds regardless of whether the condition has already become true.&nbsp; Instead, we can use SpinWait.SpinUntil:<\/p>\n<blockquote>\n<p>int prevCount = obj.Count; <br>AddAsync(data); <br>SpinWait.SpinUntil(() =&gt; prevCount != obj.Count, 4000); <br>Assert.AreEqual(prevCount + 1, obj.Count);<\/p>\n<\/blockquote>\n<p>With this version, we&rsquo;ll still wait up to four seconds if necessary, but give or take a few milliseconds as soon as the property has updated, SpinUntil will return.&nbsp; Thus, with very concise code we can avoid unnecessarily prolonging the execution of our unit tests.<\/p>\n<p>Revolutionary? Certainly not.&nbsp; But it is a nice addition to a bag of tricks.<\/p>\n<p>ps If the AddAsync method in the previous example had returned Task to represent the asynchronous operation&rsquo;s completion, you could also just Wait on that returned task, again providing a timeout if you wish.&nbsp; Not so coincidentally, Task.Wait internally uses SpinWait before falling back to a true blocking wait.<\/p>\n<p>pps As with spinning in general, this approach is only appropriate in production code if the condition you&#8217;re spinning for will be true imminently, since spinning is typically only valuable if the amount of cycles you&#8217;d waste spinning are less than what you&#8217;d waste by an unnecessary kernel transition or context switch.&nbsp; Things can be a bit more lenient for test code, such as that which I have above.<\/p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>One of the hidden gems in .NET 4 is the System.Threading.SpinWait type.&nbsp; This type is typically used for implementing lock-free solutions, and is used heavily throughout the rest of the threading and parallelism support in .NET 4.&nbsp; That&rsquo;s why I call it &ldquo;hidden&rdquo;, because most folks don&rsquo;t implement their own lock-free algorithms, and rightfully so.&nbsp; [&hellip;]<\/p>\n","protected":false},"author":360,"featured_media":58792,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[7908],"tags":[7907,7916,7909,136],"class_list":["post-56057","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-pfxteam","tag-net-4","tag-coordination-data-structures","tag-parallel-extensions","tag-testing"],"acf":[],"blog_post_summary":"<p>One of the hidden gems in .NET 4 is the System.Threading.SpinWait type.&nbsp; This type is typically used for implementing lock-free solutions, and is used heavily throughout the rest of the threading and parallelism support in .NET 4.&nbsp; That&rsquo;s why I call it &ldquo;hidden&rdquo;, because most folks don&rsquo;t implement their own lock-free algorithms, and rightfully so.&nbsp; [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/56057","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/users\/360"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/comments?post=56057"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/56057\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media\/58792"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media?parent=56057"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/categories?post=56057"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/tags?post=56057"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}