Skip to content

Commit a187254

Browse files
committed
fix: fix an issue with an infinite recursion in timer cancellation
1 parent ca58a13 commit a187254

3 files changed

Lines changed: 29 additions & 2 deletions

File tree

libs/internal/include/launchdarkly/async/promise.hpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,9 @@ class Continuation<R(Args...)> {
5454
// function pointer, or other callable; it need not be copy-constructible.
5555
// F&& is a forwarding reference: accepts any callable by move or copy,
5656
// then moves it into Impl<F> so Continuation itself owns the callable.
57-
template <typename F>
57+
template <typename F,
58+
typename = std::enable_if_t<
59+
!std::is_same_v<std::decay_t<F>, Continuation>>>
5860
Continuation(F&& f)
5961
: impl_(std::make_unique<Impl<std::decay_t<F>>>(std::forward<F>(f))) {}
6062
Continuation(Continuation&&) = default;

libs/internal/include/launchdarkly/async/timer.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ Future<bool> Delay(boost::asio::any_io_executor executor,
5151
return {};
5252
},
5353
[executor](Continuation<void()> f) {
54-
boost::asio::post(executor, f);
54+
boost::asio::post(executor, std::move(f));
5555
});
5656
};
5757

libs/internal/tests/cancellation_test.cpp

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
#include <gtest/gtest.h>
22

3+
#include <boost/asio/io_context.hpp>
4+
35
#include <condition_variable>
46
#include <memory>
57
#include <mutex>
68
#include <thread>
79

810
#include "launchdarkly/async/cancellation.hpp"
11+
#include "launchdarkly/async/timer.hpp"
912

1013
using namespace launchdarkly::async;
1114

@@ -214,3 +217,25 @@ TEST(Cancellation, SameThreadDeregister_NoDeadlock) {
214217

215218
source.Cancel(); // cb1 fires, destroys cb2 from the same thread
216219
}
220+
221+
TEST(Cancellation, TimerCancelDoesNotRecurseInContinuationConstructor) {
222+
boost::asio::io_context ioc;
223+
auto work = boost::asio::make_work_guard(ioc);
224+
std::thread t([&] { ioc.run(); });
225+
226+
CancellationSource cancel;
227+
228+
auto future =
229+
Delay(ioc.get_executor(), std::chrono::seconds(60), cancel.GetToken());
230+
231+
std::this_thread::sleep_for(std::chrono::milliseconds(50));
232+
cancel.Cancel();
233+
234+
auto const result = future.WaitForResult(std::chrono::seconds(5));
235+
236+
ASSERT_TRUE(result.has_value());
237+
EXPECT_FALSE(*result);
238+
239+
work.reset();
240+
t.join();
241+
}

0 commit comments

Comments
 (0)