Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

try safe handle external handle #733

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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})
Expand Down Expand Up @@ -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)
Expand Down
4 changes: 4 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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 && \
Expand Down
8 changes: 8 additions & 0 deletions src/handle.c
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,14 @@ static int luv_close(lua_State* L) {
static void luv_handle_free(uv_handle_t* handle) {
luv_handle_t* data = (luv_handle_t*)handle->data;
if (data) {
luv_ctx_t* ctx = data->ctx;
lua_State* L = ctx->L;
// release handle in ht_ref
lua_rawgeti(L, LUA_REGISTRYINDEX, ctx->ht_ref);
lua_pushnil(L);
lua_rawsetp(L, -2, data);
lua_pop(L, 1);

if (data->extra_gc)
data->extra_gc(data->extra);
free(data);
Expand Down
7 changes: 7 additions & 0 deletions src/lhandle.c
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,13 @@ static luv_handle_t* luv_setup_handle(lua_State* L, luv_ctx_t* ctx) {
data->ctx = ctx;
data->extra = NULL;
data->extra_gc = NULL;

// record data in ht_ref
lua_rawgeti(L, LUA_REGISTRYINDEX, ctx->ht_ref);
lua_pushboolean(L, 1);
lua_rawsetp(L, -2, data);
lua_pop(L, 1);

return data;
}

Expand Down
14 changes: 12 additions & 2 deletions src/loop.c
Original file line number Diff line number Diff line change
Expand Up @@ -90,9 +90,18 @@ static int luv_update_time(lua_State* L) {
}

static void luv_walk_cb(uv_handle_t* handle, void* arg) {
lua_State* L = (lua_State*)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 (lua_isnil(L, -1)) {
lua_pop(L, 2);
return;
}

// Sanity check
// Most invalid values are large and refs are small, 0x1000000 is arbitrary.
assert(data && data->ref < 0x1000000);
Expand All @@ -103,8 +112,9 @@ static void luv_walk_cb(uv_handle_t* handle, void* arg) {
}

static int luv_walk(lua_State* L) {
luv_ctx_t* ctx = luv_context(L);
luaL_checktype(L, 1, LUA_TFUNCTION);
uv_walk(luv_loop(L), luv_walk_cb, L);
uv_walk(luv_loop(L), luv_walk_cb, ctx);
return 0;
}

Expand Down
3 changes: 3 additions & 0 deletions src/luv.c
Original file line number Diff line number Diff line change
Expand Up @@ -770,6 +770,9 @@ LUALIB_API luv_ctx_t* luv_context(lua_State* L) {
ctx = (luv_ctx_t*)lua_newuserdata(L, sizeof(*ctx));
memset(ctx, 0, sizeof(*ctx));
lua_rawset(L, LUA_REGISTRYINDEX);
// create table to contain internal handle
lua_newtable(L);
ctx->ht_ref = luaL_ref(L, LUA_REGISTRYINDEX);
} else {
ctx = (luv_ctx_t*)lua_touserdata(L, -1);
}
Expand Down
2 changes: 2 additions & 0 deletions src/luv.h
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,8 @@ typedef struct {
luv_CFcpcall thrd_cpcall; /* luv thread c function in protected mode*/

int mode; /* the mode used to run the loop (-1 if not running) */
int ht_ref; /* bookkeeping: maintain table of luv_handle_t pointers,
to distinguish between internal and external handles */

void* extra; /* extra data */
} luv_ctx_t;
Expand Down
155 changes: 155 additions & 0 deletions src/test.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
#include "luv.h"
#include "util.h"
#include "lhandle.h"
#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>
#include <uv.h>
#include <stdint.h>
#include <string.h>
#include <inttypes.h>

#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;
}
60 changes: 60 additions & 0 deletions tests/manual-test-external-loop.lua
Original file line number Diff line number Diff line change
@@ -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')
Loading