I'm trying to provide co-routine support for non WinRT types that will asynchronously execute on the Windows thread pool.
cppcoro and libunifex both provide the equivalent coroutine task type task<T>
. I imagine it would be a good idea to run these tasks on the windows thread pool, but maybe I'm just being silly and should just use one of the thread pools provided these libraries.
On the other hand, I was hoping to see how cppwinrt handles this but I cant penetrate definition of IAsyncOperation
template <typename TResult>
struct __declspec(empty_bases) IAsyncOperation :
winrt::Windows::Foundation::IInspectable,
impl::consume_t<winrt::Windows::Foundation::IAsyncOperation<TResult>>,
impl::require<winrt::Windows::Foundation::IAsyncOperation<TResult>, winrt::Windows::Foundation::IAsyncInfo>
{
static_assert(impl::has_category_v<TResult>, "TResult must be WinRT type.");
IAsyncOperation(std::nullptr_t = nullptr) noexcept {}
IAsyncOperation(void* ptr, take_ownership_from_abi_t) noexcept : winrt::Windows::Foundation::IInspectable(ptr, take_ownership_from_abi) {}
};
Kenny Kerr did a talk at cppcon 2016 on this so maybe ill try watching that and then try greping the source code/generated headers.
The most hardest part about learning to implement coroutines is disambiguating the difference between awaitable types and coroutine promise types.
concept awaitable:
await_ready() -> bool
await_suspend(coroutine_handle<> handle) -> void
await_resume() -> void
concept promise_type:
get_return_object() -> coroutine_return_type<T>
return_value(T const& v) -> void
unhandled_exception() -> void
initial_suspend() -> awaitable
final_suspend() noexcept -> awaitable
For now, I only need to poll if the object returned by my coroutine is ready (rather than co_await
it), but I need to be able to await on winrt::resume_on_signal
in the coroutine. cppwinrt does the work implementing the awaitable type in winrt::resume_on_signal
and I need to do the work implementing the promise type returned by the coroutine I call winrt::resume_on_signal
in.
The simplest possible type you could return from a coroutine might be std::shared_ptr<std::optional<T>
and specializing coroutine_traits allows you to do this:
namespace std::experimental
{
template <typename T, typename... Args>
struct coroutine_traits<std::shared_ptr<std::optional<T>>, Args...>
{
struct promise_type
{
using result_type = std::shared_ptr<std::optional<T>>;
result_type result = std::make_shared<std::optional<T>>(std::nullopt);
result_type get_return_object() { return result; }
void return_value(T const& v) { *result = std::optional<T>(v); }
void unhandled_exception() { assert(0); }
std::experimental::suspend_never initial_suspend() { return{}; }
std::experimental::suspend_never final_suspend() noexcept { return{}; }
};
};
}
which you can use as
std::shared_ptr<std::optional<int>> my_coroutine2(HANDLE h)
{
co_await winrt::resume_on_signal(h);
co_return int(1);
}
...
auto shared_optional = my_coroutine2();
if (*shared_optional)
{
auto result = **shared_optional;
}
However, there are caveats. You will create a race condition if you also make this an awaitable type oldnewthing, although I don't beleive you even could make it awaitable because you need to be able call a continuation when the value is set. And it doesn't handle exceptions.