From f250c4ca3395759780892a7c76cb209298fe34ff Mon Sep 17 00:00:00 2001 From: garethsb-sony Date: Fri, 24 Nov 2017 23:00:46 +0000 Subject: [PATCH 1/4] When the C++ REST SDK is built as a DLL on Windows, and unloaded before 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. --- Release/src/pplx/threadpool.cpp | 39 +++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/Release/src/pplx/threadpool.cpp b/Release/src/pplx/threadpool.cpp index 8d3eadadc6..742b8d9f39 100644 --- a/Release/src/pplx/threadpool.cpp +++ b/Release/src/pplx/threadpool.cpp @@ -48,6 +48,16 @@ struct threadpool_impl final : crossplat::threadpool pthread_t t = *iter; void* res; pthread_join(t, &res); +#elif defined (_WIN32) && defined(_WINDLL) + if (boost::asio::detail::win_thread::terminate_threads()) + { + ::TerminateThread(iter->native_handle(), 0); + iter->detach(); + } + else + { + iter->join(); + } #else iter->join(); #endif @@ -133,6 +143,35 @@ threadpool& threadpool::shared_instance() return s_shared; } +#elif defined (_WIN32) && defined(_WINDLL) + +// if built as a DLL, the threadpool shared instance will be destroyed at DLL_PROCESS_DETACH, +// at which stage joining threads causes deadlock, hence this dance +threadpool& threadpool::shared_instance() +{ + static bool terminate_threads = false; + static struct restore_terminate_threads + { + ~restore_terminate_threads() + { + boost::asio::detail::win_thread::set_terminate_threads(terminate_threads); + } + } destroyed_after; + + static threadpool_impl s_shared(40); + + static struct enforce_terminate_threads + { + ~enforce_terminate_threads() + { + terminate_threads = boost::asio::detail::win_thread::terminate_threads(); + boost::asio::detail::win_thread::set_terminate_threads(true); + } + } destroyed_before; + + return s_shared; +} + #else // initialize the static shared threadpool From 01d5c19fe698a4a26f4247422dd024ba2d60375b Mon Sep 17 00:00:00 2001 From: garethsb-sony Date: Mon, 27 Nov 2017 11:44:01 +0000 Subject: [PATCH 2/4] Take 2. We can't just use TerminateThread indiscriminately because the 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. --- Release/src/pplx/threadpool.cpp | 50 +++++---------------------------- 1 file changed, 7 insertions(+), 43 deletions(-) diff --git a/Release/src/pplx/threadpool.cpp b/Release/src/pplx/threadpool.cpp index 742b8d9f39..308df3bf4d 100644 --- a/Release/src/pplx/threadpool.cpp +++ b/Release/src/pplx/threadpool.cpp @@ -9,16 +9,7 @@ #if !defined(CPPREST_EXCLUDE_WEBSOCKETS) || !defined(_WIN32) #include "pplx/threadpool.h" -#if !defined(_WIN32) -#define CPPREST_PTHREADS -#endif - -#if defined(CPPREST_PTHREADS) -#include -#else -#include -#endif - +#include #include #if defined(__ANDROID__) @@ -44,37 +35,14 @@ struct threadpool_impl final : crossplat::threadpool m_service.stop(); for (auto iter = m_threads.begin(); iter != m_threads.end(); ++iter) { -#if defined(CPPREST_PTHREADS) - pthread_t t = *iter; - void* res; - pthread_join(t, &res); -#elif defined (_WIN32) && defined(_WINDLL) - if (boost::asio::detail::win_thread::terminate_threads()) - { - ::TerminateThread(iter->native_handle(), 0); - iter->detach(); - } - else - { - iter->join(); - } -#else - iter->join(); -#endif + (*iter)->join(); } } private: void add_thread() { -#ifdef CPPREST_PTHREADS - pthread_t t; - auto result = pthread_create(&t, nullptr, &thread_start, this); - if (result == 0) - m_threads.push_back(t); -#else - m_threads.push_back(std::thread(&thread_start, this)); -#endif + m_threads.push_back(std::unique_ptr(new boost::asio::detail::thread([&]{ thread_start(this); }))); } #if defined(__ANDROID__) @@ -99,11 +67,7 @@ struct threadpool_impl final : crossplat::threadpool return arg; } -#if defined(CPPREST_PTHREADS) - std::vector m_threads; -#else - std::vector m_threads; -#endif + std::vector> m_threads; boost::asio::io_service::work m_work; }; } @@ -154,7 +118,7 @@ threadpool& threadpool::shared_instance() { ~restore_terminate_threads() { - boost::asio::detail::win_thread::set_terminate_threads(terminate_threads); + boost::asio::detail::thread::set_terminate_threads(terminate_threads); } } destroyed_after; @@ -164,8 +128,8 @@ threadpool& threadpool::shared_instance() { ~enforce_terminate_threads() { - terminate_threads = boost::asio::detail::win_thread::terminate_threads(); - boost::asio::detail::win_thread::set_terminate_threads(true); + terminate_threads = boost::asio::detail::thread::terminate_threads(); + boost::asio::detail::thread::set_terminate_threads(true); } } destroyed_before; From 9da20abfac1facb68752e52c3c69ff41506a2f1f Mon Sep 17 00:00:00 2001 From: garethsb-sony Date: Fri, 15 Dec 2017 11:26:59 +0000 Subject: [PATCH 3/4] Export the shared instance to allow cross-platform implementation of functionality (e.g. complete_after) that needs access to the io_service when using the asio-based default scheduler --- Release/include/pplx/threadpool.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Release/include/pplx/threadpool.h b/Release/include/pplx/threadpool.h index c67db358a0..72bb018d4a 100644 --- a/Release/include/pplx/threadpool.h +++ b/Release/include/pplx/threadpool.h @@ -52,7 +52,7 @@ using java_local_ref = std::unique_ptr::type, ja class threadpool { public: - static threadpool& shared_instance(); + _ASYNCRTIMP static threadpool& shared_instance(); _ASYNCRTIMP static std::unique_ptr __cdecl construct(size_t num_threads); virtual ~threadpool() = default; From e7566e29b40fe3265b8a563c56221fe6f231585b Mon Sep 17 00:00:00 2001 From: Robert Schumacher Date: Wed, 24 Jan 2018 08:28:29 -0800 Subject: [PATCH 4/4] Use improved shutdown logic even when building as static lib, because users could include it in a DLL. --- Release/src/pplx/threadpool.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Release/src/pplx/threadpool.cpp b/Release/src/pplx/threadpool.cpp index 308df3bf4d..d2dc6161a2 100644 --- a/Release/src/pplx/threadpool.cpp +++ b/Release/src/pplx/threadpool.cpp @@ -107,9 +107,9 @@ threadpool& threadpool::shared_instance() return s_shared; } -#elif defined (_WIN32) && defined(_WINDLL) +#elif defined(_WIN32) -// if built as a DLL, the threadpool shared instance will be destroyed at DLL_PROCESS_DETACH, +// if linked into a DLL, the threadpool shared instance will be destroyed at DLL_PROCESS_DETACH, // at which stage joining threads causes deadlock, hence this dance threadpool& threadpool::shared_instance() { @@ -159,4 +159,4 @@ std::unique_ptr crossplat::threadpool::construct(size_t n { return std::unique_ptr(new threadpool_impl(num_threads)); } -#endif \ No newline at end of file +#endif