[Date Prev][Date Next][Thread Prev][Thread Next]
[Date Index]
[Thread Index]
- Subject: Re: LuaThreads with LuaJIT (and maybe lua_tcc)
- From: Mildred <ml.mildred593@...>
- Date: Wed, 12 Apr 2006 11:47:27 +0200
Hi,
I wrote something that may be useful. It compiles but it isn't tested
yet.
It's a function lua_xcopy that copies n lua values from one lua Stack
to another, like lua_xmove does. But contrary to lua_xmove, I try to
make a full copy, that is without making referances between the two
lua_Stacks.
It's not yet tested but if it works, it should copy without problems
numbers, strings, light userdatas, booleans, nils, tables (with
metatables) and functions (both C and lua, with upvalues and
environment) but not lua threads.
It will also copy userdatas with their metatables. The problem there is
that if a userdata is collected in one lua_Stack, lua will call the
__gc metamethod, and on the C side, some memory may be freed at this
time. So the same userdata on the other lua Stack may cause bugs
(trying to access freed memory). To prevent that, a metamethod __xcopy
is called on both metatables. Then the C side will know that it needs
two calls to __gc to free the data :)
I looked at pluto to help me with that.
I coded that because I also want multithreading in Lua :) A solution
that copies lua datas like that can make independant lua stacks
communicate without the need to lock the whole state by lua_lock and
lua_unlock.
Any thoughts about that ?
Mildred
--
Mildred <xmpp:mildred@jabber.fr> <http://mildred632.free.fr/>
Clef GPG : <hkp://pgp.mit.edu> ou <http://mildred632.free.fr/gpg_key>
Fingerprint : 197C A7E6 645B 4299 6D37 684B 6F9D A8D6 [9A7D 2E2B]
--
Mildred <xmpp:mildred@jabber.fr> <http://mildred632.free.fr/>
Clef GPG : <hkp://pgp.mit.edu> ou <http://mildred632.free.fr/gpg_key>
Fingerprint : 197C A7E6 645B 4299 6D37 684B 6F9D A8D6 [9A7D 2E2B]
/******************************************************************************
**
** Copyright (C) 2006 Mildred.
**
** Permission is hereby granted, free of charge, to any person obtaining a copy
** of this software and associated documentation files (the "Software"), to
** deal in the Software without restriction, including without limitation the
** rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
** sell copies of the Software, and to permit persons to whom the Software is
** furnished to do so, subject to the following conditions:
**
** The above copyright notice and this permission notice shall be included in
** all copies or substantial portions of the Software.
**
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
** FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
** IN THE SOFTWARE.
**
******************************************************************************/
#include <lua.h>
#include <lauxlib.h>
#include "lua_src/lapi.c"
#define lua_userdatalength(L,i) (uvalue(index2adr((L),(i)))->len)
#define lua_iscfunction(L,i) (clvalue(index2adr((L),(i)))->c.isC)
#define lua_getcfunction(L,i) (clvalue(index2adr((L),(i)))->c.f)
typedef struct {
int top;
lua_State* L;
} xStack;
static void xcopy_value(lua_State* S, xStack* F, int idx, xStack* T);
/*
Notation :
xStack* F From stack
xStack* T To stack
*/
/* any nil, boolean, lightuserdata, number, string is equal to any other object with the same value. So there is no need to store those values in the stack S which is used to store referances to objects that can be encoutred multiples times */
static void xcopy_nil(xStack* T) {
lua_pushnil(T->L);
T->top++;
}
static void xcopy_boolean(xStack* F, int idx, xStack* T) {
lua_pushboolean(T->L, lua_toboolean(F->L, idx));
T->top++;
}
static void xcopy_lightuserdata(xStack* F, int idx, xStack* T) {
lua_pushlightuserdata(T->L, lua_touserdata(F->L, idx));
T->top++;
}
static void xcopy_number(xStack* F, int idx, xStack* T) {
lua_pushnumber(T->L, lua_tonumber(F->L, idx));
T->top++;
}
static void xcopy_string(xStack* F, int idx, xStack* T) {
size_t sz;
const char* s = lua_tolstring(F->L, idx, &sz);
lua_pushlstring(T->L, s, sz);
T->top++;
}
/* Tables and userdata have individual metatables (although multiple tables and userdata can share a same table as their metatable); values of all other types share one single metatable per type. So, there is one single metatable for all numbers, and for all strings, etc. */
/* First some utility functions */
static int already_exists(lua_State* S, xStack* F, int idx){
/* idx : any valid index */
lua_pushlightuserdata(S, (void*) lua_topointer(F->L, idx));
lua_rawget(S, 1);
if(lua_isnil(S, -1)) {
lua_pop(S, 1);
return 0;
}
return 1;
}
static void register_value(lua_State* S, xStack* T, int idx){
/* idx : any valid index */
lua_pushlightuserdata(S, (void*) lua_topointer(T->L, idx));
lua_pushvalue(T->L, idx);
lua_xmove(T->L, S, 1);
lua_rawset(S, 1);
}
static void do_metatable(lua_State* S, xStack* F, int idx, xStack* T){
/* idx : any valid index */
lua_getmetatable(F->L, idx);
F->top++;
xcopy_value(S, F, F->top, T);
lua_pop(F->L, 1);
F->top--;
lua_setmetatable(T->L, -2);
}
/* Now the xcopy_* functions */
static void xcopy_table(lua_State* S, xStack* F, int idx, xStack* T) {
/* idx : a positive index ! */
if(already_exists(S, F, idx)) return;
lua_newtable(T->L);
T->top++;
lua_pushnil(F->L); /* first index */
F->top++;
while (lua_next(F->L, idx) != 0) {
F->top++;
xcopy_value(S, F, F->top-1, T); /* the key */
xcopy_value(S, F, F->top, T); /* the value */
lua_rawset(T->L, -3);
T->top--;
T->top--;
lua_pop(F->L, 1); /* remove value, keep the key */
F->top--;
}
F->top--;
do_metatable(S, F, idx, T);
register_value(S, T, -1);
}
static void xcopy_userdata(lua_State* S, xStack* F, int idx, xStack* T) {
/* idx : a positive index ! */
if(already_exists(S, F, idx)) return;
size_t sz = lua_userdatalength(F->L, idx);
register char* u1 = (char*)lua_touserdata(F->L, idx);
register char* u2 = lua_newuserdata(T->L, sz);
size_t i;
for(i=0; i<sz; i++) u2[i]=u1[i];
T->top++;
do_metatable(S, F, idx, T);
/* There is a need to count the number of times the userdata is copied and garbage collected. If there is allocated memory, it mustn't be freed on the first call to __gc metamethod because the copy needs it. So, with that, it is possible to know exactly when the userdata is copied, and then count the times it is copied. */
luaL_callmeta(F->L, idx, "__xcopy");
lua_pop(F->L, 1);
luaL_callmeta(T->L, -1, "__xcopy");
lua_pop(T->L, 1);
register_value(S, T, -1);
}
static int writer (lua_State *L, const void* b, size_t size, void* B) {
(void)L;
luaL_addlstring((luaL_Buffer*) B, (const char *)b, size);
return 0;
}
static void xcopy_function(lua_State* S, xStack* F, int idx, xStack* T) {
/* idx : a positive index ! */
if(already_exists(S, F, idx)) return;
/* get the number of upvalues */
lua_Debug ar;
lua_pushvalue(F->L, idx);
lua_getinfo(F->L, ">u", &ar);
lua_pop(F->L, 1);
/* depending on the function type */
if(lua_iscfunction(F->L, idx)) {
/* push those upvalues */
int i;
/* ipvalues indexes start at 0,
see lua_pushcclosure source */
for(i=0; i<ar.nups; i++){
lua_getupvalue(F->L, idx, i);
F->top++;
xcopy_value(S, F, F->top, T);
lua_pop(F->L, 1);
F->top--;
}
lua_pushcclosure(T->L, lua_getcfunction(F->L, idx), ar.nups);
T->top = T->top - ar.nups; /* remove upvalues */
T->top++;
}else{
/* dump lua function and reload it */
luaL_Buffer b;
luaL_buffinit(T->L, &b);
T->top++;
if(lua_dump(F->L, writer, &b) != 0){
/* error in dump, push nil function */
lua_pushnil(T->L);
return;
}
/* load the function */
size_t sz;
const char* s = lua_tolstring(T->L, -1, &sz);
luaL_loadbuffer(T->L, s, sz, NULL);
lua_remove(T->L, -2);
/* set upvalues */
int i;
for(i=0; i<ar.nups; i++){
lua_getupvalue(F->L, idx, i);
F->top++;
xcopy_value(S, F, F->top, T);
lua_setupvalue(T->L, -2, i);
T->top--;
lua_pop(F->L, 1);
F->top--;
}
}
lua_getfenv(F->L, idx);
F->top++;
xcopy_value(S, F, F->top, T);
lua_setfenv(T->L, -1);
T->top--; /* setfenv pops an element */
lua_pop(F->L, 1);
F->top--;
do_metatable(S, F, idx, T);
register_value(S, T, -1);
}
static void xcopy_thread(lua_State* S, xStack* F, int idx, xStack* T) {
/* no idea yet */
xcopy_nil(T);
}
/* push on the stack L the value at index idx from stack O */
static void xcopy_value(lua_State* S, xStack* F, int idx, xStack* T) {
/* idx : a positive index ! */
int t = lua_type(F->L, idx);
switch(t) {
default:
case LUA_TNONE: {printf("unknown type %i\n", t); } break;
case LUA_TPROTO: {printf("can't handle proto\n"); } break;
case LUA_TUPVAL: {printf("can't handle upval\n"); } break;
case LUA_TDEADKEY: {printf("can't handle deadkey\n");} break;
case LUA_TNIL: {xcopy_nil (T); } break;
case LUA_TBOOLEAN: {xcopy_boolean (F, idx, T); } break;
case LUA_TLIGHTUSERDATA:
{xcopy_lightuserdata(F, idx, T); } break;
case LUA_TNUMBER: {xcopy_number (F, idx, T); } break;
case LUA_TSTRING: {xcopy_string (F, idx, T); } break;
case LUA_TTABLE: {xcopy_table (S, F, idx, T); } break;
case LUA_TFUNCTION:{xcopy_function (S, F, idx, T); } break;
case LUA_TUSERDATA:{xcopy_userdata (S, F, idx, T); } break;
case LUA_TTHREAD: {xcopy_thread (S, F, idx, T); } break;
}
}
int lua_xcopy(lua_State* L1, lua_State* L2, int num) {
/* if nothing to copy */
if(num<=0) return 1;
if(L1 == L2){
lua_State* L = lua_newthread(L1);
int top = lua_gettop(L1);
lua_insert(L1, top-num);
int res = lua_xcopy(L1, L, num);
lua_xmove(L, L1, num);
lua_remove(L1, top-num);
return res;
}
xStack S1;
S1.L = L1;
S1.top = lua_gettop(L1);
/* if wants to move more objects that the stack contains */
if(S1.top < num) return 0;
xStack S2;
S2.L = L2;
S2.top = lua_gettop(L2);
/* save the top values */
int top1 = S1.top;
int top2 = S2.top;
/* create a stack to cache values */
lua_State* S = lua_newthread(S2.L);
/* idx = first index (positive notation) */
int idx = S1.top - num + 1;
for(; idx <= top1; idx++){
/* for each value to copy */
xcopy_value(S, &S1, idx, &S2);
}
/* remove the tempoary stack S
done at the end to prevent garbage collection */
lua_remove(S2.L, top2 + 1);
return 1;
}