From ab8d2b7d64a433c53366ed8ae2bd3f21443dca16 Mon Sep 17 00:00:00 2001 From: Thomas Harning Jr Date: Mon, 11 Jun 2007 01:08:59 +0000 Subject: [PATCH] * Completed mostly working version * Moved to a mode where addevent calls a callback rather than it being instantiated within. * If the callback returns -1, then no event is ever setup * Otherwise the integer value is used to setup the event * This allows for using coroutine.wrap rather than a cooked-up wrapper * Tests work, although there are a few remaining issues: * Need to figure a good way of preserving the event object, not sure if current method is good enough, since the socket is the only anchor, and it is only held inside the coro.. circular reference, something that Lua 'handles' well. * Doing more than the maximum sockets the process is allows causes strangeness to occur in libevent.. somehow it is getting around to epoll_add which is causing valgrind to barf. --- luaevent/luaevent.lua | 54 ++++++++++++++++++++++------------------- luaevent/src/luaevent.c | 32 ++++++++++++++++++++----- luaevent/test/test.lua | 57 ++++++++++++++++++-------------------------- luaevent/test/testClient.lua | 37 ++++++++++++++++------------ 4 files changed, 100 insertions(+), 80 deletions(-) rewrite luaevent/test/test.lua (63%) rewrite luaevent/test/testClient.lua (80%) diff --git a/luaevent/luaevent.lua b/luaevent/luaevent.lua index 3b8ed18..be7f296 100644 --- a/luaevent/luaevent.lua +++ b/luaevent/luaevent.lua @@ -9,18 +9,10 @@ local EV_READ = luaevent.core.EV_READ local EV_WRITE = luaevent.core.EV_WRITE local fair = false --- Weak keys.. the keys are the client sockets -local clientTable = {} or setmetatable({}, {'__mode', 'k'}) +local hookedObjectMt = false -local function getWrapper() - local running = coroutine.running() - return function(...) - print(coroutine.running(), running) - print(debug.traceback()) - if coroutine.running() == running then return end - return select(2, coroutine.resume(running, ...)) - end -end +-- Weak keys.. the keys are the client sockets +local clientTable = setmetatable({}, {'__mode', 'k'}) function send(sock, data, start, stop) local s, err @@ -35,7 +27,6 @@ function send(sock, data, start, stop) coroutine.yield(EV_WRITE) end if s or err ~= "timeout" then return s, err, sent end - if not clientTable[sock] then clientTable[sock] = luaevent.core.addevent(sock, EV_WRITE, getWrapper()) end coroutine.yield(EV_WRITE) until false end @@ -45,7 +36,6 @@ function receive(sock, pattern, part) repeat s, err, part = sock:receive(pattern, part) if s or err ~= "timeout" then return s, err, part end - if not clientTable[sock] then clientTable[sock] = luaevent.core.addevent(sock, EV_READ, getWrapper()) end coroutine.yield(EV_READ) until false end @@ -58,7 +48,6 @@ function receivePartial(client, pattern) s, err, part = client:receive(pattern) if s or ( (type(pattern)=="number") and part~="" and part ~=nil ) or err ~= "timeout" then return s, err, part end - if not clientTable[sock] then clientTable[sock] = luaevent.core.addevent(sock, EV_READ, getWrapper()) end coroutine.yield(EV_READ) until false end @@ -66,7 +55,6 @@ function connect(sock, ...) sock:settimeout(0) local ret, err = sock:connect(...) if ret or err ~= "timeout" then return ret, err end - if not clientTable[sock] then clientTable[sock] = luaevent.core.addevent(sock, EV_WRITE, getWrapper()) end coroutine.yield(EV_WRITE) ret, err = sock:connect(...) if err == "already connected" then @@ -81,28 +69,44 @@ local function clientCoroutine(sock, handler) -- Figure out what to do ...... return handler(sock) end -local function handleClient(co, client, handler) - local ok, res, event = coroutine.resume(co, client, handler) -end + local function serverCoroutine(sock, callback) - local listenItem = luaevent.core.addevent(sock, EV_READ, getWrapper()) repeat local event = coroutine.yield(EV_READ) -- Get new socket local client = sock:accept() if client then + --cl[#cl + 1] = client client:settimeout(0) - local co = coroutine.create(clientCoroutine) - handleClient(co, client, callback) + local coFunc = coroutine.wrap(clientCoroutine) + clientTable[client] = luaevent.core.addevent(client, coFunc, client, callback) end until false end + +local oldAddEvent = luaevent.core.addevent +luaevent.core.addevent = function(...) + local item = oldAddEvent(...) + print("SETUP ITEM FOR: ", debug.getmetatable(item).getfd(item)) + if not hookedObjectMt then + hookedObjectMt = true + local mt = debug.getmetatable(item) + local oldGC = mt.__gc + mt.__gc = function(...) + print("RELEASING ITEM FOR: ", mt.getfd(...)) + return oldGC(...) + end + end + return item +end + function addserver(sock, callback) - local coro = coroutine.create(serverCoroutine) - assert(coroutine.resume(coro, sock, callback)) + local coFunc = coroutine.wrap(serverCoroutine) + clientTable[sock] = luaevent.core.addevent(sock, coFunc, sock, callback) end -function addthread(func, ...) - return coroutine.resume(coroutine.create(func), ...) +function addthread(sock, func, ...) + local coFunc = coroutine.wrap(func) + clientTable[sock] = luaevent.core.addevent(sock, coFunc, ...) end local _skt_mt = {__index = { connect = function(self, ...) diff --git a/luaevent/src/luaevent.c b/luaevent/src/luaevent.c index 311ad0e..d79345e 100644 --- a/luaevent/src/luaevent.c +++ b/luaevent/src/luaevent.c @@ -74,6 +74,12 @@ static int luaevent_cb_gc(lua_State* L) { return 0; } +static int luaevent_cb_getfd(lua_State* L) { + le_callback* arg = luaL_checkudata(L, 1, EVENT_CALLBACK_ARG_MT); + lua_pushinteger(L, arg->ev.ev_fd); + return 1; +} + int getSocketFd(lua_State* L, int idx) { int fd; luaL_checktype(L, idx, LUA_TUSERDATA); @@ -90,23 +96,35 @@ int getSocketFd(lua_State* L, int idx) { /* Expected to be called at the beginning of the coro that uses it.. Value must be kept until coro is complete.... */ -/* sock, event, callback */ +/* sock, callback */ static int luaevent_addevent(lua_State* L) { - int fd, event, callbackRef; + int fd, callbackRef; + int top, ret; le_callback* arg; fd = getSocketFd(L, 1); - event = luaL_checkinteger(L, 2); - luaL_checktype(L, 3, LUA_TFUNCTION); - lua_pushvalue(L, 3); + luaL_checktype(L, 2, LUA_TFUNCTION); + top = lua_gettop(L); + /* Preserve the callback function */ + lua_pushvalue(L, 2); callbackRef = luaL_ref(L, LUA_REGISTRYINDEX); + + /* Call the callback with all arguments after it to get the loop primed.. */ + lua_call(L, top - 2, 1); + ret = lua_tointeger(L, -1); + lua_pop(L, 1); + if(ret == -1) { /* Done, no need to setup event */ + luaL_unref(L, LUA_REGISTRYINDEX, callbackRef); + return 0; + } arg = lua_newuserdata(L, sizeof(*arg)); luaL_getmetatable(L, EVENT_CALLBACK_ARG_MT); lua_setmetatable(L, -2); arg->L = L; arg->callbackRef = callbackRef; + /* Setup event... */ - event_set(&arg->ev, fd, event | EV_PERSIST, luaevent_callback, arg); + event_set(&arg->ev, fd, ret | EV_PERSIST, luaevent_callback, arg); event_base_set(getEventBase(L), &arg->ev); event_add(&arg->ev, NULL); return 1; @@ -157,6 +175,8 @@ int luaopen_luaevent_core(lua_State* L) { luaL_newmetatable(L, EVENT_CALLBACK_ARG_MT); lua_pushcfunction(L, luaevent_cb_gc); lua_setfield(L, -2, "__gc"); + lua_pushcfunction(L, luaevent_cb_getfd); + lua_setfield(L, -2, "getfd"); lua_pop(L, 1); setEventBase(L, event_init()); diff --git a/luaevent/test/test.lua b/luaevent/test/test.lua dissimilarity index 63% index 1bcd173..f7a44da 100644 --- a/luaevent/test/test.lua +++ b/luaevent/test/test.lua @@ -1,34 +1,23 @@ --- Tests Copas with a simple Echo server --- --- Run the test file and the connect to the server by telnet on the used port --- to stop the test just send the command "quit" - -require"luaevent" -require"socket" -local function echoHandler(skt) - while true do - print(skt) - local data,ret = luaevent.receive(skt, 10) - print("GOT: ", data, ret) - if data == "quit" or ret == 'closed' then - break - end - luaevent.send(skt, data) - end - print("DONE") -end -local function setupHook(thread) - if not thread then debug.sethook(function(event) print("TRACE >: ", debug.getinfo(2, 'n').name) end, 'c') - else debug.sethook(thread, function(event) print("TRACE ", thread,">: ", debug.getinfo(2, 'n').name) end, 'c') end -end -local server = assert(socket.bind("localhost", 20000)) -server:settimeout(0) -setupHook() -local coro = coroutine.create -coroutine.create = function(...) - local ret = coro(...) - setupHook(ret) - return ret -end -luaevent.addserver(server, echoHandler) -luaevent.loop() +-- Tests Copas with a simple Echo server +-- +-- Run the test file and the connect to the server by telnet on the used port +-- to stop the test just send the command "quit" + +require"luaevent" +require"socket" +local function echoHandler(skt) + while true do + local data,ret = luaevent.receive(skt, 10) + if data == "quit" or ret == 'closed' then + break + end + --collectgarbage() + luaevent.send(skt, data) + end +end + +local server = assert(socket.bind("localhost", 20000)) +server:settimeout(0) + +luaevent.addserver(server, echoHandler) +luaevent.loop() \ No newline at end of file diff --git a/luaevent/test/testClient.lua b/luaevent/test/testClient.lua dissimilarity index 80% index b6dfa44..0557230 100644 --- a/luaevent/test/testClient.lua +++ b/luaevent/test/testClient.lua @@ -1,15 +1,22 @@ -require"luaevent" -require"socket" - -local function func() - print("ACTIVATED") - local sock = socket.tcp() - --sock: - sock = luaevent.wrap(sock) - print(assert(sock:connect("localhost", 20000))) - for i = 1, 100000 do assert(sock:send("Greet me ")) assert(sock:receive(10)) collectgarbage() end -end - -luaevent.addthread(func) - -luaevent.loop() \ No newline at end of file +require"luaevent" +require"socket" +local function setupHook(thread) + if not thread then debug.sethook(function(event) print("TRACE >: ", debug.getinfo(2, 'n').name) end, 'c') + else debug.sethook(thread, function(event) print("TRACE ", thread,">: ", debug.getinfo(2, 'n').name) end, 'c') end +end + +local function func(sock) + sock = luaevent.wrap(sock) + assert(sock:connect("localhost", 20000)) + for i = 1, 10 do + for z = 1, 100 do + assert(sock:send("Greet me ")) + end + assert(sock:receive(10 * 100)) + end +end +for i = 1, 1020 do + local sock = assert(socket.tcp()) + luaevent.addthread(sock, func, sock) +end +luaevent.loop() -- 2.11.4.GIT