c++协程连续循环导致堆栈溢出

如何解决c++协程连续循环导致堆栈溢出

我在循环中的协程内的 Visual Studio 中遇到堆栈溢出异常,并发现该循环有一个阻止它终止的错误,但我想知道为什么堆栈溢出?协程甚至可能不使用堆栈而是使用堆,即使使用堆栈也根本没有任何递归调用

经过一些实验,我可以重现崩溃:

  • msvc 19.28
  • wsl 和 mingw64 上的 g++-10
  • windows 上的 clang-cl 10 和 linux 上的 clang++-10

这段代码导致堆栈溢出:

#include <stdexcept>
#include <utility>
#include <cstdio>

#ifdef __clang__
#ifdef _WIN32
#pragma message "using clang coroutine header"
#include "clang-cl-coro.h"
#else
#pragma message "using coroutine experimental header"
#include <experimental/coroutine>
#endif

namespace std
{
    template<class P = void>
    using coroutine_handle = experimental::coroutine_handle<P>;

    using suspend_never = experimental::suspend_never;

    using suspend_always = experimental::suspend_always;

}

#else
#pragma message "using coroutine header"
#include <coroutine>
#endif

class vtask
{
    inline static size_t task_count = 0;

public:

    struct promise_type
    {
        inline static size_t promise_count = 0;

        std::coroutine_handle<> waiter;
        std::exception_ptr ex_ptr = nullptr;

        struct resume_waiter
        {
            inline static size_t awaiter_count = 0;

            std::coroutine_handle<> waiter;

            resume_waiter(std::coroutine_handle<> waiter) noexcept : waiter{ waiter }
            {
                ++awaiter_count;
                printf("[%zu] resume_waiter(std::coroutine_handle<> waiter)\n",awaiter_count);
            }

            ~resume_waiter()
            {
                --awaiter_count;
                printf("[%zu] ~resume_waiter()\n",awaiter_count);
            }

            bool await_ready() const noexcept { return false; }

            auto await_suspend(std::coroutine_handle<>) noexcept
            {
                return waiter;
            }

            void await_resume() const noexcept {}
        };

        promise_type()
        {
            ++promise_count;
            printf("[%zu] vtask::promise_type()\n",promise_count);

        }

        ~promise_type()
        {
            --promise_count;
            printf("[%zu] ~vtask::promise_type()\n",promise_count);
        }

        vtask get_return_object() { return { *this }; }

        constexpr std::suspend_always initial_suspend() noexcept { return {}; }

        resume_waiter final_suspend() const noexcept { return { waiter }; }

        void unhandled_exception() noexcept
        {
            ex_ptr = std::current_exception();
        }

        void return_void() const noexcept {}
    };

    vtask(promise_type& p) : coro{ std::coroutine_handle<promise_type>::from_promise(p) }
    {
        ++task_count;
        printf("[%zu] vtask(promise_type& p)\n",task_count);
    }

    vtask(vtask&& other) noexcept : coro{ std::exchange(other.coro,nullptr) }
    {
        ++task_count;
        printf("[%zu] vtask(vtask&& other)\n",task_count);
    }

    ~vtask()
    {
        if (coro)
            coro.destroy();
        --task_count;
        printf("[%zu] ~vtask()\n",task_count);
    }

    bool await_ready() const noexcept
    {
        return false;
    }

    void await_suspend(std::coroutine_handle<> waiter)
    {
        coro.promise().waiter = waiter;
        coro.resume();
    }

    void await_resume() noexcept {}

private:
    std::coroutine_handle<promise_type> coro;
};

struct detached_task
{
    struct promise_type
    {
        inline static size_t promise_count = 0;

        promise_type()
        {
            ++promise_count;
            printf("[%zu] detached_task::promise_type()\n",promise_count);
        }

        ~promise_type()
        {
            --promise_count;
            printf("[%zu] ~detached_task::promise_type()\n",promise_count);
        }

        detached_task get_return_object() { return {}; }

        std::suspend_never initial_suspend() noexcept { return {}; }

        std::suspend_never final_suspend() noexcept { return {}; }

        void unhandled_exception() noexcept
        {
            std::terminate();
        }

        constexpr void return_void() const noexcept {}
    };

    inline static size_t task_count = 0;

    detached_task()
    {
        ++task_count;
        printf("[%zu] detached_task()\n",task_count);
    }

    ~detached_task()
    {
        --task_count;
        printf("[%zu] ~detached_task()\n",task_count);
    }
};

vtask do_stackoverflow() { co_return; }

detached_task stackoverflow()
{
    for (;;)
        co_await do_stackoverflow();
}

int main()
{
    stackoverflow();
}

使用的命令行:

cl /std:c++latest coro-stackoverflow.cpp /EHsc 用于 msvc

g++ -std=c++20 coro-stackoverflow.cpp -fcoroutines 用于 mingw64

clang-cl /std:c++latest coro-stackoverflow.cpp /EHsc

g++-10 -std=c++20 coro-stackoverflow.cpp -fcoroutines -o overflow.bug 在 wsl 上

clang++-10 -std=c++20 -stdlib=libc++ coro-stackoverflow.cpp -o overflow-clang.bug 在 wsl 上

这是 windows 上的 clang coro 标题:

#pragma once

