lua-users home
lua-l archive

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



On 21-Aug-05, at 8:18 AM, Mike Pall wrote:

 But IMHO this only hides violations of
the referential model a bit longer. Not holding a reference
to a coroutine is a bug.

It's certainly a bug if you're planning on resuming the coroutine. If, for some reason, you know that you're never going to resume the coroutine, it's completely legitimate IMHO. (For example, you may have a contract with the coroutine that it gets called with a special message telling it to clean up because it is about to be terminated.)

Making the crash more likely (by
letting it happen even while the coroutine is running) may
lead to earlier discovery of the bug.

If the coroutine is running, then it's reachable, so the garbage collector should mark it. Not doing so would be a violation of the gc contract.

Unfortunately, just marking the currently running thread is not enough. If I resume a luathread for which I have not kept a reference, and that luathread uses the coroutine mechanism itself, then the atomic phase of the garbage collection could take place in a subthread, as it were. The subthread does not retain a reference to the invoking thread as far as I can see, so it would then crash when it yielded.

This can't happen with a Lua program for the simple reason that it is impossible to resume a coroutine without pushing the thread object (or a wrapper to it) onto the stack, where it stays until the coroutine yields. Consequently, there must be a chain of references from the main thread's stack to the currently running thread.

So the root problem here is that luathreads (lua_State *) are the only Lua internal object exposed to the API without being wrapped into the Lua universe. Lua cannot know what lua_State*'s might be floating around in a C executable, and so the gc cannot guarantee to mark them all.

One possible workaround might be to change a luathread from a lua_State * to a lua_State ** (but opaquely to the C program, so as not to tempt fate). This would change the appearance of a program which used threads a bit:

current:

lua_State *L1 = lua_newthread(L);
//...
lua_resume(L1, 0);

changes to:

lua_Thread *th = lua_newthread(L);
//...
lua_resume(lua_thread2state(L, th), 0);


where we have something like:

lua_State *lua_thread2state(lua_State *L, lua_Thread *th) {
  if (**th == NULL)
    lua_pushstring(L, "You seem to have lost the thread");
    lua_error(L);
  }
  return **th;
}

Then the garbage collector could clear the lua_Thread handle when it collected the lua_thread, and an error would be thrown if an attempt were made to use it.