使用co_await挂起点后,传递给返回可等待状态的函数的临时值是否仍然有效 [英] Do temporaries passed to a function that returns awaitable remains valid after suspension point with co_await

查看:99
本文介绍了使用co_await挂起点后,传递给返回可等待状态的函数的临时值是否仍然有效的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在基于Windows io完成端口的异步套接字类中添加了对协程的支持.没有协程,io可以像这样完成:

I'm adding support for coroutines ts in an async socket class based on windows io completion ports . Without coroutines the io may be done like this :

sock.async_write(io::buffer(somebuff), [](auto&& ... args){ /* in handler */ });

sock.async_write(std::vector<io::const_buffer>{ ... }, [](auto&& ... args){ /* in handler */ })

每个函数都将返回void并将通过处理程序通知结果,并且不需要缓存参数,因为该操作将在从函数返回时被提交

where each one will returns void and will notify the result through the handler and doesn't need to cache the parameters because the operation will have been submitted upon returning from the function

但是使用协程,该函数将返回一个等待的对象,在使用 operator co_await 等待它时将提交操作,因此我需要将参数缓存在等待的对象中,以避免使用破坏性的临时变量:

But with coroutines the function will return an awaitable that upon awaiting it with operator co_await will submit the operation so I need to cache the parameters in the awaitable to avoid using destructed temporaries :

awaitable coro_write(const io::const_buffer& buff)
{
    return awaitable{ *this, buff }; 
}

awaitable coro_write(const std::vector<io::const_buffer>& buffs)
{
    return awaitable{ *this, buffs };
}

第一个副本没有害处,但第二个副本却有害,因为它将触发堆分配并复制矢量内容.

the copy in the first one doesn't harm but in the second it does , cause it will trigger a heap allocation and copy the vector contents.

因此,我在阅读此页面时正在寻找解决方案,协程ts 我碰到了这个:

So I was searching for a solution to this and while reading this page coroutines ts I came across this :

典型生成器的yield_value会将其自变量存储(复制/移动或仅存储其地址,因为自变量的生命周期跨越co_await内部的悬浮点)并将其自变量返回到生成器对象并返回std :: suspend_always,将控制权转移给调用方/恢复.

A typical generator's yield_value would store (copy/move or just store the address of, since the argument's lifetime crosses the suspension point inside the co_await) its argument into the generator object and return std::suspend_always, transferring control to the caller/resumer.

,并且在同一页面上声明 co_yield 表达式等效于:

and from the same page it is stated that co_yield expression is equivalent to :

co_await promise.yield_value(expr)

也类似于:

co_await sock.coro_write(expr)

我打开了Visual Studio 2019随附的生成器标头,发现它还将参数的地址存储到 yield_value 中,并随后通过 generator :: iterator :: operator *()在协程暂停后的呼叫者网站中:

I opened the generator header shipped with visual studio 2019 and saw that it also stored the address of the parameter to yield_value and retrieved it later through generator::iterator::operator *() in the caller site after the coroutine suspension :

struct promise_type {
    _Ty const* _CurrentValue;
     auto yield_value(_Ty const& _Value) {
         _CurrentValue = _STD addressof(_Value);
         return suspend_always{};
     }
}

struct iterator {
    _NODISCARD reference operator*() const {
        return *_Coro.promise()._CurrentValue;
    }
    _NODISCARD pointer operator->() const {
        return _Coro.promise()._CurrentValue;
    }
}

由此我得出结论,传递给该函数的参数将返回与 co_await 一起使用的等待者,在协程恢复或销毁之前,该参数也将保持有效,对吗?还是对诺言类型中的 yield_value 特殊?

from this I concluded that the parameter passed to the function that returns an awaiter used with co_await will also remain valid until the coroutine is resumed or destoryed , is this right ? or this is special for yield_value in a promise type ?

推荐答案

已在Visual Studio 2019 16.4.4中进行了测试,并且可以正常工作.实际上,传递的参数和转换后的参数也仍然有效,直到 await_resume 返回或由于抛出异常或返回值而导致 await_suspend 不挂起之前,它们都不会被破坏.表示无意暂停协程.

Tested with visual studio 2019 16.4.4 and it works . In fact the passed parameters and converted parameters also remains valid and doesn't get destructed until after await_resume returns or await_suspend doesn't suspend due to a thrown exception or a return value that indicates no intention to suspend the coroutine .

这就是我所做的:

1-为缓冲区类型创建析构函数,如下所示:

1 - created destructors for the buffers types as follows :

~const_buffer()
{
    std::cout << __FUNCSIG__ << std::endl;
}

~mutable_buffer()
{
    std::cout << __FUNCSIG__ << std::endl;
}

// mutable to const conversion

operator const_buffer() const noexcept { return { data_, size_ }; }

2-在 await_suspend 调用的 issue_coro_op 的末尾,我打印了类似的文字:

2 - at the end of issue_coro_op which is called from await_suspend I put similar print :

void issue_coro_op(awaitable& a)
{
    // use the awaitable to issue the op
    std::cout << "~ " << __FUNCSIG__ << std::endl;
}

3-在 await_resume 的末尾,我打印了类似的照片

3 - at the end of await_resume I put a similar print

4-这是传递的缓冲区参数的类型:

4 - this is the type of buffers parameters passed :


awaitable coro_read(const io::mutable_buffer& buff, transfer_flags);

awaitable coro_write(const io::const_buffer& buff, transfer_flags);

template </**/>
awaitable::awaitable(const io::mutable_buffer& buff) { /*store in ptr*/ }

template </**/>
awaitale::awaitable(const io::const_buffer& buff) { /*store in ptr*/ }

5-这是回声协程:

io::task<> run_session(tcp::async_socket sock)
{
    char buff[1024];
    string server_msg;
    try
    {
        for (;;)
        {
            auto n = co_await sock.coro_read(io::buffer(buff), transfer_flags::unspecified);
            if (buff[n - 1] == '\n')
                --n;
            cout << ">> " << string_view{ buff, n } << endl;

            server_msg = fmt::format("{{ server message : {} }}\n", string_view{ buff, n });
            n = co_await sock.coro_write(io::buffer(server_msg), transfer_flags::unspecified);
        }
    }
    catch (std::exception & ex)
    {
        cout << "[!] a client has disconnected : " << ex.what() << endl;
    }
}

6-已通过nc测试:

6 - tested with nc :

nc localhost 4567
some message
{ server message : some message }

7-服务器输出:

issue_coro_op -> read
await_resume -> read
~mutable_buffer -> read
>> some message
issue_coro_op -> write
await_resume -> write
~const_buffer -> write
~mutable_buffer -> write

注意到 io :: buffer 返回的 io :: mutable_buffer 在写操作中被转换为 io :: const_buffer ,而这两个仍然存在有效,直到恢复后,它们以相反的顺序被破坏

noting that io::buffer returns io::mutable_buffer which in write operations gets converted to io::const_buffer and the two remains valid until after resumption then they are destructed in the reverse order

我无法使用clang-cl 8进行测试,因为它在编译代码时崩溃!以及带有gcc 8的mingw-w64(尚不支持协程)

I couldn't test with clang-cl 8 because it crashes while compiling the code ! and mingw-w64 with gcc 8 which doesn't support coroutines yet

这篇关于使用co_await挂起点后,传递给返回可等待状态的函数的临时值是否仍然有效的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