{"id":1163,"date":"2010-11-30T02:23:27","date_gmt":"2010-11-30T00:23:27","guid":{"rendered":"https:\/\/alax.info\/blog\/?p=1163"},"modified":"2010-11-30T02:39:16","modified_gmt":"2010-11-30T00:39:16","slug":"recursive-srw-locks","status":"publish","type":"post","link":"https:\/\/alax.info\/blog\/1163","title":{"rendered":"Recursive SRW Locks"},"content":{"rendered":"<p>Windows Vista added new synchronization API, <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/aa904937%28VS.85%29.aspx\">Slim Reader\/Writer (SRW) Locks<\/a>, which is a powerful alternative to <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/ms682530(VS.85).aspx\">critical sections<\/a>. The detailed description is, as always on MSDN, and what makes it really cool is simple:<\/p>\n<ul>\n<li>unlike critical sections, SRW Locks provide reader and writer access synchronization making it possible for 2 and more reader to not block one another<\/li>\n<li>SRW Locks do not reference any resources and have size of a pointer, which is the simplest possible scenario; as a result, they don&#8217;t need a destructor and their initialization is simple zeroing of memory\/variable (for which you however should use <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/ms683483%28VS.85%29.aspx\">InitializeSRWLock<\/a> API<\/li>\n<\/ul>\n<p>Being lightweight they are efficient. To understand how at all they can work, one can imagine that a reader might be trying to <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/ms683614%28VS.85%29.aspx\">InterlockedIncrement<\/a> a synchronization variable. If result is positive, then it&#8217;s OK to go. Otherwise, reader should decrement it back, wait and retry. A writer, instead, does <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/ms683504%28VS.85%29.aspx\">InterlockedAdd<\/a> with an argument of -0x1000 and checks that result of the operation is exactly -0x1000.<\/p>\n<p>This post is about a trap one cat enter into by neglecting one of the SRW lock warnings:<\/p>\n<blockquote><p><a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/aa904937%28VS.85%29.aspx\">&#8230; so SRW locks cannot be acquired recursively. In addition, a thread that  owns an SRW lock in shared mode cannot upgrade its ownership of the lock  to exclusive mode.<\/a><\/p><\/blockquote>\n<p>SRW locks cannot be acquired recursively, but it is very easy to make a mistake. If you attempt to recursively acquire, you are likely to succeed, without a warning, error code, exception or assertion failure. You pass this point and you can write quite some code before you realize something is wrong.<\/p>\n<p>It can be as simple as this:<\/p>\n<p><!--more--><\/p>\n<pre style=\"color: #000000; background: #ffffff;\">SIZE_T GetCount<span style=\"color: #808030;\">(<\/span><span style=\"color: #808030;\">)<\/span> <span style=\"color: #800000; font-weight: bold;\">const<\/span> <span style=\"color: #800000; font-weight: bold;\">throw<\/span><span style=\"color: #808030;\">(<\/span><span style=\"color: #808030;\">)<\/span>\r\n<span style=\"color: #800080;\">{<\/span>\r\n    AcquireSRWLockShared<span style=\"color: #808030;\">(<\/span><span style=\"color: #808030;\">&amp;<\/span>m_Lock<span style=\"color: #808030;\">)<\/span><span style=\"color: #800080;\">;<\/span>\r\n    <span style=\"color: #800000; font-weight: bold;\">const<\/span> SIZE_T nCount <span style=\"color: #808030;\">=<\/span> <span style=\"color: #808030;\">.<\/span><span style=\"color: #808030;\">.<\/span><span style=\"color: #808030;\">.<\/span><span style=\"color: #800080;\">;<\/span>\r\n    ReleaseSRWLockShared<span style=\"color: #808030;\">(<\/span><span style=\"color: #808030;\">&amp;<\/span>m_Lock<span style=\"color: #808030;\">)<\/span><span style=\"color: #800080;\">;<\/span>\r\n    <span style=\"color: #800000; font-weight: bold;\">return<\/span> nCount<span style=\"color: #800080;\">;<\/span>\r\n<span style=\"color: #800080;\">}<\/span>\r\n<span style=\"color: #603000;\">BOOL<\/span> IsEmpty<span style=\"color: #808030;\">(<\/span><span style=\"color: #808030;\">)<\/span> <span style=\"color: #800000; font-weight: bold;\">const<\/span> <span style=\"color: #800000; font-weight: bold;\">throw<\/span><span style=\"color: #808030;\">(<\/span><span style=\"color: #808030;\">)<\/span>\r\n<span style=\"color: #800080;\">{<\/span>\r\n    AcquireSRWLockShared<span style=\"color: #808030;\">(<\/span><span style=\"color: #808030;\">&amp;<\/span>m_Lock<span style=\"color: #808030;\">)<\/span><span style=\"color: #800080;\">;<\/span>\r\n    <span style=\"color: #800000; font-weight: bold;\">const<\/span> <span style=\"color: #603000;\">BOOL<\/span> bResult <span style=\"color: #808030;\">=<\/span> GetCount<span style=\"color: #808030;\">(<\/span><span style=\"color: #808030;\">)<\/span> <span style=\"color: #808030;\">&gt;<\/span> <span style=\"color: #008c00;\">0<\/span><span style=\"color: #800080;\">;<\/span>\r\n    ReleaseSRWLockShared<span style=\"color: #808030;\">(<\/span><span style=\"color: #808030;\">&amp;<\/span>m_Lock<span style=\"color: #808030;\">)<\/span><span style=\"color: #800080;\">;<\/span>\r\n    <span style=\"color: #800000; font-weight: bold;\">return<\/span> bResult<span style=\"color: #800080;\">;<\/span>\r\n<span style=\"color: #800080;\">}<\/span>\r\n<\/pre>\n<p>IsEmpty calls GetCount which again attempts to acquire lock.<\/p>\n<p>In multit-hreaded environment the problem may come up in the following scenario: thread A acquires shared lock, thread B attempts to acquire exclusive lock, thread A attempts to re-acquire (recursively) shared lock again and both threads freeze at this point. Note that without thread B recursive locking succeeds on thread A.<\/p>\n<p><a href=\"http:\/\/www.assembla.com\/code\/roatl-utilities\/subversion\/nodes\/trunk\/SrwLockTest01\">SrwLockTest01<\/a> project provides a sample code to reproduce the deadlock around misused SRW Lock. <a href=\"http:\/\/www.assembla.com\/code\/roatl-utilities\/subversion\/nodes\/trunk\/SrwLockTest01\/SrwLockTest01.cpp?rev=227#ln29\">RecursiveSharedLock and ExclusiveLock thread functions<\/a> keep acquiring lock simultaneously, while main thread is monitoring current worker thread positions.<\/p>\n<p>Shared access only worker thread, which does recursive lock, runs just fine alone, if the other concurrent thread is commented out:<\/p>\n<pre style=\"color: #000000; background: #ffffff;\">CHandle SharedLockThread<span style=\"color: #808030;\">,<\/span> ExclusiveLockThread<span style=\"color: #800080;\">;<\/span>\r\nSharedLockThread<span style=\"color: #808030;\">.<\/span>Attach<span style=\"color: #808030;\">(<\/span>AtlCreateThread<span style=\"color: #800080;\">&lt;<\/span>INT_PTR<span style=\"color: #800080;\">&gt;<\/span><span style=\"color: #808030;\">(<\/span><span style=\"color: #808030;\">&amp;<\/span>RecursiveSharedLock<span style=\"color: #808030;\">,<\/span> <span style=\"color: #008c00;\">0<\/span><span style=\"color: #808030;\">)<\/span><span style=\"color: #808030;\">)<\/span><span style=\"color: #800080;\">;<\/span>\r\n<span style=\"color: #696969;\">\/\/ExclusiveLockThread.Attach(AtlCreateThread&lt;INT_PTR&gt;(&amp;ExclusiveLock, 0));<\/span>\r\n<\/pre>\n<pre>g_nRecursiveSharedLockLine 27, g_nExclusiveLockLine 0\r\ng_nRecursiveSharedLockLine 29, g_nExclusiveLockLine 0\r\ng_nRecursiveSharedLockLine 31, g_nExclusiveLockLine 0\r\ng_nRecursiveSharedLockLine 31, g_nExclusiveLockLine 0\r\ng_nRecursiveSharedLockLine 29, g_nExclusiveLockLine 0\r\ng_nRecursiveSharedLockLine 25, g_nExclusiveLockLine 0\r\ng_nRecursiveSharedLockLine 27, g_nExclusiveLockLine 0\r\ng_nRecursiveSharedLockLine 25, g_nExclusiveLockLine 0\r\ng_nRecursiveSharedLockLine 29, g_nExclusiveLockLine 0\r\ng_nRecursiveSharedLockLine 29, g_nExclusiveLockLine 0\r\ng_nRecursiveSharedLockLine 33, g_nExclusiveLockLine 0<\/pre>\n<p>Status output captures worker thread at random line, and CPU is maxed out on one of the cores.<\/p>\n<p>With concurrent threads, a deadlock takes place very soon:<\/p>\n<pre>g_nRecursiveSharedLockLine 35, g_nExclusiveLockLine 49\r\ng_nRecursiveSharedLockLine 35, g_nExclusiveLockLine 49\r\ng_nRecursiveSharedLockLine 35, g_nExclusiveLockLine 49\r\ng_nRecursiveSharedLockLine 35, g_nExclusiveLockLine 49\r\ng_nRecursiveSharedLockLine 35, g_nExclusiveLockLine 49\r\ng_nRecursiveSharedLockLine 35, g_nExclusiveLockLine 49\r\ng_nRecursiveSharedLockLine 35, g_nExclusiveLockLine 49<\/pre>\n<p>CPU consumption for the process is nearly zero &#8211; threads are in deadlock: shared access thread is i nAPI call at <a href=\"http:\/\/www.assembla.com\/code\/roatl-utilities\/subversion\/nodes\/trunk\/SrwLockTest01\/SrwLockTest01.cpp#ln36\">line 36<\/a>, exclusive access thread is at <a href=\"http:\/\/www.assembla.com\/code\/roatl-utilities\/subversion\/nodes\/trunk\/SrwLockTest01\/SrwLockTest01.cpp#ln50\">line 50<\/a>.<\/p>\n<p>As soon as the problem is clear, a reasonable safety measures are to be taken to avoid misuse. To use SRW Locks safely, it is suggested to use a wrapper class that asserts on misuse in debug builds (to be continued).<\/p>\n<p>Sample code is Visual Studio 2010 C++ project accessible from <a href=\"http:\/\/www.assembla.com\/code\/roatl-utilities\/subversion\/nodes\/trunk\/SrwLockTest01\">SVN repository<\/a>.<\/p>\n<p>See also:<\/p>\n<ul>\n<li><a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/aa904937%28VS.85%29.aspx\">Slim Reader\/Writer (SRW) Locks<\/a><\/li>\n<li><a href=\"http:\/\/msdn.microsoft.com\/en-us\/magazine\/cc163405.aspx\">Synchronization Primitives New To Windows Vista<\/a><\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>Windows Vista added new synchronization API, Slim Reader\/Writer (SRW) Locks, which is a powerful alternative to critical sections. The detailed description is, as always on MSDN, and what makes it really cool is simple: unlike critical sections, SRW Locks provide reader and writer access synchronization making it possible for 2 and more reader to not&hellip; <\/p>\n<p><a class=\"moretag\" href=\"https:\/\/alax.info\/blog\/1163\">Read the full article<\/a><\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[11,13],"tags":[487,233,313,312,311,234,150],"class_list":["post-1163","post","type-post","status-publish","format-standard","hentry","category-atl","category-source","tag-atl","tag-deadlock","tag-lock","tag-slim","tag-srw","tag-synchronization","tag-windows"],"_links":{"self":[{"href":"https:\/\/alax.info\/blog\/wp-json\/wp\/v2\/posts\/1163","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/alax.info\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/alax.info\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/alax.info\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/alax.info\/blog\/wp-json\/wp\/v2\/comments?post=1163"}],"version-history":[{"count":0,"href":"https:\/\/alax.info\/blog\/wp-json\/wp\/v2\/posts\/1163\/revisions"}],"wp:attachment":[{"href":"https:\/\/alax.info\/blog\/wp-json\/wp\/v2\/media?parent=1163"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/alax.info\/blog\/wp-json\/wp\/v2\/categories?post=1163"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/alax.info\/blog\/wp-json\/wp\/v2\/tags?post=1163"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}