alibaba / alibaba/async_simple

asio使用自定义完成令牌的方式兼容Lazy

Open
#423 2 comments 0 reactions 0 assignees View on GitHub
Dominant language
C++
Stars
2.2k
Forks
316
Avg merge
1d 13h
Merged PRs (30d)
3

Description

我想提交一个pull,替代asio_coro_util.hpp,让asio::awaitable无缝兼容async_simple::coro::Lazy,但是现在遇到点阻碍请求指导下,msvc 平台可以正常运行,clang-21运行错误。

boost version:1.87
async_simple version: main
msvc verion: 14.43.34808
clang version: 21.0.0 (++20250416083926+11857bef8a5f-1~exp1~20250416084034.851)

## use_lazy.hpp
```

#ifndef ASYNC_SIMPLE_LAZY_TOKENS_3_HPP
#define ASYNC_SIMPLE_LAZY_TOKENS_3_HPP

#include
#include
#include
#include

// --- Asio 头文件配置 ---
// 如果使用独立版 Asio 而不是 Boost.Asio, 在此之前定义 USE_STANDALONE_ASIO
#ifdef USE_STANDALONE_ASIO
#include
#include
#include
#include
#include
#include

namespace ASIO_NAMESPACE = ::asio;
namespace SYSTEM_NAMESPACE = ::asio;
#else // 默认使用 Boost.Asio
#include
#include
#include
#include
#include
#include

namespace ASIO_NAMESPACE = ::boost::asio;
namespace SYSTEM_NAMESPACE = ::boost::system;
#endif
// --- Asio 头文件配置结束 ---

#include
#include
#include
#include
#include
#include
#include

// async_simple 的 Asio 集成命名空间
namespace async_simple
{
// Asio/System 类型别名
using error_code_type = SYSTEM_NAMESPACE::error_code; // 错误码
using system_error_type = SYSTEM_NAMESPACE::system_error; // 系统错误 (异常)
using io_context_type = ASIO_NAMESPACE::io_context; // IO 上下文

// --- 令牌定义 ---

// 令牌 1: use_lazy
// 行为: 返回单个主结果值(或void)。出错(且非eof)时抛出 system_error_type 异常。
// 用法: auto result = co_await async_op(..., use_lazy);
struct use_lazy_t {};
constexpr use_lazy_t use_lazy{};

// 令牌 2: use_lazy_tuple
// 行为: 返回包含所有回调参数(含ec)的 std::tuple。不抛出异常。
// 用法: auto [ec, result] = co_await async_op(..., use_lazy_tuple);
struct use_lazy_tuple_t {};
constexpr use_lazy_tuple_t use_lazy_tuple{};

namespace detail
{
// --- use_lazy_t 的辅助模板 ---

// 推断 Lazy 中的主结果类型 T
template struct DeduceLazyType;
template
struct DeduceLazyType { using type = T; };
template <> struct DeduceLazyType { using type = void; };
template <> struct DeduceLazyType<> { using type = void; };
template
requires(!std::is_same_v, error_code_type>)
struct DeduceLazyType { using type = T; };

// 为 use_lazy_t 解包结果: 检查 ec (忽略 EOF), 出错则抛异常, 否则返回主值
template
struct AsioResultUnwrapper
{
using type = T;
template
static T get(error_code_type ec, ValueType&& value, RestArgs&&... /* rest */) {
if (ec && ec != ASIO_NAMESPACE::error::eof) { throw system_error_type(ec); }
return T(std::forward(value));
}
static T get(error_code_type ec) {
if (ec && ec != ASIO_NAMESPACE::error::eof) { throw system_error_type(ec); }
throw std::logic_error("AsioResultUnwrapper: Non-void T expected, but Asio signature was void(error_code). Deduction mismatch?");
}
static T get() {
throw std::logic_error("AsioResultUnwrapper: Non-void T expected, but Asio signature was void(). Cannot determine success or failure.");
}
};
// void 返回类型的特化
template <>
struct AsioResultUnwrapper
{
using type = void;
template
static void get(error_code_type ec, OpArgs&&... /* rest */) {
if (ec && ec != ASIO_NAMESPACE::error::eof) { throw system_error_type(ec); }
}
static void get() {} // No error code to check
};

// 从签名推断 Lazy 的值类型 T
template struct SignatureHelper;
template
struct SignatureHelper {
using LazyValueType = typename detail::DeduceLazyType...>::type;
};

} // namespace detail

// --- AsioExecutor (通用) ---
// 基于 Asio io_context 实现 async_simple::Executor
class AsioExecutor : public async_simple::Executor {
public:
explicit AsioExecutor(io_context_type &io_context) : io_context_(io_context) {}
virtual bool schedule(Func func) override {
ASIO_NAMESPACE::post(io_context_, std::move(func));
return true;
}
io_context_type& context() { return io_context_; }
const io_context_type& context() const { return io_context_; }
private:
io_context_type &io_context_;
};

} // namespace async_simple

// --- Asio async_result 特化 ---
// 必须位于 boost::asio 或 asio 命名空间内
#ifdef USE_STANDALONE_ASIO
namespace asio {
#else
namespace boost::asio {
#endif

// --- 特化 1: 针对 use_lazy_t (单一值 / 异常) ---
template
struct async_result
{
using LazyValueType = typename async_simple::detail::SignatureHelper::LazyValueType;
using return_type = async_simple::coro::Lazy; // 返回 Lazy
using promise_type = async_simple::Promise;

// State 结构体,在堆上持有 promise
struct PromiseState { promise_type promise; };

template
static return_type initiate(Initiation initiation,
async_simple::use_lazy_t /*token*/,
InitArgs &&...args)
{
auto state = std::make_shared(); // 在堆上分配
async_simple::Future future = state->promise.getFuture();

std::invoke(
std::move(initiation),
// 完成处理器 Lambda: 使用 Unwrapper 处理结果或异常
[state](Args... operation_args) mutable
{
try {
if constexpr (std::is_void_v) {
async_simple::detail::AsioResultUnwrapper::get(std::forward(operation_args)...);
state->promise.setValue();
} else {
auto value = async_simple::detail::AsioResultUnwrapper::get(std::forward(operation_args)...);
state->promise.setValue(std::move(value));
}
} catch (...) {
state->promise.setException(std::current_exception());
}
},
std::forward(args)...
);
// co_await future: 获取值或重新抛出异常
co_return co_await std::move(future);
}
};

// --- 特化 2: 针对 use_lazy_tuple_t (元组返回 / 无异常) ---
template
struct async_result
{
using ResultTupleType = std::tuple...>; // 结果是元组
using return_type = async_simple::coro::Lazy; // 返回 Lazy
using promise_type = async_simple::Promise;

// State 结构体,在堆上持有 promise
struct PromiseState { promise_type promise; };

template
static return_type initiate(Initiation initiation,
async_simple::use_lazy_tuple_t /*token*/,
InitArgs &&...args)
{
auto state = std::make_shared(); // 在堆上分配
async_simple::Future future = state->promise.getFuture();

std::invoke(
std::move(initiation),
// 完成处理器 Lambda: 直接将参数打包成元组
[state](Args... operation_args) mutable
{
try {
state->promise.setValue(ResultTupleType(std::forward(operation_args)...));
} catch (...) {
state->promise.setException(std::current_exception());
}
},
std::forward(args)...
);
// co_await future: 获取包含 error_code 和结果的元组
co_return co_await std::move(future);
}
};

} // namespace boost::asio or asio

#endif // ASYNC_SIMPLE_LAZY_TOKENS_3_HPP
```

