Skip to content

critical issue with nested continuables #65

@Spongman

Description

@Spongman

@Naios

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

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions