英文:
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:
#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:
#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?
答案1
得分: 2
await_ready() 返回 true 意味着协程不会被挂起,因此不会调用 await_suspend。
由于未调用 await_suspend,promise 未初始化。然后,在执行 await_resume() 时,start_ = promise->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->iterpair.first; uses the dangling pointer promise, which causes the crash.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论