{"id":501,"date":"2013-12-16T13:00:22","date_gmt":"2013-12-16T13:00:22","guid":{"rendered":"http:\/\/writeasync.net\/?p=501"},"modified":"2013-12-02T05:12:42","modified_gmt":"2013-12-02T05:12:42","slug":"building-an-async-exclusive-lock","status":"publish","type":"post","link":"http:\/\/writeasync.net\/?p=501","title":{"rendered":"Building an async exclusive lock"},"content":{"rendered":"<p>The <a title=\"Monitor Class\" href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/system.threading.monitor(v=vs.110).aspx\"><code>Monitor<\/code> class<\/a> is useful for establishing <a title=\"Critical section\" href=\"http:\/\/en.wikipedia.org\/wiki\/Critical_section\">critical sections<\/a> in your .NET code. However, this is of no use in asynchronous scenarios for at least two reasons:<\/p>\n<ol>\n<li><code>Monitor<\/code> has thread affinity, meaning the same thread must acquire and release.<\/li>\n<li><code>Monitor<\/code> blocks while waiting to acquire &#8212; not good for async code paths.<\/li>\n<\/ol>\n<p>There is in fact one synchronization primitive in .NET that is thread-neutral and provides nonblocking behavior, <a title=\"Semaphore and SemaphoreSlim\" href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/z6zx288a(v=vs.110).aspx\"><code>SemaphoreSlim<\/code><\/a>. It could be used as follows:<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\r\n\/\/ Assumes initialization using &quot;new SemaphoreSlim(1, 1)&quot; to provide exclusive\r\n\/\/ access, i.e. number of free slots == number of total slots == 1\r\nprivate async Task DoExclusiveWorkAsync(SemaphoreSlim slimLock)\r\n{\r\n    await slimLock.WaitAsync();\r\n    try\r\n    {\r\n        \/\/ . . . exclusive work here . . .\r\n    }\r\n    finally\r\n    {\r\n        slimLock.Release();\r\n    }\r\n}\r\n<\/pre>\n<p>As an educational exercise, I decided I wanted to build my own async exclusive lock. My lock has one bonus feature: it returns a <code>Token<\/code> that can be used to prevent someone who does not own the lock from releasing it. Typically, &#8220;It is the programmer&#8217;s responsibility to ensure that threads do not release the <a title=\"Semaphore Class\" href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/system.threading.semaphore(v=vs.110).aspx\">semaphore<\/a> too many times.&#8221; The <code>Token<\/code> is a design fix to prevent this problem from ever occurring in any reasonable situation &#8212; at the expense of one additional object allocation.<\/p>\n<p>So, putting on my async TDD hat, I got started with the minimal interface&#8230;<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\r\npublic class ExclusiveLock\r\n{\r\n    public ExclusiveLock()\r\n    {\r\n    }\r\n\r\n    public Task&lt;Token&gt; AcquireAsync()\r\n    {\r\n        throw new NotImplementedException();\r\n    }\r\n\r\n    public void Release(Token token)\r\n    {\r\n        throw new NotImplementedException();\r\n    }\r\n\r\n    public abstract class Token\r\n    {\r\n        protected Token()\r\n        {\r\n        }\r\n    }\r\n}\r\n<\/pre>\n<p>&#8230;and forged ahead with the first unit test (shown after refactoring):<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\r\n&#x5B;Fact]\r\npublic void Acquire_completes_sync_then_release_succeeds()\r\n{\r\n    ExclusiveLock l = new ExclusiveLock();\r\n\r\n    ExclusiveLock.Token token = AssertTaskCompleted(l.AcquireAsync());\r\n\r\n    l.Release(token);\r\n}\r\n<\/pre>\n<p>You can follow the <a title=\"History for writeasync \/ projects \/ LockSample\" href=\"https:\/\/github.com\/brian-dot-net\/writeasync\/commits\/master\/projects\/LockSample\">commit history<\/a> and watch the unit tests spring forth before your eyes. Given the simplicity of the interface, only six tests were needed to get to a complete (<em>single-threaded<\/em>) implementation:<\/p>\n<ul>\n<li><a href=\"https:\/\/github.com\/brian-dot-net\/writeasync\/commit\/bdde69ae5caba813fe39083c58f6f77d78346224\">Acquire completes sync then release succeeds<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/brian-dot-net\/writeasync\/commit\/ed2dc8cfd1b404f759be3f955daf713bf39c8903\">First acquire completes sync, next acquire is pending until first release<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/brian-dot-net\/writeasync\/commit\/a6f0c7b8378fb0b96553c5c5f19461638cec3138\">Release invalid token throws InvalidOperation<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/brian-dot-net\/writeasync\/commit\/02f966ed6e3b637201ae862413fd9aaae5f7b7db\">Release same token twice throws InvalidOperation<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/brian-dot-net\/writeasync\/commit\/453b3a809446b14716652ee36b1db6b7ecb04545\">Three acquires, first completes sync, next acquires are pending until previous owners release<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/brian-dot-net\/writeasync\/commit\/c6d04e201413bdbd70261a1ca24e168caf2d79ad\">Acquire and release three times in a row completes sync each time<\/a><\/li>\n<\/ul>\n<p>Note above that I said <em>single-threaded<\/em> &#8212; again, TDD helps us get the logic right but is not typically suited for concurrency verification. In the next post, I will discuss an integration test which can give us more confidence in the threading aspects.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>The Monitor class is useful for establishing critical sections in your .NET code. However, this is of no use in asynchronous scenarios for at least two reasons: Monitor has thread affinity, meaning the same thread must acquire and release. Monitor&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-501","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\/501","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=501"}],"version-history":[{"count":0,"href":"http:\/\/writeasync.net\/index.php?rest_route=\/wp\/v2\/posts\/501\/revisions"}],"wp:attachment":[{"href":"http:\/\/writeasync.net\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=501"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/writeasync.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=501"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/writeasync.net\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=501"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}