Search code examples
c++exceptioncasablancapplcpprest-sdk

How to catch exceptions from multiple tasks in Casablanca


I'm trying to join two pplx tasks using the && operator of task, where both sub tasks can throw exceptions.

I understand from the ppl documentation that I can catch an exception in a final, task-based continuation. This works in Casablanca as well. However, I can catch only one exception in my final continuation. If both sub tasks throw, one remains unhandled.

Here's a minimal example illustrating my problem:

#include <pplx/pplxtasks.h>
#include <iostream>

int main(int argc, char *argv[])
{
    int a = 0; int b = 0;

    auto t1 = pplx::create_task([a] { return a+1; })
    .then([](int a) { throw std::runtime_error("a");
                      return a+1; });

    auto t2 = pplx::create_task([b] { return b+1; })
    .then([](int b) { throw std::runtime_error("b");
                      return b+1; });

    (t1 && t2)
    .then([] (std::vector<int>) { /*...*/ })
    .then([] (pplx::task<void> prev) {
        try {
            prev.get();
        } catch (std::runtime_error e) {
            std::cout << "caught " << e.what() << std::endl;
        }
    });

    std::cin.get();
}

The try/catch is able to catch whichever of the two exceptions occurs first. How can I catch the other?


Solution

  • You would have to add a final task-based continuation to each sub-task. I would suggest re-throwing any exception you catch, however, that would likely be a bad idea since the continuation task doesn't realize that the 2 exceptions are equivalent see below example for proof.
    Output:
    caught a
    caught final a
    caught b

    Also, if you remove the sleep, you will receive a "Trace/breakpoint trap" exception.

    #include <pplx/pplxtasks.h>
    #include <iostream>
    
    int main(int argc, char *argv[])
    {
        int a = 0; int b = 2;
    
        auto t1 = pplx::create_task([a] { return a+1; })
        .then([](int a) { throw std::runtime_error("a"); return a+1; })
        .then([] (pplx::task<int> prev)
        {
            int retVal = -1;
            try
            {
                retVal = prev.get();
            }
            catch (std::runtime_error e)
            {
                std::cout << "caught " << e.what() << std::endl;
                throw e;
            }
    
            return retVal;
        });
    
        auto t2 = pplx::create_task([b] { return b+1; })
        .then([](int b) { throw std::runtime_error("b"); return b+1; })
        .then([] (pplx::task<int> prev)
        {
            int retVal = -1;
            try
            {
                sleep(1);
                retVal = prev.get();
            }
            catch (std::runtime_error e)
            {
                std::cout << "caught " << e.what() << std::endl;
                throw e;
            }
    
            return retVal;
        });
    
        (t1 && t2)
        .then([] (std::vector<int> v) { for(int i : v) { std::cout << i << std::endl; } })
        .then([] (pplx::task<void> prev)
        {
            try
            {
                prev.get();
            }
            catch (std::runtime_error e)
            {
                std::cout << "caught final " << e.what() << std::endl;
            }
        }).get();
    }