{"id":112286,"date":"2026-04-29T07:00:00","date_gmt":"2026-04-29T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=112286"},"modified":"2026-04-30T07:52:09","modified_gmt":"2026-04-30T14:52:09","slug":"20260429-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20260429-00\/?p=112286","title":{"rendered":"Developing a cross-process reader\/writer lock with limited readers, part 2: Taking turns when being grabby"},"content":{"rendered":"<p><a title=\"Developing a cross-process reader\/writer lock with limited readers, part 1: A semaphore\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20260428-00\/?p=112278\"> Last time, we built a cross-process reader\/writer lock with a cap on the number of readers<\/a>, but I noted that there was still a problem.<\/p>\n<p>The problem occurs when two threads both try to acquire the lock exclusively. In that case, both threads try to claim all the tokens. And the problem is that they can get into a stalemate, where one thread has half of the tokens, and the other thread has the other half, and neither side will back down, resulting in an impasse.<\/p>\n<p>We can avoid this by serializing all the attempts to acquire exclusive locks. That way, there is at most one greedy thread at a time.<\/p>\n<pre>HANDLE sharedSemaphore;\r\n<span style=\"border: solid 1px currentcolor;\">HANDLE sharedMutex;<\/span>\r\n\r\nvoid AcquireExclusive()\r\n{\r\n    <span style=\"border: solid 1px currentcolor;\">WaitForSingleObject(sharedMutex, INFINITE);<\/span>\r\n\r\n    for (unsigned i = 0; i &lt; MAX_SHARED; i++) {\r\n        WaitForSingleObject(sharedSemaphore, INFINITE);\r\n    }\r\n\r\n    <span style=\"border: solid 1px currentcolor;\">ReleaseMutex(sharedMutex);<\/span>\r\n}\r\n\r\nbool AcquireExclusiveWithTimeout(DWORD timeout)\r\n{\r\n    DWORD start = GetTickCount();\r\n    <span style=\"border: solid 1px currentcolor;\">WaitForSingleObject(sharedMutex, INFINITE);<\/span>\r\n\r\n    for (unsigned i = 0; i &lt; MAX_SHARED; i++) {\r\n        DWORD elapsed = GetTickCount() - start;\r\n        if (elapsed &gt; timeout ||\r\n            WaitForSingleObject(sharedSemaphore, timeout - elapsed) == WAIT_TIMEOUT)) {\r\n            \/\/ Restore the tokens we already claimed.\r\n            if (i &gt; 0) {\r\n                ReleaseSemaphore(sharedSemaphore, i, nullptr);\r\n            }\r\n            <span style=\"border: solid 1px currentcolor;\">ReleaseMutex(sharedMutex);<\/span>\r\n            return false;\r\n        }\r\n    }\r\n    <span style=\"border: solid 1px currentcolor;\">ReleaseMutex(sharedMutex);<\/span>\r\n    return true;\r\n}\r\n<\/pre>\n<p>Okay, this avoids the problem of two exclusive acquisitions, but we still have a problem: Exclusive access throughput is poor. We&#8217;ll look at this next time.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Please, not everybody, everything all at once.<\/p>\n","protected":false},"author":1069,"featured_media":111744,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[25],"class_list":["post-112286","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Please, not everybody, everything all at once.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/112286","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/users\/1069"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/comments?post=112286"}],"version-history":[{"count":1,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/112286\/revisions"}],"predecessor-version":[{"id":112287,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/112286\/revisions\/112287"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media\/111744"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media?parent=112286"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=112286"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=112286"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}