title: Asio TCP server crash on DLL unload
body: <p>I'm using the standalone <a href="http://think-async.com" rel="nofollow noreferrer">Asio</a> library to host a simple TCP server. The code lives inside a dynamic library which is loaded by a host application I don't have control over.</p>
  
  <p>The library exposes an <code>Open</code> and <code>Close</code> function, which the host application should call after loading/before unloading the library.<br>
  In the <code>Open</code> function, I run the <code>asio::io_service</code> in a separate thread:</p>
  
  <pre><code>// start accepting connections
  server.accept();
  
  // start the asio service in a new thread
  serviceThread = std::make_unique&lt;std::thread&gt;([this]() {
      ioService.run();
  });
  </code></pre>
  
  <p>In the <code>Close</code> function, I stop the service and join the thread:</p>
  
  <pre><code>// stop the asio service
  ioService.stop();
  serviceThread-&gt;join();
  </code></pre>
  
  <p>The server's <code>accept</code> function is implemented as follows:</p>
  
  <pre><code>void Server::accept() {
      acceptor.async_accept(socket, [this](std::error_code err) {
          if (!err) {
              std::lock_guard&lt;std::mutex&gt; lock(sessionsMutex);
              sessions.emplace_back(new Session(*this, ioService, std::move(socket)));
          }
  
          accept();
      });
  }
  </code></pre>
  
  <p>Other functions to be called by the <code>asio::io_service</code> are implemented similarly, i.e. a lambda function binding <code>this</code> is passed to an asio <code>async</code> function.</p>
  
  <p>This code works fine when the host application calls <code>Open</code> and <code>Close</code> as required.<br>
  However, sometimes, the host application does not call the <code>Close</code> function before unloading the library, leading to a segmentation fault caused by a bad memory read:</p>
  
  <pre><code>Thread 14 Crashed:
  0   com.                            0x0000000121f1b14b asio::detail::scheduler::post_immediate_completion(asio::detail::scheduler_operation*, bool) + 27 (scheduler.ipp:281)
  1   com.                            0x0000000121f1f17c void asio::io_context::executor_type::post&lt;asio::detail::work_dispatcher&lt;myproject::Server::Session::write()::$_3&gt;, std::__1::allocator&lt;void&gt; &gt;(asio::detail::work_dispatcher&lt;myproject::Server::Session::write()::$_3&gt;&amp;&amp;, std::__1::allocator&lt;void&gt; const&amp;) const + 124 (io_context.hpp:270)
  2   com.                            0x0000000121f1f027 asio::async_result&lt;std::__1::decay&lt;myproject::Server::Session::write()::$_3&gt;::type, void ()&gt;::return_type asio::post&lt;asio::io_context::executor_type, myproject::Server::Session::write()::$_3&gt;(asio::io_context::executor_type const&amp;, myproject::Server::Session::write()::$_3&amp;&amp;, std::__1::enable_if&lt;is_executor&lt;asio::io_context::executor_type&gt;::value, void&gt;::type*) + 87 (post.hpp:58)
  3   com.                            0x0000000121f11cb2 asio::async_result&lt;std::__1::decay&lt;myproject::Server::Session::write()::$_3&gt;::type, void ()&gt;::return_type asio::post&lt;asio::io_context, myproject::Server::Session::write()::$_3&gt;(asio::io_context&amp;, myproject::Server::Session::write()::$_3&amp;&amp;, std::__1::enable_if&lt;is_convertible&lt;asio::io_context&amp;, asio::execution_context&amp;&gt;::value, void&gt;::type*) + 50 (post.hpp:69)
  [...]
  </code></pre>
  
  <p>What are my options? Is there a cross-platform way to detect the unloading of the library (must work on macOS and Windows), which allows me to perform the clean-up in any case?<br>
  Can I modify my usage of Asio to deal with this behaviour?</p>