<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	
  <title>The Old New Thing - Dreamcatcher Edition</title>
	<atom:link href="https://devblogs.microsoft.com/oldnewthing/feed" rel="self" type="application/rss+xml" />
	<link>https://devblogs.microsoft.com/oldnewthing</link>
	<description>Practical development throughout the evolution of Windows.</description>
	<lastBuildDate>Wed, 13 May 2026 16:54:08 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	

<image>
	<url>https://devblogs.microsoft.com/oldnewthing/wp-content/uploads/sites/38/2021/03/Microsoft-Favicon.png</url>
	
  <title>The Old New Thing - Dreamcatcher Edition</title>
	<link>https://devblogs.microsoft.com/oldnewthing</link>
	<width>32</width>
	<height>32</height>
</image> 
	
  <item>
    <title>The case of the hang when the user changed keyboard layouts</title>
    <link>https://devblogs.microsoft.com/oldnewthing/20260513-00/?p=112318</link>
    <comments>https://devblogs.microsoft.com/oldnewthing/20260513-00/?p=112318#comments</comments>
    <dc:creator><![CDATA[Raymond Chen]]></dc:creator>
    <pubDate>Wed, 13 May 2026 14:00:00 +0000</pubDate>
    <category><![CDATA[Old New Thing]]></category>
    <category><![CDATA[Code]]></category>
    <guid isPermaLink="false">https://devblogs.microsoft.com/oldnewthing/?p=112318</guid>
    <content:encoded><![CDATA[<p>A customer reported that their program hung when the user changed keyboard layouts, say by using the <kbd>Win</kbd>+<kbd>Space</kbd> hotkey sequence. They debugged it as far as observing that the foreground window in their application received a <code>WM_<wbr />INPUT­LANG­CHANGE­REQUEST</code>, and when that message was passed to <code>Def­Window­Proc</code>, the call never returned. What&#8217;s so haunted about the <code>WM_<wbr />INPUT­LANG­CHANGE­REQUEST</code> message?</p>
<p>What&#8217;s so haunted about it is that the default behavior of the <code>WM_<wbr />INPUT­LANG­CHANGE­REQUEST</code> message is to change input language!</p>
<p>For historical (and therefore now compatibility) reasons, when a hotkey-initiated input language change request is accepted, the system applies the change to all threads of that process. This means that all UI threads of the process need to be pumping messages so that they can receive the notification that their keyboard state has changed.</p>
<p>In this case, the customer had a background thread that created a window but was not pumping messages. That prevented the language change from completing and caused the main UI thread to hang.</p>
<p>The customer wanted to know if there was a way to configure their program so that hotkey-initiated input language changes don&#8217;t require all threads to be pumping messages. But that&#8217;s trying to solve too narrow a problem. If your thread has created a window, then it must pump messages. Today it&#8217;s causing trouble with input language changes. Tomorrow, it&#8217;s going to cause problems with DDE, and the day after tomorrow, it&#8217;s going to cause problems with theme changes.</p>
<p>Even if you had a way to change the way language changes work, that&#8217;s just one of the problems that your non-responding thread is causing. You should fix the root cause: Either pump messages or destroy the window so that it is no longer a UI thread and is no longer obligated to pump messages.</p>
<p>The post <a href="https://devblogs.microsoft.com/oldnewthing/20260513-00/?p=112318">The case of the hang when the user changed keyboard layouts</a> appeared first on <a href="https://devblogs.microsoft.com/oldnewthing">The Old New Thing</a>.</p>
]]></content:encoded>
    <wfw:commentRss>https://devblogs.microsoft.com/oldnewthing/20260513-00/?p=112318/feed</wfw:commentRss>
    <slash:comments>1</slash:comments>
    <image type="image/png" url="https://devblogs.microsoft.com/oldnewthing/wp-content/uploads/sites/38/2025/10/banner-oldnewthing-blue.webp"/>
  </item>
		
  <item>
    <title>Additional notes on controlling which handles are inherited by Create&#173;Process</title>
    <link>https://devblogs.microsoft.com/oldnewthing/20260511-00/?p=112313</link>
    <comments>https://devblogs.microsoft.com/oldnewthing/20260511-00/?p=112313#comments</comments>
    <dc:creator><![CDATA[Raymond Chen]]></dc:creator>
    <pubDate>Mon, 11 May 2026 14:00:00 +0000</pubDate>
    <category><![CDATA[Old New Thing]]></category>
    <category><![CDATA[Code]]></category>
    <guid isPermaLink="false">https://devblogs.microsoft.com/oldnewthing/?p=112313</guid>
    <content:encoded><![CDATA[<p>Some time ago, I wrote about <a title="Programmatically controlling which handles are inherited by new processes in Win32" href="https://devblogs.microsoft.com/oldnewthing/20111216-00/?p=8873"> programmatically controlling which handles are inherited by new processes in Win32</a> by using the <code>PROC_<wbr />THREAD_<wbr />ATTRIBUTE_<wbr />HANDLE_<wbr />LIST</code> to limit exactly which handles are inherited. That way, when you create a new process, you have precise control over which handles get inherited and don&#8217;t accidentally inherit handles created by unrelated components in your process.</p>
<p>A collegue of mine pointed out that you still have the reverse problem: Since handles must be marked as inheritable for them to participate in <code>PROC_<wbr />THREAD_<wbr />ATTRIBUTE_<wbr />HANDLE_<wbr />LIST</code>, if another thread calls <code>Create­Process</code> with <code>bInheritHandles</code> = <code>TRUE</code> but without using <code>PROC_<wbr />THREAD_<wbr />ATTRIBUTE_<wbr />HANDLE_<wbr />LIST</code>, then they will accidentally inherit all of <i>your</i> handles.</p>
<p>This problem could have been avoided if the <code>PROC_<wbr />THREAD_<wbr />ATTRIBUTE_<wbr />HANDLE_<wbr />LIST</code> allowed you to include non-inheritable handles, in which case they would be non-inheritable by normal <code>Create­Process</code> but inheritable if explicitly opted back in. But alas, that&#8217;s not how it was designed.</p>
<p>Instead, you can create a helper process. All this helper process does is wait for the main process to exit, and then exit itself.</p>
<pre>WaitForSingleObject(hMainProcess, INFINITE);
ExitProcess(0);
</pre>
<p>This process doesn&#8217;t sound like it&#8217;s doing anything useful, and it&#8217;s not. But what makes it useful is not what it&#8217;s doing but rather what is done <i>to</i> it.</p>
<p>The components in the main process create their handles as non-inheritable. When they wants to create a process with specific inherited handles, they duplicate the desired handles into the helper process (as inheritable), and then build a <code>PROC_<wbr />THREAD_<wbr />ATTRIBUTE_<wbr />HANDLE_<wbr />LIST</code> that lists those duplicates as handles to inherit. They also use the <code>PROC_<wbr />THREAD_<wbr />ATTRIBUTE_<wbr />PARENT_<wbr />PROCESS</code> to specify that the <i>helper</i> process is the parent process that the handles should be inherited from. Then they pass those thread attributes to <code>Create­Process</code>, and the new process will inherit exactly those handles. Then they clean up by closing the handles in the helper process with the help of <code>Duplicate­Handle</code> and <code>DUPLICATE_<wbr />CLOSE_<wbr />SOURCE</code>.</p>
<p>Notice that multiple threads can simultaneously be operating on the helper process in this way, so you need only one helper process to service all your handle-inheritance-control needs.</p>
<p>This avoids the accidental inheritance problem because the handles that belong to the components in the main process are still marked non-inheritable, so any other code in the main process that does a <code>Create­Process</code> will not inherit them.</p>
<p>The post <a href="https://devblogs.microsoft.com/oldnewthing/20260511-00/?p=112313">Additional notes on controlling which handles are inherited by &lt;CODE&gt;Create&shy;Process&lt;/CODE&gt;</a> appeared first on <a href="https://devblogs.microsoft.com/oldnewthing">The Old New Thing</a>.</p>
]]></content:encoded>
    <wfw:commentRss>https://devblogs.microsoft.com/oldnewthing/20260511-00/?p=112313/feed</wfw:commentRss>
    <slash:comments>5</slash:comments>
    <image type="image/png" url="https://devblogs.microsoft.com/oldnewthing/wp-content/uploads/sites/38/2025/10/banner-oldnewthing-blue.webp"/>
  </item>
		
  <item>
    <title>Developing more confidence when tracking renames via Read&#173;Directory&#173;ChangesW</title>
    <link>https://devblogs.microsoft.com/oldnewthing/20260508-00/?p=112310</link>
    <comments>https://devblogs.microsoft.com/oldnewthing/20260508-00/?p=112310#comments</comments>
    <dc:creator><![CDATA[Raymond Chen]]></dc:creator>
    <pubDate>Fri, 08 May 2026 14:00:00 +0000</pubDate>
    <category><![CDATA[Old New Thing]]></category>
    <category><![CDATA[Code]]></category>
    <guid isPermaLink="false">https://devblogs.microsoft.com/oldnewthing/?p=112310</guid>
    <content:encoded><![CDATA[<p>A customer was using <code>Read­Directory­ChangesW</code> to monitor the contents of a directory, and they were concerned about the <code>FILE_<wbr />ACTION_<wbr />RENAMED_<wbr />OLD_<wbr />FILE</code> and <code>FILE_<wbr />ACTION_<wbr />RENAMED_<wbr />NEW_<wbr />FILE</code> pair of actions. The documentation doesn&#8217;t guarantee that the two always occur consecutively, or even that they always appear in pairs. For peace of mind, the customer was looking for a way to match up each <code>FILE_<wbr />ACTION_<wbr />RENAMED_<wbr />OLD_<wbr />FILE</code> with a <code>FILE_<wbr />ACTION_<wbr />RENAMED_<wbr />NEW_<wbr />FILE</code> to make sure they were tracking the rename properly.</p>
<p>Yes, you can do it by switching from <code>Read­Directory­ChangesW</code>. to <code>Read­Directory­Changes­ExW</code> and asking for <code>Read­Directory­Notify­Extended­Information</code>. This produces the <code>FILE_<wbr />NOTIFY_<wbr />EXTENDED_<wbr />INFORMATION</code> structure, and that structure includes the <code>FileId</code> of the affected file. You can then match that up between the <code>FILE_<wbr />ACTION_<wbr />RENAMED_<wbr />OLD_<wbr />FILE</code> and <code>FILE_<wbr />ACTION_<wbr />RENAMED_<wbr />NEW_<wbr />FILE</code> to confirm that they are the two halves of the same rename operation.</p>
<p>The post <a href="https://devblogs.microsoft.com/oldnewthing/20260508-00/?p=112310">Developing more confidence when tracking renames via &lt;CODE&gt;Read&shy;Directory&shy;ChangesW&lt;/CODE&gt;</a> appeared first on <a href="https://devblogs.microsoft.com/oldnewthing">The Old New Thing</a>.</p>
]]></content:encoded>
    <wfw:commentRss>https://devblogs.microsoft.com/oldnewthing/20260508-00/?p=112310/feed</wfw:commentRss>
    <slash:comments>7</slash:comments>
    <image type="image/png" url="https://devblogs.microsoft.com/oldnewthing/wp-content/uploads/sites/38/2025/10/banner-oldnewthing-blue.webp"/>
  </item>
		
  <item>
    <title>When you upgrade your resource strings to Unicode, don&#8217;t forget to specify the L prefix</title>
    <link>https://devblogs.microsoft.com/oldnewthing/20260507-00/?p=112307</link>
    <comments>https://devblogs.microsoft.com/oldnewthing/20260507-00/?p=112307#comments</comments>
    <dc:creator><![CDATA[Raymond Chen]]></dc:creator>
    <pubDate>Thu, 07 May 2026 14:00:00 +0000</pubDate>
    <category><![CDATA[Old New Thing]]></category>
    <category><![CDATA[Code]]></category>
    <guid isPermaLink="false">https://devblogs.microsoft.com/oldnewthing/?p=112307</guid>
    <content:encoded><![CDATA[<p>Some time ago, I discussed how <a title="The Resource Compiler defaults to CP_ACP, even in the face of subtle hints that the file is UTF-8" href="https://devblogs.microsoft.com/oldnewthing/20190607-00/?p=102569"> the Resource Compiler defaults to CP_ACP, even in the face of subtle hints that the file is UTF-8</a>.</p>
<p>After yet another incident of <a title="Making sure the Microsoft Visual C++ compiler chooses the right source encoding" href="https://devblogs.microsoft.com/oldnewthing/20241231-00/?p=110696"> Visual Studio secretly changing the file encoding from 1252 to UTF-8</a> and breaking all non-ASCII strings, combined with <a title="How various git diff viewers represent file encoding changes in pull requests" href="https://devblogs.microsoft.com/oldnewthing/20241230-00/?p=110692"> Azure DevOps and Visual Studio simply ignoring encoding changes when showing diffs</a>, a colleague decided to solve the problem once and for all by using explicit Unicode escapes <tt>\x####</tt> to represent non-ASCII characters. That way, it doesn&#8217;t matter whether the file encoding is 1252 or UTF-8 because the two code pages agree on the common ASCII subset.</p>
<p>What used to be</p>
<pre>IDS_AWESOME "That’s great!"
</pre>
<p>was changed to</p>
<pre>IDS_AWESOME "That\x2019s great!"
</pre>
<p>Unfortunately, the resulting string that appeared on screen was</p>
<blockquote class="q"><p>That 19s great!</p></blockquote>
<p>What went wrong?</p>
<p>If you are encoding Unicode into your string, you have to put an <code>L</code> prefix on the quoted string. Otherwise, the <tt>\xABCD</tt> sequence is interpreted as an 8-bit <tt>\xAB</tt> escape sequence, followed by two literal characters <tt>CD</tt>. In this case, the <tt>\x2019</tt> was interpreted as <tt>\x20</tt> (which encodes a space) followed by the literal characters <tt>19</tt>, resulting in the string <tt>That␣19s great!</tt>.</p>
<p>The correct conversion includes the <code>L</code> prefix.</p>
<pre>IDS_AWESOME <span style="border: solid 1px currentcolor;">L</span>"That\x2019s great!"
</pre>
<p>The post <a href="https://devblogs.microsoft.com/oldnewthing/20260507-00/?p=112307">When you upgrade your resource strings to Unicode, don&#8217;t forget to specify the L prefix</a> appeared first on <a href="https://devblogs.microsoft.com/oldnewthing">The Old New Thing</a>.</p>
]]></content:encoded>
    <wfw:commentRss>https://devblogs.microsoft.com/oldnewthing/20260507-00/?p=112307/feed</wfw:commentRss>
    <slash:comments>2</slash:comments>
    <image type="image/png" url="https://devblogs.microsoft.com/oldnewthing/wp-content/uploads/sites/38/2025/10/banner-oldnewthing-blue.webp"/>
  </item>
		
  <item>
    <title>Why not have changes in API behavior depend on the SDK you link against?</title>
    <link>https://devblogs.microsoft.com/oldnewthing/20260506-00/?p=112303</link>
    <comments>https://devblogs.microsoft.com/oldnewthing/20260506-00/?p=112303#comments</comments>
    <dc:creator><![CDATA[Raymond Chen]]></dc:creator>
    <pubDate>Wed, 06 May 2026 14:00:00 +0000</pubDate>
    <category><![CDATA[Old New Thing]]></category>
    <category><![CDATA[Code]]></category>
    <guid isPermaLink="false">https://devblogs.microsoft.com/oldnewthing/?p=112303</guid>
    <content:encoded><![CDATA[<p>Some time ago, I noted that <a title="The Co­Initialize­Security function demands an absolute security descriptor" href="https://devblogs.microsoft.com/oldnewthing/20240902-00/?p=110201"> the <code>Co­Initialize­Security</code> function demands an absolute security descriptor</a>, even though many functions in Windows produce self-relative security descriptors, forcing you to perform a relative-to-absolute conversion, even though the function internally just converts it back from absolute to relative.</p>
<p><a href="https://devblogs.microsoft.com/oldnewthing/20240902-00/?p=110201&amp;commentid=141890#comment-141890"> Commenter tbodt wrote</a>,</p>
<blockquote class="q"><p>This one seems easy enough to fix by Apple&#8217;s technique of giving the function the old behavior when the program is linked against the old SDK.</p></blockquote>
<p>This sure sounds easy. If your program links with the newer SDK, then it gets the new behavior of accepting self-relative security descriptors. But if it links with the old SDK, then it gets the old behavior of requiring absolute security descriptors. If you want the new behavior, then you link with the new SDK.</p>
<p>This does create a subtlety that if you choose the wrong SDK to link against, everything still builds, but the results are different. Traditionally, Windows SDKs are forward-compatible: You can take an old program and link it against a newer SDK, and it will work exactly the same because the old program uses only the backward-compatible subset of the newer SDK. If you change behavior based on the SDK version that you link with, then it may not be obvious that the change in behavior you are experiencing is due to having upgraded the SDK libraries.</p>
<p>Also, what if a program is linked with one version of the SDK, but a DLL that it uses is linked with a different version of the SDK? Maybe you&#8217;re using a UI framework library that hasn&#8217;t seen any need to update to the newer SDK. Or maybe your program is the one using an old version of the SDK, but the UI framework library is using the newer one. Do you let the main program&#8217;s SDK version dictate the behavior of the function, even though the DLL is expecting different behavior? The poor DLL is going to call <code>Co­Initialize­Security</code>, and it won&#8217;t behave the way it expects.</p>
<p>Okay, so maybe you decide that the function changes its behavior not based on the program&#8217;s linked SDK version but rather the version of the calling DLL. But how does a function know which DLL called it? You might say, &#8220;Well, you can look at which DLL the return address belongs to.&#8221; But that doesn&#8217;t work in the case of tail call optimization.</p>
<pre>// some function in a DLL
HRESULT InitializeWidgets(
    UINT maxWidgets,
    const WIDGET_ID* ownerId,
    PCWSTR ownerDescription,
    PCWSTR countainerName,
    PCWSTR containerDescription,
    COLORREF defaultColor,
    UINT defaultWidth,
    UINT defaultHeight,
    bool isRemoteAccessible,
    bool isPersistent)
{
    ⟦ various initialization steps ⟧

    static BYTE sd[] = { 0x01, ⟦ hard-coded values ⟧ };

    return CoInitializeSecurity(sd, -1, nullptr, nullptr,
                                RPC_C_AUTHN_LEVEL_DEFAULT,
                                RPC_C_IMP_LEVEL_IDENTIFY,
                                nullptr, EOAC_NONE, nullptr);
}
</pre>
<p>That final call to <code>Co­Initialize­Security</code> could be optimized into a tail call, in which case the subroutine call instruction changes to an unconditional branch, with the return address being the address of <code>Initialize­Widget</code>&#8216;s caller. If <code>Co­Initialize­Security</code> snooped at its return address, it would be checking the SDK version of the wrong DLL.</p>
<p>Conversely, what if the function in the DLL is just a wrapper?</p>
<pre>HRESULT CoInitializeSecuritywithLogging(
    _In_opt_ PSECURITY_DESCRIPTOR pSecDesc,
    _In_ LONG cAuthSvc,
    _In_reads_opt_(cAuthSvc) SOLE_AUTHENTICATION_SERVICE* asAuthSvc,
    _In_opt_ void* pReserved1,
    _In_ DWORD dwAuthnLevel,
    _In_ DWORD dwImpLevel,
    _In_opt_ void* pAuthList,
    _In_ DWORD dwCapabilities,
    _In_opt_ void* pReserved3)
{
    if (dwCapabilities &amp; EOAC_APPID) {
        LogUuid("CoInitializeSecurity with APPID", (UUID*)pSecDesc);
    } else if (dwCapabilities &amp; EOAC_ACCESS_CONTROL) {
        Log("CoInitializeSecurity with IAccessControl");
    } else {
        LogSecurityDescriptor("CoInitializeSecurity with security descriptor", pSecDesc);
    }
    HRESULT hr = CoInitializeSecurity(pSecDesc, cAuthSvc, asAuthSvc, pReserved1,
                        dwAuthnLevel, dwImpLevel, pAuthList, dwCapabilities, pReserved3);
    Log("CoInitializeSecurity returned", hr);
}
</pre>
<p>If you look at the return address, you will find the wrapper function and change your behavior to match the version that the wrapper function was built with, but that wrapper function is just passing through the parameters from its caller. It&#8217;s really the caller whose behavior we want to match, not the wrapper.</p>
<p>And what if the library is a static library rather than a DLL? It was written for one version of the SDK, but you link to another, and the behavior changes, and even if the function checks the return address, it will get the DLL&#8217;s address and see the DLL&#8217;s SDK version rather than the version the library wanted.</p>
<p>Changing behavior based on the SDK version you link to works only if programs are monolithic.</p>
<p><b>Bonus chatter</b>: Changing to a newer SDK&#8217;s <i>header files</i> do create behavioral changes because, for example, structures with an explicit size member might get extended to contain additional fields, and the API uses the value of the size member to decide which version of the SDK the caller is using. But this is not dependent on the SDK that the caller links to, which is a good thing, because it lets you take static libraries which use different versions of the SDK header files and link them all together into a single program or DLL, and they will still work.</p>
<p>The post <a href="https://devblogs.microsoft.com/oldnewthing/20260506-00/?p=112303">Why not have changes in API behavior depend on the SDK you link against?</a> appeared first on <a href="https://devblogs.microsoft.com/oldnewthing">The Old New Thing</a>.</p>
]]></content:encoded>
    <wfw:commentRss>https://devblogs.microsoft.com/oldnewthing/20260506-00/?p=112303/feed</wfw:commentRss>
    <slash:comments>10</slash:comments>
    <image type="image/png" url="https://devblogs.microsoft.com/oldnewthing/wp-content/uploads/sites/38/2025/10/banner-oldnewthing-blue.webp"/>
  </item>
		
  <item>
    <title>A dispute over the TAB key highlights a mismatch between Microsoft and IBM organizational structures</title>
    <link>https://devblogs.microsoft.com/oldnewthing/20260505-00/?p=112298</link>
    <comments>https://devblogs.microsoft.com/oldnewthing/20260505-00/?p=112298#comments</comments>
    <dc:creator><![CDATA[Raymond Chen]]></dc:creator>
    <pubDate>Tue, 05 May 2026 14:00:00 +0000</pubDate>
    <category><![CDATA[Old New Thing]]></category>
    <category><![CDATA[History]]></category>
    <guid isPermaLink="false">https://devblogs.microsoft.com/oldnewthing/?p=112298</guid>
    <content:encoded><![CDATA[<p><a title="The Microsoft/IBM joint development was built on mutual respect, wait, is respect the right word?" href="https://devblogs.microsoft.com/oldnewthing/20240827-00/?p=110186"> I&#8217;ve written in the past</a> about the cultural mismatch between Microsoft and IBM during the collaboration on OS/2, with the Microsofties viewing their IBM colleagues as <a title="Be careful with that thing, it's a confidential coffee maker" href="https://devblogs.microsoft.com/oldnewthing/20220426-00/?p=106528"> mired in pointless bureaucracy</a> and the IBM folks viewing Microsofties as undisciplined hackers.¹</p>
<p>One of many points of mismatch was the organizational structure.</p>
<p>A colleague recalls that while he was assigned to the IBM offices in Boca Raton, Florida, there was a dispute over what key should be used to move from one field to another in dialog boxes. The folks at IBM were not happy with my colleague&#8217;s decision to use the <kbd>TAB</kbd> key, so they asked him to escalate the issue to his manager back in Redmond.</p>
<p>My colleague&#8217;s manager replied, &#8220;The reason you are in Boca is to make these decisions so I don&#8217;t have to be in Boca.&#8221;</p>
<p>My colleague rephrased this reply in a more corporate manner before passing it on to IBM: &#8220;Microsoft supports the use of the <kbd>TAB</kbd> key for this purpose.&#8221;</p>
<p>Unsatisfied, the IBM folks escalated the issue up their organizational chain for several levels, and replied that their VP (who was around seven levels of management above the programmers) was absolutely opposed to the use of the <kbd>TAB</kbd> for this purpose, and they wanted confirmation from the equivalent-level manager at Microsoft that Microsoft stands by the choice of the <kbd>TAB</kbd> key.</p>
<p>My colleague replied, &#8220;Bill Gates&#8217;s mother is not interested in the <kbd>TAB</kbd> key.&#8221;</p>
<p>This apparently ended the discussion, and the <kbd>TAB</kbd> key stayed.</p>
<p><b>Note</b>: This upcoming Sunday is Mother&#8217;s Day in the United States. You probably shouldn&#8217;t ask her for her opinion on the <kbd>TAB</kbd> key.</p>
<p>¹ There was probably merit to both arguments.</p>
<p>The post <a href="https://devblogs.microsoft.com/oldnewthing/20260505-00/?p=112298">A dispute over the &lt;KBD&gt;TAB&lt;/KBD&gt; key highlights a mismatch between Microsoft and IBM organizational structures</a> appeared first on <a href="https://devblogs.microsoft.com/oldnewthing">The Old New Thing</a>.</p>
]]></content:encoded>
    <wfw:commentRss>https://devblogs.microsoft.com/oldnewthing/20260505-00/?p=112298/feed</wfw:commentRss>
    <slash:comments>11</slash:comments>
    <image type="image/png" url="https://devblogs.microsoft.com/oldnewthing/wp-content/uploads/sites/38/2025/10/banner-oldnewthing-blue.webp"/>
  </item>
		
  <item>
    <title>How do I inform Windows that I&#8217;m writing a binary file?</title>
    <link>https://devblogs.microsoft.com/oldnewthing/20260504-00/?p=112296</link>
    <comments>https://devblogs.microsoft.com/oldnewthing/20260504-00/?p=112296#comments</comments>
    <dc:creator><![CDATA[Raymond Chen]]></dc:creator>
    <pubDate>Mon, 04 May 2026 14:00:00 +0000</pubDate>
    <category><![CDATA[Old New Thing]]></category>
    <category><![CDATA[Code]]></category>
    <guid isPermaLink="false">https://devblogs.microsoft.com/oldnewthing/?p=112296</guid>
    <content:encoded><![CDATA[<p>A customer wanted to know how to inform Windows that they were opening a file in text mode, as opposed to binary mode. That way, Windows can perform text conversions as necessary, like adding carriage returns before linefeeds, or converting ASCII to Unicode.</p>
<p>Windows doesn&#8217;t know whether your file is binary or text. As far as Windows is concerned, it&#8217;s just a bunch of bytes, and it&#8217;s up to you to interpret it. So in a sense, all files are binary files. If you want to insert carriage returns before linefeeds, you will have to do it yourself.</p>
<p>Now, it is often the case that you are using a higher level library, like the C runtime, in which case you can ask the library to do it for you, such as opening the file in <tt>"w"</tt> mode to indicate that the runtime should treat the file as a text file, or in <tt>"wb"</tt> to open as a binary file. But this work happens in the runtime library, not in Windows itself. The runtime library performs the necessary transformations and passes binary data to Windows. There are no further transformations once the data hits <code>Write­File</code>.</p>
<p>&#8220;But wait, there&#8217;s an old MS-DOS ioctl <a href="https://www.ctyme.com/intr/rb-2821.htm">AH=4401h (Set device information)</a> where you pass flags in DX, and <a href="https://www.ctyme.com/intr/rb-2820.htm"> bit 5 is the raw (binary) mode bit</a>. So what&#8217;s the Windows version of this ioctl?&#8221;</p>
<p>If you look more closely, that MS-DOS ioctl applies only to character devices. <a href="https://github.com/microsoft/MS-DOS/blob/2d04cacc5322951f187bb17e017c12920ac8ebe2/v2.0/source/XENIX2.ASM#L470"> If you try to use it on a disk file, you get <code>ERROR_<wbr />INVALID_<wbr />FUNCTION</code></a>.</p>
<pre>ioctl_check_permissions:
        CMP     AL,2
        JAE     ioctl_control_string
        CMP     AL,0
        MOV     AL,BYTE PTR ES:[DI+sf_fcb+fcb_devid]
        JZ      ioctl_read              ; read the byte
        OR      DH,DH
        JZ      ioctl_check_device      ; can I set with this data?
        error   error_invalid_data      ; no DH &lt;&gt; 0

ioctl_check_device:
        TEST    AL,devid_ISDEV          ; can I set this handle?
        <span style="border: solid 1px currentcolor;">JZ      ioctl_bad_fun           ; no, it is a file.</span>

...

ioctl_bad_fun:
        error   error_invalid_function
</pre>
<p>This IOCTL can be used to tell the console things like whether to perform line buffering on input. The Win32 equivalent is <code>Set­Console­Mode</code>, roughly corresponding to the Unix <tt>stty</tt>.</p>
<p>If you want to perform content transformations on files, you&#8217;ll have to do it yourself, or ask someone else (like the runtime library) to do it for you.</p>
<p>The post <a href="https://devblogs.microsoft.com/oldnewthing/20260504-00/?p=112296">How do I inform Windows that I&#8217;m writing a binary file?</a> appeared first on <a href="https://devblogs.microsoft.com/oldnewthing">The Old New Thing</a>.</p>
]]></content:encoded>
    <wfw:commentRss>https://devblogs.microsoft.com/oldnewthing/20260504-00/?p=112296/feed</wfw:commentRss>
    <slash:comments>1</slash:comments>
    <image type="image/png" url="https://devblogs.microsoft.com/oldnewthing/wp-content/uploads/sites/38/2025/10/banner-oldnewthing-blue.webp"/>
  </item>
		
  <item>
    <title>Developing a cross-process reader/writer lock with limited readers, part 4: Abandonment</title>
    <link>https://devblogs.microsoft.com/oldnewthing/20260501-00/?p=112291</link>
    <comments>https://devblogs.microsoft.com/oldnewthing/20260501-00/?p=112291#comments</comments>
    <dc:creator><![CDATA[Raymond Chen]]></dc:creator>
    <pubDate>Fri, 01 May 2026 14:00:00 +0000</pubDate>
    <category><![CDATA[Old New Thing]]></category>
    <category><![CDATA[Code]]></category>
    <guid isPermaLink="false">https://devblogs.microsoft.com/oldnewthing/?p=112291</guid>
    <content:encoded><![CDATA[<p>We&#8217;ve been building a cross-process reader/writer lock with a cap on the number of readers, we concluded our investigation last time by noting <a title="Developing a cross-process reader/writer lock with limited readers, part 3: Fairness" href="https://devblogs.microsoft.com/oldnewthing/20260430-00/?p=112288"> that there is a serious problem that needs to be fixed</a>.</p>
<p>That serious problem is abandonment.</p>
<p>Suppose a process crashes while it holds a shared or exclusive lock on our cross-process reader/writer lock. <a title="Semaphores don't have owners" href="https://devblogs.microsoft.com/oldnewthing/20051123-14/?p=33233"> Semaphores don&#8217;t have owners</a>, so if a thread terminates while in possession of a semaphore token, that token is lost forever. For our cross-process reader/writer lock, that means that the maximum number of shared acquirers goes down by one, and exclusive acquisitions will never succeed, since they will be waiting for that last token which will never be returned.</p>
<p>A synchronization object that does have the concept of ownership is the mutex, so we can build our reader/writer lock out of mutexes.</p>
<p>The idea here is that instead of claiming semaphore tokens, we claim mutexes. This means that we need one mutex for each potential shared acquisition, plus one more to avoid the starvation problem.</p>
<p>The outline is</p>
<ul>
<li>Shared acquisition: Claim any available token mutex.</li>
<li>Shared release: Release the claimed token mutex.</li>
<li>Exclusive acquisition: Claim all token mutexes.</li>
<li>Exclusive release: Release all token mutexes.</li>
</ul>
<pre>HANDLE sharedMutex;
HANDLE tokenMutexes[MAX_SHARED];

struct TimeoutTracker
{
    explicit TimeoutTracker(DWORD timeout)
        : m_timeout(timeout) {}

    DWORD m_start = GetTickCount();

    <span style="border: solid 1px currentcolor;">DWORD</span> Wait(HANDLE h)
    {
        DWORD elapsed = GetTickCount() - m_start;
        if (elapsed &gt; m_timeout) return <span style="border: solid 1px currentcolor;">WAIT_TIMEOUT</span>;
        return <span style="border: solid 1px currentcolor;">WaitForSingleObject(h, m_timeout - elapsed)</span>;
    }

    <span style="border: solid 1px currentcolor; border-bottom: none;">DWORD WaitMultiple(DWORD count, const HANDLE* handles, BOOL waitAll)            </span>
    <span style="border: 1px currentcolor; border-style: none solid;">{                                                                               </span>
    <span style="border: 1px currentcolor; border-style: none solid;">    DWORD elapsed = GetTickCount() - m_start;                                   </span>
    <span style="border: 1px currentcolor; border-style: none solid;">    if (elapsed &gt; m_timeout) return WAIT_TIMEOUT;                               </span>
    <span style="border: 1px currentcolor; border-style: none solid;">    return WaitForMultipleObjects(count, handles, waitAll, m_timeout - elapsed);</span>
    <span style="border: solid 1px currentcolor; border-top: none;">}                                                                               </span>
};
</pre>
<p>We change the return value of the <code>Wait</code> method so it returns the wait result rather than a success/failure. We also add a <code>Wait­Multiple</code> method for wrapping <code>Wait­For­Multiple­Objects</code>.</p>
<p>Next is a handy helper function.</p>
<pre>int WaitResultToindex(DWORD result)
{
    auto index = result - WAIT_OBJECT_0;
    if (index &lt; MAX_SHARED) return static_cast&lt;int&gt;(index);

    index = result - WAIT_ABANDONED_0;
    if (index &lt; MAX_SHARED) return static_cast&lt;int&gt;(index);

    return -1;
}
</pre>
<p>The <code>Wait­Result­To­Index</code> function takes the wait result and returns the index of the acquired mutex, or <tt>-1</tt> if no mutex was acquired.</p>
<p>Notice that this code treats the abandoned the state the same as the normal wait state. <!-- backref: What should I do if a wait call reports WAIT_ABANDONED? --> We are assuming that the code can recover from inconsistent data somehow. (For example, maybe the shared and exclusive accesses are to control access to a set of files, so the existing code already has to deal with file corruption.)</p>
<p>All that&#8217;s left is to implement the outline.</p>
<pre>int AcquireShared()
{
    WaitForSingleObject(sharedMutex, INFINITE);

    auto result = WaitForMultipleObjects(MAX_SHARED, tokenMutexes, FALSE /* bWaitAll */, INFINITE);

    ReleaseMutex(sharedMutex);

    return WaitResultToIndex(result);
}

void ReleaseShared(int index)
{
    ReleaseMutex(tokenMutexes[index]);
}

int AcquireSharedWithTimeout(DWORD timeout)
{
    TimeoutTracker tracker(timeout);
    DWORD result = tracker.Wait(hSharedMutex);
    if (result != WAIT_OBJECT_0) return -1;
    result = tracker.WaitMultiple(MAX_SHARED, tokenMutexes, FALSE /* waitAll */);
    ReleaseMutex(sharedMutex);

    return WaitResultToIndex(result);
}

void AcquireExclusive()
{
    WaitForSingleObject(sharedMutex, INFINITE);

    auto result = WaitForMultipleObjects(MAX_SHARED, tokenMutexes, TRUE /* bWaitAll */, INFINITE);

    ReleaseMutex(sharedMutex);
}

void ReleaseExclusive()
{
    for (unsigned i = 0; i &lt; MAX_SHARED; i++) {
        ReleaseMutex(tokenMutexes[i]);
    }
}

bool AcquireExclusiveWithTimeout(DWORD timeout)
{
    TimeoutTracker tracker(timeout);
    DWORD result = tracker.Wait(hSharedMutex);
    if (result != WAIT_OBJECT_0) return -1;
    result = tracker.WaitMultiple(MAX_SHARED, tokenMutexes, TRUE /* waitAll */);
    ReleaseMutex(sharedMutex);

    return result != WAIT_TIMEOUT;
}
</pre>
<p>The post <a href="https://devblogs.microsoft.com/oldnewthing/20260501-00/?p=112291">Developing a cross-process reader/writer lock with limited readers, part 4: Abandonment</a> appeared first on <a href="https://devblogs.microsoft.com/oldnewthing">The Old New Thing</a>.</p>
]]></content:encoded>
    <wfw:commentRss>https://devblogs.microsoft.com/oldnewthing/20260501-00/?p=112291/feed</wfw:commentRss>
    <slash:comments>1</slash:comments>
    <image type="image/png" url="https://devblogs.microsoft.com/oldnewthing/wp-content/uploads/sites/38/2025/10/banner-oldnewthing-blue.webp"/>
  </item>
		
  <item>
    <title>Developing a cross-process reader/writer lock with limited readers, part 3: Fairness</title>
    <link>https://devblogs.microsoft.com/oldnewthing/20260430-00/?p=112288</link>
    <comments>https://devblogs.microsoft.com/oldnewthing/20260430-00/?p=112288#comments</comments>
    <dc:creator><![CDATA[Raymond Chen]]></dc:creator>
    <pubDate>Thu, 30 Apr 2026 14:00:00 +0000</pubDate>
    <category><![CDATA[Old New Thing]]></category>
    <category><![CDATA[Code]]></category>
    <guid isPermaLink="false">https://devblogs.microsoft.com/oldnewthing/?p=112288</guid>
    <content:encoded><![CDATA[<p>We&#8217;ve been building a cross-process reader/writer lock with a cap on the number of readers, we concluded our investigation last time by noting <a title="Developing a cross-process reader/writer lock with limited readers, part 2: Taking turns when being grabby" href="https://devblogs.microsoft.com/oldnewthing/20260429-00/?p=112286"> that throughput of exclusive accesses was poor</a>. What&#8217;s going on?</p>
<p>The problem is that exclusive acquisitions are working to claim semaphore tokens one at a time, so it can lose out to shared acquisitions that are requested even after the exclusive acquisition had started, effectively letting shared acquisitions &#8220;jump ahead of the exclusive acquisition&#8221;, and thereby starving out exclusive acquisitions.</p>
<table class="cp3" style="border-collapse: collapse;" border="0" cellspacing="0" cellpadding="3">
<tbody>
<tr>
<th style="border: solid 1px currentcolor;" rowspan="2">Token count</th>
<th style="border: solid 1px currentcolor;" rowspan="2">Exclusive<br />
acquirer</th>
<th style="border: solid 1px currentcolor;" colspan="4">Shared acquirers</th>
</tr>
<tr>
<th style="border: solid 1px currentcolor;">A</th>
<th style="border: solid 1px currentcolor;">B</th>
<th style="border: solid 1px currentcolor;">C</th>
<th style="border: solid 1px currentcolor;">D</th>
</tr>
<tr>
<td style="border: solid 1px currentcolor;">5</td>
<td style="border: solid 1px currentcolor;"> </td>
<td style="border: solid 1px currentcolor;"> </td>
<td style="border: solid 1px currentcolor;"> </td>
<td style="border: solid 1px currentcolor;"> </td>
<td style="border: solid 1px currentcolor;"> </td>
</tr>
<tr>
<td style="border: solid 1px currentcolor;">4</td>
<td style="border: solid 1px currentcolor;"> </td>
<td style="border: solid 1px currentcolor;">Acq</td>
<td style="border: solid 1px currentcolor;"> </td>
<td style="border: solid 1px currentcolor;"> </td>
<td style="border: solid 1px currentcolor;"> </td>
</tr>
<tr>
<td style="border: solid 1px currentcolor;">3</td>
<td style="border: solid 1px currentcolor;"> </td>
<td style="border: solid 1px currentcolor;"> </td>
<td style="border: solid 1px currentcolor;">Acq</td>
<td style="border: solid 1px currentcolor;"> </td>
<td style="border: solid 1px currentcolor;"> </td>
</tr>
<tr>
<td style="border: solid 1px currentcolor;">2</td>
<td style="border: solid 1px currentcolor;">1st Acq</td>
<td style="border: solid 1px currentcolor;"> </td>
<td style="border: solid 1px currentcolor;"> </td>
<td style="border: solid 1px currentcolor;"> </td>
<td style="border: solid 1px currentcolor;"> </td>
</tr>
<tr>
<td style="border: solid 1px currentcolor;">1</td>
<td style="border: solid 1px currentcolor;">2nd Acq</td>
<td style="border: solid 1px currentcolor;"> </td>
<td style="border: solid 1px currentcolor;"> </td>
<td style="border: solid 1px currentcolor;"> </td>
<td style="border: solid 1px currentcolor;"> </td>
</tr>
<tr>
<td style="border: solid 1px currentcolor;">0</td>
<td style="border: solid 1px currentcolor;">3rd Acq</td>
<td style="border: solid 1px currentcolor;"> </td>
<td style="border: solid 1px currentcolor;"> </td>
<td style="border: solid 1px currentcolor;"> </td>
<td style="border: solid 1px currentcolor;"> </td>
</tr>
<tr>
<td style="border: solid 1px currentcolor;">0</td>
<td style="border: solid 1px currentcolor;">4th Acq (blocks)</td>
<td style="border: solid 1px currentcolor;"> </td>
<td style="border: solid 1px currentcolor;"> </td>
<td style="border: solid 1px currentcolor;"> </td>
<td style="border: solid 1px currentcolor;"> </td>
</tr>
<tr>
<td style="border: solid 1px currentcolor;">0</td>
<td style="border: solid 1px currentcolor;"> </td>
<td style="border: solid 1px currentcolor;"> </td>
<td style="border: solid 1px currentcolor;"> </td>
<td style="border: solid 1px currentcolor;">Acq (blocks)</td>
<td style="border: solid 1px currentcolor;"> </td>
</tr>
<tr>
<td style="border: solid 1px currentcolor;">0</td>
<td style="border: solid 1px currentcolor;"> </td>
<td style="border: solid 1px currentcolor;"> </td>
<td style="border: solid 1px currentcolor;"> </td>
<td style="border: solid 1px currentcolor;"> </td>
<td style="border: solid 1px currentcolor;">Acq (blocks)</td>
</tr>
<tr>
<td style="border: solid 1px currentcolor;">1</td>
<td style="border: solid 1px currentcolor;"> </td>
<td style="border: solid 1px currentcolor;">Rel</td>
<td style="border: solid 1px currentcolor;"> </td>
<td style="border: solid 1px currentcolor;"> </td>
<td style="border: solid 1px currentcolor;"> </td>
</tr>
<tr>
<td style="border: solid 1px currentcolor;">0</td>
<td style="border: solid 1px currentcolor;">4th Acq</td>
<td style="border: solid 1px currentcolor;"> </td>
<td style="border: solid 1px currentcolor;"> </td>
<td style="border: solid 1px currentcolor;"> </td>
<td style="border: solid 1px currentcolor;"> </td>
</tr>
<tr>
<td style="border: solid 1px currentcolor;">0</td>
<td style="border: solid 1px currentcolor;">5th Acq (blocks)</td>
<td style="border: solid 1px currentcolor;"> </td>
<td style="border: solid 1px currentcolor;"> </td>
<td style="border: solid 1px currentcolor;"> </td>
<td style="border: solid 1px currentcolor;"> </td>
</tr>
<tr>
<td style="border: solid 1px currentcolor;">1</td>
<td style="border: solid 1px currentcolor;"> </td>
<td style="border: solid 1px currentcolor;"> </td>
<td style="border: solid 1px currentcolor;">Rel</td>
<td style="border: solid 1px currentcolor;"> </td>
<td style="border: solid 1px currentcolor;"> </td>
</tr>
<tr>
<td style="border: solid 1px currentcolor;">0</td>
<td style="border: solid 1px currentcolor;"> </td>
<td style="border: solid 1px currentcolor;"> </td>
<td style="border: solid 1px currentcolor;"> </td>
<td style="border: solid 1px currentcolor;">Acq</td>
<td style="border: solid 1px currentcolor;"> </td>
</tr>
<tr>
<td style="border: solid 1px currentcolor;">1</td>
<td style="border: solid 1px currentcolor;"> </td>
<td style="border: solid 1px currentcolor;"> </td>
<td style="border: solid 1px currentcolor;"> </td>
<td style="border: solid 1px currentcolor;">Rel</td>
<td style="border: solid 1px currentcolor;"> </td>
</tr>
<tr>
<td style="border: solid 1px currentcolor;">0</td>
<td style="border: solid 1px currentcolor;"> </td>
<td style="border: solid 1px currentcolor;"> </td>
<td style="border: solid 1px currentcolor;"> </td>
<td style="border: solid 1px currentcolor;"> </td>
<td style="border: solid 1px currentcolor;">Acq</td>
</tr>
</tbody>
</table>
<p>Let&#8217;s say that we have capped the number of shared acquisitions to five. In the above scenario, we have an exclusive acquiring thread and four shared acquiring threads. The first two shared acquiring threads (call them A and B) succeed at their shared acquisitions, and then the exclusive acquiring thread tries to enter exclusively. The exclusive acquiring thread needs five tokens, and it quickly gets three of them before blocking when it tries to get the fourth.</p>
<p>While the exclusive acquiring thread is waiting to get its fourth token, two other shared acquiring threads (call them C and D) try to enter in shared mode. They too block.</p>
<p>One of the original shared acquiring threads releases its shared lock, which release a token, and that token is quickly snapped up by the exclusive acquiring thread, thanks to the &#8220;mostly FIFO&#8221; policy for synchronization objects. (Let&#8217;s assume for the purpose of this discussion that none of the things that violate FIFO-ness has occurred.) The exclusive acquiring thread now waits to claim its fifth token.</p>
<p>When the second of the original shared acquiring threads releases its token, it is given to thread C, even though thread C started its shared acquisition <i>after</i> the exclusive acquiring thread tried to acquire exclusively.</p>
<p>And then when thread C releases its token, that token is given to thread D, since its request for the token precedes the exclusive thread&#8217;s request for the fifth token. The exclusive acquiring thread has once again gotten boxed out.</p>
<p>To fix this, we can make <i>all</i> acquisitions claim the shared mutex. The shared mutex then does the work of enforcing &#8220;mostly FIFO&#8221; acquisition behavior across all acquisitions.</p>
<p>Since we&#8217;re going to be doing combined timeouts, I&#8217;ll refactor the timeout management into a helper class.</p>
<pre>struct TimeoutTracker
{
    explicit TimeoutTracker(DWORD timeout)
        : m_timeout(timeout) {}

    DWORD m_start = GetTickCount();

    bool Wait(HANDLE h)
    {
        DWORD elapsed = GetTickCount() - m_start;
        if (elapsed &gt; m_timeout) return false;
        return WaitForSingleObject(h, m_timeout - elapsed)
                    == WAIT_OBJECT_0;
    }
};
</pre>
<p>We can use this helper class to manage our timeouts.</p>
<pre>HANDLE sharedSemaphore;
HANDLE sharedMutex;

void AcquireShared()
{
    <span style="border: solid 1px currentcolor;">WaitForSingleObject(sharedMutex, INFINITE);</span>

    WaitForSingleObject(sharedSemaphore, INFINITE);

    <span style="border: solid 1px currentcolor;">ReleaseMutex(sharedMutex);</span>
}

bool AcquireSharedWithTimeout(DWORD timeout)
{
    <span style="border: solid 1px currentcolor; border-bottom: none;">TimeoutTracker tracker(timeout);         </span>
    <span style="border: 1px currentcolor; border-style: none solid;">bool result = tracker.Wait(hSharedMutex);</span>
    <span style="border: 1px currentcolor; border-style: none solid;">if (!result) return false;               </span>
    <span style="border: 1px currentcolor; border-style: none solid;">result = tracker.Wait(sharedSemaphore);  </span>
    <span style="border: 1px currentcolor; border-style: none solid;">ReleaseMutex(sharedMutex);               </span>
    <span style="border: solid 1px currentcolor; border-top: none;">return result;                           </span>
}

// no change to AcquireExclusive
void AcquireExclusive()
{
    WaitForSingleObject(sharedMutex, INFINITE);

    for (unsigned i = 0; i &lt; MAX_SHARED; i++) {
        WaitForSingleObject(sharedSemaphore, INFINITE);
    }

    ReleaseMutex(sharedMutex);
}

// no functional change, but using the new helper class
bool AcquireExclusiveWithTimeout(DWORD timeout)
{
    <span style="border: solid 1px currentcolor; border-bottom: none;">TimeoutTracker tracker(timeout);        </span>
    <span style="border: 1px currentcolor; border-style: none solid;">bool result = tracker.Wait(sharedMutex);</span>
    <span style="border: solid 1px currentcolor; border-top: none;">if (!result) return false;              </span>

    for (unsigned i = 0; i &lt; MAX_SHARED; i++) {
        <span style="border: solid 1px currentcolor;">if (!tracker.Wait(sharedSemaphore)) {</span>
            // Restore the tokens we already claimed.
            if (i &gt; 0) {
                ReleaseSemaphore(sharedSemaphore, i, nullptr);
            }
            ReleaseMutex(sharedMutex);
            return false;
        }
    }
    ReleaseMutex(sharedMutex);
    return true;
}
</pre>
<p>(Yes, I&#8217;m not using RAII. I&#8217;ve made that choice for expository purposes, since it lets you see exactly when each synchronization object is acquired and released.)</p>
<p>Are we done?</p>
<p>No, we&#8217;re not done.</p>
<p>There is still a serious problem that needs to be fixed. We&#8217;ll look at it next time.</p>
<p>The post <a href="https://devblogs.microsoft.com/oldnewthing/20260430-00/?p=112288">Developing a cross-process reader/writer lock with limited readers, part 3: Fairness</a> appeared first on <a href="https://devblogs.microsoft.com/oldnewthing">The Old New Thing</a>.</p>
]]></content:encoded>
    <wfw:commentRss>https://devblogs.microsoft.com/oldnewthing/20260430-00/?p=112288/feed</wfw:commentRss>
    <slash:comments>3</slash:comments>
    <image type="image/png" url="https://devblogs.microsoft.com/oldnewthing/wp-content/uploads/sites/38/2025/10/banner-oldnewthing-blue.webp"/>
  </item>
		
  <item>
    <title>Developing a cross-process reader/writer lock with limited readers, part 2: Taking turns when being grabby</title>
    <link>https://devblogs.microsoft.com/oldnewthing/20260429-00/?p=112286</link>
    <comments>https://devblogs.microsoft.com/oldnewthing/20260429-00/?p=112286#comments</comments>
    <dc:creator><![CDATA[Raymond Chen]]></dc:creator>
    <pubDate>Wed, 29 Apr 2026 14:00:00 +0000</pubDate>
    <category><![CDATA[Old New Thing]]></category>
    <category><![CDATA[Code]]></category>
    <guid isPermaLink="false">https://devblogs.microsoft.com/oldnewthing/?p=112286</guid>
    <content:encoded><![CDATA[<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>
<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>
<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>
<pre>HANDLE sharedSemaphore;
<span style="border: solid 1px currentcolor;">HANDLE sharedMutex;</span>

void AcquireExclusive()
{
    <span style="border: solid 1px currentcolor;">WaitForSingleObject(sharedMutex, INFINITE);</span>

    for (unsigned i = 0; i &lt; MAX_SHARED; i++) {
        WaitForSingleObject(sharedSemaphore, INFINITE);
    }

    <span style="border: solid 1px currentcolor;">ReleaseMutex(sharedMutex);</span>
}

bool AcquireExclusiveWithTimeout(DWORD timeout)
{
    DWORD start = GetTickCount();
    <span style="border: solid 1px currentcolor;">WaitForSingleObject(sharedMutex, INFINITE);</span>

    for (unsigned i = 0; i &lt; MAX_SHARED; i++) {
        DWORD elapsed = GetTickCount() - start;
        if (elapsed &gt; timeout ||
            WaitForSingleObject(sharedSemaphore, timeout - elapsed) == WAIT_TIMEOUT)) {
            // Restore the tokens we already claimed.
            if (i &gt; 0) {
                ReleaseSemaphore(sharedSemaphore, i, nullptr);
            }
            <span style="border: solid 1px currentcolor;">ReleaseMutex(sharedMutex);</span>
            return false;
        }
    }
    <span style="border: solid 1px currentcolor;">ReleaseMutex(sharedMutex);</span>
    return true;
}
</pre>
<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>
<p>The post <a href="https://devblogs.microsoft.com/oldnewthing/20260429-00/?p=112286">Developing a cross-process reader/writer lock with limited readers, part 2: Taking turns when being grabby</a> appeared first on <a href="https://devblogs.microsoft.com/oldnewthing">The Old New Thing</a>.</p>
]]></content:encoded>
    <wfw:commentRss>https://devblogs.microsoft.com/oldnewthing/20260429-00/?p=112286/feed</wfw:commentRss>
    <slash:comments>1</slash:comments>
    <image type="image/png" url="https://devblogs.microsoft.com/oldnewthing/wp-content/uploads/sites/38/2025/10/banner-oldnewthing-blue.webp"/>
  </item>
	</channel>
</rss>
