Last active
January 30, 2019 23:48
-
-
Save kennykerr/13082fbb4b6502fd7fa9570a896db4f0 to your computer and use it in GitHub Desktop.
How to use a private thread pool with a C++ coroutine
This file contains 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
#include "winrt/Windows.Foundation.h" | |
#include <windows.h> | |
using namespace winrt; | |
using namespace Windows::Foundation; | |
struct private_pool | |
{ | |
private_pool() : | |
m_pool(check_pointer(CreateThreadpool(nullptr))) | |
{ | |
InitializeThreadpoolEnvironment(&m_environment); | |
SetThreadpoolCallbackPool(&m_environment, m_pool.get()); | |
} | |
void thread_limits(uint32_t const high, uint32_t const low) | |
{ | |
SetThreadpoolThreadMaximum(m_pool.get(), high); | |
check_bool(SetThreadpoolThreadMinimum(m_pool.get(), low)); | |
} | |
~private_pool() noexcept | |
{ | |
DestroyThreadpoolEnvironment(&m_environment); | |
} | |
bool await_ready() const noexcept | |
{ | |
return false; | |
} | |
void await_resume() const noexcept | |
{ | |
} | |
void await_suspend(std::experimental::coroutine_handle<> handle) | |
{ | |
if (!TrySubmitThreadpoolCallback(callback, handle.address(), &m_environment)) | |
{ | |
throw_last_error(); | |
} | |
} | |
private: | |
static void WINRT_CALL callback(PTP_CALLBACK_INSTANCE, void* context) noexcept | |
{ | |
std::experimental::coroutine_handle<>::from_address(context)(); | |
} | |
struct pool_traits | |
{ | |
using type = PTP_POOL; | |
static void close(type value) noexcept | |
{ | |
CloseThreadpool(value); | |
} | |
static constexpr type invalid() noexcept | |
{ | |
return nullptr; | |
} | |
}; | |
handle_type<pool_traits> m_pool; | |
TP_CALLBACK_ENVIRON m_environment; | |
}; | |
struct Queue | |
{ | |
private_pool m_pool; | |
Queue() | |
{ | |
m_pool.thread_limits(1, 1); | |
} | |
IAsyncAction Async(delegate<> callback) | |
{ | |
// This uses the default process-wide thread pool. | |
// co_await resume_background(); | |
// This uses the private m_pool thread pool. Just | |
// remember that the pool must outlive the coroutine. | |
co_await m_pool; | |
callback(); | |
} | |
}; | |
int main() | |
{ | |
init_apartment(); | |
Queue queue; | |
std::vector<IAsyncAction> results; | |
for (uint32_t item{}; item < 20; ++item) | |
{ | |
results.push_back(queue.Async([=] | |
{ | |
printf("Item=%d Thread=%d\n", item, GetCurrentThreadId()); | |
})); | |
} | |
puts("Queued"); | |
for (auto&& async : results) | |
{ | |
async.get(); | |
} | |
puts("Done"); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment