From 3f7ceec4b7382df23cd068feaccf19897a94dc3f Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Mon, 17 Dec 2018 12:59:46 +0700 Subject: [PATCH 1/2] life after except --- promise.hpp | 130 ++++++++++++++++++++++++++++++++-------------- promise_tests.cpp | 119 +++++++++++++++++++++++++++++++++++++----- 2 files changed, 197 insertions(+), 52 deletions(-) diff --git a/promise.hpp b/promise.hpp index d410372..1d4ca29 100644 --- a/promise.hpp +++ b/promise.hpp @@ -280,7 +280,8 @@ namespace promise_hpp state_->attach( next, std::forward(on_resolve), - std::forward(on_reject)); + std::forward(on_reject), + true); return next; } @@ -290,9 +291,15 @@ namespace promise_hpp !is_promise::value, promise> then(ResolveF&& on_resolve) { - return then( + promise next; + state_->attach( + next, std::forward(on_resolve), - [](std::exception_ptr){}); + [](std::exception_ptr e) -> ResolveFR { + std::rethrow_exception(e); + }, + false); + return next; } template < typename RejectF > @@ -422,18 +429,28 @@ namespace promise_hpp template < typename U, typename ResolveF, typename RejectF > std::enable_if_t::value, void> - attach(promise& next, ResolveF&& on_resolve, RejectF&& on_reject) { + attach( + promise& next, + ResolveF&& on_resolve, + RejectF&& on_reject, + bool has_reject) + { auto reject_h = [ n = next, - f = std::forward(on_reject) + f = std::forward(on_reject), + has_reject ](std::exception_ptr e) mutable { - try { - invoke_hpp::invoke( - std::forward(f), - e); + if ( has_reject ) { + try { + invoke_hpp::invoke( + std::forward(f), + e); + n.resolve(); + } catch (...) { + n.reject(std::current_exception()); + } + } else { n.reject(e); - } catch (...) { - n.reject(std::current_exception()); } }; @@ -457,18 +474,28 @@ namespace promise_hpp template < typename U, typename ResolveF, typename RejectF > std::enable_if_t::value, void> - attach(promise& next, ResolveF&& on_resolve, RejectF&& on_reject) { + attach( + promise& next, + ResolveF&& on_resolve, + RejectF&& on_reject, + bool has_reject) + { auto reject_h = [ n = next, - f = std::forward(on_reject) + f = std::forward(on_reject), + has_reject ](std::exception_ptr e) mutable { - try { - invoke_hpp::invoke( - std::forward(f), - e); + if ( has_reject ) { + try { + auto r = invoke_hpp::invoke( + std::forward(f), + e); + n.resolve(std::move(r)); + } catch (...) { + n.reject(std::current_exception()); + } + } else { n.reject(e); - } catch (...) { - n.reject(std::current_exception()); } }; @@ -683,7 +710,8 @@ namespace promise_hpp state_->attach( next, std::forward(on_resolve), - std::forward(on_reject)); + std::forward(on_reject), + true); return next; } @@ -693,9 +721,15 @@ namespace promise_hpp !is_promise::value, promise> then(ResolveF&& on_resolve) { - return then( + promise next; + state_->attach( + next, std::forward(on_resolve), - [](std::exception_ptr){}); + [](std::exception_ptr e) -> ResolveFR { + std::rethrow_exception(e); + }, + false); + return next; } template < typename RejectF > @@ -821,18 +855,28 @@ namespace promise_hpp template < typename U, typename ResolveF, typename RejectF > std::enable_if_t::value, void> - attach(promise& next, ResolveF&& on_resolve, RejectF&& on_reject) { + attach( + promise& next, + ResolveF&& on_resolve, + RejectF&& on_reject, + bool has_reject) + { auto reject_h = [ n = next, - f = std::forward(on_reject) + f = std::forward(on_reject), + has_reject ](std::exception_ptr e) mutable { - try { - invoke_hpp::invoke( - std::forward(f), - e); + if ( has_reject ) { + try { + invoke_hpp::invoke( + std::forward(f), + e); + n.resolve(); + } catch (...) { + n.reject(std::current_exception()); + } + } else { n.reject(e); - } catch (...) { - n.reject(std::current_exception()); } }; @@ -855,18 +899,28 @@ namespace promise_hpp template < typename U, typename ResolveF, typename RejectF > std::enable_if_t::value, void> - attach(promise& next, ResolveF&& on_resolve, RejectF&& on_reject) { + attach( + promise& next, + ResolveF&& on_resolve, + RejectF&& on_reject, + bool has_reject) + { auto reject_h = [ n = next, - f = std::forward(on_reject) + f = std::forward(on_reject), + has_reject ](std::exception_ptr e) mutable { - try { - invoke_hpp::invoke( - std::forward(f), - e); + if ( has_reject ) { + try { + auto r = invoke_hpp::invoke( + std::forward(f), + e); + n.resolve(std::move(r)); + } catch (...) { + n.reject(std::current_exception()); + } + } else { n.reject(e); - } catch (...) { - n.reject(std::current_exception()); } }; diff --git a/promise_tests.cpp b/promise_tests.cpp index 41233c3..51eff9a 100644 --- a/promise_tests.cpp +++ b/promise_tests.cpp @@ -179,16 +179,6 @@ TEST_CASE("promise") { }); REQUIRE(check_42_int == 42); } - { - int check_42_int = 0; - auto p = pr::promise(); - p.resolve(42); - p.except([](std::exception_ptr){ - }).then([&check_42_int](int value){ - check_42_int = value; - }); - REQUIRE(check_42_int == 42); - } { int check_84_int = 0; bool check_void_call = false; @@ -252,8 +242,9 @@ TEST_CASE("promise") { int check_multi_fail = 0; auto p = pr::promise<>(); p.reject(std::logic_error("hello fail")); - p.except([&check_multi_fail](std::exception_ptr){ + p.except([&check_multi_fail](std::exception_ptr e){ ++check_multi_fail; + std::rethrow_exception(e); }).except([&check_multi_fail](std::exception_ptr){ ++check_multi_fail; }); @@ -316,6 +307,7 @@ TEST_CASE("promise") { }); p.except([&call_fail_with_logic_error](std::exception_ptr e){ call_fail_with_logic_error = check_hello_fail_exception(e); + return 0; }); REQUIRE(call_fail_with_logic_error); } @@ -328,6 +320,7 @@ TEST_CASE("promise") { }); p.except([&call_fail_with_logic_error](std::exception_ptr e){ call_fail_with_logic_error = check_hello_fail_exception(e); + return 0; }); REQUIRE(call_fail_with_logic_error); } @@ -356,6 +349,7 @@ TEST_CASE("promise") { pr::make_rejected_promise(std::logic_error("hello fail")) .except([&call_fail_with_logic_error](std::exception_ptr e){ call_fail_with_logic_error = check_hello_fail_exception(e); + return 0; }); REQUIRE(call_fail_with_logic_error); } @@ -703,7 +697,7 @@ TEST_CASE("promise") { class o_t { public: o_t() = delete; - o_t(int) {} + o_t(int i) { (void)i; } }; pr::promise<>() @@ -759,7 +753,7 @@ TEST_CASE("promise") { class o_t { public: o_t() = delete; - o_t(int) {} + o_t(int i) { (void)i; } }; pr::promise<>() @@ -988,6 +982,7 @@ TEST_CASE("promise") { pr::make_resolved_promise(42)}; }).except([&call_fail_with_logic_error](std::exception_ptr e){ call_fail_with_logic_error = check_hello_fail_exception(e); + return 0; }); REQUIRE(call_fail_with_logic_error); } @@ -1044,6 +1039,7 @@ TEST_CASE("promise") { pr::make_rejected_promise(std::logic_error("hello fail"))); }).except([&call_fail_with_logic_error](std::exception_ptr e){ call_fail_with_logic_error = check_hello_fail_exception(e); + return 0; }); REQUIRE(call_fail_with_logic_error); } @@ -1077,6 +1073,25 @@ TEST_CASE("get_and_wait") { }}; REQUIRE_NOTHROW(p.get()); } + { + const auto time_now = [](){ + return std::chrono::high_resolution_clock::now(); + }; + + auto p1 = pr::make_resolved_promise(); + REQUIRE_NOTHROW(p1.wait()); + REQUIRE(p1.wait_for(std::chrono::milliseconds(-1)) == pr::promise_wait_status::no_timeout); + REQUIRE(p1.wait_for(std::chrono::milliseconds(0)) == pr::promise_wait_status::no_timeout); + REQUIRE(p1.wait_until(time_now() + std::chrono::milliseconds(-1)) == pr::promise_wait_status::no_timeout); + REQUIRE(p1.wait_until(time_now() + std::chrono::milliseconds(0)) == pr::promise_wait_status::no_timeout); + + auto p2 = pr::make_resolved_promise(5); + REQUIRE_NOTHROW(p2.wait()); + REQUIRE(p2.wait_for(std::chrono::milliseconds(-1)) == pr::promise_wait_status::no_timeout); + REQUIRE(p2.wait_for(std::chrono::milliseconds(0)) == pr::promise_wait_status::no_timeout); + REQUIRE(p2.wait_until(time_now() + std::chrono::milliseconds(-1)) == pr::promise_wait_status::no_timeout); + REQUIRE(p2.wait_until(time_now() + std::chrono::milliseconds(0)) == pr::promise_wait_status::no_timeout); + } { auto p = pr::promise(); auto_thread t{[p]() mutable { @@ -1242,7 +1257,9 @@ TEST_CASE("promise_transformations") { .then([](int)->int{ throw std::logic_error("hello fail"); }) - .except([](std::exception_ptr){}); + .except([](std::exception_ptr){ + return 0; + }); static_assert( std::is_same::value, "unit test fail"); @@ -1253,6 +1270,7 @@ TEST_CASE("promise_transformations") { throw std::logic_error("hello fail"); }) .except([](std::exception_ptr){ + return 0; }); static_assert( std::is_same::value, @@ -1260,3 +1278,76 @@ TEST_CASE("promise_transformations") { } } } + +TEST_CASE("life_after_except") { + { + int check_42_int = 0; + bool call_fail_with_logic_error = false; + auto p = pr::make_rejected_promise(std::logic_error("hello fail")); + p.then([](int v){ + return v; + }) + .except([&call_fail_with_logic_error](std::exception_ptr e){ + call_fail_with_logic_error = check_hello_fail_exception(e); + return 42; + }) + .then([&check_42_int](int value){ + check_42_int = value; + }); + REQUIRE(check_42_int == 42); + REQUIRE(call_fail_with_logic_error); + } + { + int check_42_int = 0; + bool call_fail_with_logic_error = false; + auto p = pr::make_rejected_promise(std::logic_error("hello fail")); + p.then([](int v){ + return v; + }) + .except([&call_fail_with_logic_error](std::exception_ptr e) -> int { + call_fail_with_logic_error = check_hello_fail_exception(e); + std::rethrow_exception(e); + }) + .except([&call_fail_with_logic_error](std::exception_ptr e){ + call_fail_with_logic_error = call_fail_with_logic_error && check_hello_fail_exception(e); + return 42; + }) + .then([&check_42_int](int value){ + check_42_int = value; + }); + REQUIRE(check_42_int == 42); + REQUIRE(call_fail_with_logic_error); + } + { + bool call_then_after_except = false; + bool call_fail_with_logic_error = false; + auto p = pr::make_rejected_promise(std::logic_error("hello fail")); + p.then([](){}) + .except([&call_fail_with_logic_error](std::exception_ptr e){ + call_fail_with_logic_error = check_hello_fail_exception(e); + }) + .then([&call_then_after_except](){ + call_then_after_except = true; + }); + REQUIRE(call_then_after_except); + REQUIRE(call_fail_with_logic_error); + } + { + bool call_fail_with_logic_error = false; + bool call_then_after_multi_except = false; + auto p = pr::make_rejected_promise(std::logic_error("hello fail")); + p.then([](){}) + .except([&call_fail_with_logic_error](std::exception_ptr e){ + call_fail_with_logic_error = check_hello_fail_exception(e); + std::rethrow_exception(e); + }) + .except([&call_fail_with_logic_error](std::exception_ptr e){ + call_fail_with_logic_error = call_fail_with_logic_error && check_hello_fail_exception(e); + }) + .then([&call_then_after_multi_except](){ + call_then_after_multi_except = true; + }); + REQUIRE(call_fail_with_logic_error); + REQUIRE(call_then_after_multi_except); + } +} From 5dfcf5911c8b0879ab98dd5398c12a9f3933e52f Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Mon, 17 Dec 2018 13:03:17 +0700 Subject: [PATCH 2/2] scheduler: process_one_task --- jobber.hpp | 2 +- promise_tests.cpp | 2 +- scheduler.hpp | 11 +++++++++++ scheduler_tests.cpp | 16 ++++++++++++++++ 4 files changed, 29 insertions(+), 2 deletions(-) diff --git a/jobber.hpp b/jobber.hpp index 032f64f..db4341e 100644 --- a/jobber.hpp +++ b/jobber.hpp @@ -277,7 +277,7 @@ namespace jobber_hpp tasks_.emplace_back(priority, std::move(task)); std::push_heap(tasks_.begin(), tasks_.end()); ++active_task_count_; - cond_var_.notify_all(); + cond_var_.notify_one(); } inline jobber::task_ptr jobber::pop_task_() noexcept { diff --git a/promise_tests.cpp b/promise_tests.cpp index 51eff9a..c9762d2 100644 --- a/promise_tests.cpp +++ b/promise_tests.cpp @@ -1039,7 +1039,7 @@ TEST_CASE("promise") { pr::make_rejected_promise(std::logic_error("hello fail"))); }).except([&call_fail_with_logic_error](std::exception_ptr e){ call_fail_with_logic_error = check_hello_fail_exception(e); - return 0; + return std::make_tuple(0, 0.f); }); REQUIRE(call_fail_with_logic_error); } diff --git a/scheduler.hpp b/scheduler.hpp index 97af654..309a7c9 100644 --- a/scheduler.hpp +++ b/scheduler.hpp @@ -65,6 +65,7 @@ namespace scheduler_hpp , typename R = schedule_invoke_result_t > promise schedule(scheduler_priority scheduler_priority, F&& f, Args&&... args); + bool process_one_task() noexcept; scheduler_wait_status process_all_tasks() noexcept; template < typename Rep, typename Period > @@ -157,6 +158,15 @@ namespace scheduler_hpp return future; } + inline bool scheduler::process_one_task() noexcept { + std::unique_lock lock(tasks_mutex_); + if ( tasks_.empty() ) { + return false; + } + process_task_(std::move(lock)); + return true; + } + inline scheduler_wait_status scheduler::process_all_tasks() noexcept { while ( !cancelled_ && active_task_count_ ) { std::unique_lock lock(tasks_mutex_); @@ -237,6 +247,7 @@ namespace scheduler_hpp if ( task ) { lock.unlock(); task->run(); + lock.lock(); --active_task_count_; cond_var_.notify_all(); } diff --git a/scheduler_tests.cpp b/scheduler_tests.cpp index f092eb9..b2065ad 100644 --- a/scheduler_tests.cpp +++ b/scheduler_tests.cpp @@ -46,6 +46,22 @@ TEST_CASE("scheduler") { s.process_all_tasks(); REQUIRE(counter == 3); } + { + sd::scheduler s; + int counter = 0; + s.schedule([&counter](){ ++counter; }); + s.schedule([&counter](){ ++counter; }); + s.schedule([&counter](){ ++counter; }); + REQUIRE(counter == 0); + REQUIRE(s.process_one_task()); + REQUIRE(counter == 1); + REQUIRE(s.process_one_task()); + REQUIRE(counter == 2); + REQUIRE(s.process_one_task()); + REQUIRE(counter == 3); + REQUIRE_FALSE(s.process_one_task()); + REQUIRE(counter == 3); + } { sd::scheduler s; int counter = 0;