diff options
Diffstat (limited to '3rdparty/luv/src/thread.c')
-rw-r--r-- | 3rdparty/luv/src/thread.c | 353 |
1 files changed, 353 insertions, 0 deletions
diff --git a/3rdparty/luv/src/thread.c b/3rdparty/luv/src/thread.c new file mode 100644 index 00000000000..cc53011e6ed --- /dev/null +++ b/3rdparty/luv/src/thread.c @@ -0,0 +1,353 @@ +/* +* Copyright 2014 The Luvit Authors. All Rights Reserved. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +*/ +#include "luv.h" +#include "lthreadpool.h" + +typedef struct { + uv_thread_t handle; + char* code; + int len; + int argc; + luv_thread_arg_t arg; +} luv_thread_t; + +static luv_acquire_vm acquire_vm_cb = NULL; +static luv_release_vm release_vm_cb = NULL; + +static lua_State* luv_thread_acquire_vm() { + lua_State* L = luaL_newstate(); + + // Add in the lua standard libraries + luaL_openlibs(L); + + // Get package.loaded, so we can store uv in it. + lua_getglobal(L, "package"); + lua_getfield(L, -1, "loaded"); + lua_remove(L, -2); // Remove package + + // Store uv module definition at loaded.uv + luaopen_luv(L); + lua_setfield(L, -2, "luv"); + lua_pop(L, 1); + + return L; +} + +static void luv_thread_release_vm(lua_State* L) { + lua_close(L); +} + +static int luv_thread_arg_set(lua_State* L, luv_thread_arg_t* args, int idx, int top, int flag) +{ + int i; + idx = idx > 0 ? idx : 1; + i = idx; + while (i <= top && i <= LUV_THREAD_MAXNUM_ARG + idx) + { + luv_val_t *arg = args->argv + i - idx; + arg->type = lua_type(L, i); + switch (arg->type) + { + case LUA_TNIL: + break; + case LUA_TBOOLEAN: + arg->val.boolean = lua_toboolean(L, i); + break; + case LUA_TNUMBER: + arg->val.num = lua_tonumber(L, i); + break; + case LUA_TLIGHTUSERDATA: + arg->val.userdata = lua_touserdata(L, i); + break; + case LUA_TSTRING: + { + const char* p = lua_tolstring(L, i, &arg->val.str.len); + arg->val.str.base = malloc(arg->val.str.len); + if (arg->val.str.base == NULL) + { + perror("out of memory"); + return 0; + } + memcpy((void*)arg->val.str.base, p, arg->val.str.len); + break; + } + case LUA_TUSERDATA: + if (flag == 1) { + arg->val.userdata = luv_check_handle(L, i); + break; + } + default: + fprintf(stderr, "Error: thread arg not support type '%s' at %d", + luaL_typename(L, arg->type), i); + exit(-1); + break; + } + i++; + } + args->argc = i - idx; + return args->argc; +} + +static void luv_thread_arg_clear(luv_thread_arg_t* args) { + int i; + for (i = 0; i < args->argc; i++) + { + if (args->argv[i].type == LUA_TSTRING) + { + free((void*)args->argv[i].val.str.base); + } + } + memset(args, 0, sizeof(*args)); + args->argc = 0; +} + +static void luv_thread_setup_handle(lua_State* L, uv_handle_t* handle) { + *(void**) lua_newuserdata(L, sizeof(void*)) = handle; + +#define XX(uc, lc) case UV_##uc: \ + luaL_getmetatable(L, "uv_"#lc); \ + break; + switch (handle->type) { + UV_HANDLE_TYPE_MAP(XX) + default: + luaL_error(L, "Unknown handle type"); + } +#undef XX + + lua_setmetatable(L, -2); +} + +static int luv_thread_arg_push(lua_State* L, const luv_thread_arg_t* args) { + int i = 0; + while (i < args->argc) + { + const luv_val_t* arg = args->argv + i; + switch (arg->type) + { + case LUA_TNIL: + lua_pushnil(L); + break; + case LUA_TBOOLEAN: + lua_pushboolean(L, arg->val.boolean); + break; + case LUA_TLIGHTUSERDATA: + lua_pushlightuserdata(L, arg->val.userdata); + break; + case LUA_TNUMBER: + lua_pushnumber(L, arg->val.num); + break; + case LUA_TSTRING: + lua_pushlstring(L, arg->val.str.base, arg->val.str.len); + break; + case LUA_TUSERDATA: + luv_thread_setup_handle(L, arg->val.userdata); + break; + default: + fprintf(stderr, "Error: thread arg not support type %s at %d", + luaL_typename(L, arg->type), i + 1); + } + i++; + }; + return i; +} + +int thread_dump(lua_State* L, const void* p, size_t sz, void* B) +{ + (void)L; + luaL_addlstring((luaL_Buffer*) B, (const char*) p, sz); + return 0; +} + +static const char* luv_thread_dumped(lua_State* L, int idx, size_t* l) { + if (lua_isstring(L, idx)) { + return lua_tolstring(L, idx, l); + } else { + const char* buff = NULL; + int top = lua_gettop(L); + luaL_Buffer b; + luaL_checktype(L, idx, LUA_TFUNCTION); + lua_pushvalue(L, idx); + luaL_buffinit(L, &b); +#if LUA_VERSION_NUM>=503 + int test_lua_dump = (lua_dump(L, thread_dump, &b, 1) == 0); +#else + int test_lua_dump = (lua_dump(L, thread_dump, &b) == 0); +#endif + if (test_lua_dump) { + luaL_pushresult(&b); + buff = lua_tolstring(L, -1, l); + } else + luaL_error(L, "Error: unable to dump given function"); + lua_settop(L, top); + + return buff; + } +} + +static luv_thread_t* luv_check_thread(lua_State* L, int index) +{ + luv_thread_t* thread = luaL_checkudata(L, index, "uv_thread"); + return thread; +} + +static int luv_thread_gc(lua_State* L) { + luv_thread_t* tid = luv_check_thread(L, 1); + free(tid->code); + tid->code = NULL; + tid->len = 0; + luv_thread_arg_clear(&tid->arg); + return 0; +} + +static int luv_thread_tostring(lua_State* L) +{ + luv_thread_t* thd = luv_check_thread(L, 1); + lua_pushfstring(L, "uv_thread_t: %p", thd->handle); + return 1; +} + +static void luv_thread_cb(void* varg) { + luv_thread_t* thd = (luv_thread_t*)varg; + lua_State* L = acquire_vm_cb(); + if (luaL_loadbuffer(L, thd->code, thd->len, "=thread") == 0) + { + int top = lua_gettop(L); + int i = luv_thread_arg_push(L, &thd->arg); + + for (i = 0; i < thd->arg.argc; i++) { + if (thd->arg.argv[i].type == LUA_TUSERDATA) { + lua_pushlightuserdata(L, thd->arg.argv[i].val.userdata); + lua_pushvalue(L, top + i + 1); + lua_rawset(L, LUA_REGISTRYINDEX); + } + } + + if (lua_pcall(L, i, 0, 0)) { + fprintf(stderr, "Uncaught Error in thread: %s\n", lua_tostring(L, -1)); + } + + for (i = 0; i < thd->arg.argc; i++) { + if (thd->arg.argv[i].type == LUA_TUSERDATA) { + lua_pushlightuserdata(L, thd->arg.argv[i].val.userdata); + lua_rawget(L, LUA_REGISTRYINDEX); + lua_pushnil(L); + lua_setmetatable(L, -2); + lua_pop(L, 1); + + lua_pushlightuserdata(L, thd->arg.argv[i].val.userdata); + lua_pushnil(L); + lua_rawset(L, LUA_REGISTRYINDEX); + } + } + + } else { + fprintf(stderr, "Uncaught Error: %s\n", lua_tostring(L, -1)); + } + release_vm_cb(L); +} + +static int luv_new_thread(lua_State* L) { + int ret; + size_t len; + const char* buff; + luv_thread_t* thread; + thread = lua_newuserdata(L, sizeof(*thread)); + memset(thread, 0, sizeof(*thread)); + luaL_getmetatable(L, "uv_thread"); + lua_setmetatable(L, -2); + + buff = luv_thread_dumped(L, 1, &len); + + thread->argc = luv_thread_arg_set(L, &thread->arg, 2, lua_gettop(L) - 1, 1); + thread->len = len; + thread->code = malloc(thread->len); + memcpy(thread->code, buff, len); + + ret = uv_thread_create(&thread->handle, luv_thread_cb, thread); + if (ret < 0) return luv_error(L, ret); + + return 1; +} + +static int luv_thread_join(lua_State* L) { + luv_thread_t* tid = luv_check_thread(L, 1); + int ret = uv_thread_join(&tid->handle); + if (ret < 0) return luv_error(L, ret); + lua_pushboolean(L, 1); + return 1; +} + +static int luv_thread_self(lua_State* L) +{ + luv_thread_t* thread; + uv_thread_t t = uv_thread_self(); + thread = lua_newuserdata(L, sizeof(*thread)); + memset(thread, 0, sizeof(*thread)); + memcpy(&thread->handle, &t, sizeof(t)); + luaL_getmetatable(L, "uv_thread"); + lua_setmetatable(L, -2); + return 1; +} + +static int luv_thread_equal(lua_State* L) { + luv_thread_t* t1 = luv_check_thread(L, 1); + luv_thread_t* t2 = luv_check_thread(L, 2); + int ret = uv_thread_equal(&t1->handle, &t2->handle); + lua_pushboolean(L, ret); + return 1; +} + +/* Pause the calling thread for a number of milliseconds. */ +static int luv_thread_sleep(lua_State* L) { +#ifdef _WIN32 + DWORD msec = luaL_checkinteger(L, 1); + Sleep(msec); +#else + lua_Integer msec = luaL_checkinteger(L, 1); + usleep(msec * 1000); +#endif + return 0; +} + +static const luaL_Reg luv_thread_methods[] = { + {"equal", luv_thread_equal}, + {"join", luv_thread_join}, + {NULL, NULL} +}; + +static void luv_thread_init(lua_State* L) { + luaL_newmetatable(L, "uv_thread"); + lua_pushcfunction(L, luv_thread_tostring); + lua_setfield(L, -2, "__tostring"); + lua_pushcfunction(L, luv_thread_equal); + lua_setfield(L, -2, "__eq"); + lua_pushcfunction(L, luv_thread_gc); + lua_setfield(L, -2, "__gc"); + lua_newtable(L); + luaL_setfuncs(L, luv_thread_methods, 0); + lua_setfield(L, -2, "__index"); + lua_pop(L, 1); + + if (acquire_vm_cb == NULL) acquire_vm_cb = luv_thread_acquire_vm; + if (release_vm_cb == NULL) release_vm_cb = luv_thread_release_vm; +} + +LUALIB_API void luv_set_thread_cb(luv_acquire_vm acquire, luv_release_vm release) +{ + acquire_vm_cb = acquire; + release_vm_cb = release; +} |