aggregate_exception for ANY promise

This commit is contained in:
BlackMATov
2020-01-01 04:56:04 +07:00
parent 661b30b395
commit 1cce4c305e
2 changed files with 107 additions and 29 deletions

View File

@@ -86,6 +86,54 @@ namespace promise_hpp
no_timeout,
timeout
};
//
// aggregate_exception
//
class aggregate_exception final : public std::exception {
private:
using exceptions_t = std::vector<std::exception_ptr>;
using internal_state_t = std::shared_ptr<exceptions_t>;
public:
aggregate_exception()
: state_(std::make_shared<exceptions_t>()) {}
explicit aggregate_exception(exceptions_t exceptions)
: state_(std::make_shared<exceptions_t>(std::move(exceptions))) {}
aggregate_exception(const aggregate_exception& other) noexcept
: state_(other.state_) {}
aggregate_exception& operator=(const aggregate_exception& other) noexcept {
if ( this != &other ) {
state_ = other.state_;
}
return *this;
}
const char* what() const noexcept override {
return "Aggregate exception";
}
bool empty() const noexcept {
return (*state_).empty();
}
std::size_t size() const noexcept {
return (*state_).size();
}
std::exception_ptr at(std::size_t index) const {
return (*state_).at(index);
}
std::exception_ptr operator[](std::size_t index) const noexcept {
return (*state_)[index];
}
private:
internal_state_t state_;
};
}
// -----------------------------------------------------------------------------
@@ -1165,15 +1213,12 @@ namespace promise_hpp
, results(count) {}
};
return make_promise<ResultPromiseValueType>([begin, end](auto&& resolver, auto&& rejector){
return make_promise<ResultPromiseValueType>(
[begin, end](auto&& resolver, auto&& rejector){
std::size_t result_index = 0;
auto context = std::make_shared<context_t>(std::distance(begin, end));
for ( Iter iter = begin; iter != end; ++iter, ++result_index ) {
(*iter).then([
context,
resolver,
result_index
](auto&& v) mutable {
(*iter).then([context, resolver, result_index](auto&& v) mutable {
context->results[result_index] = std::forward<decltype(v)>(v);
if ( !--context->success_counter ) {
std::vector<SubPromiseResult> results;
@@ -1183,7 +1228,9 @@ namespace promise_hpp
}
resolver(std::move(results));
}
}).except(rejector);
}).except([rejector](std::exception_ptr e) mutable {
rejector(e);
});
}
});
}
@@ -1201,27 +1248,33 @@ namespace promise_hpp
template < typename Iter
, typename SubPromise = typename std::iterator_traits<Iter>::value_type
, typename SubPromiseResult = typename SubPromise::value_type >
promise<SubPromiseResult>
, typename SubPromiseResult = typename SubPromise::value_type
, typename ResultPromiseValueType = SubPromiseResult >
promise<ResultPromiseValueType>
make_any_promise(Iter begin, Iter end) {
if ( begin == end ) {
throw std::logic_error("at least one input promise must be provided for make_any_promise");
return make_rejected_promise<ResultPromiseValueType>(aggregate_exception());
}
struct context_t {
std::atomic_size_t failure_counter{0u};
std::vector<std::exception_ptr> exceptions;
context_t(std::size_t count)
: failure_counter(count) {}
: failure_counter(count)
, exceptions(count) {}
};
return make_promise<SubPromiseResult>([begin, end](auto&& resolver, auto&& rejector){
return make_promise<ResultPromiseValueType>(
[begin, end](auto&& resolver, auto&& rejector){
std::size_t exception_index = 0;
auto context = std::make_shared<context_t>(std::distance(begin, end));
for ( Iter iter = begin; iter != end; ++iter ) {
for ( Iter iter = begin; iter != end; ++iter, ++exception_index ) {
(*iter).then([resolver](auto&& v) mutable {
resolver(std::forward<decltype(v)>(v));
}).except([context, rejector](std::exception_ptr e) mutable {
}).except([context, rejector, exception_index](std::exception_ptr e) mutable {
context->exceptions[exception_index] = e;
if ( !--context->failure_counter ) {
rejector(e);
rejector(aggregate_exception(std::move(context->exceptions)));
}
});
}
@@ -1241,14 +1294,12 @@ namespace promise_hpp
template < typename Iter
, typename SubPromise = typename std::iterator_traits<Iter>::value_type
, typename SubPromiseResult = typename SubPromise::value_type >
promise<SubPromiseResult>
, typename SubPromiseResult = typename SubPromise::value_type
, typename ResultPromiseValueType = SubPromiseResult >
promise<ResultPromiseValueType>
make_race_promise(Iter begin, Iter end) {
if ( begin == end ) {
throw std::logic_error("at least one input promise must be provided for make_race_promise");
}
return make_promise<SubPromiseResult>([begin, end](auto&& resolver, auto&& rejector){
return make_promise<ResultPromiseValueType>(
[begin, end](auto&& resolver, auto&& rejector){
for ( Iter iter = begin; iter != end; ++iter ) {
(*iter)
.then(resolver)

View File

@@ -30,6 +30,30 @@ namespace
}
}
bool check_empty_aggregate_exception(std::exception_ptr e) {
try {
std::rethrow_exception(e);
} catch (pr::aggregate_exception& ee) {
return ee.empty();
} catch (...) {
return false;
}
}
bool check_two_aggregate_exception(std::exception_ptr e) {
try {
std::rethrow_exception(e);
} catch (pr::aggregate_exception& ee) {
if ( ee.size() != 2 ) {
return false;
}
return check_hello_fail_exception(ee[0])
&& check_hello_fail_exception(ee[1]);
} catch (...) {
return false;
}
}
class auto_thread final {
public:
template < typename F, typename... Args >
@@ -890,9 +914,15 @@ TEST_CASE("promise") {
}
}
SECTION("make_any_promise") {
REQUIRE_THROWS_AS(
pr::make_any_promise(std::vector<pr::promise<int>>{}),
std::logic_error);
{
bool all_is_ok = false;
auto p = pr::make_any_promise(std::vector<pr::promise<int>>{});
p.except([&all_is_ok](std::exception_ptr e){
all_is_ok = check_empty_aggregate_exception(e);
return 0;
});
REQUIRE(all_is_ok);
}
{
auto p = pr::make_resolved_promise().then_any([](){
return std::vector<pr::promise<int>>{
@@ -937,7 +967,7 @@ TEST_CASE("promise") {
pr::make_rejected_promise<int>(std::logic_error("hello fail")),
pr::make_rejected_promise<int>(std::logic_error("hello fail"))
}).except([&all_is_ok](std::exception_ptr e){
all_is_ok = true;
all_is_ok = check_two_aggregate_exception(e);
return 0;
});
REQUIRE(all_is_ok);
@@ -1140,9 +1170,6 @@ TEST_CASE("promise") {
}
}
SECTION("make_race_promise_fail") {
REQUIRE_THROWS_AS(
pr::make_race_promise(std::vector<pr::promise<int>>{}),
std::logic_error);
{
bool call_fail_with_logic_error = false;
bool not_call_then_on_reject = true;