diff --git a/README.md b/README.md index 4756aefa..d513ea0c 100644 --- a/README.md +++ b/README.md @@ -74,7 +74,7 @@ cmake -S . -B ../BUILD -GNinja -DCMAKE_CXX_STANDARD=17 -DCMAKE_BUILD_TYPE=Releas If you organize the build directory into subdirectories you can support multiple configurations. ``` -cmake -S . -B ../builds/portable -GXcode -DCMAKE_CXX_STANDARD=17 -DBUILD_TESTING=ON -DSTLAB_TASK_SYSTEM=portable +cmake -S . -B ../builds/portable -GXcode -DCMAKE_CXX_STANDARD=17 -DBUILD_TESTING=ON -DSTLAB_TASK_SYSTEM=portable -DCMAKE_OSX_DEPLOYMENT_TARGET=14.4 -DCMAKE_OSX_DEPLOYMENT_TARGET=macosx14.4 ``` ### Build diff --git a/stlab/concurrency/future.hpp b/stlab/concurrency/future.hpp index 4b223a61..9a291113 100644 --- a/stlab/concurrency/future.hpp +++ b/stlab/concurrency/future.hpp @@ -189,6 +189,15 @@ using packaged_task_from_signature_t = typename packaged_task_from_signature: /**************************************************************************************************/ +template +struct will_reduce : std::false_type {}; + +template +struct will_reduce> : std::true_type {}; + +template +inline constexpr bool will_reduce_v = will_reduce::value; + template struct reduced_; @@ -272,22 +281,31 @@ struct shared_base> : std::enable_shared_from_this(f)); } + /* + NOTE : executor cannot be a reference type here. When invoked it could + cause _this_ to be deleted, and the executor passed in may be + this->_executor + */ template auto recover(future&& p, E executor, F&& f) { - auto b = package>()>( - executor, [_f = std::forward(f), _p = std::move(p)]() mutable { - return std::move(_f)(std::move(_p)); - }); + using result_type = detail::result_t>; + constexpr bool will_reduce = detail::will_reduce_v; + + auto b = package(executor, + [_f = std::forward(f), _p = std::move(p)]() mutable { + return std::move(_f)(std::move(_p)); + }); bool ready; { std::unique_lock lock(_mutex); ready = _ready; - if (!ready) _then.emplace_back(std::move(executor), std::move(b.first)); + if (!ready) _then.emplace_back(move_if(executor), std::move(b.first)); } - if (ready) executor(std::move(b.first)); - return detail::reduce(executor, std::move(b.second)); + if (ready) executor(std::move(b.first)); // cannot reference this after here + + return detail::reduce(move_if(executor), std::move(b.second)); } void _detach() { @@ -378,22 +396,30 @@ struct shared_base> : std::enable_shared_from_this< return recover(std::move(p), _executor, std::forward(f)); } + /* + NOTE : executor cannot be a reference type here. When invoked it could + cause _this_ to be deleted, and the executor passed in may be + this->_executor + */ template auto recover(future&& p, E executor, F&& f) { - auto b = package>()>( - executor, [_f = std::forward(f), _p = std::move(p)]() mutable { - return std::move(_f)(std::move(_p)); - }); + using result_type = detail::result_t>; + constexpr bool will_reduce = detail::will_reduce_v; + + auto b = package(executor, + [_f = std::forward(f), _p = std::move(p)]() mutable { + return std::move(_f)(std::move(_p)); + }); bool ready; { std::unique_lock lock(_mutex); ready = _ready; - if (!ready) _then = {std::move(executor), std::move(b.first)}; + if (!ready) _then = {move_if(executor), std::move(b.first)}; } - if (ready) executor(std::move(b.first)); + if (ready) executor(std::move(b.first)); // cannot reference this after here - return detail::reduce(executor, std::move(b.second)); + return detail::reduce(move_if(executor), std::move(b.second)); } void _detach() { @@ -1666,7 +1692,7 @@ auto async(E executor, F&& f, Args&&... args) executor(std::move(p.first)); - return detail::reduce(executor, std::move(p.second)); + return detail::reduce(std::move(executor), std::move(p.second)); } /**************************************************************************************************/ @@ -1758,10 +1784,18 @@ void shared_base::set_value(F& f, Args&&... args) { /**************************************************************************************************/ +/* + NOTE : executor cannot be a reference type here. When invoked it could + cause _this_ to be deleted, and the executor passed in may be + this->_executor +*/ template auto shared_base::recover(future&& p, E executor, F&& f) { - auto b = package>()>( - executor, [_f = std::forward(f), _p = std::move(p)]() mutable { + using result_type = detail::result_t>; + constexpr bool will_reduce = detail::will_reduce_v; + + auto b = + package(executor, [_f = std::forward(f), _p = std::move(p)]() mutable { return std::move(_f)(std::move(_p)); }); @@ -1769,11 +1803,11 @@ auto shared_base::recover(future&& p, E executor, F&& f) { { std::unique_lock lock(_mutex); ready = _ready; - if (!ready) _then.emplace_back(std::move(executor), std::move(b.first)); + if (!ready) _then.emplace_back(move_if(executor), std::move(b.first)); } - if (ready) executor(std::move(b.first)); + if (ready) executor(std::move(b.first)); // cannot reference this after here - return detail::reduce(executor, std::move(b.second)); + return detail::reduce(move_if(executor), std::move(b.second)); } /**************************************************************************************************/ diff --git a/test/future_tests.cpp b/test/future_tests.cpp index 7b75d27e..50411a47 100644 --- a/test/future_tests.cpp +++ b/test/future_tests.cpp @@ -47,8 +47,7 @@ BOOST_AUTO_TEST_CASE(async_lambda_arguments) { BOOST_TEST_MESSAGE("running async lambda argument of type rvalue -> value"); annotate_counters counters; - (void)async( - immediate_executor, [](annotate) {}, annotate(counters)); + (void)async(immediate_executor, [](annotate) {}, annotate(counters)); BOOST_REQUIRE(counters.remaining() == 0); BOOST_REQUIRE(counters._copy_ctor == 0); } @@ -58,8 +57,7 @@ BOOST_AUTO_TEST_CASE(async_lambda_arguments) { annotate_counters counters; annotate x(counters); - (void)async( - immediate_executor, [](annotate) {}, x); + (void)async(immediate_executor, [](annotate) {}, x); BOOST_REQUIRE(counters.remaining() == 1); BOOST_REQUIRE(counters._copy_ctor == 1); } @@ -69,8 +67,7 @@ BOOST_AUTO_TEST_CASE(async_lambda_arguments) { annotate_counters counters; annotate x(counters); - (void)async( - immediate_executor, [](annotate) {}, std::ref(x)); + (void)async(immediate_executor, [](annotate) {}, std::ref(x)); BOOST_REQUIRE(counters.remaining() == 1); BOOST_REQUIRE(counters._copy_ctor == 1); } @@ -80,8 +77,7 @@ BOOST_AUTO_TEST_CASE(async_lambda_arguments) { annotate_counters counters; annotate x(counters); - (void)async( - immediate_executor, [](annotate) {}, std::cref(x)); + (void)async(immediate_executor, [](annotate) {}, std::cref(x)); BOOST_REQUIRE(counters.remaining() == 1); BOOST_REQUIRE(counters._copy_ctor == 1); } @@ -113,8 +109,7 @@ BOOST_AUTO_TEST_CASE(async_lambda_arguments) { annotate_counters counters; annotate x(counters); - (void)async( - immediate_executor, [](annotate&) {}, std::ref(x)); + (void)async(immediate_executor, [](annotate&) {}, std::ref(x)); BOOST_REQUIRE(counters.remaining() == 1); BOOST_REQUIRE(counters._copy_ctor == 0); } @@ -136,8 +131,7 @@ BOOST_AUTO_TEST_CASE(async_lambda_arguments) { BOOST_TEST_MESSAGE("running async lambda argument of type rvalue -> const&"); annotate_counters counters; - (void)async( - immediate_executor, [](const annotate&) {}, annotate(counters)); + (void)async(immediate_executor, [](const annotate&) {}, annotate(counters)); BOOST_REQUIRE(counters.remaining() == 0); BOOST_REQUIRE(counters._copy_ctor == 0); } @@ -147,8 +141,7 @@ BOOST_AUTO_TEST_CASE(async_lambda_arguments) { annotate_counters counters; annotate x(counters); - (void)async( - immediate_executor, [](const annotate&) {}, x); + (void)async(immediate_executor, [](const annotate&) {}, x); BOOST_REQUIRE(counters.remaining() == 1); BOOST_REQUIRE(counters._copy_ctor == 1); } @@ -158,8 +151,7 @@ BOOST_AUTO_TEST_CASE(async_lambda_arguments) { annotate_counters counters; annotate x(counters); - (void)async( - immediate_executor, [](const annotate&) {}, std::ref(x)); + (void)async(immediate_executor, [](const annotate&) {}, std::ref(x)); BOOST_REQUIRE(counters.remaining() == 1); BOOST_REQUIRE(counters._copy_ctor == 0); } @@ -169,8 +161,7 @@ BOOST_AUTO_TEST_CASE(async_lambda_arguments) { annotate_counters counters; annotate x(counters); - (void)async( - immediate_executor, [](const annotate&) {}, std::cref(x)); + (void)async(immediate_executor, [](const annotate&) {}, std::cref(x)); BOOST_REQUIRE(counters.remaining() == 1); BOOST_REQUIRE(counters._copy_ctor == 0); } @@ -179,8 +170,7 @@ BOOST_AUTO_TEST_CASE(async_lambda_arguments) { BOOST_TEST_MESSAGE("running async lambda argument of type rvalue -> &&"); annotate_counters counters; - (void)async( - immediate_executor, [](annotate&&) {}, annotate(counters)); + (void)async(immediate_executor, [](annotate&&) {}, annotate(counters)); BOOST_REQUIRE(counters.remaining() == 0); BOOST_REQUIRE(counters._copy_ctor == 0); } @@ -190,8 +180,7 @@ BOOST_AUTO_TEST_CASE(async_lambda_arguments) { annotate_counters counters; annotate x(counters); - (void)async( - immediate_executor, [](annotate&&) {}, x); + (void)async(immediate_executor, [](annotate&&) {}, x); BOOST_REQUIRE(counters.remaining() == 1); BOOST_REQUIRE(counters._copy_ctor == 1); } @@ -258,8 +247,7 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(future_constructed_minimal_fn_with_parameters, test_setup setup; { - auto sut = async( - make_executor<0>(), [](auto x) -> T { return x + T(0); }, T(42)); + auto sut = async(make_executor<0>(), [](auto x) -> T { return x + T(0); }, T(42)); BOOST_REQUIRE(sut.valid() == true); BOOST_REQUIRE(!sut.exception());