Skip to content

Commit 44bd22a

Browse files
authored
fix(coro): fix an issue with coroutine lambdas with captures with cluster::start_timer (#1576)
1 parent 36d2f31 commit 44bd22a

1 file changed

Lines changed: 18 additions & 5 deletions

File tree

include/dpp/cluster.h

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -476,16 +476,29 @@ class DPP_EXPORT cluster {
476476
* @return timer A handle to the timer, used to remove that timer later
477477
*/
478478
template <std::invocable<timer> T, std::invocable<timer> U = std::function<void(timer)>>
479-
requires (dpp::awaitable_type<typename std::invoke_result<T, timer>::type>)
479+
requires (
480+
dpp::awaitable_type<typename std::invoke_result<T, timer>::type> &&
481+
std::copy_constructible<std::decay_t<T>> && // N4988 [func.wrap.func.con]/10.1 - std::function requires a copy constructible argument
482+
std::copy_constructible<std::decay_t<U>>
483+
)
480484
timer start_timer(T&& on_tick, uint64_t frequency, U&& on_stop = {}) {
481-
std::function<void(timer)> ticker = [fun = std::forward<T>(on_tick)](timer t) mutable -> dpp::job {
485+
using tick_fun = std::decay_t<T>;
486+
using stop_fun = std::decay_t<U>;
487+
488+
// We want to ship the function object on the coroutine frame to allow for lambda captures
489+
// See https://discord.com/channels/825407338755653642/825411707521728512/1495801839827030067
490+
// and https://discord.com/channels/825407338755653642/825411707521728512/1492502683444052109
491+
// Now we can only realistically do that by copying the lambda on each invocation, I think...
492+
// People should be avoiding lambda captures to begin with (see https://dpp.dev/coro-introduction.html),
493+
// so while this isn't super efficient, it's practically zero-cost with no capture anyways
494+
std::function<void(timer)> ticker = std::bind_front([](tick_fun fun, timer t) -> dpp::job {
482495
co_await std::invoke(fun, t);
483-
};
496+
}, static_cast<tick_fun>(std::forward<T>(on_tick)));
484497
std::function<void(timer)> stopper;
485498
if constexpr (dpp::awaitable_type<typename std::invoke_result<U, timer>::type>) {
486-
stopper = [fun = std::forward<U>(on_stop)](timer t) mutable -> dpp::job {
499+
stopper = std::bind_front([](stop_fun fun, timer t) -> dpp::job {
487500
co_await std::invoke(fun, t);
488-
};
501+
}, static_cast<stop_fun>(std::forward<U>(on_stop)));
489502
} else {
490503
stopper = std::forward<U>(on_stop);
491504
}

0 commit comments

Comments
 (0)