Merge pull request #32 from BlackMATov/dev

Dev
This commit is contained in:
2019-12-30 13:19:49 +07:00
committed by GitHub
4 changed files with 282 additions and 161 deletions

View File

@@ -26,7 +26,7 @@ namespace jobber_hpp
timeout timeout
}; };
class jobber_cancelled_exception : public std::runtime_error { class jobber_cancelled_exception final : public std::runtime_error {
public: public:
jobber_cancelled_exception() jobber_cancelled_exception()
: std::runtime_error("jobber has stopped working") {} : std::runtime_error("jobber has stopped working") {}
@@ -102,7 +102,7 @@ namespace jobber_hpp
mutable std::condition_variable cond_var_; mutable std::condition_variable cond_var_;
}; };
class jobber::task : private noncopyable { class jobber::task : private detail::noncopyable {
public: public:
virtual ~task() noexcept = default; virtual ~task() noexcept = default;
virtual void run() noexcept = 0; virtual void run() noexcept = 0;
@@ -110,7 +110,7 @@ namespace jobber_hpp
}; };
template < typename R, typename F, typename... Args > template < typename R, typename F, typename... Args >
class jobber::concrete_task : public task { class jobber::concrete_task final : public task {
F f_; F f_;
std::tuple<Args...> args_; std::tuple<Args...> args_;
promise<R> promise_; promise<R> promise_;
@@ -123,7 +123,7 @@ namespace jobber_hpp
}; };
template < typename F, typename... Args > template < typename F, typename... Args >
class jobber::concrete_task<void, F, Args...> : public task { class jobber::concrete_task<void, F, Args...> final : public task {
F f_; F f_;
std::tuple<Args...> args_; std::tuple<Args...> args_;
promise<void> promise_; promise<void> promise_;

View File

@@ -78,107 +78,6 @@ namespace promise_hpp
template < typename R, typename T > template < typename R, typename T >
inline constexpr bool is_promise_r_v = is_promise_r<R, T>::value; inline constexpr bool is_promise_r_v = is_promise_r<R, T>::value;
//
// detail
//
namespace detail
{
class noncopyable {
public:
noncopyable(const noncopyable&) = delete;
noncopyable& operator=(const noncopyable&) = delete;
protected:
noncopyable() = default;
~noncopyable() = default;
};
template < typename T >
class storage final : private noncopyable {
public:
storage() = default;
~storage()
noexcept(std::is_nothrow_destructible_v<T>) {
if ( initialized_ ) {
ptr_()->~T();
}
}
void set(T&& value)
noexcept(std::is_nothrow_move_constructible_v<T>) {
assert(!initialized_);
::new(ptr_()) T(std::move(value));
initialized_ = true;
}
void set(const T& value)
noexcept(std::is_nothrow_copy_constructible_v<T>) {
assert(!initialized_);
::new(ptr_()) T(value);
initialized_ = true;
}
T get()
noexcept(std::is_nothrow_move_constructible_v<T>) {
assert(initialized_);
return std::move(*ptr_());
}
T& value() noexcept {
assert(initialized_);
return *ptr_();
}
const T& value() const noexcept {
assert(initialized_);
return *ptr_();
}
private:
T* ptr_() noexcept {
return reinterpret_cast<T*>(&data_);
}
const T* ptr_() const noexcept {
return reinterpret_cast<const T*>(&data_);
}
private:
std::aligned_storage_t<sizeof(T), alignof(T)> data_;
bool initialized_ = false;
};
template < typename T >
class storage<T&> final : private noncopyable {
public:
storage() = default;
~storage() = default;
void set(T& value) noexcept {
assert(!initialized_);
value_ = &value;
initialized_ = true;
}
T& get() noexcept {
assert(initialized_);
return *value_;
}
T& value() noexcept {
assert(initialized_);
return *value_;
}
const T& value() const noexcept {
assert(initialized_);
return *value_;
}
private:
T* value_{nullptr};
bool initialized_ = false;
};
}
// //
// promise_wait_status // promise_wait_status
// //
@@ -189,6 +88,111 @@ namespace promise_hpp
}; };
} }
// -----------------------------------------------------------------------------
//
// detail
//
// -----------------------------------------------------------------------------
namespace promise_hpp::detail
{
template < typename T >
void destroy_in_place(T& ref) noexcept {
ref.~T();
}
template < typename T, typename... Args >
void construct_in_place(T& ref, Args&&... args)
noexcept(std::is_nothrow_constructible_v<T, Args...>) {
::new (std::addressof(ref)) T(std::forward<Args>(args)...);
}
class noncopyable {
public:
noncopyable(const noncopyable&) = delete;
noncopyable& operator=(const noncopyable&) = delete;
protected:
noncopyable() = default;
~noncopyable() = default;
};
template < typename T >
class storage final : private noncopyable {
public:
storage() = default;
~storage() noexcept {
if ( initialized_ ) {
destroy_in_place(*ptr_());
}
}
storage& operator=(T&& value)
noexcept(std::is_nothrow_move_constructible_v<T>) {
assert(!initialized_);
construct_in_place(*ptr_(), std::move(value));
initialized_ = true;
return *this;
}
storage& operator=(const T& value)
noexcept(std::is_nothrow_copy_constructible_v<T>) {
assert(!initialized_);
construct_in_place(*ptr_(), value);
initialized_ = true;
return *this;
}
T& operator*() noexcept {
assert(initialized_);
return *ptr_();
}
const T& operator*() const noexcept {
assert(initialized_);
return *ptr_();
}
private:
T* ptr_() noexcept {
return reinterpret_cast<T*>(&data_);
}
const T* ptr_() const noexcept {
return reinterpret_cast<const T*>(&data_);
}
private:
std::aligned_storage_t<sizeof(T), alignof(T)> data_;
bool initialized_ = false;
};
template < typename T >
class storage<T&> final : private noncopyable {
public:
storage() = default;
~storage() = default;
storage& operator=(T& value) noexcept {
assert(!initialized_);
value_ = &value;
initialized_ = true;
return *this;
}
T& operator*() noexcept {
assert(initialized_);
return *value_;
}
const T& operator*() const noexcept {
assert(initialized_);
return *value_;
}
private:
T* value_{nullptr};
bool initialized_ = false;
};
}
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// //
// promise<T> // promise<T>
@@ -299,10 +303,10 @@ namespace promise_hpp
then([ then([
n = next, n = next,
f = std::forward<ResolveF>(on_resolve) f = std::forward<ResolveF>(on_resolve)
](auto&&... vs) mutable { ](auto&& v) mutable {
auto np = std::invoke( auto np = std::invoke(
std::forward<decltype(f)>(f), std::forward<decltype(f)>(f),
std::forward<decltype(vs)>(vs)...); std::forward<decltype(v)>(v));
std::move(np).then([n](auto&&... nvs) mutable { std::move(np).then([n](auto&&... nvs) mutable {
n.resolve(std::forward<decltype(nvs)>(nvs)...); n.resolve(std::forward<decltype(nvs)>(nvs)...);
}).except([n](std::exception_ptr e) mutable { }).except([n](std::exception_ptr e) mutable {
@@ -327,6 +331,18 @@ namespace promise_hpp
}); });
} }
template < typename ResolveF >
auto then_any(ResolveF&& on_resolve) {
return then([
f = std::forward<ResolveF>(on_resolve)
](auto&& v) mutable {
auto r = std::invoke(
std::forward<decltype(f)>(f),
std::forward<decltype(v)>(v));
return make_any_promise(std::move(r));
});
}
template < typename ResolveF > template < typename ResolveF >
auto then_race(ResolveF&& on_resolve) { auto then_race(ResolveF&& on_resolve) {
return then([ return then([
@@ -417,7 +433,7 @@ namespace promise_hpp
std::rethrow_exception(exception_); std::rethrow_exception(exception_);
} }
assert(status_ == status::resolved); assert(status_ == status::resolved);
return storage_.value(); return *storage_;
} }
void wait() const noexcept { void wait() const noexcept {
@@ -449,7 +465,7 @@ namespace promise_hpp
if ( status_ != status::pending ) { if ( status_ != status::pending ) {
return false; return false;
} }
storage_.set(std::forward<U>(value)); storage_ = std::forward<U>(value);
status_ = status::resolved; status_ = status::resolved;
invoke_resolve_handlers_(); invoke_resolve_handlers_();
cond_var_.notify_all(); cond_var_.notify_all();
@@ -553,7 +569,7 @@ namespace promise_hpp
if ( status_ == status::resolved ) { if ( status_ == status::resolved ) {
std::invoke( std::invoke(
std::forward<ResolveF>(resolve), std::forward<ResolveF>(resolve),
storage_.value()); *storage_);
} else if ( status_ == status::rejected ) { } else if ( status_ == status::rejected ) {
std::invoke( std::invoke(
std::forward<RejectF>(reject), std::forward<RejectF>(reject),
@@ -567,7 +583,7 @@ namespace promise_hpp
void invoke_resolve_handlers_() noexcept { void invoke_resolve_handlers_() noexcept {
for ( const auto& h : handlers_ ) { for ( const auto& h : handlers_ ) {
h.resolve_(storage_.value()); h.resolve_(*storage_);
} }
handlers_.clear(); handlers_.clear();
} }
@@ -599,8 +615,8 @@ namespace promise_hpp
reject_t reject_; reject_t reject_;
}; };
std::vector<handler> handlers_;
detail::storage<T> storage_; detail::storage<T> storage_;
std::vector<handler> handlers_;
}; };
}; };
} }
@@ -713,10 +729,9 @@ namespace promise_hpp
then([ then([
n = next, n = next,
f = std::forward<ResolveF>(on_resolve) f = std::forward<ResolveF>(on_resolve)
](auto&&... vs) mutable { ]() mutable {
auto np = std::invoke( auto np = std::invoke(
std::forward<decltype(f)>(f), std::forward<decltype(f)>(f));
std::forward<decltype(vs)>(vs)...);
std::move(np).then([n](auto&&... nvs) mutable { std::move(np).then([n](auto&&... nvs) mutable {
n.resolve(std::forward<decltype(nvs)>(nvs)...); n.resolve(std::forward<decltype(nvs)>(nvs)...);
}).except([n](std::exception_ptr e) mutable { }).except([n](std::exception_ptr e) mutable {
@@ -740,6 +755,17 @@ namespace promise_hpp
}); });
} }
template < typename ResolveF >
auto then_any(ResolveF&& on_resolve) {
return then([
f = std::forward<ResolveF>(on_resolve)
]() mutable {
auto r = std::invoke(
std::forward<decltype(f)>(f));
return make_any_promise(std::move(r));
});
}
template < typename ResolveF > template < typename ResolveF >
auto then_race(ResolveF&& on_resolve) { auto then_race(ResolveF&& on_resolve) {
return then([ return then([
@@ -1092,34 +1118,6 @@ namespace promise_hpp
// make_all_promise // make_all_promise
// //
namespace impl
{
template < typename ResultType >
class all_promise_context_t final : private detail::noncopyable {
public:
all_promise_context_t(std::size_t count)
: results_(count) {}
template < typename T >
bool apply_result(std::size_t index, T&& value) {
results_[index].set(std::forward<T>(value));
return ++counter_ == results_.size();
}
std::vector<ResultType> get_results() {
std::vector<ResultType> ret;
ret.reserve(results_.size());
for ( auto&& v : results_ ) {
ret.push_back(v.get());
}
return ret;
}
private:
std::atomic_size_t counter_{0};
std::vector<detail::storage<ResultType>> results_;
};
}
template < typename Iter template < typename Iter
, typename SubPromise = typename std::iterator_traits<Iter>::value_type , typename SubPromise = typename std::iterator_traits<Iter>::value_type
, typename SubPromiseResult = typename SubPromise::value_type , typename SubPromiseResult = typename SubPromise::value_type
@@ -1129,18 +1127,32 @@ namespace promise_hpp
if ( begin == end ) { if ( begin == end ) {
return make_resolved_promise(ResultPromiseValueType()); return make_resolved_promise(ResultPromiseValueType());
} }
struct context_t {
std::atomic_size_t success_counter{0u};
std::vector<detail::storage<SubPromiseResult>> results;
context_t(std::size_t count)
: success_counter(count)
, 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; std::size_t result_index = 0;
auto context = std::make_shared<impl::all_promise_context_t< auto context = std::make_shared<context_t>(std::distance(begin, end));
SubPromiseResult>>(std::distance(begin, end));
for ( Iter iter = begin; iter != end; ++iter, ++result_index ) { for ( Iter iter = begin; iter != end; ++iter, ++result_index ) {
(*iter).then([ (*iter).then([
context, context,
resolver, resolver,
result_index result_index
](auto&& v) mutable { ](auto&& v) mutable {
if ( context->apply_result(result_index, std::forward<decltype(v)>(v)) ) { context->results[result_index] = std::forward<decltype(v)>(v);
resolver(context->get_results()); if ( !--context->success_counter ) {
std::vector<SubPromiseResult> results;
results.reserve(context->results.size());
for ( auto&& r : context->results ) {
results.push_back(std::move(*r));
}
resolver(std::move(results));
} }
}).except(rejector); }).except(rejector);
} }
@@ -1154,6 +1166,46 @@ namespace promise_hpp
std::end(container)); std::end(container));
} }
//
// make_any_promise
//
template < typename Iter
, typename SubPromise = typename std::iterator_traits<Iter>::value_type
, typename SubPromiseResult = typename SubPromise::value_type >
promise<SubPromiseResult>
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");
}
struct context_t {
std::atomic_size_t failure_counter{0u};
context_t(std::size_t count)
: failure_counter(count) {}
};
return make_promise<SubPromiseResult>([begin, end](auto&& resolver, auto&& rejector){
auto context = std::make_shared<context_t>(std::distance(begin, end));
for ( Iter iter = begin; iter != end; ++iter ) {
(*iter).then([resolver](auto&& v) mutable {
resolver(std::forward<decltype(v)>(v));
}).except([context, rejector](std::exception_ptr e) mutable {
if ( !--context->failure_counter ) {
rejector(e);
}
});
}
});
}
template < typename Container >
auto make_any_promise(Container&& container) {
return make_any_promise(
std::begin(container),
std::end(container));
}
// //
// make_race_promise // make_race_promise
// //
@@ -1166,6 +1218,7 @@ namespace promise_hpp
if ( begin == end ) { if ( begin == end ) {
throw std::logic_error("at least one input promise must be provided for make_race_promise"); 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<SubPromiseResult>([begin, end](auto&& resolver, auto&& rejector){
for ( Iter iter = begin; iter != end; ++iter ) { for ( Iter iter = begin; iter != end; ++iter ) {
(*iter) (*iter)
@@ -1205,11 +1258,11 @@ namespace promise_hpp
using tuple_promise_result_t = typename tuple_promise_result<Tuple>::type; using tuple_promise_result_t = typename tuple_promise_result<Tuple>::type;
template < typename... ResultTypes > template < typename... ResultTypes >
class tuple_promise_context_t { class tuple_promise_context_t final : private detail::noncopyable {
public: public:
template < std::size_t N, typename T > template < std::size_t N, typename T >
bool apply_result(T&& value) { bool apply_result(T&& value) {
std::get<N>(results_).set(std::forward<T>(value)); std::get<N>(results_) = std::forward<T>(value);
return ++counter_ == sizeof...(ResultTypes); return ++counter_ == sizeof...(ResultTypes);
} }
@@ -1220,7 +1273,7 @@ namespace promise_hpp
private: private:
template < std::size_t... Is > template < std::size_t... Is >
std::tuple<ResultTypes...> get_results_impl(std::index_sequence<Is...>) { std::tuple<ResultTypes...> get_results_impl(std::index_sequence<Is...>) {
return {std::get<Is>(results_).get()...}; return {std::move(*std::get<Is>(results_))...};
} }
private: private:
std::atomic_size_t counter_{0}; std::atomic_size_t counter_{0};

View File

@@ -26,7 +26,7 @@ namespace scheduler_hpp
cancelled cancelled
}; };
class scheduler_cancelled_exception : public std::runtime_error { class scheduler_cancelled_exception final : public std::runtime_error {
public: public:
scheduler_cancelled_exception() scheduler_cancelled_exception()
: std::runtime_error("scheduler has stopped working") {} : std::runtime_error("scheduler has stopped working") {}
@@ -82,7 +82,7 @@ namespace scheduler_hpp
mutable std::condition_variable cond_var_; mutable std::condition_variable cond_var_;
}; };
class scheduler::task : private noncopyable { class scheduler::task : private detail::noncopyable {
public: public:
virtual ~task() noexcept = default; virtual ~task() noexcept = default;
virtual void run() noexcept = 0; virtual void run() noexcept = 0;
@@ -90,7 +90,7 @@ namespace scheduler_hpp
}; };
template < typename R, typename F, typename... Args > template < typename R, typename F, typename... Args >
class scheduler::concrete_task : public task { class scheduler::concrete_task final : public task {
F f_; F f_;
std::tuple<Args...> args_; std::tuple<Args...> args_;
promise<R> promise_; promise<R> promise_;
@@ -103,7 +103,7 @@ namespace scheduler_hpp
}; };
template < typename F, typename... Args > template < typename F, typename... Args >
class scheduler::concrete_task<void, F, Args...> : public task { class scheduler::concrete_task<void, F, Args...> final : public task {
F f_; F f_;
std::tuple<Args...> args_; std::tuple<Args...> args_;
promise<void> promise_; promise<void> promise_;

View File

@@ -205,7 +205,7 @@ TEST_CASE("promise") {
SECTION("resolved_ref") { SECTION("resolved_ref") {
{ {
int* check_42_int = nullptr; int* check_42_int = nullptr;
auto p = pr::promise<int&>(); auto p = pr::promise<std::reference_wrapper<int>>();
int i = 42; int i = 42;
p.resolve(i); p.resolve(i);
p.then([&check_42_int](int& value) mutable { p.then([&check_42_int](int& value) mutable {
@@ -216,7 +216,7 @@ TEST_CASE("promise") {
} }
{ {
const int* check_42_int = nullptr; const int* check_42_int = nullptr;
auto p = pr::promise<const int&>(); auto p = pr::promise<std::reference_wrapper<const int>>();
const int i = 42; const int i = 42;
p.resolve(i); p.resolve(i);
p.then([&check_42_int](const int& value) mutable { p.then([&check_42_int](const int& value) mutable {
@@ -692,9 +692,23 @@ TEST_CASE("promise") {
} }
{ {
bool all_is_ok = false; bool all_is_ok = false;
auto p = pr::make_all_promise(std::vector<pr::promise<int>>{ auto p = pr::make_resolved_promise().then_all([](){
pr::make_resolved_promise(32), return std::vector<pr::promise<int>>{
pr::make_resolved_promise(10) pr::make_resolved_promise(32),
pr::make_resolved_promise(10)};
}).then([&all_is_ok](const std::vector<int>& c){
all_is_ok = (2 == c.size())
&& c[0] == 32
&& c[1] == 10;
});
REQUIRE(all_is_ok);
}
{
bool all_is_ok = false;
auto p = pr::make_resolved_promise(1).then_all([](int){
return std::vector<pr::promise<int>>{
pr::make_resolved_promise(32),
pr::make_resolved_promise(10)};
}).then([&all_is_ok](const std::vector<int>& c){ }).then([&all_is_ok](const std::vector<int>& c){
all_is_ok = (2 == c.size()) all_is_ok = (2 == c.size())
&& c[0] == 32 && c[0] == 32
@@ -749,6 +763,60 @@ TEST_CASE("promise") {
}); });
} }
} }
SECTION("make_any_promise") {
REQUIRE_THROWS_AS(
pr::make_any_promise(std::vector<pr::promise<int>>{}),
std::logic_error);
{
auto p = pr::make_resolved_promise().then_any([](){
return std::vector<pr::promise<int>>{
pr::make_resolved_promise(32),
pr::make_resolved_promise(10)};
}).then([](int i){
return i;
});
REQUIRE(p.get() == 32);
}
{
auto p = pr::make_resolved_promise(1).then_any([](int){
return std::vector<pr::promise<int>>{
pr::make_resolved_promise(32),
pr::make_resolved_promise(10)};
}).then([](int i){
return i;
});
REQUIRE(p.get() == 32);
}
{
auto p = pr::make_any_promise(std::vector<pr::promise<int>>{
pr::make_resolved_promise(32),
pr::make_rejected_promise<int>(std::logic_error("hello fail"))
}).then([](int i){
return i;
});
REQUIRE(p.get() == 32);
}
{
auto p = pr::make_any_promise(std::vector<pr::promise<int>>{
pr::make_rejected_promise<int>(std::logic_error("hello fail")),
pr::make_resolved_promise(32)
}).then([](int i){
return i;
});
REQUIRE(p.get() == 32);
}
{
bool all_is_ok = false;
auto p = pr::make_any_promise(std::vector<pr::promise<int>>{
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;
return 0;
});
REQUIRE(all_is_ok);
}
}
SECTION("make_race_promise") { SECTION("make_race_promise") {
{ {
auto p1 = pr::promise<int>(); auto p1 = pr::promise<int>();
@@ -892,8 +960,8 @@ TEST_CASE("promise") {
}); });
} }
{ {
auto p1 = pr::promise<int&>(); auto p1 = pr::promise<std::reference_wrapper<int>>();
auto p2 = pr::promise<float&>(); auto p2 = pr::promise<std::reference_wrapper<float>>();
auto p3 = pr::make_tuple_promise(std::make_tuple(p1, p2)); auto p3 = pr::make_tuple_promise(std::make_tuple(p1, p2));
int i = 10; int i = 10;