C++ 协程在通过 promise 类型传递值时导致段错误。

huangapple go评论73阅读模式
英文:

C++ coroutine segfaults when passing value through promise type

问题

I have a coroutine that co_awaits three times to pick up new iterators feed in by the caller using the feed() function of the return type Parser. The values are stored within promise type and delivered on Awaitable::resume() to referenced iterators from within the coroutine body (sorry I'm new to couroutines and I bet there's a better way to do that, please feel free to show me). In any case, this code segfaults:

Demo

#include <cstdio>
#include <coroutine>
#include <string_view>

struct Promise;

struct Parser : public std::coroutine_handle<Promise> {

    using promise_type = ::Promise;

    auto feed(std::convertible_to<std::string_view> auto&& view);

    ~Parser() {
        destroy();
    }
};

struct Awaitable {
    Promise* promise;
    std::string_view::iterator& start_;
    std::string_view::iterator& end_;

    Awaitable(std::string_view::iterator& start, std::string_view::iterator& end)
        : start_{ start }
        , end_{ end }
    { }
    auto await_ready() -> bool { printf("await_ready!\n"); return true; }
    auto await_suspend(std::coroutine_handle<Promise> handle) {
        promise = &handle.promise();
        printf("await_resume!\n");
    }
    auto await_resume() -> void;
};

struct Promise {
    std::pair<std::string_view::iterator, std::string_view::iterator> iterpair;

    auto get_return_object() -> Parser { return {Parser::from_promise(*this)}; };
    auto initial_suspend() noexcept -> std::suspend_never { return {}; }
    auto final_suspend() noexcept -> std::suspend_always { return {}; }
    auto await_transform(std::pair<std::string_view::iterator, std::string_view::iterator> iterpair) -> Awaitable { return {iterpair.first, iterpair.second}; }
    auto return_void() { printf("return void!\n"); }
    // auto return_value() { printf("return void!\n"); }
    auto unhandled_exception() {}
};

auto Awaitable::await_resume() -> void {
    printf("await_resume!\n");
    start_ = promise->iterpair.first;
    end_ = promise->iterpair.second;
}

auto Parser::feed(std::convertible_to<std::string_view> auto&& view) {
    auto my_view = std::string_view{ view };
    promise().iterpair = { my_view.begin(), my_view.end() };
}


auto coro() -> Parser {

    std::string_view::iterator start, end;

    // feed1
    co_await std::pair<std::string_view::iterator, std::string_view::iterator>{ start, end };

    // feed2
    co_await std::pair<std::string_view::iterator, std::string_view::iterator>{ start, end };

    // feed3
    co_await std::pair<std::string_view::iterator, std::string_view::iterator>{ start, end };

    co_return;
}

int main() {

    std::string_view part1 = "Hello sir!";
    std::string_view part2 = "How is the world doing?";
    std::string_view part3 = "Blazingly good sir!";

    Parser myparser = coro();

    myparser.feed(part1);
    myparser.feed(part2);
    myparser.feed(part3);
}

And I can't point my finger why it does so. Any hints?

英文:

I have a coroutine that co_awaits three times to pick up new iterators feed in by the caller using the feed() function of the return type Parser. The values are stored within promise type and delivered on Awaitable::resume() to referenced iterators from within the coroutine body (sorry I'm new to couroutines and I bet there's a better way to do that, please feel free to show me). In any case, this code segfaults:

Demo

#include &lt;cstdio&gt;
#include &lt;coroutine&gt;
#include &lt;string_view&gt;
struct Promise;
struct Parser : public std::coroutine_handle&lt;Promise&gt; {
using promise_type = ::Promise;
auto feed(std::convertible_to&lt;std::string_view&gt; auto&amp;&amp; view);
~Parser() {
destroy();
}
};
struct Awaitable {
Promise* promise;
std::string_view::iterator&amp; start_;
std::string_view::iterator&amp; end_;
Awaitable(std::string_view::iterator&amp; start, std::string_view::iterator&amp; end)
: start_{ start }
, end_{ end }
{ }
auto await_ready() -&gt; bool { printf(&quot;await_ready!\n&quot;); return true; }
auto await_suspend(std::coroutine_handle&lt;Promise&gt; handle) {
promise = &amp;handle.promise();
printf(&quot;await_resume!\n&quot;);
}
auto await_resume() -&gt; void;
};
struct Promise {
std::pair&lt;std::string_view::iterator, std::string_view::iterator&gt; iterpair;
auto get_return_object() -&gt; Parser { return {Parser::from_promise(*this)}; };
auto initial_suspend() noexcept -&gt; std::suspend_never { return {}; }
auto final_suspend() noexcept -&gt; std::suspend_always { return {}; }
auto await_transform(std::pair&lt;std::string_view::iterator, std::string_view::iterator&gt; iterpair) -&gt; Awaitable { return {iterpair.first, iterpair.second}; }
auto return_void() { printf(&quot;return void!\n&quot;); }
// auto return_value() { printf(&quot;return void!\n&quot;); }
auto unhandled_exception() {}
};
auto Awaitable::await_resume() -&gt; void {
printf(&quot;await_resume!\n&quot;);
start_ = promise-&gt;iterpair.first;
end_ = promise-&gt;iterpair.second;
}
auto Parser::feed(std::convertible_to&lt;std::string_view&gt; auto&amp;&amp; view) {
auto my_view = std::string_view{ view };
promise().iterpair = { my_view.begin(), my_view.end() };
}
auto coro() -&gt; Parser {
std::string_view::iterator start, end;
// feed1
co_await std::pair&lt;std::string_view::iterator, std::string_view::iterator&gt;{ start, end };
// feed2
co_await std::pair&lt;std::string_view::iterator, std::string_view::iterator&gt;{ start, end };
// feed3
co_await std::pair&lt;std::string_view::iterator, std::string_view::iterator&gt;{ start, end };
co_return;
}
int main() {
std::string_view part1 = &quot;Hello sir!&quot;;
std::string_view part2 = &quot;How is the world doing?&quot;;
std::string_view part3 = &quot;Blazingly good sir!&quot;;
Parser myparser = coro();
myparser.feed(part1);
myparser.feed(part2);
myparser.feed(part3);
}

And I can't point my finger why it does so. Any hints?

答案1

得分: 2

await_ready() 返回 true 意味着协程不会被挂起,因此不会调用 await_suspend

由于未调用 await_suspendpromise 未初始化。然后,在执行 await_resume() 时,start_ = promise-&gt;iterpair.first; 使用悬空指针 promise,导致崩溃。

英文:

await_ready() returning true means the coroutine will not be suspended, and thus await_suspend will not be invoked.

Since await_suspend is not called, promise is not initialized. Then, when await_resume() is executed, start_ = promise-&gt;iterpair.first; uses the dangling pointer promise, which causes the crash.

huangapple
  • 本文由 发表于 2023年4月13日 18:40:27
  • 转载请务必保留本文链接:https://go.coder-hub.com/76004476.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定