Lua_resume (): What is the meaning of the second argument?

Note. Below are some questions that illustrate my thinking, but the only answer I'm looking for is the answer to the real question in the title. Not asking here for a “book” or detailed answers to all these questions.

I am trying to start a coroutine from API C, let it give in and continue it later (possibly after completing a resume from other coroutines). This is a pretty simple use case, but the documentation for lua_resume() extremely confusing:

int lua_resume (lua_State * L, lua_State * from, int nargs);

Starts and resumes the coroutine in the specified stream L.

To start a coroutine, you push the main function plus any arguments to the thread stack; then you call lua_resume, with nargs being the number of arguments. This call returns when the coroutine pauses or completes execution. When it returns, the stack contains all the values ​​passed to lua_yield or all the values ​​returned by the body function. Return lua_resume LUA_YIELD if a coroutine is issued, LUA_OK if coroutine completes without errors or an error code in case of errors (see lua_pcall).

In case of errors, the stack does not unwind, so you can use the debug API above it. The error message is at the top of the stack.

To resume coroutine, you remove any results from the last lua_yield, put on its stack only the values ​​that should be passed as the result of the crop, and then call lua_resume.

The from parameter represents a coroutine that resumes L. If there is no such coroutine, this parameter can be NULL.

The meaning "is a coroutine that resumes L" is extremely unclear here. When exactly is the "from" nil which of L and from is the "stream of the stack" and what are the exact requirements for resuming a coroutine? Is it possible to change the state of L between the initial lua_resume() and the second, which actually resumes? If so, how does the state know where / which function to resume? If not (i.e., One thread per lua_State ), then how can you create a new thread so that it lua_resume() execution context with the parent thread (global variables, environment, etc.) And what is the correct way to call lua_resume() and unwind the call for each start / resume in this case? Can the from argument be related to any of these things?

And finally, in any case - how can I get a full stack trace for debugging in the error handler (for example, it is called on an error from lua_pcall ), which respects / knows about calls through the resume? Does lua_getinfo() report correctly with a summary? Is this an out argument for?

I would really like the full working example of starting / resuming a coroutine from the C API, which illustrates the use of this second argument. There this example is on gist, but he is years old and the API has changed since then - lua_resume() now takes 3 arguments, not 2 ...

+4
source share
1 answer

When exactly "from" is nil, which of L and from are the "stream stack".

If you ask about the value of the from parameter, it may seem that there is not so much of it,. It would be permissible to use it only if the C function executing the resume itself was called from a coroutine.

L is the stack on which you put the arguments to resume (and the initial function if this is the initial resume call). So this best fits the term "thread stack".

What are the exact requirements for resuming a coroutine?

As indicated in the docs for lua_status , you can resume a stream if the stream status is LUA_OK or LUA_YIELD .

Note that there is a difference between resuming a thread and resuming a coroutine. lua_resume resumes the stream. If the status is LUA_OK , then it does not have an active coroutine, so you create a new coroutine and, therefore, must provide a function. If the stream is LUA_YIELD , then resuming the stream will resume the received coroutine.

Is it possible to change the state of L between the initial lua_resume () and the second, which actually resumes?

Of course it can; should be. In the end, you get the return / exit values ​​as values ​​placed in L When you are about to resume it, you must delete these values ​​and put the new values ​​on the stack as the values ​​that will be returned from the Lua yield function.

So yes, you can change it. But there are rules about what you can do. Two of them:

Rule number 1: You can not poke things that do not belong to you.

Let's say the Lua stack looks like this, right in front of your initial resume (growing from left to right):

 [A][B][C][F][1][2][3] 

F is the function you want to run. 1, 2 and 3 are parameters. So you pass 3 like nargs . A , B and C are just any material you put on this stack.

If the coroutine is inferior, your stack looks like this:

 [A][B][C][LOTS OF STUFF][a][b][c] 

LOTS OF STUFF represents an arbitrary amount of stack data created by coroutine processing. There is no way to know how much or how little will be there.

a , b and c are the values ​​passed to yield in Lua. You can play with them at your pleasure. You can even reach back and poke at A , B and C through their absolute stack indices 1, 2 and 3.

What you cannot do is change LOTS OF STUFF . This data represents the received state of the coroutine. You are not allowed to change this. At all. You probably also cannot remove any items below it. Therefore, you cannot even delete A , B or C

Rule number 2: You must resume exactly from where the stack stopped.

After the coroutine has lost, you can remove some things from the stack and add them to it (according to rule No. 1). Let's say the stack looks like this:

 [A][B][C][LOTS OF STUFF][a][Q][R][D] 

a is one of the original return values, and other things are temporary values ​​that you created during this time. So now it's time to resume the coroutine. You have several new options for passing, 1 and 2. How do you do this?

Your first step should be to clean up. You should return your stack to this point:

 [A][B][C][LOTS OF STUFF] 

After that, you can press 1 and 2, and then resume the coroutine with nargs as 2.

If so, how will the state know where / what function to resume?

The LOTS OF STUFF above reports “where / which function to resume”.

How to create a new thread correctly so that it shares the execution context (global variables, environment, etc.) with the parent thread

The "execution context" is not associated with a thread. These things are related to functions, not threads. Therefore, if you want a function to share the same environment as another function, then they must use the same environment as this function.

How these functions are performed (coroutines or not) is not related to the fact that their environment.

And finally, in any case - how can I get a full stack trace for debugging in the error handler (for example, caused by an error from lua_pcall) that takes into account / knows about calls through the resume?

You can not.

When lua_resume crashes due to an error, it leaves information on the stack to track what happened between the resume and the place that generated the error. But he cannot know how the resume itself happened. This is what you have to do, depending on how you resumed this coroutine from the very beginning.

The purpose of the from field is not to connect the renewer to the renewed one.

+3
source

All Articles