in Source/Task/ThreadPool_stl.cpp [32:146]
HRESULT Initialize(
_In_opt_ void* context,
_In_ ThreadPoolCallback* callback) noexcept
{
m_context = context;
m_callback = callback;
uint32_t numThreads = std::thread::hardware_concurrency();
if (numThreads == 0)
{
numThreads = 1;
}
try
{
while (numThreads != 0)
{
numThreads--;
m_pool.emplace_back(std::thread([this]
{
#if defined(HC_PLATFORM) && HC_PLATFORM == HC_PLATFORM_ANDROID
JNIEnv* jniEnv = nullptr;
JavaVM* jvm = nullptr;
#endif
std::unique_lock<std::mutex> lock(m_wakeLock);
while (true)
{
if (m_calls == 0)
{
m_wake.wait(lock);
}
if (m_terminate)
{
break;
}
#if defined(HC_PLATFORM) && HC_PLATFORM == HC_PLATFORM_ANDROID
// lazy check for the JavaVM, we do it here so that we
// will attach even if the thread pool is initialized
// before we're given the jvm
if (!jniEnv)
{
jvm = s_javaVm;
if (jvm)
{
jvm->AttachCurrentThread(&jniEnv, nullptr);
}
}
#endif
if (m_calls != 0)
{
m_calls--;
// ActionComplete is an optional call
// the callback can make to indicate
// all portions of the call have finished
// and it is safe to release the
// thread pool, even if the callback has
// not totally unwound. This is neccessary
// to allow users to close a task queue from
// within a callback. Task queue guards with an
// extra ref to ensure a safe point where
// member state is no longer accessed, but the
// final release does need to wait on outstanding
// calls.
{
std::unique_lock<std::mutex> lock(m_activeLock);
m_activeCalls++;
}
ActionCompleteImpl ac(this);
lock.unlock();
AddRef();
m_callback(m_context, ac);
lock.lock();
if (!ac.Invoked)
{
ac();
}
if (m_terminate)
{
lock.unlock();
Release(); // This could destroy us
break;
}
else
{
Release();
}
}
}
#if defined(HC_PLATFORM) && HC_PLATFORM == HC_PLATFORM_ANDROID
if (jniEnv && jvm)
{
jvm->DetachCurrentThread();
}
#endif
}));
}
}
catch (const std::bad_alloc&)
{
return E_OUTOFMEMORY;
}
return S_OK;
}