From d3ea68f3d715c62ddbf40c337660836c440f38c7 Mon Sep 17 00:00:00 2001 From: Firew0lf Date: Sat, 17 Sep 2016 19:13:50 +0200 Subject: [PATCH] Added a way to communicate easily between threads. The API _will_ change and be unified. Also fixed the icon in the example. --- sdcard/3ds/ctruLua/examples/example.lua | 2 +- sdcard/3ds/ctruLua/icon.png | Bin 1221 -> 1080 bytes source/socket.c | 4 + source/thread.c | 257 +++++++++++++++++++++++- 4 files changed, 257 insertions(+), 6 deletions(-) diff --git a/sdcard/3ds/ctruLua/examples/example.lua b/sdcard/3ds/ctruLua/examples/example.lua index 67ed406..7e52a0f 100644 --- a/sdcard/3ds/ctruLua/examples/example.lua +++ b/sdcard/3ds/ctruLua/examples/example.lua @@ -39,7 +39,7 @@ while ctr.run() do hid.read() local keys = hid.keys() - if keys.down.start then return end + if keys.down.start then break end if keys.held.right then x = x + 1 end if keys.held.left then x = x - 1 end diff --git a/sdcard/3ds/ctruLua/icon.png b/sdcard/3ds/ctruLua/icon.png index f035c569cd89ff4b8459816f3caca0d15c34a1c7..5c7a62bbacadbb208110faac588d587af6806e44 100644 GIT binary patch delta 1049 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 FV1nisSSL = false; fcntl(userdata->socket, F_SETFL, fcntl(userdata->socket, F_GETFL, 0)|O_NONBLOCK); + SOCU_AddGlobalSocket(userdata->socket); + return 1; } @@ -155,6 +157,8 @@ static int socket_udp(lua_State *L) { userdata->addr.sin_family = AF_INET; fcntl(userdata->socket, F_SETFL, fcntl(userdata->socket, F_GETFL, 0)|O_NONBLOCK); + SOCU_AddGlobalSocket(userdata->socket); + return 1; } diff --git a/source/thread.c b/source/thread.c index 88dde8c..ada84fc 100644 --- a/source/thread.c +++ b/source/thread.c @@ -17,19 +17,46 @@ The `thread` module. #include void load_ctr_lib(lua_State *L); +void load_pool_lib(lua_State *L); + +typedef struct { + enum {INT, NUM, STR, BOL, NIL} type; + union { + lua_Integer integer; + lua_Number number; + char* string; + bool boolean; + }; +} poolEntry; typedef struct { Thread thread; const char *code; char *error; - void* pool; - int poolSize; // in bytes + poolEntry* pool; + int poolSize; // in entries } thread_userdata; void entryPoint(void *thread) { + if (((thread_userdata*)thread)->poolSize>0) { + ((thread_userdata*)thread)->pool = malloc(((thread_userdata*)thread)->poolSize*sizeof(poolEntry)); + if (((thread_userdata*)thread)->pool == NULL) { + ((thread_userdata*)thread)->error = "Out of memory."; + threadExit(-1); + } else { + for (int i=0;i<((thread_userdata*)thread)->poolSize;i++) { + ((thread_userdata*)thread)->pool[i].type=NIL; + } + } + } + lua_State *T = luaL_newstate(); luaL_openlibs(T); load_ctr_lib(T); + load_pool_lib(T); + + lua_pushinteger(T, (u32)((thread_userdata*)thread)); + lua_setfield(T, LUA_REGISTRYINDEX, "LThreadSelf"); if (luaL_dostring(T, ((thread_userdata*)thread)->code)) { const char* lerror = luaL_checkstring(T, -1); @@ -42,6 +69,14 @@ void entryPoint(void *thread) { exitCode = lua_tointeger(T, -1); } lua_close(T); + if (((thread_userdata*)thread)->poolSize>0) { + for (int i=0;i<((thread_userdata*)thread)->poolSize;i++) { + if (((thread_userdata*)thread)->pool[i].type == STR) { + free(((thread_userdata*)thread)->pool[i].string); + } + } + free(((thread_userdata*)thread)->pool); + } threadExit(exitCode); } @@ -62,6 +97,7 @@ static int thread_setCpuLimit(lua_State *L) { Start a new thread. @function start @tparam string code Lua code to load in the new thread. May not work with dumped functions. +@tparam[opt=0] number poolSize size of the RAM pool for the thread (used to communicate with the main thread) @tparam[opt=0] number cpu must be >= 0 and < 2 for 3ds or < 4 for new3ds @tparam[opt=0x27] number priority must be > 0x18 and < 0x3f; the lower is higher. @tparam[opt=0x100000] number stacksize size of the stack, increase it in case of OoM @@ -69,14 +105,16 @@ Start a new thread. */ static int thread_start(lua_State *L) { const char* code = luaL_checkstring(L, 1); - s32 processor = luaL_optinteger(L, 2, 0); - s32 priority = luaL_optinteger(L, 3, 0x27); - size_t stackSize = luaL_optinteger(L, 4, 0x100000); + s32 poolSize = luaL_optinteger(L, 2, 0); + s32 processor = luaL_optinteger(L, 3, 0); + s32 priority = luaL_optinteger(L, 4, 0x27); + size_t stackSize = luaL_optinteger(L, 5, 0x100000); thread_userdata *thread = lua_newuserdata(L, sizeof(thread_userdata*)); luaL_getmetatable(L, "LThread"); lua_setmetatable(L, -2); + thread->poolSize = poolSize; thread->code = code; thread->error = NULL; thread->thread = threadCreate(entryPoint, thread, stackSize, priority, processor, true); @@ -130,10 +168,111 @@ static int thread_destroy(lua_State *L) { threadFree(thread->thread); free(thread->error); + if (thread->poolSize > 0) { + for (int i=0;ipoolSize;i++) { + if (thread->pool[i].type == STR) { + free(thread->pool[i].string); + } + } + free(thread->pool); + } return 0; } +static const struct luaL_Reg thread_methods[]; + +static int thread___index(lua_State *L) { + thread_userdata* thread = luaL_checkudata(L, 1, "LThread"); + + poolEntry* pool = thread->pool; + + if (lua_isstring(L, 2)) { + const char *mname = lua_tostring(L, 2); + for (u8 i=0;thread_methods[i].name;i++) { + if (strcmp(thread_methods[i].name, mname) == 0) { + lua_pushcfunction(L, thread_methods[i].func); + return 1; + } + } + lua_pushnil(L); + return 1; + } else if (lua_isinteger(L, 2)) { + u32 addr = lua_tointeger(L, 2); + if (addr > thread->poolSize || addr < 1) { + lua_pushnil(L); + return 1; + } + switch (pool[addr+1].type) { + case INT: + lua_pushinteger(L, pool[addr+1].integer); + break; + case NUM: + lua_pushnumber(L, pool[addr+1].number); + break; + case STR: + lua_pushstring(L, pool[addr+1].string); + break; + case BOL: + lua_pushboolean(L, pool[addr+1].boolean); + break; + + default: + lua_pushnil(L); + break; + } + return 1; + } else { + lua_pushnil(L); + } + return 1; +} + +static int thread___newindex(lua_State *L) { + thread_userdata* thread = luaL_checkudata(L, 1, "LThread"); + + poolEntry* pool = thread->pool; + + if (lua_isinteger(L, 2)) { + int addr = lua_tointeger(L, 2); + if (addr > thread->poolSize || addr < 1) return 0; + + if (pool[addr+1].type == STR) { + free(pool[addr+1].string); + } + + switch (lua_type(L, 3)) { + case LUA_TNUMBER: + if (lua_isinteger(L, 3)) { + pool[addr+1].type = INT; + pool[addr+1].integer = lua_tointeger(L, 3); + } else { + pool[addr+1].type = NUM; + pool[addr+1].number = lua_tonumber(L, 3); + } + break; + case LUA_TSTRING: + pool[addr+1].type = STR; + const char* str = lua_tostring(L, 3); + thread->pool[addr+1].string = malloc(strlen(str)+1); + if (pool[addr+1].string == NULL) { + luaL_error(L, "Memory allocation error"); + return 0; + } + strcpy(pool[addr+1].string, str); + break; + case LUA_TBOOLEAN: + pool[addr+1].type = BOL; + pool[addr+1].boolean = lua_toboolean(L, 3); + break; + default: // including LUA_TNIL + pool[addr+1].type = NIL; + break; + } + } + return 0; +} + static const struct luaL_Reg thread_lib[] = { {"start", thread_start}, {"setCpuLimit", thread_setCpuLimit}, @@ -145,6 +284,8 @@ static const struct luaL_Reg thread_methods[] = { {"lastError", thread_lastError}, {"destroy", thread_destroy}, {"__gc", thread_destroy}, + {"__index", thread___index}, + {"__newindex", thread___newindex}, {NULL, NULL} }; @@ -161,3 +302,109 @@ int luaopen_thread_lib(lua_State *L) { void load_thread_lib(lua_State *L) { luaL_requiref(L, "ctr.thread", luaopen_thread_lib, 0); } + +//////////////////////////////////////////////////////////////////////////////// +// Pool lib for accessing the pool from the thread // +// Libception // +//////////////////////////////////////////////////////////////////////////////// + +/*** +The `pool` module. Only accessible from a sub-thread. +@module ctr.thread.pool +@usage local pool = require("ctr.thread.pool") +*/ + +static int pool_set(lua_State *L) { + lua_getfield(L, LUA_REGISTRYINDEX, "LThreadSelf"); + thread_userdata* thread = (thread_userdata*)lua_tointeger(L, -1); + poolEntry* pool = thread->pool; + + if (lua_isinteger(L, 1)) { + int addr = lua_tointeger(L, 1); + if (addr > thread->poolSize || addr < 1) return 0; + + if (pool[addr+1].type == STR) { + free(pool[addr+1].string); + } + + switch (lua_type(L, 2)) { + case LUA_TNUMBER: + if (lua_isinteger(L, 2)) { + pool[addr+1].type = INT; + pool[addr+1].integer = lua_tointeger(L, 2); + } else { + pool[addr+1].type = NUM; + pool[addr+1].number = lua_tonumber(L, 2); + } + break; + case LUA_TSTRING: + pool[addr+1].type = STR; + const char* str = lua_tostring(L, 2); + thread->pool[addr+1].string = malloc(strlen(str)+1); + if (pool[addr+1].string == NULL) { + luaL_error(L, "Memory allocation error"); + return 0; + } + strcpy(pool[addr+1].string, str); + break; + case LUA_TBOOLEAN: + pool[addr+1].type = BOL; + pool[addr+1].boolean = lua_toboolean(L, 2); + break; + default: // including LUA_TNIL + pool[addr+1].type = NIL; + break; + } + } + return 0; +} + +static int pool_get(lua_State *L) { + if (!lua_isinteger(L, 1)) { + lua_pushnil(L); + return 1; + } + lua_getfield(L, LUA_REGISTRYINDEX, "LThreadSelf"); + thread_userdata* thread = (thread_userdata*)lua_tointeger(L, -1); + poolEntry* pool = thread->pool; + + u32 addr = lua_tointeger(L, 1); + if (addr > thread->poolSize || addr < 1) { + lua_pushnil(L); + return 1; + } + switch (pool[addr+1].type) { + case INT: + lua_pushinteger(L, pool[addr+1].integer); + break; + case NUM: + lua_pushnumber(L, pool[addr+1].number); + break; + case STR: + lua_pushstring(L, pool[addr+1].string); + break; + case BOL: + lua_pushboolean(L, pool[addr+1].boolean); + break; + + default: + lua_pushnil(L); + break; + } + return 1; +} + +static const struct luaL_Reg pool_lib[] = { + {"set", pool_set}, + {"get", pool_get}, + {NULL, NULL} +}; + +int luaopen_pool_lib(lua_State *L) { + luaL_newlib(L, pool_lib); + return 1; +} + +void load_pool_lib(lua_State *L) { + luaL_requiref(L, "ctr.thread.pool", luaopen_pool_lib, 0); +}