## 测试代码

```
async_simple::coro::Lazy handle_connection(boost::asio::ip::tcp::socket socket,
async_simple::AsioExecutor& executor)
{
try
{
char data[1024];
boost::asio::mutable_buffer buffer = boost::asio::buffer(data);

auto [e1, n] = co_await socket.async_read_some(buffer, async_simple::use_lazy_tuple);
if (e1)
{
std::cout << "Read " << n << " error: " << e1.what() << std::endl;
}
std::cout << "Read " << n << " bytes: " << std::string_view(data, n) << std::endl;
std::size_t written =
co_await boost::asio::async_write(socket, boost::asio::buffer(" PONG\n"), async_simple::use_lazy);
std::cout << "Wrote " << written << " bytes" << std::endl;
}
catch (const std::system_error& e)
{
std::cerr << "Connection error: " << e.what() << " (" << e.code() << ")" << std::endl;
}
catch (const std::exception& e)
{
std::cerr << "General error: " << e.what() << std::endl;
}

co_return;
}

async_simple::coro::Lazy run_server(boost::asio::io_context& io_context, unsigned short port)
{
async_simple::AsioExecutor executor(io_context);
boost::asio::ip::tcp::acceptor acceptor(io_context);

try
{
boost::asio::ip::tcp::endpoint endpoint(boost::asio::ip::tcp::v4(), port);
acceptor.open(endpoint.protocol());
boost::system::error_code set_option_ec;
acceptor.set_option(boost::asio::socket_base::reuse_address(true), set_option_ec);
if (set_option_ec)
{
std::cerr << "警告: 设置 reuse_address 失败: " << set_option_ec.message() << std::endl;
}

acceptor.bind(endpoint);

acceptor.listen(boost::asio::socket_base::max_listen_connections);

std::cout << "服务器正在监听端口 " << port << " (线程 ID: " << std::this_thread::get_id() << ")" << std::endl;

while (true)
{
boost::asio::ip::tcp::socket socket(io_context);

std::cout << "等待连接..." << std::endl;

auto [accept_ec] = co_await acceptor.async_accept(socket, async_simple::use_lazy_tuple);

if (accept_ec)
{
if (accept_ec == boost::asio::error::operation_aborted)
{
std::cout << "Acceptor 已停止。" << std::endl;
break; // 退出循环
}
std::cerr << "Accept 失败: " << accept_ec.message() << std::endl;
continue;
}
try
{

std::cout << "已接受来自 " << socket.remote_endpoint() << " 的连接" << std::endl;

handle_connection(std::move(socket), executor)
.via(&executor)
.start(
[](auto&& try_result)
{
if (try_result.hasError())
{
try
{
std::rethrow_exception(try_result.getException());
}
catch (const std::exception& e)
{
}
}
});
}
catch (const boost::system::system_error& endpoint_ex)
{

std::cerr << "获取 remote_endpoint 时出错: " << endpoint_ex.what() << std::endl;
}
}
}
catch (const std::system_error& e)
{

std::cerr << "服务器设置/循环错误: " << e.what() << " (错误码: " << e.code() << ")" << std::endl;
}
catch (const std::exception& e)
{

std::cerr << "服务器一般错误: " << e.what() << std::endl;
}

std::cout << "服务器协程结束。" << std::endl;

if (acceptor.is_open())
{
boost::system::error_code ignored_ec;
acceptor.close(ignored_ec);
}
co_return;
}

TEST(asio_, test_asio_and_a_simple)
{
try
{
boost::asio::io_context io_context;

// Start the server coroutine
run_server(io_context, 12345).start([](auto&&) {}); // Fire-and-forget the server runner itself

// Run the io_context event loop
io_context.run();
}
catch (std::exception& e)
{
std::cerr << "Exception in main: " << e.what() << std::endl;
}

}
```

