title: Asynchronous ReadDirectoryChangesW call blocks the thread from exiting
body: <h2>Introduction:</h2>
  <p>I am writing a small app that monitors a certain directory for newly added files.</p>
  <p>I would like to put the monitoring code in a separate thread, so I can leave the main thread free for other stuff and cancel the monitoring thread when I need to.</p>
  <h2>Relevant Information:</h2>
  <ul>
  <li>I am using <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/aa365465(v=vs.85).aspx" rel="nofollow noreferrer">ReadDirectoryChangesW</a> to do the monitoring</li>
  <li>I am using raw WIN32 API for thread creation/synchronization</li>
  <li>I am trying to support Windows XP onward.</li>
  </ul>
  <h2>Problem:</h2>
  <p>I was able to code everything properly, except for one thing:</p>
  <p>I cannot exit the monitoring thread properly, hence this post.</p>
  <p>I am signaling an event object in the main thread, wait for the thread to exit, and then do clean up.</p>
  <p>The problem lies in my usage of <code>ReadDirectoryChangesW</code> since everything works fine after I comment out that piece of code.</p>
  <p>Once the event handle is signaled, <code>ReadDirectoryChangesW</code> blocks the thread which prevents it to &quot;catch&quot; the event and exit. If I add a new file in the directory it &quot;unblocks&quot; <code>ReadDirectoryChangesW</code>, thread &quot;catches&quot; the event and exits.</p>
  <p>To help further, I have made a small <a href="https://stackoverflow.com/help/mcve">MVCE</a> below, which illustrates what I have stated so far.</p>
  <h2>MVCE:</h2>
  <pre><code>#include &lt;iostream&gt;
  #include &lt;Windows.h&gt;
  #include &lt;map&gt;
  
  struct SThreadParams
  {
      HANDLE hEvent;
      HANDLE hDir;
      int processDirectoryChanges(const char *buffer)
      {
          if (NULL == buffer) return -1;
  
          DWORD offset = 0;
          char fileName[MAX_PATH] = &quot;&quot;;
          FILE_NOTIFY_INFORMATION *fni = NULL;
  
          do
          {
              fni = (FILE_NOTIFY_INFORMATION*)(&amp;buffer[offset]);
              // since we do not use UNICODE, 
              // we must convert fni-&gt;FileName from UNICODE to multibyte
              int ret = ::WideCharToMultiByte(CP_ACP, 0, fni-&gt;FileName,
                  fni-&gt;FileNameLength / sizeof(WCHAR),
                  fileName, sizeof(fileName), NULL, NULL);
  
              switch (fni-&gt;Action)
              {
              case FILE_ACTION_ADDED:     
              {
                  std::cout &lt;&lt; &quot;FILE_ACTION_ADDED &quot; &lt;&lt; fileName &lt;&lt; std::endl;
              }
              break;
              case FILE_ACTION_REMOVED:
              {
                  std::cout &lt;&lt; &quot;FILE_ACTION_REMOVED &quot; &lt;&lt; fileName &lt;&lt; std::endl;
              }
              break;
              case FILE_ACTION_MODIFIED:
              {
                  std::cout &lt;&lt; &quot;FILE_ACTION_MODIFIED &quot; &lt;&lt; fileName &lt;&lt; std::endl;
              }
              break;
              case FILE_ACTION_RENAMED_OLD_NAME:
              {
                  std::cout &lt;&lt; &quot;FILE_ACTION_RENAMED_OLD_NAME &quot; &lt;&lt; fileName &lt;&lt; std::endl;
              }
              break;
              case FILE_ACTION_RENAMED_NEW_NAME:
              {
                  std::cout &lt;&lt; &quot;FILE_ACTION_RENAMED_NEW_NAME &quot; &lt;&lt; fileName &lt;&lt; std::endl;
              }
              break;
              default:
                  break;
              }
              // clear string so we can reuse it
              ::memset(fileName, '\0', sizeof(fileName));
              // advance to next entry
              offset += fni-&gt;NextEntryOffset;
  
          } while (fni-&gt;NextEntryOffset != 0);
  
          return 0;
      }
  };
  
  DWORD WINAPI thread(LPVOID arg)
  {
      SThreadParams p = *((SThreadParams *)arg);
      OVERLAPPED ovl = { 0 };
      DWORD bytesTransferred = 0, error = 0;
      char buffer[1024];
  
      if (NULL == (ovl.hEvent = ::CreateEvent(NULL, TRUE, FALSE, NULL)))
      {
          std::cout &lt;&lt; &quot;CreateEvent error = &quot; &lt;&lt; ::GetLastError() &lt;&lt; std::endl;
          return ::GetLastError();
      };
  
      do {
  
          if (::ReadDirectoryChangesW(p.hDir, buffer, sizeof(buffer), FALSE,
              FILE_NOTIFY_CHANGE_FILE_NAME,
              NULL, &amp;ovl, NULL))
          {
              if (::GetOverlappedResult(p.hDir, &amp;ovl, &amp;bytesTransferred, TRUE))
              {
                  for (int i = 0; i &lt; 5; ++i) std::cout &lt;&lt; '=';
                  std::cout &lt;&lt; std::endl;
  
                  if (-1 == p.processDirectoryChanges(buffer))
                      std::cout &lt;&lt; &quot;processDirectoryChanges error = &quot; &lt;&lt; std::endl;
              }
              else
              { 
                  bytesTransferred = 0;
                  std::cout &lt;&lt; &quot;GetOverlappedResult error = &quot; &lt;&lt; ::GetLastError() &lt;&lt; std::endl;
              }
  
              if (0 == ::ResetEvent(ovl.hEvent))
              {
                  std::cout &lt;&lt; &quot;ResetEvent error = &quot; &lt;&lt; ::GetLastError() &lt;&lt; std::endl;
                  ::CloseHandle(ovl.hEvent);
                  return ::GetLastError();
              }
          }
          else
          {
              // we shall just output the error, and try again...
              std::cout &lt;&lt; &quot;ReadDirectoryChangesW error =  &quot; &lt;&lt; ::GetLastError() &lt;&lt; std::endl;
          }
  
          error = ::WaitForSingleObject(p.hEvent, 2000);
  
      } while (WAIT_TIMEOUT == error);
  
      ::CloseHandle(ovl.hEvent);
  
      return 0;
  }
  
  int main()
  {
      SThreadParams s;
      
      s.hDir = ::CreateFile(SOME_DIRECTORY,
              FILE_LIST_DIRECTORY, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
              NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
      
      if (INVALID_HANDLE_VALUE == s.hDir)
      {
          std::cout &lt;&lt; &quot;CreateFile error = &quot; &lt;&lt; ::GetLastError() &lt;&lt; std::endl;
          return 1;
      }
  
      s.hEvent = ::CreateEvent(NULL, TRUE, FALSE, NULL);
      
      if (NULL == s.hEvent)
      {
          std::cout &lt;&lt; &quot;CreateEvent error = &quot; &lt;&lt; ::GetLastError() &lt;&lt; std::endl;
          ::CloseHandle(s.hDir);
          return 1;
      }
      
      HANDLE hThread = ::CreateThread(NULL, 0, thread, (LPVOID)&amp;s, 0, NULL);
      
      if (NULL == hThread)
      {
          std::cout &lt;&lt; &quot;CreateThread error = &quot; &lt;&lt; ::GetLastError() &lt;&lt; std::endl;
          ::CloseHandle(s.hDir);
          ::CloseHandle(s.hEvent);
          return 1;
      }
  
      std::cout &lt;&lt; &quot;press any key to close program...&quot; &lt;&lt; std::endl;
      std::cin.get();
      
      if (0 == ::CancelIoEx(s.hDir, NULL))
      {
          std::cout &lt;&lt; &quot;CancelIoEx error = &quot; &lt;&lt; ::GetLastError() &lt;&lt; std::endl;
          ::CloseHandle(s.hDir);
          ::CloseHandle(s.hEvent);
          return 1;
      }
  
      if (0 == ::SetEvent(s.hEvent))
      {
          std::cout &lt;&lt; &quot;SetEvent error = &quot; &lt;&lt; ::GetLastError() &lt;&lt; std::endl;
          ::CloseHandle(s.hDir);
          ::CloseHandle(s.hEvent);
          return 1;
      }
  
      // wait for thread to exit
      DWORD error = ::WaitForSingleObject(hThread, INFINITE);
      std::cout &lt;&lt; &quot;Thread exited with error code = &quot; &lt;&lt; error &lt;&lt; std::endl;
  
      ::CloseHandle(s.hEvent);
      ::CloseHandle(s.hDir);
      ::CloseHandle(hThread);
  
      return 0;
  }
  </code></pre>
  <h2>My Efforts to Solve:</h2>
  <ul>
  <li><p>I have moved out <code>OVERLAPPED</code> structure out of thread into structure that was passed to thread. Then I set <code>OVERLAPPED.hEvent</code> to forcibly &quot;unblock&quot; <code>ReadDirectoryChangesW</code>. This seems to work, but scares me because I think it is not safe/error prone since it is undocumented.</p>
  </li>
  <li><p>I have tried to use completion routines but got no success since I am new with all this. I was able to receive notifications, but content of the buffer (the one filled with <code>ReadDirectoryChangesW</code>) was not read properly after the first pass. I am still trying to make this work on my own, but could use help.</p>
  </li>
  <li><p>I could use I/o completion port, but since I will monitor only one directory I think this is a bit of an overkill. If I am mistaken, please instruct me how to use I/o completion port for my case, I would love to try them out.</p>
  </li>
  </ul>
  <h2>Question:</h2>
  <p>Given the MVCE above, can you instruct me on how to modify the code in the thread procedure, so it exits properly (without <code>ReadDirectoryChangesW</code> blocking).</p>
  <p><em>I have a feeling that i will have to use completion routines. In that case I would humbly ask for some pseudo code or written instructions since this would be my first time using them.</em></p>
  <p>Each time I made a progress, I will update this post with relevant data accordingly.</p>