lua-users home
lua-l archive

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


Patrick Donnelly wrote:
Hello Christian,

On Wed, Jan 6, 2010 at 8:17 AM, Christian Tellefsen
<christian.tellefsen@funcom.com> wrote:
Hi,

I have a question about the Lua registry, environment, and threads. I have
the following code:

int Test(lua_State * L )
{
 lua_getglobal(L, "print");
 lua_getglobal(L, "a");
 lua_pcall(L, 1, 0, 0);

 return 0;
}

int main (void) {
 int error;
 lua_State * L = lua_open();   /* opens Lua */
 luaL_openlibs(L);

 lua_pushnumber(L, 0);  // In the global environment, a = 0
 lua_setglobal(L, "a");

 // Create a thread with its own environment.
 lua_State * L1 = lua_newthread(L);

 lua_pushthread(L1);                  // thread
 lua_newtable(L1);                    // thread, t
 lua_pushvalue(L1, LUA_GLOBALSINDEX); // thread, t, _G
 lua_setfield(L1, -2, "__index");     // thread, t
 lua_pushvalue(L1, -1);               // thread, t, t
 lua_setmetatable(L1, -2);            // thread, t
 lua_setfenv(L1, -2);                 // thread

 lua_pushnumber(L1, 1);  // In the thread environment, a = 1
 lua_setglobal(L1, "a");

 lua_pop(L1, 1);                      // -

 // Put the C function in the registry, and retrieve it and call it from the
root and the child thread.
 lua_pushcfunction(L, &Test);
 int r0 = luaL_ref(L, LUA_REGISTRYINDEX);

 lua_rawgeti(L, LUA_REGISTRYINDEX, r0);
 lua_pcall(L, 0, 0, 0);

 lua_rawgeti(L1, LUA_REGISTRYINDEX, r0);
 lua_pcall(L1, 0, 0, 0);

 // Load the buffer and store it in registry, and retrieve it and call it
from the root and the child thread.
 const char * txt = "print(a)";
 luaL_loadbuffer(L, txt, strlen(txt), "x");

 int r1 = luaL_ref(L, LUA_REGISTRYINDEX);

 lua_rawgeti(L, LUA_REGISTRYINDEX, r1);
 lua_pcall(L, 0, 0, 0);

 lua_rawgeti(L1, LUA_REGISTRYINDEX, r1);
 lua_pcall(L1, 0, 0, 0);
}

This prints:

0
1
0
0

So, as far as I can tell, the C function will print out the 'a' in the
thread's environment, but the identical Lua function loaded through
luaL_loadbuffer() will always print out the 'a' in the global environment.

Does anyone know why this gives a different result? And is there any way I
can get the second case to work in the same way as the first one, that is,
print the 'a' in the thread's environment?

Lua functions do not use the thread environment. Instead, Lua
functions use the function environment for all global accesses. Lua C
functions, by convention, (for better or worse) use the thread
environment (LUA_GLOBALSINDEX) instead of an environment index
(LUA_ENVIRONINDEX). Your Lua function inherits the environment of the
thread it was created in. In your example, that is the main thread
environment (a = 0).

HTH,

OK, thanks.

So, in order do make a Lua function access the correct thread environment, I have to load them once per thread? It would be nice to be able to avoid that.

Can I do this trick to swap the environment before execution?
lua_rawgeti(L, LUA_REGISTRYINDEX, registryID); // stack: f
   lua_pushthread(L);   //f, thread
   lua_getfenv(L, -1);  //f, thread, fenv
   lua_setfenv(L, -3);  //f, thread
   lua_pop(L, 1);       //f

I'm not sure if this would work with coroutines if a thread swaps out the function environment of a coroutine is while it's sleeping, though. Does the function environment follow the closure or the function?

Cheers,
Christian Tellefsen.