-
Notifications
You must be signed in to change notification settings - Fork 66
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix future reduction with non copyable types #378
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -19,6 +19,7 @@ | |
|
||
#include <stlab/concurrency/config.hpp> | ||
#include <stlab/concurrency/executor_base.hpp> | ||
#include <stlab/concurrency/immediate_executor.hpp> | ||
#include <stlab/concurrency/optional.hpp> | ||
#include <stlab/concurrency/task.hpp> | ||
#include <stlab/concurrency/traits.hpp> | ||
|
@@ -35,7 +36,6 @@ | |
#define STLAB_FUTURE_COROUTINES_SUPPORT() 1 | ||
#include <experimental/coroutine> | ||
#include <stlab/concurrency/default_executor.hpp> | ||
#include <stlab/concurrency/immediate_executor.hpp> | ||
#endif | ||
#endif | ||
|
||
|
@@ -225,6 +225,19 @@ using reduced_t = typename reduced_<T>::type; | |
template <typename T> | ||
struct reduction_helper; | ||
|
||
template <typename R> | ||
R&& reduce(R&& r) { | ||
return std::forward<R>(r); | ||
} | ||
|
||
static inline auto reduce(future<future<void>>&& r) -> future<void>; | ||
|
||
template <typename R> | ||
auto reduce(future<future<R>>&& r) -> future<R> { | ||
return std::move(r).then(stlab::immediate_executor, | ||
[](auto&& f) { return *std::forward<decltype(f)>(f).get_try(); }); | ||
} | ||
|
||
/**************************************************************************************************/ | ||
|
||
template <typename T, typename = void> | ||
|
@@ -318,7 +331,7 @@ struct shared_base<T, enable_if_copyable<T>> : std::enable_shared_from_this<shar | |
} | ||
if (ready) executor(std::move(p.first)); | ||
|
||
return reduce(std::move(p.second)); | ||
return detail::reduce(std::move(p.second)); | ||
} | ||
|
||
template <typename F> | ||
|
@@ -355,24 +368,14 @@ struct shared_base<T, enable_if_copyable<T>> : std::enable_shared_from_this<shar | |
} | ||
if (ready) executor(std::move(p.first)); | ||
|
||
return reduce(std::move(p.second)); | ||
return detail::reduce(std::move(p.second)); | ||
} | ||
|
||
void _detach() { | ||
std::unique_lock<std::mutex> lock(_mutex); | ||
if (!_ready) _then.emplace_back([](auto&&) {}, [_p = this->shared_from_this()] {}); | ||
} | ||
|
||
template <typename R> | ||
auto reduce(R&& r) { | ||
return std::forward<R>(r); | ||
} | ||
|
||
auto reduce(future<future<void>>&& r) -> future<void>; | ||
|
||
template <typename R> | ||
auto reduce(future<future<R>>&& r) -> future<R>; | ||
|
||
void set_exception(std::exception_ptr error) { | ||
_exception = std::move(error); | ||
then_t then; | ||
|
@@ -486,24 +489,14 @@ struct shared_base<T, enable_if_not_copyable<T>> : std::enable_shared_from_this< | |
} | ||
if (ready) executor(std::move(p.first)); | ||
|
||
return reduce(std::move(p.second)); | ||
return detail::reduce(std::move(p.second)); | ||
} | ||
|
||
void _detach() { | ||
std::unique_lock<std::mutex> lock(_mutex); | ||
if (!_ready) _then = then_t([](auto&&){}, [_p = this->shared_from_this()] {}); | ||
} | ||
|
||
template <typename R> | ||
auto reduce(R&& r) { | ||
return std::forward<R>(r); | ||
} | ||
|
||
template <typename R> | ||
auto reduce(future<future<R>>&& r) -> future<R>; | ||
|
||
auto reduce(future<future<void>>&& r)->future<void>; | ||
|
||
void set_exception(std::exception_ptr error) { | ||
_exception = std::move(error); | ||
then_t then; | ||
|
@@ -520,8 +513,6 @@ struct shared_base<T, enable_if_not_copyable<T>> : std::enable_shared_from_this< | |
|
||
bool is_ready() const { return _ready; } | ||
|
||
auto get_try() -> stlab::optional<T> { return get_try_r(true); } | ||
|
||
auto get_try_r(bool) -> stlab::optional<T> { | ||
bool ready = false; | ||
{ | ||
|
@@ -598,16 +589,6 @@ struct shared_base<void> : std::enable_shared_from_this<shared_base<void>> { | |
return recover(std::forward<E>(executor), std::forward<F>(f)); | ||
} | ||
|
||
template <typename R> | ||
auto reduce(R&& r) { | ||
return std::forward<R>(r); | ||
} | ||
|
||
auto reduce(future<future<void>>&& r) -> future<void>; | ||
|
||
template <typename R> | ||
auto reduce(future<future<R>>&& r) -> future<R>; | ||
|
||
void set_exception(std::exception_ptr error) { | ||
_exception = std::move(error); | ||
then_t then; | ||
|
@@ -1099,7 +1080,6 @@ class STLAB_NODISCARD() future<T, enable_if_not_copyable<T>> { | |
|
||
bool is_ready() const& { return _p && _p->is_ready(); } | ||
|
||
auto get_try() const& { return _p->get_try(); } | ||
|
||
auto get_try() && { return _p->get_try_r(unique_usage(_p)); } | ||
|
||
|
@@ -1705,8 +1685,8 @@ auto when_any(E executor, F&& f, std::pair<I, I> range) { | |
/**************************************************************************************************/ | ||
|
||
template <typename E, typename F, typename... Args> | ||
auto async(E executor, F&& f, Args&&... args) | ||
-> future<detail::result_t<std::decay_t<F>, std::decay_t<Args>...>> { | ||
auto async(E executor, F&& f, Args&&... args) -> future< | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's discuss this as a breaking change on the (public) stlab slack - (let me know if you need an invite). Personally, I think I'm fine with it as I doubt there are any (or many) cases this would break and likely the break is minor but we might consider alternate names or using namespace versioning. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, this need to be discussed. I think need an invite, I can't sign in into stlab.slack.com. |
||
detail::reduced_t<std::decay_t<detail::result_t<std::decay_t<F>, std::decay_t<Args>...>>>> { | ||
using result_type = detail::result_t<std::decay_t<F>, std::decay_t<Args>...>; | ||
|
||
auto p = package<result_type()>( | ||
|
@@ -1719,7 +1699,7 @@ auto async(E executor, F&& f, Args&&... args) | |
|
||
executor(std::move(p.first)); | ||
|
||
return std::move(p.second); | ||
return detail::reduce(std::move(p.second)); | ||
} | ||
|
||
/**************************************************************************************************/ | ||
|
@@ -1739,6 +1719,10 @@ struct reduction_helper<future<void>> { | |
future<void> value; | ||
}; | ||
|
||
static inline auto reduce(future<future<void>>&& r) -> future<void> { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove static and move this definition above - if this is circular we can use a tagged dispatch in a common - reduce to resolve or another technique. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Agree, I will propose a fix in the next patch set. |
||
return std::move(r).then(stlab::immediate_executor, [](auto) {}); | ||
} | ||
|
||
/**************************************************************************************************/ | ||
|
||
template <typename T> | ||
|
@@ -1766,31 +1750,32 @@ struct value_<T, enable_if_copyable<T>> { | |
sb._result = f(std::forward<Args>(args)...); | ||
sb._reduction_helper.value = | ||
(*sb._result) | ||
.recover([_p = sb.shared_from_this()](future<R> f) { | ||
if (f.exception()) { | ||
_p->_exception = std::move(f).exception(); | ||
proceed(*_p); | ||
throw future_error(future_error_codes::reduction_failed); | ||
} | ||
return *f.get_try(); | ||
}) | ||
.then([_p = sb.shared_from_this()](auto) { proceed(*_p); }); | ||
.recover(stlab::immediate_executor, | ||
[_p = sb.shared_from_this()](future<R> f) { | ||
if (f.exception()) { | ||
_p->_exception = std::move(f).exception(); | ||
proceed(*_p); | ||
throw future_error(future_error_codes::reduction_failed); | ||
} | ||
}) | ||
.then(stlab::immediate_executor, [_p = sb.shared_from_this()]() { proceed(*_p); }); | ||
} | ||
|
||
template <typename F, typename... Args> | ||
static void set(shared_base<future<void>>& sb, F& f, Args&&... args) { | ||
sb._result = f(std::forward<Args>(args)...); | ||
sb._reduction_helper.value = | ||
(*sb._result) | ||
.recover([_p = sb.shared_from_this()](future<void> f) { | ||
if (f.exception()) { | ||
_p->_exception = std::move(f).exception(); | ||
value_::proceed(*_p); | ||
throw future_error(future_error_codes::reduction_failed); | ||
} | ||
return; | ||
}) | ||
.then([_p = sb.shared_from_this()]() { proceed(*_p); }); | ||
.recover(stlab::immediate_executor, | ||
[_p = sb.shared_from_this()](future<void> f) { | ||
if (f.exception()) { | ||
_p->_exception = std::move(f).exception(); | ||
value_::proceed(*_p); | ||
throw future_error(future_error_codes::reduction_failed); | ||
} | ||
return; | ||
}) | ||
.then(stlab::immediate_executor, [_p = sb.shared_from_this()]() { proceed(*_p); }); | ||
} | ||
}; | ||
|
||
|
@@ -1818,15 +1803,17 @@ struct value_<T, enable_if_not_copyable<T>> { | |
sb._result = f(std::forward<Args>(args)...); | ||
sb._reduction_helper.value = | ||
std::move(*sb._result) | ||
.recover([_p = sb.shared_from_this()](future<R> f) { | ||
if (auto ex = std::move(f).exception()) { | ||
_p->_exception = ex; | ||
proceed(*_p); | ||
throw future_error(future_error_codes::reduction_failed); | ||
} | ||
return *f.get_try(); | ||
}) | ||
.then([_p = sb.shared_from_this()](auto) { proceed(*_p); }); | ||
.recover(stlab::immediate_executor, | ||
[_p = sb.shared_from_this()](future<R> f) { | ||
if (auto ex = std::move(f).exception()) { | ||
_p->_exception = ex; | ||
proceed(*_p); | ||
throw future_error(future_error_codes::reduction_failed); | ||
} | ||
// We could move out the data to put it back in place in the | ||
// next 'then' call or just leave it in place and do nothing. | ||
}) | ||
.then(stlab::immediate_executor, [_p = sb.shared_from_this()]() { proceed(*_p); }); | ||
} | ||
}; | ||
|
||
|
@@ -1888,44 +1875,7 @@ auto shared_base<void>::recover(E&& executor, F&& f) | |
} | ||
if (ready) executor(std::move(p.first)); | ||
|
||
return reduce(std::move(p.second)); | ||
} | ||
|
||
/**************************************************************************************************/ | ||
|
||
template <typename T> | ||
auto shared_base<T, enable_if_copyable<T>>::reduce(future<future<void>>&& r) -> future<void> { | ||
return std::move(r).then([](auto) {}); | ||
} | ||
|
||
template <typename T> | ||
template <typename R> | ||
auto shared_base<T, enable_if_copyable<T>>::reduce(future<future<R>>&& r) -> future<R> { | ||
return std::move(r).then([](auto&& f) { return *std::forward<decltype(f)>(f).get_try(); }); | ||
} | ||
|
||
/**************************************************************************************************/ | ||
|
||
template <typename T> | ||
auto shared_base<T, enable_if_not_copyable<T>>::reduce(future<future<void>>&& r) -> future<void> { | ||
return std::move(r).then([](auto){}); | ||
} | ||
|
||
template <typename T> | ||
template <typename R> | ||
auto shared_base<T, enable_if_not_copyable<T>>::reduce(future<future<R>>&& r) -> future<R> { | ||
return std::move(r).then([](auto&& f) { return *std::forward<decltype(f)>(f).get_try(); }); | ||
} | ||
|
||
/**************************************************************************************************/ | ||
|
||
inline auto shared_base<void>::reduce(future<future<void>>&& r) -> future<void> { | ||
return std::move(r).then([](auto) {}); | ||
} | ||
|
||
template <typename R> | ||
auto shared_base<void>::reduce(future<future<R>>&& r) -> future<R> { | ||
return std::move(r).then([](auto&& f) { return *std::forward<decltype(f)>(f).get_try(); }); | ||
return detail::reduce(std::move(p.second)); | ||
} | ||
|
||
/**************************************************************************************************/ | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think
static
does anything useful here.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agree, I will propose a fix in the next patch set.