namespace std { namespace experimental { inline namespace coroutines_v1 {

template <typename R,typename...> struct coroutine_traits {
  using promise_type = typename R::promise_type;
};

template <typename Promise = void> struct coroutine_handle;

template <> struct coroutine_handle<void> {
  static coroutine_handle from_address(void *addr) noexcept {
    coroutine_handle me;
    me.ptr = addr;
    return me;
  }
  void operator()() { resume(); }
  void *address() const { return ptr; }
  void resume() const { __builtin_coro_resume(ptr); }
  void destroy() const { __builtin_coro_destroy(ptr); }
  bool done() const { return __builtin_coro_done(ptr); }
  coroutine_handle &operator=(decltype(nullptr)) {
    ptr = nullptr;
    return *this;
  }
  coroutine_handle(decltype(nullptr)) : ptr(nullptr) {}
  coroutine_handle() : ptr(nullptr) {}
//  void reset() { ptr = nullptr; } // add to P0057?
  explicit operator bool() const { return ptr; }

protected:
  void *ptr;
};

template <typename Promise> struct coroutine_handle : coroutine_handle<> {
  using coroutine_handle<>::operator=;

  static coroutine_handle from_address(void *addr) noexcept {
    coroutine_handle me;
    me.ptr = addr;
    return me;
  }

  Promise &promise() const {
    return *reinterpret_cast<Promise *>(
        __builtin_coro_promise(ptr,alignof(Promise),false));
  }
  static coroutine_handle from_promise(Promise &promise) {
    coroutine_handle p;
    p.ptr = __builtin_coro_promise(&promise,true);
    return p;
  }
};

  template <typename _PromiseT>
  bool operator==(coroutine_handle<_PromiseT> const& _Left,coroutine_handle<_PromiseT> const& _Right) noexcept
  {
    return _Left.address() == _Right.address();
  }

  template <typename _PromiseT>
  bool operator!=(coroutine_handle<_PromiseT> const& _Left,coroutine_handle<_PromiseT> const& _Right) noexcept
  {
    return !(_Left == _Right);
  }

  template <typename _PromiseT>
  bool operator==(coroutine_handle<_PromiseT> const& _Left,std::nullptr_t) noexcept
  {
      return _Left.address() == nullptr;
  }

  template <typename _PromiseT>
  bool operator==(std::nullptr_t,coroutine_handle<_PromiseT> const& _Right) noexcept
  {
      return _Right.address() == nullptr;
  }

  template <typename _PromiseT>
  bool operator!=(coroutine_handle<_PromiseT> const& _Left,std::nullptr_t) noexcept
  {


    return !(_Left == nullptr);
  }

  template <typename _PromiseT>
  bool operator!=(std::nullptr_t,coroutine_handle<_PromiseT> const& _Right) noexcept
  {
      return _Right.address() != nullptr;
  }

struct suspend_always {
  bool await_ready() { return false; }
  void await_suspend(coroutine_handle<>) {}
  void await_resume() {}
};
struct suspend_never {
  bool await_ready() { return true; }
  void await_suspend(coroutine_handle<>) {}
  void await_resume() {}
};

}}}

在 Windows 上,msvc 和 clang-cl 构建崩溃较早发生,但 mingw64 和 wsl 构建需要更多时间

我使用的 gcc 10.1 似乎有一个已知的错误,其中任务被构造两次但销毁一次,每次迭代都会泄漏任务,这似乎导致溢出

但是 clang 和 msvc 没有这个错误,它们也会崩溃!

编辑:尝试了 gcc 10.3 mingw64,它没有提到的 gcc 错误,但它也会导致堆栈溢出!被窃听的编译器甚至更快!也许这种行为是预期的?

我不知道上面的代码有什么问题

解决方法

问题出在这部分代码中:

void await_suspend(std::coroutine_handle<> waiter)
{
    coro.promise().waiter = waiter;
    coro.resume(); 
}

这种非对称传输导致stackoverflow并将代码更改为:

auto await_suspend(std::coroutine_handle<> waiter)
{
    coro.promise().waiter = waiter;
    return coro; 
}

消除了 clang 和 msvc 上的问题,但 gcc 10.3 仍然崩溃,我认为它还不支持对称传输

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-
参考1 参考2 解决方案 # 点击安装源 协议选择 http:// 路径填写 mirrors.aliyun.com/centos/8.3.2011/BaseOS/x86_64/os URL类型 软件库URL 其他路径 # 版本 7 mirrors.aliyun.com/centos/7/os/x86
报错1 [root@slave1 data_mocker]# kafka-console-consumer.sh --bootstrap-server slave1:9092 --topic topic_db [2023-12-19 18:31:12,770] WARN [Consumer clie
错误1 # 重写数据 hive (edu)&gt; insert overwrite table dwd_trade_cart_add_inc &gt; select data.id, &gt; data.user_id, &gt; data.course_id, &gt; date_format(
错误1 hive (edu)&gt; insert into huanhuan values(1,&#39;haoge&#39;); Query ID = root_20240110071417_fe1517ad-3607-41f4-bdcf-d00b98ac443e Total jobs = 1
报错1:执行到如下就不执行了,没有显示Successfully registered new MBean. [root@slave1 bin]# /usr/local/software/flume-1.9.0/bin/flume-ng agent -n a1 -c /usr/local/softwa
虚拟及没有启动任何服务器查看jps会显示jps,如果没有显示任何东西 [root@slave2 ~]# jps 9647 Jps 解决方案 # 进入/tmp查看 [root@slave1 dfs]# cd /tmp [root@slave1 tmp]# ll 总用量 48 drwxr-xr-x. 2
报错1 hive&gt; show databases; OK Failed with exception java.io.IOException:java.lang.RuntimeException: Error in configuring object Time taken: 0.474 se
报错1 [root@localhost ~]# vim -bash: vim: 未找到命令 安装vim yum -y install vim* # 查看是否安装成功 [root@hadoop01 hadoop]# rpm -qa |grep vim vim-X11-7.4.629-8.el7_9.x
修改hadoop配置 vi /usr/local/software/hadoop-2.9.2/etc/hadoop/yarn-site.xml # 添加如下 &lt;configuration&gt; &lt;property&gt; &lt;name&gt;yarn.nodemanager.res