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 f035c56..5c7a62b 100644 Binary files a/sdcard/3ds/ctruLua/icon.png and b/sdcard/3ds/ctruLua/icon.png differ diff --git a/source/socket.c b/source/socket.c index 947a035..7be3097 100644 --- a/source/socket.c +++ b/source/socket.c @@ -130,6 +130,8 @@ static int socket_tcp(lua_State *L) { userdata->isSSL = 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); +}