Here is a condensation of what I'm doing.
The key requirement is that all tasks returned by
observe_all_exceptions()
is lifted directly from here. get_vector_of_tasks()
is a stand-in for the actual much-more-complex source of the vector<tasks_t>
so don't take it too seriously. The code following it, though, is pretty much verbatim from the application, minus extraneous stuff that doesn't pertain to the use of observe_all_exceptions()
. Both that code and the real get_vector_of_tasks()
are class member functions in the actual code.The key requirement is that all tasks returned by
get_vector_of_tasks()
be created with cancel_token
, and that they check and respond to it reasonably quickly during operation. Otherwise, observe_all_exceptions()
can take a while.typedef std::vector<pplx::task<web::json::value>> tasks_t;
//...
template<class T, class InIt>
void observe_all_exceptions(InIt first, InIt last)
{
std::for_each(first, last, [](pplx::task<T> t)
{
t.then([](pplx::task<T> previousTask)
{
try { previousTask.get(); }
catch (std::exception const &x) {
log_debug("observe_all_exceptions: task ends with exception [%s]", x.what());
}
catch (...) {
log_debug("observe_all_exceptions: task ends with exception [catchall exception]");
}
});
});
}
tasks_t get_vector_of_tasks(concurrency::cancellation_token cancel_token)
{
using namespace web::http;
client::http_client client(U("http://some.web.site"));
tasks_t rtn;
for (int i = 0; i < 10; ++i)
{
http_request request(methods::GET);
http_headers &headers = request.headers();
headers.add(header_names::accept, U("application/json"));
web::uri uri;
//... make uri unique
auto task = client.request(request, cancel_token).then(
[uri](http_response response) -> pplx::task<web::json::value>
{
status_code status = response.status_code();
if (response.status_code() >= 400) //one possible source of exceptions
{
utility::ostringstream_t msg;
msg << U("URI: [") << uri.to_string() << U("] ")
<< U("response: [") << response.status_code()
<< U(' ') << response.reason_phrase()
<< U(' ') << response.extract_string().get().substr(0, 128) << U(']');
throw http_exception(msg.str());
} //if
return response.extract_json();
});
rtn.push_back(task);
} //for
return rtn;
}
concurrency::cancellation_token_source cts;
tasks_t tasks;
try {
tasks = get_vector_of_tasks(cts.get_token()).get();
while (!tasks.empty())
{
auto sep = std::partition(std::begin(tasks), std::end(tasks),
[](pplx::task<web::json::value> task){ return !task.is_done(); });
if (0 == std::distance(sep, std::end(tasks)))
{
std::this_thread::sleep_for(std::chrono::milliseconds(200));
continue;
} //if
for (auto task_it = sep; task_it != std::end(tasks); ++task_it)
{
auto const &task = *task_it;
auto json = task.get();
auto result_json = json[U("result")].as_array();
for (auto result : result_json)
{
//... non-thread-safe consumer of result is called here
} //for
} //for
tasks.erase(sep, tasks.end());
} //while
}
catch (...)
{
cts.cancel(); //tasks must timely pay attention to this
observe_all_exceptions<web::json::value>(std::begin(tasks), std::end(tasks));
throw;
}
-evan