{"id":3984,"date":"2017-05-25T14:37:34","date_gmt":"2017-05-25T19:37:34","guid":{"rendered":"http:\/\/neosmart.net\/blog\/?p=3984"},"modified":"2017-08-12T16:19:46","modified_gmt":"2017-08-12T21:19:46","slug":"asynclock-an-asyncawait-friendly-locking-library-for-c-and-net","status":"publish","type":"post","link":"https:\/\/neosmart.net\/blog\/asynclock-an-asyncawait-friendly-locking-library-for-c-and-net\/","title":{"rendered":"AsyncLock: an async\/await-friendly locking library for C# and .NET"},"content":{"rendered":"<p><a href=\"https:\/\/github.com\/neosmart\/AsyncLock\" rel=\"nofollow\"><img loading=\"lazy\" decoding=\"async\" class=\"alignright size-thumbnail wp-image-3393 colorbox-3984\" src=\"https:\/\/neosmart.net\/blog\/wp-content\/uploads\/Threads-GitHub-150x150.jpg\" alt=\"\" width=\"150\" height=\"150\" srcset=\"https:\/\/neosmart.net\/blog\/wp-content\/uploads\/Threads-GitHub-150x150.jpg 150w, https:\/\/neosmart.net\/blog\/wp-content\/uploads\/Threads-GitHub.jpg 200w\" sizes=\"auto, (max-width: 150px) 100vw, 150px\" \/><\/a>One of the biggest improvements to C# and the .NET ecosystem in recent years has been the introduction of the <code>async<\/code>\/<code>await<\/code> programming paradigm and the improvements it brings to both performance (no need to create thousands of threads if they spend most of their time blocking on IO) and productivity (no need to muck around with synchronization primitives or marshal exceptions between threads). While it takes a bit of getting used to, once you&#8217;ve gone <code>async<\/code>\/<code>await<\/code>, you (literally) can&#8217;t go back.<\/p>\n<p><!--more--><\/p>\n<p><code>async<\/code>\/<code>await<\/code> programming in C# has been repeatedly compared to a virus &#8211; once it gets into your codebase, it works its way throughout the rest, devouring all synchronous code that dares stand in its way. Due to the way it&#8217;s (quite cleverly) implemented in the compiler, you can&#8217;t really mix synchronous and asynchronous code with <code>async<\/code>\/<code>await<\/code> the way you (painfully) could with <code>BeginXXX<\/code> and <code>EndXXX<\/code> and other <code>IASync<\/code> multithreaded programming approaches.<\/p>\n<p>For the most part, that doesn&#8217;t cause too much of a problem.. except when it comes to marshaling access to non-thread-safe operations. If you&#8217;re an old WIN32 hat and your code is still built around mutexes and events, you&#8217;ll be fine (albeit running code that isn&#8217;t performing quite as awesomely as it could be). But if you&#8217;ve embraced the C# equivalent of a critical section and turned to using <code>lock (...) { \/* sensitive code here *\/ }<\/code> everywhere, you&#8217;ll quickly run into a major gotcha: <code>await<\/code> and <code>lock<\/code> don&#8217;t place nice together at all, and you&#8217;re not allowed to use <code>await<\/code> statements in a <code>lock<\/code> block to prevent a deadlock (due to the way <code>await<\/code> with the remainder of the function body stuffed into a continuation, and the same thread resuming work elsewhere).<\/p>\n<p>Here&#8217;s where <a href=\"https:\/\/github.com\/neosmart\/AsyncLock\" rel=\"nofollow\">our new AsyncLock library<\/a> steps into the picture: it&#8217;s written from the ground up to be a safe and easy-to-use replacement for <code>lock<\/code> in pretty much all contexts. While there <em>are<\/em> plenty of libraries calling themselves &#8220;asynchronous locking libraries&#8221; or &#8220;async locks&#8221; out there, if you read the fine print you&#8217;ll see that they end up being fairly useless as they do not offer any sort of ree\u0308ntrance support and will deadlock on recursion. The problem is that these are built around one of the most basic Win32 synchronization primitives: the semaphore. C#&#8217;s <code>SemaphoreSlim<\/code> is a first-class citizen of the brave, new <code>async<\/code>\/<code>await<\/code> world and even has asynchronous <code>WaitAsync<\/code> method that can pause execution of the current thread context, returning control up the stack, until the semaphore becomes available. But the problem is that as a very low level primitive, <code>SemaphoreSlim<\/code> does not offer any sort of ree\u0308ntrance support and will deadlock on recursion &#8211; if not used properly.<\/p>\n<p><code>AsyncLock<\/code> is an open source library\/wrapper around <code>SemaphoreSlim<\/code> that adds ree\u0308ntrance and recursion without taking await <code>async<\/code>\/<code>await<\/code> functionality. Using <code>AsyncLock<\/code> couldn&#8217;t be simpler: simply swap any <code>lock (lockObject) { ... }<\/code> calls with <code>using (lockObject.Lock()) { ... }<\/code> and continue as before. But let&#8217;s see how (and why) <code>AsyncLock<\/code> works, primarily by looking at the pathological synchronization cases it <em>doesn&#8217;t<\/em> choke on.<\/p>\n<p>A na\u00efve solution to working around the limitation on awaiting inside a <code>lock<\/code> block would be to replace the lock with a semapahore, and then use current thread id to detect recursion, bypassing the lock as needed. At a first glance, it certainly does the trick:<\/p>\n<pre><code class=\"language-csharp\">class SemaphoreTest\r\n{\r\n    SemaphoreSlim lock = new SemaphoreSlim(1, 1); \/\/initially available\r\n    uint lockOwnerThreadId;\r\n\r\n    public async Task Lock()\r\n    {\r\n        if (Thread.ThreadId == lockOwnerThreadId)\r\n        {\r\n            return true;\r\n        }\r\n        await lock.WaitAsync();\r\n        return true;\r\n    }\r\n\r\n    public async Task Test()\r\n    {\r\n        Lock(lock);\r\n\r\n        try\r\n        {\r\n            \/\/do something not thread safe here\r\n            if (someCondition)\r\n            {\r\n                \/\/use recursion\r\n                return await Test();\r\n            }\r\n        }\r\n        finally\r\n        {\r\n            lock.Release(1);\r\n        }\r\n    }\r\n}\r\n<\/code><\/pre>\n<p>It certainly works. We successfully obtain the lock in repeated calls to <code>Test()<\/code> and can successfully <code>await<\/code> the result of that function while holding the lock. But what happens in the following case? (For brevity, we&#8217;ve create an object called <code>BadAsyncLock<\/code> that has a method <code>Lock()<\/code> which implements the semaphore + thread id check code in the sample above.)<\/p>\n<pre><code class=\"language-csharp\">class ThreadIdConflict\r\n{\r\n    BadAsyncLock _lock = new BadAsyncLock();\r\n\r\n    async void Button1_Click()\r\n    {\r\n        using (_lock.Lock())\r\n        {\r\n            await Task.Delay(-1); \/\/at this point, control goes back to the UI thread\r\n        }\r\n    }\r\n\r\n    async void Button2_Click()\r\n    {\r\n        using (_lock.Lock())\r\n        {\r\n            await Task.Delay(-1); \/\/at this point, control goes back to the UI thread\r\n        }\r\n    }\r\n}\r\n<\/code><\/pre>\n<p>That was the &#8220;code behind&#8221; from a very simple GUI application. There&#8217;s no recursion here, just two, distinct event handlers each needing exclusive access to the same variable or method, so we gave them an async lock that uses the thread ID + semaphore approach from the previous example. But here our workaround falls flat on its face. The devil is in the detail, and in this case, how <code><code>async<\/code>\/<code>await<\/code><\/code> actually works and what it does.<\/p>\n<p>One of the primary goals of <code><code>async<\/code>\/<code>await<\/code><\/code> is to prevent needless blocking of the UI waiting on long-running activities to complete. Instead of having to create a new thread, pass data to and from the helper thread, use events to signal an abort, marshal exceptions back to the original thread for handling, etc. all you have to do is use <code><code>async<\/code>\/<code>await<\/code><\/code> and the compiler takes care of the rest. But how it does so is key &#8211; the event handler <code>Button1_Click()<\/code> is executed <em>synchronously<\/em> just like it always would have been in legacy code. There&#8217;s no asynchronous work being done here, there is only ever just the one thread. Even when it encounters the <code>await LongRunningTaskAsync()<\/code> call, it still continues <em>synchronous<\/em> execution until it reaches something that cannot be executed without blocking &#8211; typically an IO (disk, network, etc) read\/write or a wait on a semaphore or event. At that point, the current execution context is set aside and the remaining code (from the <code>await<\/code> to the end of the execution scope) is placed in a continuation. A event of sorts is set up that will signal that the asynchronous <em>non-CPU-bound<\/em> operation has completed\/is ready, at which time the main thread will continue execution of the continuation that was set up earlier.<\/p>\n<p>It&#8217;s not exactly accurate, but you can think of the main UI thread as doing something like this:<\/p>\n<pre><code class=\"language-csharp\">class UiThread\r\n{\r\n\tMap&lt;WaitHandle, Action&gt; _callbacks;\r\n\r\n\tvoid MainThreadPump()\r\n\t{\r\n\t\tint callbackId = WaitHandle.WaitAny(_callbacks.Keys, Timeout.FromMilliseconds(100))\r\n\t\tif (callbackId != -1)\r\n\t\t{\r\n\t\t\t_callbacks.Values[callbackId]();\r\n\t\t}\r\n\r\n\t\tUpdateUi();\r\n\t}\r\n}\r\n<\/code><\/pre>\n<p>The actual implementation of <code><code>async<\/code>\/<code>await<\/code><\/code> obviously looks nothing like this; there&#8217;s a lot more to it than a simple global list of callbacks. But the idea is the same: the main thread runs the UI code. It pauses execution of an event handler (but <em>not the actual thread itself<\/em>) when a &#8220;hard&#8221; <code>await<\/code> is encountered and goes back to running the main UI. When the <code>await<\/code> has finished, the continuation is executed &amp;emdash; again, on the main thread itself.<\/p>\n<p>The key here is that it&#8217;s always the same thread doing the execution (except in the event of a non-awaited call to async function). <strong>The same thread that ran <code>Button1_Click()<\/code> then paused execution of that code due to an <code>await<\/code> call is the same thread that will run <code>Button2_Click()<\/code><\/strong>. The execution of the remainder of the <code>Button1_Click<\/code> code has only been set aside, not actually paused. Meaning that when we come to execute <code>Button2_Click<\/code> while <code>Button1_Click()<\/code> has obtained &#8220;exclusive&#8221; access to a code section via the semaphore, the semaphore will still be unavailable, but the <code>OwningThreadId<\/code> comparison will <em>pass<\/em> since it <em>is<\/em> the same thread that is executing both methods! In traditional, sequential\/blocking execution, you can assume <code>same thread == recursion<\/code>, but that assumption completely falls apart in the wonderful world of <code>async<\/code>\/<code>await<\/code>.<\/p>\n<p>So what&#8217;s the solution? We need some other way to determine recursion, something that doesn&#8217;t rely on the thread id to make that decision. Fortunately, there&#8217;s a very simple (and obvious) answer: we have access to the stack trace via the <code>Environment<\/code> class. Why don&#8217;t we use <em>that<\/em> to decide whether we are recursively obtaining a lock?<\/p>\n<p><strong>Update 5\/25\/2017:\u00a0<\/strong><code>AsyncLock<\/code> is now using a different, more-efficient method of detecting recursion <a href=\"https:\/\/github.com\/neosmart\/AsyncLock\/commit\/fd9221860722e97ea6bf11e5416cf825e93838de\" rel=\"nofollow\">based off the task ID<\/a> and not the stack trace.<\/p>\n<p>Let&#8217;s update our <code>Lock.Lock()<\/code> method to something more like this:<\/p>\n<pre><code class=\"language-csharp\">List _stackTraces = new List();\r\nasync Task Lock()\r\n{\r\n\tif (!lock.locked)\r\n\t{\r\n\t\t_stackTraces.Add(Environment.StackTrace);\r\n\t\tlock.Wait();\r\n\t\treturn true;\r\n\t}\r\n\telse if (_stackTraces.Peek().IsParentOf(Environment.StackTrace))\r\n\t{\r\n\t\t_stackTraces.Add(Environment.StackTrace);\r\n\t\treturn true;\r\n\t}\r\n\telse\r\n\t{\r\n\t\t\/\/wait for the lock to become available somehow\r\n\t\treturn true;\r\n\t}\r\n}\r\n<\/code><\/pre>\n<p>In the above code, we are using the value of <code>Environment.StackTrace<\/code> to determine whether or not we are being called in a ree\u0308ntrance situation. Assume the call to <code>Lock()<\/code> doesn&#8217;t litter the stack trace, and that there is a helper method <code>StackTrace.IsParentOf(StackTrace)<\/code> that can be used to tell if the current call is a child of the previously stored stack trace. (Also assume the obvious race conditions in the code above don&#8217;t exist!) Is this enough? If we were to use this code with the previous test case (<code>Button1_Click()<\/code> vs <code>Button2_Click()<\/code>) it would certainly appear to work. The stack trace during the execution of <code>Button1_Click()<\/code> would not match that of the thread during the execution of <code>Button2_Click()<\/code>, but if <code>Button1_Click()<\/code> were to call <code>Button2_Click()<\/code> (or itself) for some reason, the owning stack id would match and the lock attempt would go through.<\/p>\n<p>However, <em>this<\/em> approach would fail to handle a case that the first solution (based on the comparison of thread ids) would have handily caught:<\/p>\n<pre><code class=\"language-csharp\">class StackTraceConflict\r\n{\r\n    BadAsyncLock _lock = new BadAsyncLock();\r\n\r\n    async void DoSomething()\r\n    {\r\n        using (_lock.Lock())\r\n        {\r\n            await Task.Delay(-1);\r\n        }\r\n    }\r\n\r\n    void DoManySomethings()\r\n    {\r\n        while(true)\r\n        {\r\n            DoSomething(); \/\/no wait here!\r\n        }\r\n    }\r\n}\r\n<\/code><\/pre>\n<p>In this code sample, several threads are spun up<sup id=\"rf1-3984\"><a href=\"#fn1-3984\" title=\"Not necessarily, it is at the discretion of the compiler and dependent on the configuration and state of the thread pool, but potentially so for sure.\" rel=\"footnote\">1<\/a><\/sup> to <em>simultaneously<\/em> execute <code>DoSomething()<\/code> several times on several, different threads. The catch? Since they&#8217;re all starting execution from the same spot, the stack trace for all threads is the same and our stack trace-based approach to lock resolution would completely fail!<\/p>\n<p>As such, the proper solution is to combine both the thread id and the stack trace to get a complete picture of who is calling whom and where all threads and continuations currently stand. Here&#8217;s some code that both demonstrates a tricky test case that our AsyncLock library gets right and at the same time just how easy it is to use AsyncLock in both its synchronous and asynchronous locking variants:<\/p>\n<pre><code class=\"language-csharp\">class AsyncLockTest\r\n{\r\n    AsyncLock _lock = new AsyncLock();\r\n    void Test()\r\n    {\r\n        \/\/the code below will be run immediately (and asynchronously, in a new thread)\r\n        Task.Run(async () =&gt;\r\n        {\r\n            \/\/this first call to LockAsync() will obtain the lock without blocking\r\n            using (await _lock.LockAsync())\r\n            {\r\n                \/\/this second call to LockAsync() will be recognized as being a ree\u0308ntrant call and go through\r\n                using (await _lock.LockAsync())\r\n                {\r\n                    \/\/we now hold the lock exclusively and no one else can use it for 1 minute\r\n                    await Task.Delay(TimeSpan.FromMinutes(1));\r\n                }\r\n            }\r\n        }).Wait(TimeSpan.FromSeconds(30));\r\n\r\n        \/\/this call to obtain the lock is synchronously made from the main thread\r\n        \/\/It will, however, block until the asynchronous code which obtained the lock above finishes\r\n        using (_lock.Lock())\r\n        {\r\n            \/\/now we have obtained exclusive access\r\n        }\r\n    }\r\n}\r\n<\/code><\/pre>\n<p>In the above code, we have a single thread that executes both a recursive and non-recursive attempt to obtain the same lock. The 30 second wait on the task is to make sure the task starts before the main code does; and once there, the lock will first be obtained normally (non-contested), then once more as a ree\u0308ntrant call, which will be allowed. The task will then pause its execution once it encounters the <code>Task.Delay()<\/code> call for one minute, <em>during which time it still holds exclusive access to the shared resource<\/em>. Once the 30 second grace period expires, the main thread will resume execution, and will try to obtain the (already locked) <code>AsyncLock<\/code> instance. The initial attempt to obtain the lock will fail, as it is still held by the (paused) task. 30 seconds later, the task finishes its wait and releases the lock, the main thread obtains the lock, and code execution continues.<\/p>\n<p>The code snippet above also demonstrates the two different lock options that <code>AsyncLock<\/code> exposes: <code>AsyncLock.Lock()<\/code> and <code>AsyncLock.LockAsync()<\/code> They are both basically identical beneath the hood, except that the async method embraces the <code><code>async<\/code>\/<code>await<\/code><\/code> paradigm and will cede its execution until a point in the future when the lock becomes available and it can attempt to re-obtain it. This makes <code>await lock.LockAsync()<\/code> non-blocking and its use is highly encouraged.<\/p>\n<p>AsyncLock is released as open source (MIT-licensed) library on GitHub. Please feel free to fork it and create issues and\/or pull requests as you see fit. AsyncLock is also available on <a href=\"https:\/\/www.nuget.org\/packages\/NeoSmart.AsyncLock\/\" rel=\"follow\">Nuget<\/a>. AsyncLock compiles against .NET Standard and has no outside dependencies, making it even easier to use in all your applications and is especially designed<\/p>\n<hr class=\"footnotes\"><ol class=\"footnotes\"><li id=\"fn1-3984\"><p>Not necessarily, it is at the discretion of the compiler and dependent on the configuration and state of the thread pool, but potentially so for sure.&nbsp;<a href=\"#rf1-3984\" class=\"backlink\" title=\"Jump back to footnote 1 in the text.\">&#8617;<\/a><\/p><\/li><\/ol>","protected":false},"excerpt":{"rendered":"<p>One of the biggest improvements to C# and the .NET ecosystem in recent years has been the introduction of the async\/await programming paradigm and the improvements it brings to both performance (no need to create thousands of threads if they &hellip; <a href=\"https:\/\/neosmart.net\/blog\/asynclock-an-asyncawait-friendly-locking-library-for-c-and-net\/\">Continue reading <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[1],"tags":[],"class_list":["post-3984","post","type-post","status-publish","format-standard","hentry","category-software"],"aioseo_notices":[],"jetpack_featured_media_url":"","jetpack_shortlink":"https:\/\/wp.me\/p4xDa-12g","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/neosmart.net\/blog\/wp-json\/wp\/v2\/posts\/3984","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/neosmart.net\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/neosmart.net\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/neosmart.net\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/neosmart.net\/blog\/wp-json\/wp\/v2\/comments?post=3984"}],"version-history":[{"count":10,"href":"https:\/\/neosmart.net\/blog\/wp-json\/wp\/v2\/posts\/3984\/revisions"}],"predecessor-version":[{"id":4139,"href":"https:\/\/neosmart.net\/blog\/wp-json\/wp\/v2\/posts\/3984\/revisions\/4139"}],"wp:attachment":[{"href":"https:\/\/neosmart.net\/blog\/wp-json\/wp\/v2\/media?parent=3984"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/neosmart.net\/blog\/wp-json\/wp\/v2\/categories?post=3984"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/neosmart.net\/blog\/wp-json\/wp\/v2\/tags?post=3984"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}