lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


Hi,

Roberto wrote:
> We should rethink the whole relationship between hooks and
> threads. Maybe debuggers and profilers also need to be aware of
> resume/yields and who is the running thread.

I have a scenario where I need native threads *and* Lua threads (coroutines).
So each native thread may have an active Lua thread and there may be a
bunch of suspended Lua threads lying around, too.

So considering the possibility of mixing native and lua threads poses
a few challenges to the profiler/debugger API. Maybe it would be a good idea
to allow extensions (such as a native thread library) to extend the set of
hooks and to call hooks from within their support code.

Then the core could just define HOOKTHREAD with the following subtypes:
START, RESUME, YIELD and EXIT. Contrary to my last post these should not
be called from luaE_newthread() and luaE_freethread() since the creation
of the data structures is not relevant to profilers and debuggers. It makes
more sense to call them from lua_resume() and lua_yield() since that is the
point where lua_State is switched.

A native thread library would add two more subtypes: TSTART and TEXIT.
The definitions should already be in lua.h to define a standard API for them.
Of course these hooks are only ever called from the extension library.

(If someone comes up with better names for these subtypes, I'm all for it.)

I'm offering to write a patch and the docs for it, if there is a consensus
that this is a good idea and Roberto wants it for the core distribution.

While we are at it, I have a few wishes for coroutines:

- A way to get the current thread from within Lua (!) code. I.e. something
  like coroutine.current(). Otherwise there is no way to write a routine in
  pure Lua code that accounts usage to a specific thread (or just to print
  the current thread id). Would be a useful addition to debug.getinfo(), too
  (this is a better fit for hooks, but not for regular code).

- A C API to reliably (and quickly) detect the difference between a yield
  and a return from lua_resume(). I know I can use the approach from
  luaB_costatus, but well ... it hardcodes several assumptions about the way
  coroutines are implemented. But lua_resume() naturally 'knows' whether
  the coroutine has returned or yielded ... it just doesn't report that fact.

- A supported way to resurrect dead coroutines (the setup/teardown overhead
  is too high). I know I could just push a closure and the args on the stack
  and call lua_resume() only with the number of arguments (omitting the
  closure). But it is not documented and I'm not sure if it is completely
  safe.

- A way to mix exception handling and coroutines. Currently there is no
  way to yield() from within a pcall(). I know this is due to the way
  pcall() is implemented (with a C stack frame holding the jmp_buf).
  Maybe there is a better solution?

- A way to resume a coroutine and throw an exception. Obviously this
  doesn't make sense without the previous point solved.

> And given all that, maybe we do not need independent hooks for each thread.

Yes, I had that in mind, too. But I was worried that there was some
undocumented reason for making them per-thread. But since you are
suggesting it: Fine with me and solves Andy's request, too. :-)

A profiler can probably get away with running solely from hooks (since
L->allowhook prevents recursive invocation). But a 'true' debugger really
needs to run in its own environment anyway (a different Lua or C environment
in a different native thread or process). So both of this would work with a
global set of hooks.

I don't know how hookcount is used in practice (killing looping code in
a restricted environment?). I think it's not that useful, the way it is
currently implemented. With a high start count it may never be called if
you create a bunch of coroutines that exit early. Changing it to a global
counter would make more sense, IMHO.

About a little performance impact: There would be an additional dereference
in luaV_execute to get at L->l_G->hookmask in the critical code path.
The compiler cannot optimize this away, so caching
  hookmaskptr = &L->l_G->hookmask
may be required. Please note that the compiler may generate better code
with hookmask being an unsigned int and not a byte.

[There is more stuff to be optimized in the core loop. Mail me if you are
interested].

Bye,
     Mike