## gdb 错误信息

```

==395384==ERROR: AddressSanitizer: stack-use-after-return on address 0x7bfff5607ec0 at pc 0x55555578cfb6 bp 0x7fffffffc700 sp 0x7fffffffc6f8
READ of size 8 at 0x7bfff5607ec0 thread T0
[Detaching after fork from child process 395495]
#0 0x55555578cfb5 in decltype(std::declval::initiate_async_accept>()(std::declval*>(), std::declval*>())) std::__1::__invoke[abi:ne210000]::initiate_async_accept, async_simple::coro::Lazy> boost::asio::async_result::initiate::initiate_async_accept, boost::asio::basic_socket*, boost::asio::ip::basic_endpoint*>(boost::asio::basic_socket_acceptor::initiate_async_accept, async_simple::use_lazy_tuple_t, boost::asio::basic_socket*&&, boost::asio::ip::basic_endpoint*&&)::'lambda'(boost::system::error_code), boost::asio::basic_socket*, boost::asio::ip::basic_endpoint*>(boost::asio::basic_socket_acceptor::initiate_async_accept&&, boost::asio::basic_socket*&&, boost::asio::ip::basic_endpoint*&&) /usr/lib/llvm-21/bin/../include/c++/v1/__type_traits/invoke.h:179:70
#1 0x555555784bfc in std::__1::invoke_result::initiate_async_accept, boost::asio::basic_socket*, boost::asio::ip::basic_endpoint*>::type std::__1::invoke[abi:ne210000]::initiate_async_accept, async_simple::coro::Lazy> boost::asio::async_result::initiate::initiate_async_accept, boost::asio::basic_socket*, boost::asio::ip::basic_endpoint*>(boost::asio::basic_socket_acceptor::initiate_async_accept, async_simple::use_lazy_tuple_t, boost::asio::basic_socket*&&, boost::asio::ip::basic_endpoint*&&)::'lambda'(boost::system::error_code), boost::asio::basic_socket*, boost::asio::ip::basic_endpoint*>(boost::asio::basic_socket_acceptor::initiate_async_accept&&, boost::asio::basic_socket*&&, boost::asio::ip::basic_endpoint*&&) /usr/lib/llvm-21/bin/../include/c++/v1/__functional/invoke.h:29:10
#2 0x55555572990d in async_simple::coro::Lazy> boost::asio::async_result::initiate::initiate_async_accept, boost::asio::basic_socket*, boost::asio::ip::basic_endpoint*>(boost::asio::basic_socket_acceptor::initiate_async_accept, async_simple::use_lazy_tuple_t, boost::asio::basic_socket*&&, boost::asio::ip::basic_endpoint*&&) (.resume) /mnt/h/study/aggre_cpp23_modules/src/common/asio_uitls_my_3.hpp:209:13
#3 0x55555571b8e5 in void async_simple::coro::detail::LazyBase::start(asio__test_asio_and_a_simple_Test::TestBody()::$_0&&) requires isLazyCallback::'lambda'(async_simple::coro::detail::LazyBase, asio__test_asio_and_a_simple_Test::TestBody()::$_0)::operator()(async_simple::coro::detail::LazyBase, asio__test_asio_and_a_simple_Test::TestBody()::$_0) const /mnt/h/study/aggre_cpp23_modules/third_party/async_simple/async_simple/coro/Lazy.h:423:16
#4 0x5555557138d4 in void async_simple::coro::detail::LazyBase::start(asio__test_asio_and_a_simple_Test::TestBody()::$_0&&) requires isLazyCallback /mnt/h/study/aggre_cpp23_modules/third_party/async_simple/async_simple/coro/Lazy.h:426:13
#5 0x555555713536 in asio__test_asio_and_a_simple_Test::TestBody() /mnt/h/study/aggre_cpp23_modules/demo/test_asio_and_a_simple.cpp:175:39
#6 0x5555558735dd in void testing::internal::HandleSehExceptionsInMethodIfSupported(testing::Test*, void (testing::Test::*)(), char const*) /mnt/h/study/aggre_cpp23_modules/third_party/googletest/googletest/src/gtest.cc:2664:10
#7 0x555555815ab5 in void testing::internal::HandleExceptionsInMethodIfSupported(testing::Test*, void (testing::Test::*)(), char const*) /mnt/h/study/aggre_cpp23_modules/third_party/googletest/googletest/src/gtest.cc:2700:14
#8 0x5555557d3b5d in testing::Test::Run() /mnt/h/study/aggre_cpp23_modules/third_party/googletest/googletest/src/gtest.cc:2739:5
#9 0x5555557d4f42 in testing::TestInfo::Run() /mnt/h/study/aggre_cpp23_modules/third_party/googletest/googletest/src/gtest.cc:2885:11
#10 0x5555557d6165 in testing::TestSuite::Run() /mnt/h/study/aggre_cpp23_modules/third_party/googletest/googletest/src/gtest.cc:3063:30
#11 0x5555557f3d42 in testing::internal::UnitTestImpl::RunAllTests() /mnt/h/study/aggre_cpp23_modules/third_party/googletest/googletest/src/gtest.cc:6054:44
#12 0x555555879ecd in bool testing::internal::HandleSehExceptionsInMethodIfSupported(testing::internal::UnitTestImpl*, bool (testing::internal::UnitTestImpl::*)(), char const*) /mnt/h/study/aggre_cpp23_modules/third_party/googletest/googletest/src/gtest.cc:2664:10
#13 0x555555819f3a in bool testing::internal::HandleExceptionsInMethodIfSupported(testing::internal::UnitTestImpl*, bool (testing::internal::UnitTestImpl::*)(), char const*) /mnt/h/study/aggre_cpp23_modules/third_party/googletest/googletest/src/gtest.cc:2700:14
#14 0x5555557f2cef in testing::UnitTest::Run() /mnt/h/study/aggre_cpp23_modules/third_party/googletest/googletest/src/gtest.cc:5594:10
#15 0x5555556942d0 in RUN_ALL_TESTS() /mnt/h/study/aggre_cpp23_modules/third_party/googletest/googletest/include/gtest/gtest.h:2334:73
#16 0x55555569426e in main /mnt/h/study/aggre_cpp23_modules/main.cpp:7:12
#17 0x7ffff73af1c9 in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16
#18 0x7ffff73af28a in __libc_start_main csu/../csu/libc-start.c:360:3
#19 0x5555555a9e24 in _start (/mnt/h/study/aggre_cpp23_modules/cmake-build-debug-wsl-24-clang-cmake3/aggre_cpp23_modules+0x55e24) (BuildId: 78b00f0db8589fe1b8c4bd38fd7088c0dd50a425)

Address 0x7bfff5607ec0 is located in stack of thread T0 at offset 64 in frame
#0 0x55555574806f in decltype(async_initiate(decltype(std::__declval::initiate_async_accept>(0)) std::__1::declval[abi:ne210000]::initiate_async_accept>()(), fp0, &fp, static_cast*>(0))) boost::asio::basic_socket_acceptor::async_accept(boost::asio::basic_socket::initiate_async_accept, boost::asio::any_io_executor>&, async_simple::use_lazy_tuple_t const&, boost::asio::constraint::initiate_async_accept>::value, int>::type) /mnt/f/runtime/dep_lib/ubuntu24/clang-boost/boost_1_87_0/boost/asio/basic_socket_acceptor.hpp:1397

This frame has 3 object(s):
[32, 40) 'ref.tmp' (line 1398)
[64, 72) 'ref.tmp' (line 1398) <== Memory access at offset 64 is inside this variable
[96, 104) 'ref.tmp' (line 1398)
HINT: this may be a false positive if your program uses some custom stack unwind mechanism, swapcontext or vfork
(longjmp and C++ exceptions *are* supported)
SUMMARY: AddressSanitizer: stack-use-after-return /mnt/h/study/aggre_cpp23_modules/src/common/asio_uitls_my_3.hpp:209:13 in async_simple::coro::Lazy> boost::asio::async_result::initiate::initiate_async_accept, boost::asio::basic_socket*, boost::asio::ip::basic_endpoint*>(boost::asio::basic_socket_acceptor::initiate_async_accept, async_simple::use_lazy_tuple_t, boost::asio::basic_socket*&&, boost::asio::ip::basic_endpoint*&&) (.resume)
Shadow bytes around the buggy address:
0x7bfff5607c00: f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5
0x7bfff5607c80: f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5
0x7bfff5607d00: f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5
0x7bfff5607d80: f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5
0x7bfff5607e00: f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5
=>0x7bfff5607e80: f5 f5 f5 f5 f5 f5 f5 f5[f5]f5 f5 f5 f5 f5 f5 f5
0x7bfff5607f00: f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5
0x7bfff5607f80: f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5
0x7bfff5608000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x7bfff5608080: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x7bfff5608100: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Shadow byte legend (one shadow byte represents 8 application bytes):
Addressable: 00
Partially addressable: 01 02 03 04 05 06 07
Heap left redzone: fa
Freed heap region: fd
Stack left redzone: f1
Stack mid redzone: f2
Stack right redzone: f3
Stack after return: f5
Stack use after scope: f8
Global redzone: f9
Global init order: f6
Poisoned by user: f7
Container overflow: fc
Array cookie: ac
Intra object redzone: bb
ASan internal: fe
Left alloca redzone: ca
Right alloca redzone: cb

```

Contributor guide

No contributing guide indexed for this repository

Research direction

Start with the proposed use_lazy.hpp and the existing asio_coro_util.hpp, then reproduce the reported failure using Boost 1.87, async_simple main, and clang 21; compare it with the working MSVC configuration. Done means an agreed implementation replaces the existing integration, supports both use_lazy and use_lazy_tuple with async_simple::coro::Lazy, and demonstrates compatibility on the affected platforms.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp
Domain
backend
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.