-
Notifications
You must be signed in to change notification settings - Fork 47
Open
Description
I think I have found a critical issue when awaiting nested continuables. It's a little hard to describe, but here's the code:
#include <string>
#include <thread>
#include <iostream>
#include <boost/asio/io_context.hpp>
#include <boost/asio/steady_timer.hpp>
#include <boost/asio/defer.hpp>
#include <coroutine>
#include <continuable/continuable.hpp>
#include <continuable/external/asio.hpp>
using namespace std;
using namespace chrono;
using namespace boost::asio;
struct Foo
{
inline static int i__ = 0;
volatile int i = i__++;
Foo() { cout << i << " Foo()\n"; }
Foo(const Foo&) { cout << i << " Foo(const Foo&)\n"; }
Foo(Foo&&) { cout << i << " Foo(Foo&&)\n"; }
Foo& operator=(const Foo&) { cout << i << " operator=(const Foo&)\n"; return *this; }
Foo& operator=(Foo&&) { cout << i << " operator=(Foo&&)\n"; return *this; }
~Foo() { cout << i << " ~Foo()\n"; i = -i; }
};
int main() {
io_context ctx;
auto workGuard = make_work_guard(ctx);
thread thd{[&]() { ctx.run(); }};
Foo foo;
defer(ctx, cti::use_continuable)
.then([foo, &ctx]() -> cti::continuable<> {
cout << "before: " << foo.i << "\n";
boost::asio::steady_timer t1{ctx, milliseconds{10}};
co_await t1.async_wait(cti::use_continuable);
cout << "after: " << foo.i << "\n";
})
.apply(cti::transforms::wait());
return 0;
}The issue is concerning the lifetime of the lambda object that's passed to the then() clause. That lambda object has a Foo member (the foo capture), and that struct prints out the construction/deletion of the various copies of that member as the lambda object gets moved about.
and the important part of the output is:
22 Foo(Foo&&) <-- instance #22 is constructed
21 ~Foo()
before: 22 <-- instance #22 is the one that's actually used during the invocation
22 ~Foo() <-- instance #22 is deleted BEFORE the "after:" print statement
after: 1193563136 <-- the instance's `i` member is now corrupt
the local foo, and in fact the entire lambda object is being deleted during the handling of the nested timer callback:
#0 Foo::~Foo (this=0x7fffe62c14a0, __in_chrg=<optimized out>) at Main.cpp:79
#1 0x00000000004855fc in ~<lambda>(void) (this=0x7fffe62c14a0, __in_chrg=<optimized out>) at Main.cpp:115
#2 0x000000000048803c in cti::detail::base::callbacks::callback_base<cti::detail::identity<>, (cti::detail::base::handle_results)1, (cti::detail::base::handle_errors)0, main(int, char**)::<lambda()>, cti::detail::types::this_thread_executor_tag, cti::detail::base::callbacks::callback_base<cti::detail::identity<>, (cti::detail::base::handle_results)1, (cti::detail::base::handle_errors)1, cti::detail::transforms::unsafe_unlocker<cti::result<> >, cti::detail::types::this_thread_executor_tag, cti::detail::base::callbacks::final_callback<> > >::~callback_base(void) (this=0x7fffe62c14a0, __in_chrg=<optimized out>) at /home/piersh/staging/common/utils/continuable/detail/core/base.hpp:689
#3 0x000000000048826c in cti::detail::asio::promise_resolver<cti::detail::base::callbacks::callback_base<cti::detail::identity<>, (cti::detail::base::handle_results)1, (cti::detail::base::handle_errors)0, main(int, char**)::<lambda()>, cti::detail::types::this_thread_executor_tag, cti::detail::base::callbacks::callback_base<cti::detail::identity<>, (cti::detail::base::handle_results)1, (cti::detail::base::handle_errors)1, cti::detail::transforms::unsafe_unlocker<cti::result<> >, cti::detail::types::this_thread_executor_tag, cti::detail::base::callbacks::final_callback<> > >, cti::use_continuable_t<cti::detail::asio::map_default> >::~promise_resolver(void) (this=0x7fffe62c14a0, __in_chrg=<optimized out>) at /home/piersh/staging/common/utils/continuable/detail/external/asio.hpp:113
#4 0x0000000000488a6a in boost::asio::detail::binder0<cti::detail::asio::promise_resolver<cti::detail::base::callbacks::callback_base<cti::detail::identity<>, (cti::detail::base::handle_results)1, (cti::detail::base::handle_errors)0, main(int, char**)::<lambda()>, cti::detail::types::this_thread_executor_tag, cti::detail::base::callbacks::callback_base<cti::detail::identity<>, (cti::detail::base::handle_results)1, (cti::detail::base::handle_errors)1, cti::detail::transforms::unsafe_unlocker<cti::result<> >, cti::detail::types::this_thread_executor_tag, cti::detail::base::callbacks::final_callback<> > >, cti::use_continuable_t<cti::detail::asio::map_default> > >::~binder0(void) (this=0x7fffe62c14a0, __in_chrg=<optimized out>) at /usr/include/boost/asio/detail/bind_handler.hpp:32
#5 0x0000000000489c3f in boost::asio::detail::executor_op<boost::asio::detail::binder0<cti::detail::asio::promise_resolver<cti::detail::base::callbacks::callback_base<cti::detail::identity<>, (cti::detail::base::handle_results)1, (cti::detail::base::handle_errors)0, main(int, char**)::<lambda()>, cti::detail::types::this_thread_executor_tag, cti::detail::base::callbacks::callback_base<cti::detail::identity<>, (cti::detail::base::handle_results)1, (cti::detail::base::handle_errors)1, cti::detail::transforms::unsafe_unlocker<cti::result<> >, cti::detail::types::this_thread_executor_tag, cti::detail::base::callbacks::final_callback<> > >, cti::use_continuable_t<cti::detail::asio::map_default> > >, std::allocator<void>, boost::asio::detail::scheduler_operation>::do_complete(void *, boost::asio::detail::scheduler_operation *, const boost::system::error_code &, std::size_t) (owner=0xcfe200, base=0xcfa000) at /usr/include/boost/asio/detail/executor_op.hpp:73
- OS: Centos7
- Compiler and version: g++ 12.2
Metadata
Metadata
Assignees
Labels
No labels