#include "llthreads.h" // luasocket #include "luasocket.h" #include "mime.h" #include "luasocketscripts.h" // cjson #include "lua_cjson.h" static luaL_Reg luax_exts[] = { {"socket.core", luaopen_socket_core}, {"mime.core", luaopen_mime_core}, {"socket", luaopen_socket}, {"socket.smtp", luaopen_socket_smtp}, {"socket.http", luaopen_socket_http}, {"socket.ftp", luaopen_socket_ftp}, {"socket.tp", luaopen_socket_tp}, {"socket.url", luaopen_socket_url}, {"mime", luaopen_socket_mime}, {"ltn12", luaopen_socket_ltn12}, {"cjson", luaopen_cjson}, {NULL, NULL} }; /* use static pointer as key to weak userdata table. */ static char *obj_udata_weak_ref_key = "obj_udata_weak_ref_key"; #define obj_type_id_Lua_LLThread 0 #define obj_type_Lua_LLThread (obj_types[obj_type_id_Lua_LLThread]) static obj_type obj_types[] = { {NULL, 0, OBJ_TYPE_FLAG_WEAK_REF, "Lua_LLThread"}, {NULL, -1, 0, NULL}, }; static lua_State* parent_L = 0; FUNC_UNUSED obj_udata *obj_udata_toobj(lua_State *L, int _index) { obj_udata *ud; size_t len; /* make sure it's a userdata value. */ ud = (obj_udata *)lua_touserdata(L, _index); if(ud == NULL) { luaL_typerror(L, _index, "userdata"); /* is not a userdata value. */ } /* verify userdata size. */ len = lua_objlen(L, _index); if(len != sizeof(obj_udata)) { /* This shouldn't be possible */ luaL_error(L, "invalid userdata size: size=%d, expected=%d", len, sizeof(obj_udata)); } return ud; } FUNC_UNUSED int obj_udata_is_compatible(lua_State *L, obj_udata *ud, void **obj, base_caster_t *caster, obj_type *type) { obj_base *base; obj_type *ud_type; lua_pushlightuserdata(L, type); lua_rawget(L, LUA_REGISTRYINDEX); /* type's metatable. */ if(lua_rawequal(L, -1, -2)) { *obj = ud->obj; /* same type no casting needed. */ return 1; } else { /* Different types see if we can cast to the required type. */ lua_rawgeti(L, -2, type->id); base = lua_touserdata(L, -1); lua_pop(L, 1); /* pop obj_base or nil */ if(base != NULL) { *caster = base->bcaster; /* get the obj_type for this userdata. */ lua_pushliteral(L, ".type"); lua_rawget(L, -3); /* type's metatable. */ ud_type = lua_touserdata(L, -1); lua_pop(L, 1); /* pop obj_type or nil */ if(base == NULL) { luaL_error(L, "bad userdata, missing type info."); return 0; } /* check if userdata is a simple object. */ if(ud_type->flags & OBJ_TYPE_SIMPLE) { *obj = ud; } else { *obj = ud->obj; } return 1; } } return 0; } FUNC_UNUSED obj_udata *obj_udata_luacheck_internal(lua_State *L, int _index, void **obj, obj_type *type, int not_delete) { obj_udata *ud; base_caster_t caster = NULL; /* make sure it's a userdata value. */ ud = (obj_udata *)lua_touserdata(L, _index); if(ud != NULL) { /* check object type by comparing metatables. */ if(lua_getmetatable(L, _index)) { if(obj_udata_is_compatible(L, ud, obj, &(caster), type)) { lua_pop(L, 2); /* pop both metatables. */ /* apply caster function if needed. */ if(caster != NULL && *obj != NULL) { caster(obj); } /* check object pointer. */ if(*obj == NULL) { if(not_delete) { luaL_error(L, "null %s", type->name); /* object was garbage collected? */ } return NULL; } return ud; } } } if(not_delete) { luaL_typerror(L, _index, type->name); /* is not a userdata value. */ } return NULL; } FUNC_UNUSED void *obj_udata_luacheck(lua_State *L, int _index, obj_type *type) { void *obj = NULL; obj_udata_luacheck_internal(L, _index, &(obj), type, 1); return obj; } FUNC_UNUSED void *obj_udata_luadelete(lua_State *L, int _index, obj_type *type, int *flags) { void *obj; obj_udata *ud = obj_udata_luacheck_internal(L, _index, &(obj), type, 0); if(ud == NULL) return NULL; *flags = ud->flags; /* null userdata. */ ud->obj = NULL; ud->flags = 0; return obj; } FUNC_UNUSED void obj_udata_luapush(lua_State *L, void *obj, obj_type *type, int flags) { obj_udata *ud; /* convert NULL's into Lua nil's. */ if(obj == NULL) { lua_pushnil(L); return; } /* check for type caster. */ if(type->dcaster) { (type->dcaster)(&obj, &type); } /* create new userdata. */ ud = (obj_udata *)lua_newuserdata(L, sizeof(obj_udata)); ud->obj = obj; ud->flags = flags; /* get obj_type metatable. */ lua_pushlightuserdata(L, type); lua_rawget(L, LUA_REGISTRYINDEX); /* type's metatable. */ lua_setmetatable(L, -2); } FUNC_UNUSED void *obj_udata_luadelete_weak(lua_State *L, int _index, obj_type *type, int *flags) { void *obj; obj_udata *ud = obj_udata_luacheck_internal(L, _index, &(obj), type, 0); if(ud == NULL) return NULL; *flags = ud->flags; /* null userdata. */ ud->obj = NULL; ud->flags = 0; /* get objects weak table. */ lua_pushlightuserdata(L, obj_udata_weak_ref_key); lua_rawget(L, LUA_REGISTRYINDEX); /* weak ref table. */ /* remove object from weak table. */ lua_pushlightuserdata(L, obj); lua_pushnil(L); lua_rawset(L, -3); return obj; } FUNC_UNUSED void obj_udata_luapush_weak(lua_State *L, void *obj, obj_type *type, int flags) { obj_udata *ud; /* convert NULL's into Lua nil's. */ if(obj == NULL) { lua_pushnil(L); return; } /* check for type caster. */ if(type->dcaster) { (type->dcaster)(&obj, &type); } /* get objects weak table. */ lua_pushlightuserdata(L, obj_udata_weak_ref_key); lua_rawget(L, LUA_REGISTRYINDEX); /* weak ref table. */ /* lookup userdata instance from pointer. */ lua_pushlightuserdata(L, obj); lua_rawget(L, -2); if(!lua_isnil(L, -1)) { lua_remove(L, -2); /* remove objects table. */ return; } lua_pop(L, 1); /* pop nil. */ /* create new userdata. */ ud = (obj_udata *)lua_newuserdata(L, sizeof(obj_udata)); /* init. obj_udata. */ ud->obj = obj; ud->flags = flags; /* get obj_type metatable. */ lua_pushlightuserdata(L, type); lua_rawget(L, LUA_REGISTRYINDEX); /* type's metatable. */ lua_setmetatable(L, -2); /* add weak reference to object. */ lua_pushlightuserdata(L, obj); /* push object pointer as the 'key' */ lua_pushvalue(L, -2); /* push object's udata */ lua_rawset(L, -4); /* add weak reference to object. */ lua_remove(L, -2); /* remove objects table. */ } /* default object equal method. */ FUNC_UNUSED int obj_udata_default_equal(lua_State *L) { obj_udata *ud1 = obj_udata_toobj(L, 1); obj_udata *ud2 = obj_udata_toobj(L, 2); lua_pushboolean(L, (ud1->obj == ud2->obj)); return 1; } /* default object tostring method. */ FUNC_UNUSED int obj_udata_default_tostring(lua_State *L) { obj_udata *ud = obj_udata_toobj(L, 1); /* get object's metatable. */ lua_getmetatable(L, 1); lua_remove(L, 1); /* remove userdata. */ /* get the object's name from the metatable */ lua_getfield(L, 1, ".name"); lua_remove(L, 1); /* remove metatable */ /* push object's pointer */ lua_pushfstring(L, ": %p, flags=%d", ud->obj, ud->flags); lua_concat(L, 2); return 1; } /* * Simple userdata objects. */ FUNC_UNUSED void *obj_simple_udata_toobj(lua_State *L, int _index) { void *ud; /* make sure it's a userdata value. */ ud = lua_touserdata(L, _index); if(ud == NULL) { luaL_typerror(L, _index, "userdata"); /* is not a userdata value. */ } return ud; } FUNC_UNUSED void * obj_simple_udata_luacheck(lua_State *L, int _index, obj_type *type) { void *ud; /* make sure it's a userdata value. */ ud = lua_touserdata(L, _index); if(ud != NULL) { /* check object type by comparing metatables. */ if(lua_getmetatable(L, _index)) { lua_pushlightuserdata(L, type); lua_rawget(L, LUA_REGISTRYINDEX); /* type's metatable. */ if(lua_rawequal(L, -1, -2)) { lua_pop(L, 2); /* pop both metatables. */ return ud; } } } luaL_typerror(L, _index, type->name); /* is not a userdata value. */ return NULL; } FUNC_UNUSED void * obj_simple_udata_luadelete(lua_State *L, int _index, obj_type *type, int *flags) { void *obj; obj = obj_simple_udata_luacheck(L, _index, type); *flags = OBJ_UDATA_FLAG_OWN; /* clear the metatable to invalidate userdata. */ lua_pushnil(L); lua_setmetatable(L, _index); return obj; } FUNC_UNUSED void *obj_simple_udata_luapush(lua_State *L, void *obj, int size, obj_type *type) { /* create new userdata. */ void *ud = lua_newuserdata(L, size); memcpy(ud, obj, size); /* get obj_type metatable. */ lua_pushlightuserdata(L, type); lua_rawget(L, LUA_REGISTRYINDEX); /* type's metatable. */ lua_setmetatable(L, -2); return ud; } /* default simple object equal method. */ FUNC_UNUSED int obj_simple_udata_default_equal(lua_State *L) { void *ud1 = obj_simple_udata_toobj(L, 1); size_t len1 = lua_objlen(L, 1); void *ud2 = obj_simple_udata_toobj(L, 2); size_t len2 = lua_objlen(L, 2); if(len1 == len2) { lua_pushboolean(L, (memcmp(ud1, ud2, len1) == 0)); } else { lua_pushboolean(L, 0); } return 1; } /* default simple object tostring method. */ FUNC_UNUSED int obj_simple_udata_default_tostring(lua_State *L) { void *ud = obj_simple_udata_toobj(L, 1); /* get object's metatable. */ lua_getmetatable(L, 1); lua_remove(L, 1); /* remove userdata. */ /* get the object's name from the metatable */ lua_getfield(L, 1, ".name"); lua_remove(L, 1); /* remove metatable */ /* push object's pointer */ lua_pushfstring(L, ": %p", ud); lua_concat(L, 2); return 1; } int obj_constructor_call_wrapper(lua_State *L) { /* replace '__call' table with constructor function. */ lua_pushvalue(L, lua_upvalueindex(1)); lua_replace(L, 1); /* call constructor function with all parameters after the '__call' table. */ lua_call(L, lua_gettop(L) - 1, LUA_MULTRET); /* return all results from constructor. */ return lua_gettop(L); } void obj_type_register_constants(lua_State *L, const obj_const *constants, int tab_idx) { /* register constants. */ while(constants->name != NULL) { lua_pushstring(L, constants->name); switch(constants->type) { case CONST_BOOLEAN: lua_pushboolean(L, constants->num != 0.0); break; case CONST_NUMBER: lua_pushnumber(L, constants->num); break; case CONST_STRING: lua_pushstring(L, constants->str); break; default: lua_pushnil(L); break; } lua_rawset(L, tab_idx - 2); constants++; } } void obj_type_register_package(lua_State *L, const reg_sub_module *type_reg) { const luaL_reg *reg_list = type_reg->pub_funcs; /* create public functions table. */ if(reg_list != NULL && reg_list[0].name != NULL) { /* register functions */ luaL_register(L, NULL, reg_list); } obj_type_register_constants(L, type_reg->constants, -1); lua_pop(L, 1); /* drop package table */ } void obj_type_register(lua_State *L, const reg_sub_module *type_reg, int priv_table) { const luaL_reg *reg_list; obj_type *type = type_reg->type; const obj_base *base = type_reg->bases; if(type_reg->is_package == 1) { obj_type_register_package(L, type_reg); return; } /* create public functions table. */ reg_list = type_reg->pub_funcs; if(reg_list != NULL && reg_list[0].name != NULL) { /* register "constructors" as to object's public API */ luaL_register(L, NULL, reg_list); /* fill public API table. */ /* make public API table callable as the default constructor. */ lua_newtable(L); /* create metatable */ lua_pushliteral(L, "__call"); lua_pushcfunction(L, reg_list[0].func); /* push first constructor function. */ lua_pushcclosure(L, obj_constructor_call_wrapper, 1); /* make __call wrapper. */ lua_rawset(L, -3); /* metatable.__call = */ #if OBJ_DATA_HIDDEN_METATABLE lua_pushliteral(L, "__metatable"); lua_pushboolean(L, 0); lua_rawset(L, -3); /* metatable.__metatable = false */ #endif /* setmetatable on public API table. */ lua_setmetatable(L, -2); lua_pop(L, 1); /* pop public API table, don't need it any more. */ /* create methods table. */ lua_newtable(L); } else { /* register all methods as public functions. */ #if OBJ_DATA_HIDDEN_METATABLE lua_pop(L, 1); /* pop public API table, don't need it any more. */ /* create methods table. */ lua_newtable(L); #endif } luaL_register(L, NULL, type_reg->methods); /* fill methods table. */ luaL_newmetatable(L, type->name); /* create metatable */ lua_pushliteral(L, ".name"); lua_pushstring(L, type->name); lua_rawset(L, -3); /* metatable['.name'] = "" */ lua_pushliteral(L, ".type"); lua_pushlightuserdata(L, type); lua_rawset(L, -3); /* metatable['.type'] = lightuserdata -> obj_type */ lua_pushlightuserdata(L, type); lua_pushvalue(L, -2); /* dup metatable. */ lua_rawset(L, LUA_REGISTRYINDEX); /* REGISTRY[type] = metatable */ #if LUAJIT_FFI /* add metatable to 'priv_table' */ lua_pushstring(L, type->name); lua_pushvalue(L, -2); /* dup metatable. */ lua_rawset(L, priv_table); /* priv_table[""] = metatable */ #else (void)priv_table; #endif luaL_register(L, NULL, type_reg->metas); /* fill metatable */ /* add obj_bases to metatable. */ while(base->id >= 0) { lua_pushlightuserdata(L, (void *)base); lua_rawseti(L, -2, base->id); base++; } obj_type_register_constants(L, type_reg->constants, -2); lua_pushliteral(L, "__index"); lua_pushvalue(L, -3); /* dup methods table */ lua_rawset(L, -3); /* metatable.__index = methods */ #if OBJ_DATA_HIDDEN_METATABLE lua_pushliteral(L, "__metatable"); lua_pushboolean(L, 0); lua_rawset(L, -3); /* hide metatable: metatable.__metatable = false */ #endif lua_pop(L, 2); /* drop metatable & methods */ } FUNC_UNUSED int lua_checktype_ref(lua_State *L, int _index, int _type) { luaL_checktype(L,_index,_type); lua_pushvalue(L,_index); return luaL_ref(L, LUA_REGISTRYINDEX); } #if LUAJIT_FFI int nobj_udata_new_ffi(lua_State *L) { size_t size = luaL_checkinteger(L, 1); luaL_checktype(L, 2, LUA_TTABLE); lua_settop(L, 2); /* create userdata. */ lua_newuserdata(L, size); lua_replace(L, 1); /* set userdata's metatable. */ lua_setmetatable(L, 1); return 1; } int nobj_try_loading_ffi(lua_State *L, const char *ffi_mod_name, const char *ffi_init_code, const ffi_export_symbol *ffi_exports, int priv_table) { int err; /* export symbols to priv_table. */ while(ffi_exports->name != NULL) { lua_pushstring(L, ffi_exports->name); lua_pushlightuserdata(L, ffi_exports->sym); lua_settable(L, priv_table); ffi_exports++; } err = luaL_loadbuffer(L, ffi_init_code, strlen(ffi_init_code), ffi_mod_name); if(0 == err) { lua_pushvalue(L, -2); /* dup C module's table. */ lua_pushvalue(L, priv_table); /* move priv_table to top of stack. */ lua_remove(L, priv_table); lua_pushcfunction(L, nobj_udata_new_ffi); err = lua_pcall(L, 3, 0, 0); } if(err) { const char *msg = ""; if(lua_isstring(L, -1)) { msg = lua_tostring(L, -1); } printf("Failed to install FFI-based bindings: %s\n", msg); lua_pop(L, 1); /* pop error message. */ } return err; } #endif #define obj_type_Lua_LLThread_check(L, _index) \ obj_udata_luacheck(L, _index, &(obj_type_Lua_LLThread)) #define obj_type_Lua_LLThread_delete(L, _index, flags) \ obj_udata_luadelete_weak(L, _index, &(obj_type_Lua_LLThread), flags) #define obj_type_Lua_LLThread_push(L, obj, flags) \ obj_udata_luapush_weak(L, (void *)obj, &(obj_type_Lua_LLThread), flags) /* maximum recursive depth of table copies. */ #define MAX_COPY_DEPTH 30 #define ERROR_LEN 1024 /****************************************************************************** * traceback() function from Lua 5.1.x source. * Copyright (C) 1994-2008 Lua.org, PUC-Rio. All rights reserved. * * 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. ******************************************************************************/ int traceback (lua_State *L) { if (!lua_isstring(L, 1)) /* 'message' not a string? */ return 1; /* keep it intact */ lua_getfield(L, LUA_GLOBALSINDEX, "debug"); if (!lua_istable(L, -1)) { lua_pop(L, 1); return 1; } lua_getfield(L, -1, "traceback"); if (!lua_isfunction(L, -1)) { lua_pop(L, 2); return 1; } lua_pushvalue(L, 1); /* pass error message */ lua_pushinteger(L, 2); /* skip this function and traceback */ lua_call(L, 2, 1); /* call debug.traceback */ return 1; } Lua_LLThread_child *llthread_child_new() { Lua_LLThread_child *this; this = (Lua_LLThread_child *)calloc(1, sizeof(Lua_LLThread_child)); /* create new lua_State for the thread. */ this->L = luaL_newstate(); /* open standard libraries. */ luaL_openlibs(this->L); /* load modules */ luaL_Reg* lib = luax_exts; luaL_findtable(this->L, LUA_GLOBALSINDEX, "package.preload", sizeof(luax_exts)/sizeof(luax_exts[0])-1); for (; lib->func; lib++) { lua_pushstring(this->L, lib->name); lua_pushcfunction(this->L, lib->func); lua_rawset(this->L, -3); } lua_pop(this->L, 1); /* push traceback function as first value on stack. */ lua_pushcfunction(this->L, traceback); return this; } void llthread_child_destroy(Lua_LLThread_child *this) { lua_close(this->L); free(this); } Lua_LLThread *llthread_new() { Lua_LLThread *this; this = (Lua_LLThread *)calloc(1, sizeof(Lua_LLThread)); this->state = TSTATE_NONE; this->child = llthread_child_new(); return this; } void llthread_destroy(Lua_LLThread *this) { /* We still own the child thread object iff the thread was not started or * we have joined the thread. */ if((this->state & TSTATE_JOINED) == TSTATE_JOINED || this->state == TSTATE_NONE) { if(this->child) llthread_child_destroy(this->child); this->child = NULL; } free(this); } RUN_CHILD_THREAD run_child_thread(void *arg) { Lua_LLThread_child *this = (Lua_LLThread_child *)arg; lua_State *L = this->L; int nargs = lua_gettop(L) - 2; this->status = lua_pcall(L, nargs, LUA_MULTRET, 1); /* alwasy print errors here, helps with debugging bad code. */ if(this->status != 0) { const char *err_msg = lua_tostring(L, -1); fprintf(stderr, "Error from thread: %s\n", err_msg); fflush(stderr); } if (this->callback) { lua_pushstring(parent_L, LLTHREAD_REFID_FUNC_MAPPING); lua_rawget(parent_L, LUA_REGISTRYINDEX); /* stack: refid_func */ lua_pushinteger(parent_L, this->callback); /* stack: refid_func refid */ lua_rawget(parent_L, -2); /* stack: refid_func func */ // printf("---- parent_L stackDump ----\n"); // tolua_stack_dump(parent_L); // printf("---- child->L stackDump ----\n"); // tolua_stack_dump(this->L); // if (!lua_isfunction(parent_L, -1)) { printf("[LLTHREAD ERROR] callback refid '%d' does not reference a Lua function", this->callback); lua_pop(parent_L, 2); } else { int top = lua_gettop(this->L); nargs = llthread_push_results(parent_L, this, 2, top); /* stack: refid_func func ... */ lua_pcall(parent_L, nargs, 0, 0); /* stack: refid_func */ lua_pushinteger(parent_L, this->callback); /* stack: refid_func refid */ lua_pushnil(parent_L); /* stack: refid_func refid nil */ lua_rawget(parent_L, -2); /* delete refid_func[refid], stack: refid_func */ lua_pop(parent_L, 1); /* stack: - */ } } /* if thread is detached, then destroy the child state. */ if(this->is_detached != 0) { /* thread is detached, so it must clean-up the child state. */ llthread_child_destroy(this); this = NULL; } #ifdef __WINDOWS__ if(this) { /* attached thread, don't close thread handle. */ _endthreadex(0); } else { /* detached thread, close thread handle. */ _endthread(); } #else return this; #endif } int llthread_start(Lua_LLThread *this, int start_detached) { Lua_LLThread_child *child; int rc = 0; child = this->child; child->is_detached = start_detached; #ifdef __WINDOWS__ this->thread = (HANDLE)_beginthread(run_child_thread, 0, child); if(this->thread != (HANDLE)-1L) { this->state = TSTATE_STARTED; if(start_detached) { this->state |= TSTATE_DETACHED; } } #else rc = pthread_create(&(this->thread), NULL, run_child_thread, child); if(rc == 0) { this->state = TSTATE_STARTED; if(start_detached) { this->state |= TSTATE_DETACHED; rc = pthread_detach(this->thread); } } #endif return rc; } int llthread_join(Lua_LLThread *this) { #ifdef __WINDOWS__ WaitForSingleObject( this->thread, INFINITE ); /* Destroy the thread object. */ CloseHandle( this->thread ); return 0; #else Lua_LLThread_child *child; int rc; /* then join the thread. */ rc = pthread_join(this->thread, (void **)&(child)); if(rc == 0) { this->state |= TSTATE_JOINED; /* if the child thread returns NULL, then it freed the child object. */ this->child = child; } return rc; #endif } int llthread_copy_table_from_cache(llthread_copy_state *state, int idx) { void *ptr; /* convert table to pointer for lookup in cache. */ ptr = (void *)lua_topointer(state->from_L, idx); if(ptr == NULL) return 0; /* can't convert to pointer. */ /* check if we need to create the cache. */ if(!state->has_cache) { lua_newtable(state->to_L); lua_replace(state->to_L, state->cache_idx); state->has_cache = 1; } lua_pushlightuserdata(state->to_L, ptr); lua_rawget(state->to_L, state->cache_idx); if(lua_isnil(state->to_L, -1)) { /* not in cache. */ lua_pop(state->to_L, 1); /* create new table and add to cache. */ lua_newtable(state->to_L); lua_pushlightuserdata(state->to_L, ptr); lua_pushvalue(state->to_L, -2); lua_rawset(state->to_L, state->cache_idx); return 0; } /* found table in cache. */ return 1; } int llthread_copy_value(llthread_copy_state *state, int depth, int idx) { const char *str; size_t str_len; int kv_pos; /* Maximum recursive depth */ if(++depth > MAX_COPY_DEPTH) { return luaL_error(state->from_L, "Hit maximum copy depth (%d > %d).", depth, MAX_COPY_DEPTH); } /* only support string/number/boolean/nil/table/lightuserdata. */ switch(lua_type(state->from_L, idx)) { case LUA_TNIL: lua_pushnil(state->to_L); break; case LUA_TNUMBER: lua_pushnumber(state->to_L, lua_tonumber(state->from_L, idx)); break; case LUA_TBOOLEAN: lua_pushboolean(state->to_L, lua_toboolean(state->from_L, idx)); break; case LUA_TSTRING: str = lua_tolstring(state->from_L, idx, &(str_len)); lua_pushlstring(state->to_L, str, str_len); break; case LUA_TLIGHTUSERDATA: lua_pushlightuserdata(state->to_L, lua_touserdata(state->from_L, idx)); break; case LUA_TTABLE: /* make sure there is room on the new state for 3 values (table,key,value) */ if(!lua_checkstack(state->to_L, 3)) { return luaL_error(state->from_L, "To stack overflow!"); } /* make room on from stack for key/value pairs. */ luaL_checkstack(state->from_L, 2, "From stack overflow!"); /* check cache for table. */ if(llthread_copy_table_from_cache(state, idx)) { /* found in cache don't need to copy table. */ break; } lua_pushnil(state->from_L); while (lua_next(state->from_L, idx) != 0) { /* key is at (top - 1), value at (top), but we need to normalize these * to positive indices */ kv_pos = lua_gettop(state->from_L); /* copy key */ llthread_copy_value(state, depth, kv_pos - 1); /* copy value */ llthread_copy_value(state, depth, kv_pos); /* Copied key and value are now at -2 and -1 in state->to_L. */ lua_settable(state->to_L, -3); /* Pop value for next iteration */ lua_pop(state->from_L, 1); } break; case LUA_TFUNCTION: case LUA_TUSERDATA: case LUA_TTHREAD: default: if (state->is_arg) { return luaL_argerror(state->from_L, idx, "function/userdata/thread types un-supported."); } else { /* convert un-supported types to an error string. */ lua_pushfstring(state->to_L, "Un-supported value: %s: %p", lua_typename(state->from_L, lua_type(state->from_L, idx)), lua_topointer(state->from_L, idx)); } } return 1; } int llthread_copy_values(lua_State *from_L, lua_State *to_L, int idx, int top, int is_arg) { llthread_copy_state state; int nvalues = 0; int n; nvalues = (top - idx) + 1; /* make sure there is room on the new state for the values. */ if(!lua_checkstack(to_L, nvalues + 1)) { return luaL_error(from_L, "To stack overflow!"); } /* setup copy state. */ state.from_L = from_L; state.to_L = to_L; state.is_arg = is_arg; state.has_cache = 0; /* don't create cache table unless it is needed. */ lua_pushnil(to_L); state.cache_idx = lua_gettop(to_L); nvalues = 0; for(n = idx; n <= top; n++) { llthread_copy_value(&state, 0, n); ++nvalues; } /* remove cache table. */ lua_remove(to_L, state.cache_idx); return nvalues; } int llthread_push_args(lua_State *L, Lua_LLThread_child *child, int idx, int top) { return llthread_copy_values(L, child->L, idx, top, 1 /* is_arg */); } int llthread_push_results(lua_State *L, Lua_LLThread_child *child, int idx, int top) { return llthread_copy_values(child->L, L, idx, top, 0 /* is_arg */); } Lua_LLThread *llthread_create(lua_State *L, const char *code, size_t code_len, int callbackFunctionRefID) { Lua_LLThread *this; Lua_LLThread_child *child; const char *str; size_t str_len; int rc; int top; this = llthread_new(); child = this->child; child->callback = callbackFunctionRefID; /* load Lua code into child state. */ rc = luaL_loadbuffer(child->L, code, code_len, code); if(rc != 0) { /* copy error message to parent state. */ str = lua_tolstring(child->L, -1, &(str_len)); if(str != NULL) { lua_pushlstring(L, str, str_len); } else { /* non-string error message. */ lua_pushfstring(L, "luaL_loadbuffer() failed to load Lua code: rc=%d", rc); } llthread_destroy(this); lua_error(L); return NULL; } /* copy extra args from main state to child state. */ top = lua_gettop(L); /* Push all args after the Lua code. */ llthread_push_args(L, child, 2, top); return this; } /* method: delete */ int Lua_LLThread__delete__meth(lua_State *L) { int this_flags_idx1 = 0; Lua_LLThread * this_idx1 = obj_type_Lua_LLThread_delete(L,1,&(this_flags_idx1)); Lua_LLThread_child *child; if(!(this_flags_idx1 & OBJ_UDATA_FLAG_OWN)) { return 0; } /* if the thread has been started and has not been detached/joined. */ if((this_idx1->state & TSTATE_STARTED) == TSTATE_STARTED && (this_idx1->state & (TSTATE_DETACHED|TSTATE_JOINED)) == 0) { /* then join the thread. */ llthread_join(this_idx1); child = this_idx1->child; if(child && child->status != 0) { const char *err_msg = lua_tostring(child->L, -1); fprintf(stderr, "Error from non-joined thread: %s\n", err_msg); fflush(stderr); } } llthread_destroy(this_idx1); return 0; } /* method: start */ int Lua_LLThread__start__meth(lua_State *L) { Lua_LLThread * this_idx1 = obj_type_Lua_LLThread_check(L,1); bool start_detached_idx2 = lua_toboolean(L,2); bool res_idx1 = 0; char buf[ERROR_LEN]; int rc; if(this_idx1->state != TSTATE_NONE) { lua_pushboolean(L, 0); /* false */ lua_pushliteral(L, "Thread already started."); return 2; } if((rc = llthread_start(this_idx1, start_detached_idx2)) != 0) { lua_pushboolean(L, 0); /* false */ strerror_r(errno, buf, ERROR_LEN); lua_pushstring(L, buf); return 2; } res_idx1 = true; lua_pushboolean(L, res_idx1); return 1; } /* method: join */ int Lua_LLThread__join__meth(lua_State *L) { Lua_LLThread * this_idx1 = obj_type_Lua_LLThread_check(L,1); bool res_idx1 = 0; const char * err_msg_idx2 = NULL; Lua_LLThread_child *child; char buf[ERROR_LEN]; int top; int rc; if((this_idx1->state & TSTATE_STARTED) == 0) { lua_pushboolean(L, 0); /* false */ lua_pushliteral(L, "Can't join a thread that hasn't be started."); return 2; } if((this_idx1->state & TSTATE_DETACHED) == TSTATE_DETACHED) { lua_pushboolean(L, 0); /* false */ lua_pushliteral(L, "Can't join a thread that has been detached."); return 2; } if((this_idx1->state & TSTATE_JOINED) == TSTATE_JOINED) { lua_pushboolean(L, 0); /* false */ lua_pushliteral(L, "Can't join a thread that has already been joined."); return 2; } /* join the thread. */ rc = llthread_join(this_idx1); child = this_idx1->child; /* Push all results after the Lua code. */ if(rc == 0 && child) { if(child->status != 0) { const char *err_msg = lua_tostring(child->L, -1); lua_pushboolean(L, 0); lua_pushfstring(L, "Error from child thread: %s", err_msg); return 2; } else { lua_pushboolean(L, 1); } top = lua_gettop(child->L); /* return results to parent thread. */ llthread_push_results(L, child, 2, top); return top; } else { res_idx1 = false; err_msg_idx2 = buf; strerror_r(errno, buf, ERROR_LEN); } lua_pushboolean(L, res_idx1); lua_pushstring(L, err_msg_idx2); return 2; } /* method: new */ int llthreads__new__func(lua_State *L) { static int functionRefIDCount = 0; ++functionRefIDCount; size_t lua_code_len_idx1; const char * lua_code_idx1 = luaL_checklstring(L,1,&(lua_code_len_idx1)); int callback = 0; if (lua_isfunction(L, 2)) { /* reference to callback function */ lua_pushstring(L, LLTHREAD_REFID_FUNC_MAPPING); lua_rawget(L, LUA_REGISTRYINDEX); /* stack: code callback ... refid_func */ lua_pushinteger(L, functionRefIDCount); /* stack: code callback ... refid_func refid */ lua_pushvalue(L, 2); /* stack: code callback ... refid_func refid callback */ lua_rawset(L, -3); /* refid_func[refid] = func, stack: code callback ... refid_func */ lua_pop(L, 1); /* stack: code callback ... */ lua_remove(L, 2); /* stack: code ... */ callback = functionRefIDCount; } int this_flags_idx1 = OBJ_UDATA_FLAG_OWN; Lua_LLThread * this_idx1; this_idx1 = llthread_create(L, lua_code_idx1, lua_code_len_idx1, callback); obj_type_Lua_LLThread_push(L, this_idx1, this_flags_idx1); return 1; } static const luaL_reg obj_Lua_LLThread_pub_funcs[] = { {NULL, NULL} }; static const luaL_reg obj_Lua_LLThread_methods[] = { {"start", Lua_LLThread__start__meth}, {"join", Lua_LLThread__join__meth}, {NULL, NULL} }; static const luaL_reg obj_Lua_LLThread_metas[] = { {"__gc", Lua_LLThread__delete__meth}, {"__tostring", obj_udata_default_tostring}, {"__eq", obj_udata_default_equal}, {NULL, NULL} }; static const obj_base obj_Lua_LLThread_bases[] = { {-1, NULL} }; static const obj_field obj_Lua_LLThread_fields[] = { {NULL, 0, 0, 0} }; static const obj_const obj_Lua_LLThread_constants[] = { {NULL, NULL, 0.0 , 0} }; static const luaL_reg llthreads_function[] = { {"new", llthreads__new__func}, {NULL, NULL} }; static const obj_const llthreads_constants[] = { {NULL, NULL, 0.0 , 0} }; static const reg_sub_module reg_sub_modules[] = { { &(obj_type_Lua_LLThread), 0, obj_Lua_LLThread_pub_funcs, obj_Lua_LLThread_methods, obj_Lua_LLThread_metas, obj_Lua_LLThread_bases, obj_Lua_LLThread_fields, obj_Lua_LLThread_constants}, {NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL} }; static const luaL_Reg submodule_libs[] = { {NULL, NULL} }; static void create_object_instance_cache(lua_State *L) { lua_pushlightuserdata(L, obj_udata_weak_ref_key); /* key for weak table. */ lua_rawget(L, LUA_REGISTRYINDEX); /* check if weak table exists already. */ if(!lua_isnil(L, -1)) { lua_pop(L, 1); /* pop weak table. */ return; } lua_pop(L, 1); /* pop nil. */ /* create weak table for object instance references. */ lua_pushlightuserdata(L, obj_udata_weak_ref_key); /* key for weak table. */ lua_newtable(L); /* weak table. */ lua_newtable(L); /* metatable for weak table. */ lua_pushliteral(L, "__mode"); lua_pushliteral(L, "v"); lua_rawset(L, -3); /* metatable.__mode = 'v' weak values. */ lua_setmetatable(L, -2); /* add metatable to weak table. */ lua_rawset(L, LUA_REGISTRYINDEX); /* create reference to weak table. */ } int luaopen_llthreads(lua_State *L) { parent_L = L; const reg_sub_module *reg = reg_sub_modules; const luaL_Reg *submodules = submodule_libs; int priv_table = -1; /* reference to callback function */ lua_pushstring(L, LLTHREAD_REFID_FUNC_MAPPING); lua_newtable(L); lua_rawset(L, LUA_REGISTRYINDEX); #if LUAJIT_FFI /* private table to hold reference to object metatables. */ lua_newtable(L); priv_table = lua_gettop(L); #endif /* create object cache. */ create_object_instance_cache(L); /* module table. */ luaL_register(L, "llthreads", llthreads_function); /* register module constants. */ obj_type_register_constants(L, llthreads_constants, -1); for(; submodules->func != NULL ; submodules++) { lua_pushcfunction(L, submodules->func); lua_pushstring(L, submodules->name); lua_call(L, 1, 0); } /* register objects */ for(; reg->type != NULL ; reg++) { lua_newtable(L); /* create public API table for object. */ lua_pushvalue(L, -1); /* dup. object's public API table. */ lua_setfield(L, -3, reg->type->name); /* module[""] = */ #if REG_OBJECTS_AS_GLOBALS lua_pushvalue(L, -1); /* dup value. */ lua_setglobal(L, reg->type->name); /* global: = */ #endif obj_type_register(L, reg, priv_table); } #if LUAJIT_FFI nobj_try_loading_ffi(L, "llthreads", llthreads_ffi_lua_code, llthreads_ffi_export, priv_table); #endif return 1; }