From 0522b7dac4dc6032186f5ffa2a34eec781fc7d99 Mon Sep 17 00:00:00 2001 From: George Zhao Date: Wed, 13 Nov 2024 16:04:00 +0800 Subject: [PATCH] add test for external loop --- CMakeLists.txt | 34 ++++++ Makefile | 4 + src/test.c | 155 ++++++++++++++++++++++++++++ tests/manual-test-external-loop.lua | 60 +++++++++++ 4 files changed, 253 insertions(+) create mode 100644 src/test.c create mode 100644 tests/manual-test-external-loop.lua diff --git a/CMakeLists.txt b/CMakeLists.txt index 601deab2..d5df1afe 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -157,6 +157,7 @@ else (LUA) else(USE_LUAJIT) include(deps/lua.cmake) set(LUA_INCLUDE_DIR deps/lua) + set(LUA_LIBRARIES lualib) endif (USE_LUAJIT) include_directories(${LUA_INCLUDE_DIR}) set(LUA_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/${LUA_INCLUDE_DIR}) @@ -261,6 +262,39 @@ foreach(TARGET_NAME ${ACTIVE_TARGETS}) endif() endforeach() +if(LUAJIT_LIBRARIES OR LUA_LIBRARIES) + add_executable(test src/test.c src/luv.c) + if(WIN32 OR CYGWIN) + if (LUA) + target_link_libraries(test ${LIBUV_LIBRARIES} ${LUA_LIBRARIES}) + else (LUA) + if (USE_LUAJIT) + target_link_libraries(test ${LIBUV_LIBRARIES} ${LUAJIT_LIBRARIES}) + else (USE_LUAJIT) + if (LUA_BUILD_TYPE STREQUAL System) + target_link_libraries(test ${LIBUV_LIBRARIES} ${LUA_LIBRARIES}) + else (LUA_BUILD_TYPE STREQUAL System) + target_link_libraries(test ${LIBUV_LIBRARIES} lualib) + endif (LUA_BUILD_TYPE STREQUAL System) + endif (USE_LUAJIT) + endif (LUA) + elseif("${CMAKE_SYSTEM_NAME}" MATCHES "Linux") + if (USE_LUAJIT) + target_link_libraries(test ${LIBUV_LIBRARIES} ${LUAJIT_LIBRARIES} rt) + else () + target_link_libraries(test ${LIBUV_LIBRARIES} ${LUA_LIBRARIES} rt) + endif () + else() + if (USE_LUAJIT) + target_link_libraries(test ${LIBUV_LIBRARIES} ${LUAJIT_LIBRARIES}) + else () + target_link_libraries(test ${LIBUV_LIBRARIES} ${LUA_LIBRARIES}) + endif () + endif() +else() + message(STATUS "Lua/LuaJIT libraries not found, test not built.") +endif() + if (NOT LUA) if (BUILD_MODULE) if (WIN32) diff --git a/Makefile b/Makefile index 0a0bcf38..ddd20c07 100644 --- a/Makefile +++ b/Makefile @@ -70,6 +70,10 @@ clean: test: luv ${LUABIN} tests/run.lua + @if [ -f $(BUILD_DIR)/test ]; then \ + echo "$(BUILD_DIR)/test exists."; \ + $(BUILD_DIR)/test tests/manual-test-external-loop.lua; \ + fi reset: git submodule update --init --recursive && \ diff --git a/src/test.c b/src/test.c new file mode 100644 index 00000000..cf719f52 --- /dev/null +++ b/src/test.c @@ -0,0 +1,155 @@ +#include "luv.h" +#include "util.h" +#include "lhandle.h" +#include +#include +#include +#include +#include +#include +#include + +#if (LUA_VERSION_NUM < 503) +#include "compat-5.3.h" +#endif + +#define ASSERT_BASE(a, operator, b, type, conv) \ + do { \ + volatile type eval_a = (type) (a); \ + volatile type eval_b = (type) (b); \ + if (!(eval_a operator eval_b)) { \ + fprintf(stderr, \ + "Assertion failed in %s on line %d: `%s %s %s` " \ + "(%"conv" %s %"conv")\n", \ + __FILE__, \ + __LINE__, \ + #a, \ + #operator, \ + #b, \ + eval_a, \ + #operator, \ + eval_b); \ + abort(); \ + } \ + } while (0) + +#define ASSERT_OK(a) ASSERT_BASE(a, ==, 0, int64_t, PRId64) + +static lua_State *vm_acquire(uv_loop_t* loop) { + lua_State* L = luaL_newstate(); + if (L == NULL) + return L; + + // Add in the lua standard and compat libraries + luaL_openlibs(L); + + if (loop) + luv_set_loop(L, loop); + + // Get package.loaded so that we can load modules + lua_getglobal(L, "package"); + lua_getfield(L, -1, "loaded"); + + // load luv into uv in advance so that the metatables for async work. + luaL_requiref(L, "uv", luaopen_luv, 0); + lua_setfield(L, -2, "luv"); + + // remove package.loaded + lua_remove(L, -1); + + return L; +} + +static void vm_release(lua_State* L) { lua_close(L); } + + +static lua_State* luv_thread_acquire_vm() { + lua_State* L = vm_acquire(NULL); /* create state */ + + lua_pushboolean(L, 1); + lua_setglobal(L, "_THREAD"); + + return L; +} + +static void luv_thread_release_vm(lua_State* L) { + vm_release(L); +} + +static int pmain(lua_State* L) +{ + const char* fn = luaL_checkstring(L, 1); + return luaL_dofile(L, fn); +} + +static void walk_cb(uv_handle_t* handle, void* arg) { + luv_ctx_t* ctx = (luv_ctx_t*)arg; + lua_State* L = ctx->L; + luv_handle_t* data = (luv_handle_t*)handle->data; + + // Skip foreign handles (shared event loop) + lua_rawgeti(L, LUA_REGISTRYINDEX, ctx->ht_ref); + lua_rawgetp(L, -1, data); + +#if LUV_UV_VERSION_GEQ(1, 19, 0) + printf("handle(%s:%p) can be process by %s\n", + uv_handle_type_name(handle->type), handle, + (lua_isnil(L, -1) ? "C" : "Lua" )); +#else + printf("handle(%d:%p) can be process by %s\n", + handle->type, handle, (lua_isnil(L, -1) ? "C" : "Lua" )); +#endif + lua_pop(L, 2); +} + +static void call_walk(uv_timer_t* handle) { + luv_ctx_t* ctx = (luv_ctx_t*)handle->data; + uv_walk(ctx->loop, walk_cb, ctx); + uv_close((uv_handle_t*)handle, NULL); +} + +int main(int argc, char *argv[]) { + + lua_State* L; + int index; + int res; + int errfunc; + uv_loop_t loop; + uv_timer_t timer_handle; + + if (argc != 2) { + printf("usage: %s luafile\n", argv[0]); + return 1; + }; + + // Hooks in libuv that need to be done in main. + argv = uv_setup_args(argc, argv); + uv_loop_init(&loop); + + luv_set_thread_cb(luv_thread_acquire_vm, luv_thread_release_vm); + // Create the lua state. + L = vm_acquire(&loop); + if (L == NULL) { + fprintf(stderr, "luaL_newstate has failed\n"); + return 1; + }; + + + ASSERT_OK(uv_timer_init(&loop, &timer_handle)); + timer_handle.data = luv_context(L); + ASSERT_OK(uv_timer_start(&timer_handle, (uv_timer_cb) call_walk, 800, 0)); + uv_unref((uv_handle_t*) &timer_handle); + + lua_pushcfunction(L, pmain); + lua_pushstring(L, argv[1]); + res = lua_pcall(L, 1, 0, 0); + if (res != 0) { + fprintf(stderr, "lua error: code(%d) with message: %s", res, lua_tostring(L, -1)); + lua_pop(L, 1); + } + uv_run(&loop, UV_RUN_DEFAULT); + + vm_release(L); + uv_loop_close(&loop); + return res; +} diff --git a/tests/manual-test-external-loop.lua b/tests/manual-test-external-loop.lua new file mode 100644 index 00000000..053df199 --- /dev/null +++ b/tests/manual-test-external-loop.lua @@ -0,0 +1,60 @@ +local uv = require('uv') + +local function setTimeout(callback, ms) + local timer = uv.new_timer() + timer:start(ms, 0, function() + uv.walk(function(...) + print('uv.walk() in Main Lua', ...) + end) + timer:stop() + timer:close() + callback() + end) + return timer +end + +setTimeout(function() + print('Main Lua done') +end, 1000) + +local delay = 100 +uv.update_time() +local before = uv.now() +local thread = uv.new_thread(function(delay) + local uv = require('uv') + local t1 = uv.thread_self() + uv.sleep(delay) + local t2 = uv.thread_self() + assert(t1:equal(t2)) + assert(tostring(t1)==tostring(t2)) + _G.print('Runing', uv.thread_self()) + + assert(_THREAD) + + local function setTimeout(callback, ms) + local timer = uv.new_timer() + timer:start(ms, 0, function() + uv.walk(function(...) + print('in thread Lua', ...) + end) + timer:stop() + timer:close() + callback() + end) + return timer + end + + setTimeout(function() + print('thread Lua done') + end, 1000) + uv.run() +end,delay) +print('launch thread:', thread) + +uv.thread_join(thread) +-- uv.update_time() +-- local elapsed = uv.now() - before +-- assert(elapsed >= delay, "elapsed should be at least delay ") + +uv.run() +print('DONE')