cpprestsdk
cpprestsdk copied to clipboard
pplx::when_all crashes with default constructed pplx::tasks
A default constructed task behaves quite similarly to a task containing an exception, e.g. by pplx::task_from_exception or that has been canceled, in that the default constructed task throws pplx::invalid_operation from wait and get, where a canceled task throws pplx::task_canceled. There are differences, e.g. is_done throws rather than returning true as a canceled task would, but much task-handling code will only use either wait or `get.
However, pplx::when_all does not handle default tasks.
when_all_test.cpp
#include <iostream>
#include "pplx/pplxtasks.h"
int main()
{
pplx::task<void> default_task;
std::cout << "wait:" << std::endl;
try
{
default_task.wait();
}
catch (const pplx::invalid_operation& e)
{
std::cout << e.what() << '\n';
}
std::cout << "when_all:" << std::endl;
auto tasks = { default_task };
pplx::when_all(tasks.begin(), tasks.end()).then([](pplx::task<void> finally)
{
try
{
finally.wait();
}
catch (const pplx::invalid_operation& e)
{
std::cout << e.what() << '\n';
}
}).wait();
std::cout << "done" << std::endl;
}
Output in gdb:
wait:
wait() cannot be called on a default constructed task.
when_all:
Program received signal SIGSEGV, Segmentation fault.
pplx::details::_WhenAllImpl<void, pplx::task<void> const*>::_Perform (
_TaskOptions=..., _Begin=0x7fffffffce50, _End=0x7fffffffce60)
at .../include/pplx/pplxtasks.h:6746
6746 _JoinAllTokens_add(_MergedSource, _PTasks->_GetImpl()->_M_pTokenState);
I also tried when_any:
< pplx::when_all(tasks.begin(), tasks.end()).then([](pplx::task<void> finally)
> pplx::when_any(tasks.begin(), tasks.end()).then([](pplx::task<size_t> finally)
In this case, the call to when_any produces the exception:
is_apartment_aware() cannot be called on a default constructed task.
At least this doesn't result in a core dump, but wouldn't it be more consistent if that exception were contained in the resulting task rather than thrown from when_any?
As an additional note, the documentation for pplx::task says that the exception thrown by a default constructed instance is std::invalid_argument. However, pplx::invalid_operation does not have that as a base.