Skip to content

Commit eba38b8

Browse files
garethsbras0219-msft
authored andcommitted
Making crossplat::threadpool::shared_instance() safe to use in cpprest DLL (#611)
* 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. * 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. * 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 * Use improved shutdown logic even when building as static lib, because users could include it in a DLL.
1 parent 3915408 commit eba38b8

File tree

2 files changed

+35
-32
lines changed

2 files changed

+35
-32
lines changed

Release/include/pplx/threadpool.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ using java_local_ref = std::unique_ptr<typename std::remove_pointer<T>::type, ja
5252
class threadpool
5353
{
5454
public:
55-
static threadpool& shared_instance();
55+
_ASYNCRTIMP static threadpool& shared_instance();
5656
_ASYNCRTIMP static std::unique_ptr<threadpool> __cdecl construct(size_t num_threads);
5757

5858
virtual ~threadpool() = default;

Release/src/pplx/threadpool.cpp

Lines changed: 34 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,7 @@
99
#if !defined(CPPREST_EXCLUDE_WEBSOCKETS) || !defined(_WIN32)
1010
#include "pplx/threadpool.h"
1111

12-
#if !defined(_WIN32)
13-
#define CPPREST_PTHREADS
14-
#endif
15-
16-
#if defined(CPPREST_PTHREADS)
17-
#include <pthread.h>
18-
#else
19-
#include <thread>
20-
#endif
21-
12+
#include <boost/asio/detail/thread.hpp>
2213
#include <vector>
2314

2415
#if defined(__ANDROID__)
@@ -44,27 +35,14 @@ struct threadpool_impl final : crossplat::threadpool
4435
m_service.stop();
4536
for (auto iter = m_threads.begin(); iter != m_threads.end(); ++iter)
4637
{
47-
#if defined(CPPREST_PTHREADS)
48-
pthread_t t = *iter;
49-
void* res;
50-
pthread_join(t, &res);
51-
#else
52-
iter->join();
53-
#endif
38+
(*iter)->join();
5439
}
5540
}
5641

5742
private:
5843
void add_thread()
5944
{
60-
#ifdef CPPREST_PTHREADS
61-
pthread_t t;
62-
auto result = pthread_create(&t, nullptr, &thread_start, this);
63-
if (result == 0)
64-
m_threads.push_back(t);
65-
#else
66-
m_threads.push_back(std::thread(&thread_start, this));
67-
#endif
45+
m_threads.push_back(std::unique_ptr<boost::asio::detail::thread>(new boost::asio::detail::thread([&]{ thread_start(this); })));
6846
}
6947

7048
#if defined(__ANDROID__)
@@ -89,11 +67,7 @@ struct threadpool_impl final : crossplat::threadpool
8967
return arg;
9068
}
9169

92-
#if defined(CPPREST_PTHREADS)
93-
std::vector<pthread_t> m_threads;
94-
#else
95-
std::vector<std::thread> m_threads;
96-
#endif
70+
std::vector<std::unique_ptr<boost::asio::detail::thread>> m_threads;
9771
boost::asio::io_service::work m_work;
9872
};
9973
}
@@ -133,6 +107,35 @@ threadpool& threadpool::shared_instance()
133107
return s_shared;
134108
}
135109

110+
#elif defined(_WIN32)
111+
112+
// if linked into a DLL, the threadpool shared instance will be destroyed at DLL_PROCESS_DETACH,
113+
// at which stage joining threads causes deadlock, hence this dance
114+
threadpool& threadpool::shared_instance()
115+
{
116+
static bool terminate_threads = false;
117+
static struct restore_terminate_threads
118+
{
119+
~restore_terminate_threads()
120+
{
121+
boost::asio::detail::thread::set_terminate_threads(terminate_threads);
122+
}
123+
} destroyed_after;
124+
125+
static threadpool_impl s_shared(40);
126+
127+
static struct enforce_terminate_threads
128+
{
129+
~enforce_terminate_threads()
130+
{
131+
terminate_threads = boost::asio::detail::thread::terminate_threads();
132+
boost::asio::detail::thread::set_terminate_threads(true);
133+
}
134+
} destroyed_before;
135+
136+
return s_shared;
137+
}
138+
136139
#else
137140

138141
// initialize the static shared threadpool
@@ -156,4 +159,4 @@ std::unique_ptr<crossplat::threadpool> crossplat::threadpool::construct(size_t n
156159
{
157160
return std::unique_ptr<crossplat::threadpool>(new threadpool_impl(num_threads));
158161
}
159-
#endif
162+
#endif

0 commit comments

Comments
 (0)