Making crossplat::threadpool::shared_instance() safe to use in cpprest DLL#611
Merged
ras0219-msft merged 4 commits intomicrosoft:masterfrom Jan 24, 2018
Merged
Conversation
…re the process exits, the crossplat::threadpool::shared_instance() is destroyed at DLL_PROCESS_DETACH, at which stage joining threads causes deadlock. Use the workaround provided by asio to terminate the threads in this case.
…e thread may be holding e.g. the heap lock, so we need to ensure the thread is in a known state. Rather than reinvent the wheel, use asio's own thread class which already provides the necessary mechanism.
…functionality (e.g. complete_after) that needs access to the io_service when using the asio-based default scheduler
… users could include it in a DLL.
Contributor
|
Thank you for the PR and the detailed writeup! I have to concede as well that using I've removed the restriction to only follow this process in DLLs, because it's unfortunately common for people to link things built as static libs into DLLs. It shouldn't harm EXEs either, so it seems clear that we should just always use this new path. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This took me a while to track down. While investigating PR #608, on Windows / VS 2013, I found that many of the test suites would more often than not complete successfully, but then the test_runner exe wouldn't exit. I found this was due to deadlock while the
test_module_loaderwas unloading DLLs. The culprit is the destruction of thestatic threadpool_implinsidecrossplat::threadpool::shared_instance(). Its destructor must join all the threads in the pool. This is likely to deadlock because inDllMainatDLL_PROCESS_DETACH, the loader lock is held, and the thread to be joined therefore cannot signal thread termination byDLL_THREAD_DETACH. (A quick web search finds many explanations of this, including some good ones on Microsoft's own The Old New Thing.)Why hasn't this been reported before? I assume mostly on Windows, people aren't using e.g.
CPPREST_FORCE_HTTP_LISTENER_ASIOor if they are, they aren't explicitly unloading the cpprest DLL until process termination.There are two approaches to fixing this:
shared_ptr<threadpool>, could work.Assuming 2 is preferable, I ultimately discovered that asio already provides the mechanism to solve this exact issue. And it has been present and documented in asio since at least Boost 1.37.0, so although it is in the namespace
boost::asio::detailI think it can be relied on. I have tried Boost 1.65.1 (current) and Boost 1.54.0 (cpprestsdk's documented minimum version). I have run tests on both Windows and Linux platforms.Implementation notes:
#if defined(CPPREST_PTHREADS)alternative, butboost::asio::detail::threadpicks Windows threads or POSIX threads (or falls back tostd::thread) appropriately anyway, so the code is now mostly simpler than it was too.std::vector<boost::asio::detail::thread>, but for some reason thethreadtype isn't movable, hence the use ofstd::unique_ptr.