{"id":105933,"date":"2021-11-17T07:00:00","date_gmt":"2021-11-17T15:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=105933"},"modified":"2021-11-17T07:10:05","modified_gmt":"2021-11-17T15:10:05","slug":"20211117-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20211117-00\/?p=105933","title":{"rendered":"The mental model for StartThreadpoolIo"},"content":{"rendered":"<p>A customer was having trouble using asynchronous I\/O with the Windows thread pool. Their proof-of-concept program was crashing once they issue their second write. Here&#8217;s a sketch:<\/p>\n<pre>auto io = CreateThreadpoolIo(fileHandle, callback, nullptr, nullptr);\r\nStartThreadpoolIo(io);\r\n\r\nOVERLAPPED pending[NUMBER] = {};\r\n\r\nfor (int i = 0; i &lt; NUMBER; i++) {\r\n    pending[i].Offset = offset[i];\r\n    pending[i].OffsetHigh = 0;\r\n    bool result = WriteFile(fileHandle, data[i], size[i],\r\n            &amp;bytesWritten, &amp;pending[i]);\r\n\r\n    if (!result &amp;&amp; GetLastError() != ERROR_IO_PENDING) {\r\n        CancelThreadpoolIo(io);\r\n    }\r\n}\r\n<\/pre>\n<p>They found that if <code>NUMBER<\/code> is 1, then everything works great. If <code>NUMBER<\/code> is greater than 1, then the first I\/O completion is successful, but the second one crashes.<\/p>\n<p>The confusion is over what <code>StartThreadpoolIo<\/code> does. The customer thought that it needed to be called once for each file handle. But really, it needs to be called once for each I\/O operation. If you issue ten writes against a file handle, you need to call <code>Start\u00adThreadpool\u00adIo<\/code> ten times, once before each call.<\/p>\n<p>The point of <code>Start\u00adThreadpool\u00adIo<\/code> is to tell the thread pool to prepare for an incoming completion against the file handle. If you issue an I\/O without first preparing the thread pool, then the completion arrives and the thread pool doesn&#8217;t know what to do with it.<\/p>\n<p>The fix is to move the call to <code>Start\u00adThreadpool\u00adIo<\/code> to immediately before issuing the I\/O operation:<\/p>\n<pre>auto io = CreateThreadpoolIo(fileHandle, callback, nullptr, nullptr);\r\n<span style=\"color: red;\">\/\/ <span style=\"text-decoration: line-through;\">StartThreadpoolIo(io);<\/span><\/span> \/\/ from here\r\n\r\nOVERLAPPED pending[NUMBER] = {};\r\n\r\nfor (int i = 0; i &lt; NUMBER; i++) {\r\n    pending[i].Offset = offset[i];\r\n    pending[i].OffsetHigh = 0;\r\n    <span style=\"color: blue;\">StartThreadpoolIo(io);<\/span> \/\/ to here\r\n    bool result = WriteFile(fileHandle, data[i], size[i],\r\n            &amp;bytesWritten, &amp;pending[i]);\r\n\r\n    if (!result &amp;&amp; GetLastError() != ERROR_IO_PENDING) {\r\n        CancelThreadpoolIo(io);\r\n    }\r\n}\r\n<\/pre>\n<p>If you discover that the I\/O won&#8217;t generate a completion after all (because it failed synchronously, or because it succeeded synchronously on a handle that is marked as <code>FILE_<wbr \/>SKIP_<wbr \/>COMPLETION_<wbr \/>PORT_<wbr \/>ON_<wbr \/>SUCCESS<\/code>), then you need to call <code>Cancel\u00adThreadpool\u00adIo<\/code> to say, &#8220;Um, it looks like there won&#8217;t be any completion at all. Sorry.&#8221; That way, the thread pool knows that it shouldn&#8217;t be expecting one.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>It&#8217;s internally managing a reference count.<\/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-105933","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>It&#8217;s internally managing a reference count.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/105933","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=105933"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/105933\/revisions"}],"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=105933"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=105933"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=105933"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}