g++协程演示

main h1=00000000 h2=00000000
 main h1=009B0048 h2=00000000
  main h1=009B0048 h2=009C0078
   main h1=009B0048 h2=009C0078
coroutine:  0 @ 009B005C
coroutine:  9 @ 009C008C
   main h1=009B0048 h2=009C0078
coroutine:  1 @ 009B005C
coroutine: 10 @ 009C008C
   main h1=009B0048 h2=009C0078
coroutine:  2 @ 009B005C
coroutine: 11 @ 009C008C

// cr_func是协程函数。main里启动了两个协程,参数不同
// 协程里printf("coroutine: %2d @ %p\n", i++, &i);
int main() {
    HCR h1, h2; printf("main h1=%p h2=%p\n", h1, h2);
    cr_func(&h1, 0); printf(" main h1=%p h2=%p\n", h1, h2);
    cr_func(&h2, 9); printf("  main h1=%p h2=%p\n", h1, h2);
    for (int i = 0; i < 3; ++i) {
        printf("   main h1=%p h2=%p\n", h1, h2);
        h1.resume(), h2.resume();
    }
    h2.destroy(), h1.destroy();
}

勾起兴趣再往下看:

struct awaitable : public std::suspend_always {
    HCR* m_phcr;
    awaitable(HCR* phcr) : m_phcr(phcr) {}
    void await_suspend(HCR new_handle) { *m_phcr = new_handle; }
};

cr_ret_type cr_func(HCR* phcr, int i) {
    char    data[65536];
    awaitable   a(phcr); // *phcr = new_handle;修改的是main里的handle.
    while (1) {
        co_await a; // 干3件事:
        // 1. Save all local variables (如data) and "jmp_buf" to a heap-allocated object.
        // 2. "setjmp", 设置一个"书签", 以后再"longjmp"跳回来继续执行(resume)。官话:
        //    Create a new coroutine handle X and pass it to a.await_suspend().
        //    HCR是the heap-allocated object的句柄,是个void*, 黑盒子能用不能看。
        // 3. "return"
        // resume回来后执行"return"后的下一条语句。
        // jmp_buf ~= 数据寄存器和instruction pointer/program counter. 局部变量宜只放个指针:
        // struct big_data* pdata;
        printf("coroutine: %2d @ %p\n", i++, &i);
    }
}

丑陋的东西在最后:

typedef std::coroutine_handle<> HCR;

struct cr_ret_type {
  struct promise_type {
    cr_ret_type get_return_object() { return {}; }
    std::suspend_never initial_suspend() { return {}; }
    std::suspend_never final_suspend() noexcept { return {}; }
    void unhandled_exception() {}
  };
};

// My tutorial and take on C++20 coroutines (stanford.edu)
// g++ -fcoroutines crdemo.cpp (gcc 10.2或更高版; tdm-gcc-10.3.0.exe ok)
#include <coroutine>
#include <stdio.h>

协程必须返回cr_ret_type,void cr_func()是不行的。promise_type和它里面的东西不要改。cr_ret_type里可以再加东西。可以像python那样做生成器。

1. 我完全尊重C++设计者的劳动,而且我远没有做出这样工作的能力

2. 他们疑似死心眼子,我不能每个协程一个struct data *p; data里放个T return_value吗?哦,明白了,要支持递归:

def permute(x): # 假的,y只有1份,但能达到目的
    def pmt(x, y, n, i):
        if i == n: yield y
        for j in range(n):
            if x[j] == 99: continue
            y[i] = s = x[j]; x[j] = 99
            yield from pmt(x, y, n, i + 1)
            x[j] = s
    n = len(x)
    return pmt(x, list(range(n)), n, 0)

def gen(n, i): # 得到6个0. 动态语言没做到的,静态语言要做到?不加-std=c++20没有co_yield,加了也没有co_yield_from啊
  if n == 0: yield i; return
  for i in range(n):
    yield from gen(n - 1, i) # 去掉from得到3个generator
for i in gen(3, 0): print(i)

这样的需求多吗?能T return_value_stack[]吗? 做floodfill时我干过切换stack (sp)的事,所以老念念不忘。 也许有点像placement new, 函数能用using stack(buf)来控制,或者:

coroutine X {
 void run() { // 不能有参数和返回值
    // 不能有局部变量,但也不用写this->t
    // this还是得放在堆栈里, but how? or ecx?
    // 协程不可能同时调别的函数,所以堆栈基本上可以共享
   ...
 }
 int    t;
 int    m_i;
 char   m_buf[];
} x; // 不能放在堆栈里?
// c++ - Linux 64bit calling convention uses register to pass 'this' pointer, but code is less efficient? - Stack Overflow
// 用汇编写些代码?
瞎想瞎说,不关我的事
// 给个__builtin_coro_suspend啊!
gcc里的coroutine_handle
// 撸了把排位,我方猴子大后期不行了
// memcpy(&(int((void*)this)), &m_that, sizeof(void*)); 编译通不过。32位x86下,this在寄存器ecx里的可能性不是100%。
// 搞个coroutine.h,把丑陋的东西都藏进去得了: #define i_am_a_corutine ... #define i_am_so_nice_that_i_give_up_cpu ...
// 原来h代表hidden和hideous啊! :-)
posted @ 2021-12-26 19:23  华容道专家  阅读(112)  评论(0)    收藏  举报