🚀 coroutine.resume can pass arguments

This commit is contained in:
ValKmjolnir 2023-01-02 18:53:58 +08:00
parent e6e89039b8
commit 48611c50f7
7 changed files with 36 additions and 9 deletions

View File

@ -587,7 +587,7 @@ Then we call `resume`, this function will change stack.
As we can see, coroutine stack already has some values on it, As we can see, coroutine stack already has some values on it,
but if we first enter it, the stack top will be `vm_ret`, and the return `pc` is `0`. but if we first enter it, the stack top will be `vm_ret`, and the return `pc` is `0`.
So for safe running, `resume` will return `gc.top[0]`. So for safe running, when first calling the coroutine, `resume` will return `gc.top[0]`.
`op_callb` will do `top[0]=resume()`, so the value does not change. `op_callb` will do `top[0]=resume()`, so the value does not change.
```C++ ```C++

View File

@ -526,7 +526,7 @@ __接下来我们解释这个协程的运行原理:__
接着我们调用`resume`,这个函数会替换操作数栈。我们会看到,协程的操作数栈上已经保存了一些数据,但是我们首次进入协程执行时,这个操作数栈的栈顶将会是`vm_ret`,并且返回的`pc`值是`0`。 接着我们调用`resume`,这个函数会替换操作数栈。我们会看到,协程的操作数栈上已经保存了一些数据,但是我们首次进入协程执行时,这个操作数栈的栈顶将会是`vm_ret`,并且返回的`pc`值是`0`。
为了保证栈顶的数据不会被破坏,`resume`会返回`gc.top[0]`。`op_callb`将会执行`top[0]=resume()`,所以栈顶的数据虽然被覆盖了一次,但是实际上还是原来的数据。 首次调用时,为了保证栈顶的数据不会被破坏,`resume`会返回`gc.top[0]`。`op_callb`将会执行`top[0]=resume()`,所以栈顶的数据虽然被覆盖了一次,但是实际上还是原来的数据。
```C++ ```C++
+----------------------+(协程操作数栈) +----------------------+(协程操作数栈)

View File

@ -1165,7 +1165,19 @@ var builtin_coresume(var* local,gc& ngc) {
// fetch coroutine's stack top and return // fetch coroutine's stack top and return
// so the coroutine's stack top in fact is not changed // so the coroutine's stack top in fact is not changed
return ngc.top[0]; if (ngc.top[0].type==vm_ret) {
// when first calling this coroutine, the stack top must be vm_ret
return ngc.top[0];
}
// after first calling the coroutine, each time coroutine.yield triggered
// a new space will be reserved on stack with value nil
// so we could fill this place with args
// the coroutine seems like coroutine.yield returns the value
// but in fact coroutine.yield stop the coroutine
// until main context calls the coroutine.resume
return local[2];
} }
var builtin_coyield(var* local,gc& ngc) { var builtin_coyield(var* local,gc& ngc) {

View File

@ -715,7 +715,7 @@ void gc::ctxchg(nas_co& ctx) {
void gc::ctxreserve() { void gc::ctxreserve() {
// pc=0 means this coroutine is finished // pc=0 means this coroutine is finished
cort->status=pc?nas_co::suspended:nas_co::dead; cort->status=pc? nas_co::suspended:nas_co::dead;
cort->pc=pc; cort->pc=pc;
cort->localr=localr; cort->localr=localr;
cort->memr=memr; cort->memr=memr;

View File

@ -624,6 +624,7 @@ void vm::o_feach() {
} }
void vm::o_callg() { void vm::o_callg() {
// get main stack directly
(++top)[0]=stack[imm[pc]]; (++top)[0]=stack[imm[pc]];
} }

View File

@ -507,9 +507,9 @@ var compile=func(code,filename="<compile>"){
} }
var coroutine={ var coroutine={
create: func(function){return __cocreate;}, create: func(function) {return __cocreate;},
resume: func(co) {return __coresume;}, resume: func(co,args...) {return __coresume;},
yield: func(args...) {return __coyield; }, yield: func(args...) {return __coyield; },
status: func(co) {return __costatus;}, status: func(co) {return __costatus;},
running:func() {return __corun; } running:func() {return __corun; }
}; };

View File

@ -40,6 +40,20 @@ func(){
} }
}(); }();
# test coroutine.resume passing arguments to coroutine
func{
var co=coroutine.create(func(){
var (a,b)=coroutine.yield(a+b);
println("coroutine.yield get ",a," ",b);
(a,b)=coroutine.yield(a+b);
println("coroutine.yield get ",a," ",b);
return "end";
});
for(var i=0;i<5;i+=1)
println("coroutine.resume get ",coroutine.resume(co,i,i+1));
}();
# test crash in coroutines # test crash in coroutines
var co=coroutine.create(func{ var co=coroutine.create(func{
var b=func(){b()} var b=func(){b()}