├── ev.def ├── .gitignore ├── test ├── tap.lua ├── help.lua ├── test_ev_async.lua ├── test_ev_child.lua ├── dumper.lua ├── test_ev_stat.lua ├── test_ev_loop.lua ├── test_ev_idle.lua ├── test_ev_io.lua ├── test_ev_signal.lua └── test_ev_timer.lua ├── Makefile ├── publish ├── rockspec └── lua-ev-scm-1.rockspec ├── lua-ev.rockspec ├── LICENSE ├── cmake └── Modules │ └── FindLua5X.cmake ├── idle_lua_ev.c ├── signal_lua_ev.c ├── async_lua_ev.c ├── io_lua_ev.c ├── CMakeLists.txt ├── contrib └── ev-async.lua ├── lua_ev.c ├── timer_lua_ev.c ├── obj_lua_ev.c ├── stat_lua_ev.c ├── tapview ├── example.lua ├── child_lua_ev.c ├── watcher_lua_ev.c ├── lua_ev.h ├── loop_lua_ev.c ├── README.md └── README /ev.def: -------------------------------------------------------------------------------- 1 | EXPORTS 2 | luaopen_ev 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .*.swp 2 | ev.so 3 | lua_ev.o -------------------------------------------------------------------------------- /test/tap.lua: -------------------------------------------------------------------------------- 1 | local tap = {} 2 | 3 | local counter = 1 4 | 5 | function tap.ok(assert_true, desc) 6 | local msg = ( assert_true and "ok " or "not ok " ) .. counter 7 | if ( desc ) then 8 | msg = msg .. " - " .. desc 9 | end 10 | print(msg) 11 | counter = counter + 1 12 | end 13 | 14 | return tap -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: build test 2 | .DEFAULT: test 3 | 4 | build: 5 | luarocks make rockspec/lua-ev-scm-1.rockspec 6 | 7 | test: build 8 | for f in test/test_*.lua; do \ 9 | echo " testing $$f"; \ 10 | if ! lua $$f test/ . | ./tapview; then \ 11 | echo "$$f test failure, see above"; \ 12 | exit 1; \ 13 | fi; \ 14 | done 15 | -------------------------------------------------------------------------------- /test/help.lua: -------------------------------------------------------------------------------- 1 | local help = {} 2 | local tap = require("tap") 3 | local ev = require("ev") 4 | local ok = tap.ok 5 | 6 | function help.collect_and_assert_no_watchers(test, test_name) 7 | collectgarbage("collect") 8 | local base = ev.object_count() 9 | 10 | -- The default event loop counts as an object: 11 | if base == 0 then base = 1 end 12 | 13 | test() 14 | collectgarbage("collect") 15 | local count = ev.object_count() 16 | ok(count == base, 'no active watchers after ' .. test_name .. ' got: ' .. count .. ' expected: ' .. base) 17 | end 18 | 19 | return help -------------------------------------------------------------------------------- /publish: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | # 3 | # TODO: 4 | # 5 | # * Make it generate a new version number. 6 | # * Set a tag in git. 7 | # * Generate a rockspec. 8 | # * Upload the tarball to github. 9 | # * Announce 10 | 11 | version=$(git tag -l | tail -1) 12 | 13 | name="lua-ev-$version" 14 | 15 | tmp="$TEMPDIR" 16 | if [ -z "$tmp" ]; then 17 | tmp="$HOME/Desktop" 18 | fi 19 | 20 | src="$(cd "$(dirname $0)" && pwd)" 21 | 22 | cd $tmp 23 | rm -f "$name" 24 | ln -sf "$src" "$name" 25 | 26 | echo "Creating $tmp/$name.tar.gz" 27 | tar -czvpf "$name.tar.gz" \ 28 | --dereference \ 29 | --exclude "$name/.git" \ 30 | --exclude "$name/rockspec" \ 31 | --exclude "$name/$(basename $0)" \ 32 | "$name" 33 | echo "Creating $tmp/$name-1.rockspec" 34 | cat "$src/lua-ev.rockspec" | \ 35 | sed s/@VERSION@/$version/ > \ 36 | "$name-1.rockspec" 37 | -------------------------------------------------------------------------------- /rockspec/lua-ev-scm-1.rockspec: -------------------------------------------------------------------------------- 1 | package = "lua-ev" 2 | version = "scm-1" 3 | 4 | source = { 5 | url = "git+https://github.com/brimworks/lua-ev.git" 6 | } 7 | 8 | description = { 9 | summary = "Lua integration with libev", 10 | detailed = [[ 11 | lua-ev: Lua integration with libev (http://dist.schmorp.de/libev) 12 | ]], 13 | homepage = "http://github.com/brimworks/lua-ev", 14 | license = "MIT/X11" 15 | } 16 | 17 | dependencies = { 18 | "lua >= 5.1" 19 | } 20 | 21 | external_dependencies = { 22 | LIBEV = { 23 | header = "ev.h", 24 | library = "libev.so" 25 | } 26 | } 27 | 28 | build = { 29 | type = "builtin", 30 | modules = { 31 | ev = { 32 | sources = { 33 | "lua_ev.c" 34 | }, 35 | libraries = { 36 | "ev" 37 | } 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /test/test_ev_async.lua: -------------------------------------------------------------------------------- 1 | print '1..3' 2 | 3 | local src_dir, build_dir = ... 4 | package.path = src_dir .. "?.lua;" .. package.path 5 | package.cpath = build_dir .. "?.so;" .. package.cpath 6 | 7 | local tap = require("tap") 8 | local ev = require("ev") 9 | local help = require("help") 10 | local dump = require("dumper").dump 11 | local ok = tap.ok 12 | 13 | local noleaks = help.collect_and_assert_no_watchers 14 | local loop = ev.Loop.default 15 | 16 | function test_basic() 17 | local async1 = ev.Async.new( 18 | function(loop, async, revents) 19 | ok(true, 'async callback') 20 | ok(ev.ASYNC == revents, 'ev.ASYNC(' .. ev.ASYNC .. ') == revents (' .. revents .. ')') 21 | async:stop(loop) 22 | end) 23 | async1:start(loop) 24 | async1:send(loop) 25 | loop:loop() 26 | end 27 | 28 | noleaks(test_basic, "test_basic") 29 | -------------------------------------------------------------------------------- /lua-ev.rockspec: -------------------------------------------------------------------------------- 1 | package = "lua-ev" 2 | version = "@VERSION@-1" 3 | 4 | source = { 5 | url = "http://github.com/downloads/brimworks/lua-ev/lua-ev-@VERSION@.tar.gz" 6 | } 7 | 8 | description = { 9 | summary = "Lua integration with libev", 10 | detailed = [[ 11 | lua-ev: Lua integration with libev (http://dist.schmorp.de/libev) 12 | ]], 13 | homepage = "http://github.com/brimworks/lua-ev", 14 | license = "MIT/X11" 15 | } 16 | 17 | dependencies = { 18 | "lua >= 5.1" 19 | } 20 | 21 | external_dependencies = { 22 | LIBEV = { 23 | header = "ev.h", 24 | library = "libev.so" 25 | } 26 | } 27 | 28 | build = { 29 | type = "builtin", 30 | modules = { 31 | ev = { 32 | sources = { 33 | "lua_ev.c" 34 | }, 35 | libraries = { 36 | "ev" 37 | } 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Brian Maher 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /cmake/Modules/FindLua5X.cmake: -------------------------------------------------------------------------------- 1 | 2 | find_path(LUA_INCLUDE_DIR lua.h 3 | HINTS 4 | $ENV{LUA_DIR} 5 | PATH_SUFFIXES include include/lua include/lua53 include/lua5.3 include/lua52 include/lua5.2 include/lua51 include/lua5.1 6 | PATHS 7 | ~/Library/Frameworks 8 | /Library/Frameworks 9 | /usr/local 10 | /usr 11 | /sw 12 | /opt/local 13 | /opt/csw 14 | /opt 15 | ) 16 | 17 | find_library(LUA_LIBRARY 18 | NAMES lua lua53 lua5.3 lua52 lua5.2 lua-5.2 lua51 lua5.1 luajit-5.1 luajit51 luajit5.1 19 | HINTS 20 | $ENV{LUA_DIR} 21 | PATH_SUFFIXES lib64 lib 22 | PATHS 23 | ~/Library/Frameworks 24 | /Library/Frameworks 25 | /usr/local 26 | /usr 27 | /sw 28 | /opt/local 29 | /opt/csw 30 | /opt 31 | ) 32 | 33 | if(LUA_LIBRARY) 34 | if(UNIX AND NOT APPLE) 35 | find_library(LUA_MATH_LIBRARY m) 36 | set( LUA_LIBRARIES "${LUA_LIBRARY};${LUA_MATH_LIBRARY}" CACHE STRING "Lua Libraries") 37 | else(UNIX AND NOT APPLE) 38 | set( LUA_LIBRARIES "${LUA_LIBRARY}" CACHE STRING "Lua Libraries") 39 | endif(UNIX AND NOT APPLE) 40 | endif(LUA_LIBRARY) 41 | 42 | include(FindPackageHandleStandardArgs) 43 | 44 | find_package_handle_standard_args(Lua5X DEFAULT_MSG LUA_LIBRARIES LUA_INCLUDE_DIR) 45 | 46 | mark_as_advanced(LUA_INCLUDE_DIR LUA_LIBRARIES LUA_LIBRARY LUA_MATH_LIBRARY) 47 | -------------------------------------------------------------------------------- /test/test_ev_child.lua: -------------------------------------------------------------------------------- 1 | print '1..9' 2 | 3 | local src_dir, build_dir = ... 4 | package.path = src_dir .. "?.lua;" .. package.path 5 | package.cpath = build_dir .. "?.so;" .. package.cpath 6 | 7 | local tap = require("tap") 8 | local ev = require("ev") 9 | local help = require("help") 10 | local dump = require("dumper").dump 11 | local ok = tap.ok 12 | 13 | local noleaks = help.collect_and_assert_no_watchers 14 | local loop = ev.Loop.default 15 | 16 | function test_basic() 17 | local pid 18 | local child_called = false 19 | local child = ev.Child.new(function(loop, child, revents) 20 | child_called = true 21 | local status = child:getstatus() 22 | ok(child:getrpid() == pid, 'got proper pid') 23 | ok(child:getpid() == 0, 'pid == 0') 24 | ok(status.exited, 'process exited') 25 | ok(status.exit_status == 0, 'process exited with exit status == 0') 26 | ok(status.stopped == false, 'process not stopped') 27 | ok(status.signaled == false, 'process not signaled') 28 | child:stop(loop) 29 | end, 0, false) 30 | child:start(loop) 31 | pid = io.popen("echo $$", "r"):read("*n") 32 | ok(pid > -1, 'fork successful') 33 | loop:loop() 34 | ok(child_called, "child forked") 35 | end 36 | 37 | noleaks(test_basic, "test_basic") 38 | -------------------------------------------------------------------------------- /test/dumper.lua: -------------------------------------------------------------------------------- 1 | local dumper = {} 2 | 3 | function dump_impl(input, output, path, indent, done) 4 | if nil == input then 5 | table.insert(output, "nil") 6 | elseif "table" == type(input) then 7 | if done[input] then 8 | table.insert(output, done[input]); 9 | else 10 | done[input] = path 11 | table.insert(output, tostring(input)) 12 | table.insert(output, " {\n"); 13 | for key, value in pairs(input) do 14 | table.insert(output, string.rep(" ", indent + 1)) -- indent it 15 | table.insert(output, string.format("[%s] = ", tostring(key))) 16 | local kpath = string.format("%s[%s]", path, tostring(key)) 17 | dump_impl(value, output, kpath, indent + 1, done) 18 | table.insert(output, "\n") 19 | end 20 | table.insert(output, string.rep(" ", indent)) -- indent it 21 | table.insert(output, "}"); 22 | end 23 | elseif "string" == type(input) then 24 | table.insert(output, "\"") 25 | table.insert(output, input) 26 | table.insert(output, "\""); 27 | elseif "number" == type(input) or "boolean" == type(input) then 28 | table.insert(output, tostring(input)) 29 | else 30 | table.insert(output, "[") 31 | table.insert(output, tostring(input)) 32 | table.insert(output, "]"); 33 | end 34 | end 35 | 36 | function dumper.dump(name, input) 37 | local output = { tostring(name) } 38 | table.insert(output, " = ") 39 | dump_impl(input, output, name, 0, {}) 40 | return table.concat(output) 41 | end 42 | 43 | return dumper -------------------------------------------------------------------------------- /test/test_ev_stat.lua: -------------------------------------------------------------------------------- 1 | print '1..8' 2 | 3 | local src_dir, build_dir = ... 4 | package.path = src_dir .. "?.lua;" .. package.path 5 | package.cpath = build_dir .. "?.so;" .. package.cpath 6 | 7 | local tap = require("tap") 8 | local ev = require("ev") 9 | local help = require("help") 10 | local dump = require("dumper").dump 11 | local ok = tap.ok 12 | 13 | local noleaks = help.collect_and_assert_no_watchers 14 | local loop = ev.Loop.default 15 | 16 | function touch_file(path) 17 | ev.Timer.new(function(loop, timer, revents) 18 | os.execute('touch ' .. path) 19 | timer:stop(loop) 20 | end, 1):start(loop) 21 | end 22 | 23 | function remove_file(path) 24 | ev.Timer.new(function(loop, timer, revents) 25 | os.execute('rm ' .. path) 26 | timer:stop(loop) 27 | end, 1):start(loop) 28 | end 29 | 30 | function test_basic() 31 | local path = os.tmpname() 32 | local stat = ev.Stat.new(function(loop, stat, revents) 33 | local data = stat:getdata() 34 | ok(data.path == path, 'got proper path') 35 | ok(data.attr.nlink > 0, 'file exists') 36 | ok(data.attr.size == 0, 'file has size of 0 bytes') 37 | os.execute('rm ' .. path) 38 | stat:stop(loop) 39 | end, path) 40 | stat:start(loop) 41 | touch_file(path) 42 | loop:loop() 43 | end 44 | 45 | function test_remove() 46 | local path = os.tmpname() 47 | local stat = ev.Stat.new(function(loop, stat, revents) 48 | local data = stat:getdata() 49 | ok(data.path == path, 'got proper path') 50 | ok(data.attr.nlink == 0, 'file doesn\'t exist') 51 | ok(data.prev.nlink > 0, 'file previously existed') 52 | stat:stop(loop) 53 | end, path) 54 | stat:start(loop) 55 | remove_file(path) 56 | loop:loop() 57 | end 58 | 59 | noleaks(test_basic, "test_basic") 60 | noleaks(test_remove, "test_remove") 61 | 62 | -------------------------------------------------------------------------------- /test/test_ev_loop.lua: -------------------------------------------------------------------------------- 1 | print '1..9' 2 | 3 | local src_dir, build_dir = ... 4 | package.path = src_dir .. "?.lua;" .. package.path 5 | package.cpath = build_dir .. "?.so;" .. package.cpath 6 | 7 | local tap = require("tap") 8 | local ev = require("ev") 9 | local help = require("help") 10 | local ok = tap.ok 11 | 12 | local noleaks = help.collect_and_assert_no_watchers 13 | 14 | local HAVE_LOOP_DEPTH = (not not ev.Loop.default.depth) 15 | 16 | ok(type(ev.version()) == "number", 17 | "version=" .. tostring(ev.version())); 18 | 19 | ok(type(select(2, ev.version())) == "number", 20 | "minor_version=" .. tostring(select(2, ev.version()))); 21 | 22 | -- TODO: This check may be confused by mismatch between 23 | -- libev versions we've compiled with and actually loaded. 24 | do 25 | local major, minor = ev.version() 26 | if major > 3 or (major == 3 and minor >= 7) then 27 | ok(HAVE_LOOP_DEPTH, 28 | "version "..major.."."..minor.." should have loop.depth" 29 | ) 30 | else 31 | ok(not HAVE_LOOP_DEPTH, 32 | "version "..major.."."..minor.." should not have loop.depth" 33 | ) 34 | end 35 | end 36 | 37 | local num = ev.Loop.default:iteration() 38 | ok(num == 0, 39 | "iteration=" .. num) 40 | 41 | if HAVE_LOOP_DEPTH then 42 | num = ev.Loop.default:depth() 43 | ok(num == 0, 44 | "depth=" .. num) 45 | else 46 | ok(true, 'skip HAVE_LOOP_DEPTH is not installed') 47 | end 48 | 49 | -- Shouldn't do anything: 50 | ev.Loop.default:fork() 51 | 52 | -- Do one event loop: 53 | ev.Loop.default:loop() 54 | 55 | -- Be sure old API still works: 56 | num = ev.Loop.default:count() 57 | ok(num == 1, 58 | "iteration=" .. num) 59 | 60 | ok(type(ev.Loop.default:backend()) == "number", 61 | "default backend = " .. tostring(ev.Loop.default:backend())) 62 | 63 | ok(ev.Loop.new(1):backend() == 1, 64 | "Able to choose backend 1 (select), may fail if LIBEV_FLAGS environment variable is set") 65 | 66 | ok(ev.Loop.new(2):backend() == 2, 67 | "Able to choose backend 2 (poll), fails on windows or if LIBEV_FLAGS environment variable excludes this backend") 68 | -------------------------------------------------------------------------------- /idle_lua_ev.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Create a table for ev.Idle that gives access to the constructor for 3 | * idle objects. 4 | * 5 | * [-0, +1, ?] 6 | */ 7 | static int luaopen_ev_idle(lua_State *L) { 8 | lua_pop(L, create_idle_mt(L)); 9 | 10 | lua_createtable(L, 0, 1); 11 | 12 | lua_pushcfunction(L, idle_new); 13 | lua_setfield(L, -2, "new"); 14 | 15 | return 1; 16 | } 17 | 18 | /** 19 | * Create the idle metatable in the registry. 20 | * 21 | * [-0, +1, ?] 22 | */ 23 | static int create_idle_mt(lua_State *L) { 24 | 25 | static luaL_Reg fns[] = { 26 | { "stop", idle_stop }, 27 | { "start", idle_start }, 28 | { NULL, NULL } 29 | }; 30 | luaL_newmetatable(L, IDLE_MT); 31 | add_watcher_mt(L); 32 | luaL_setfuncs(L, fns, 0); 33 | 34 | return 1; 35 | } 36 | 37 | /** 38 | * Create a new idle object. Arguments: 39 | * 1 - callback function. 40 | * 41 | * @see watcher_new() 42 | * 43 | * [+1, -0, ?] 44 | */ 45 | static int idle_new(lua_State* L) { 46 | ev_idle* idle; 47 | 48 | idle = watcher_new(L, sizeof(ev_idle), IDLE_MT); 49 | ev_idle_init(idle, &idle_cb); 50 | return 1; 51 | } 52 | 53 | /** 54 | * @see watcher_cb() 55 | * 56 | * [+0, -0, m] 57 | */ 58 | static void idle_cb(struct ev_loop* loop, ev_idle* idle, int revents) { 59 | watcher_cb(loop, idle, revents); 60 | } 61 | 62 | /** 63 | * Stops the idle so it won't be called by the specified event loop. 64 | * 65 | * Usage: 66 | * idle:stop(loop) 67 | * 68 | * [+0, -0, e] 69 | */ 70 | static int idle_stop(lua_State *L) { 71 | ev_idle* idle = check_idle(L, 1); 72 | struct ev_loop* loop = *check_loop_and_init(L, 2); 73 | 74 | loop_stop_watcher(L, 2, 1); 75 | ev_idle_stop(loop, idle); 76 | 77 | return 0; 78 | } 79 | 80 | /** 81 | * Starts the idle so it won't be called by the specified event loop. 82 | * 83 | * Usage: 84 | * idle:start(loop [, is_daemon]) 85 | * 86 | * [+0, -0, e] 87 | */ 88 | static int idle_start(lua_State *L) { 89 | ev_idle* idle = check_idle(L, 1); 90 | struct ev_loop* loop = *check_loop_and_init(L, 2); 91 | int is_daemon = lua_toboolean(L, 3); 92 | 93 | ev_idle_start(loop, idle); 94 | loop_start_watcher(L, 2, 1, is_daemon); 95 | 96 | return 0; 97 | } 98 | -------------------------------------------------------------------------------- /signal_lua_ev.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Create a table for ev.SIGNAL that gives access to the constructor for 3 | * signal objects. 4 | * 5 | * [-0, +1, ?] 6 | */ 7 | static int luaopen_ev_signal(lua_State *L) { 8 | lua_pop(L, create_signal_mt(L)); 9 | 10 | lua_createtable(L, 0, 1); 11 | 12 | lua_pushcfunction(L, signal_new); 13 | lua_setfield(L, -2, "new"); 14 | 15 | return 1; 16 | } 17 | 18 | /** 19 | * Create the signal metatable in the registry. 20 | * 21 | * [-0, +1, ?] 22 | */ 23 | static int create_signal_mt(lua_State *L) { 24 | 25 | static luaL_Reg fns[] = { 26 | { "stop", signal_stop }, 27 | { "start", signal_start }, 28 | { NULL, NULL } 29 | }; 30 | luaL_newmetatable(L, SIGNAL_MT); 31 | add_watcher_mt(L); 32 | luaL_setfuncs(L, fns, 0); 33 | 34 | return 1; 35 | } 36 | 37 | /** 38 | * Create a new signal object. Arguments: 39 | * 1 - callback function. 40 | * 2 - signal number 41 | * 42 | * @see watcher_new() 43 | * 44 | * [+1, -0, ?] 45 | */ 46 | static int signal_new(lua_State* L) { 47 | #if LUA_VERSION_NUM > 502 48 | int signum = (int)luaL_checkinteger(L, 2); 49 | #else 50 | int signum = luaL_checkint(L, 2); 51 | #endif 52 | ev_signal* sig; 53 | 54 | sig = watcher_new(L, sizeof(ev_signal), SIGNAL_MT); 55 | ev_signal_init(sig, &signal_cb, signum); 56 | return 1; 57 | } 58 | 59 | /** 60 | * @see watcher_cb() 61 | * 62 | * [+0, -0, m] 63 | */ 64 | static void signal_cb(struct ev_loop* loop, ev_signal* sig, int revents) { 65 | watcher_cb(loop, sig, revents); 66 | } 67 | 68 | /** 69 | * Stops the signal so it won't be called by the specified event loop. 70 | * 71 | * Usage: 72 | * signal:stop(loop) 73 | * 74 | * [+0, -0, e] 75 | */ 76 | static int signal_stop(lua_State *L) { 77 | ev_signal* sig = check_signal(L, 1); 78 | struct ev_loop* loop = *check_loop_and_init(L, 2); 79 | 80 | loop_stop_watcher(L, 2, 1); 81 | ev_signal_stop(loop, sig); 82 | 83 | return 0; 84 | } 85 | 86 | /** 87 | * Starts the signal so it will be called by the specified event loop. 88 | * 89 | * Usage: 90 | * signal:start(loop [, is_daemon]) 91 | * 92 | * [+0, -0, e] 93 | */ 94 | static int signal_start(lua_State *L) { 95 | ev_signal* sig = check_signal(L, 1); 96 | struct ev_loop* loop = *check_loop_and_init(L, 2); 97 | int is_daemon = lua_toboolean(L, 3); 98 | 99 | ev_signal_start(loop, sig); 100 | loop_start_watcher(L, 2, 1, is_daemon); 101 | 102 | return 0; 103 | } 104 | -------------------------------------------------------------------------------- /test/test_ev_idle.lua: -------------------------------------------------------------------------------- 1 | print '1..20' 2 | 3 | local src_dir, build_dir = ... 4 | package.path = src_dir .. "?.lua;" .. package.path 5 | package.cpath = build_dir .. "?.so;" .. package.cpath 6 | 7 | local tap = require("tap") 8 | local ev = require("ev") 9 | local help = require("help") 10 | local dump = require("dumper").dump 11 | local ok = tap.ok 12 | 13 | local noleaks = help.collect_and_assert_no_watchers 14 | local loop = ev.Loop.default 15 | 16 | -- Simply see if idle watchers work at all: 17 | function test_basic() 18 | local idle1 = ev.Idle.new( 19 | function(loop, idle, revents) 20 | ok(true, 'simple idle') 21 | ok(ev.IDLE == revents, 'ev.IDLE(' .. ev.IDLE .. ') == revents (' .. revents .. ')') 22 | idle:stop(loop) 23 | end) 24 | idle1:start(loop) 25 | loop:loop() 26 | end 27 | 28 | -- See if idle prorities make sense 29 | function test_priority() 30 | local high_count, low_count = 0, 0 31 | local idle_high = ev.Idle.new( 32 | function(loop, idle, revents) 33 | high_count = high_count + 1 34 | ok(low_count == 0, 'high idle running first') 35 | if high_count == 3 then 36 | idle:stop(loop) 37 | end 38 | end) 39 | ok(idle_high:priority(ev.MAXPRI) == 0, 'priority was default (0)') 40 | ok(idle_high:priority() == ev.MAXPRI, 'priority correctly set') 41 | idle_high:start(loop) 42 | local idle_low = ev.Idle.new( 43 | function(loop, idle, revents) 44 | low_count = low_count + 1 45 | ok(high_count == 3, 'low idle running last') 46 | if low_count == 3 then 47 | idle:stop(loop) 48 | end 49 | end) 50 | idle_low:start(loop) 51 | local daemon_count = 0 52 | local idle_daemon = ev.Idle.new( 53 | function(loop, idle, revents) 54 | daemon_count = daemon_count + 1 55 | ok(false, "daemon idle shouldn't run at all") 56 | end) 57 | ok(idle_daemon:priority(ev.MINPRI) == 0, 'priority was default (0)') 58 | ok(idle_daemon:priority() == ev.MINPRI, 'priority correctly set') 59 | idle_daemon:start(loop, true) 60 | loop:loop() 61 | ok(high_count == 3, 'high idle ran thrice') 62 | ok(low_count == 3, 'low idle ran thrice') 63 | ok(daemon_count == 0, 'daemon idle never ran') 64 | idle_daemon:stop(loop) 65 | end 66 | 67 | function test_shadow_table() 68 | local idle = ev.Idle.new( 69 | function(loop, idle, revents) 70 | idle:stop(loop) 71 | ok(idle.user_data == "foo", 'shadow table works in callback') 72 | end) 73 | idle:start(loop) 74 | idle.user_data = "foo" 75 | ok(idle.user_data == "foo", 'shadow table works') 76 | loop:loop() 77 | end 78 | 79 | noleaks(test_basic, "test_basic") 80 | noleaks(test_priority, "test_priority") 81 | noleaks(test_shadow_table, "test_shadow_table") 82 | -------------------------------------------------------------------------------- /async_lua_ev.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Create a table for ev.Async that gives access to the constructor for 3 | * async objects. 4 | * 5 | * [-0, +1, ?] 6 | */ 7 | static int luaopen_ev_async(lua_State *L) { 8 | lua_pop(L, create_async_mt(L)); 9 | 10 | lua_createtable(L, 0, 1); 11 | 12 | lua_pushcfunction(L, async_new); 13 | lua_setfield(L, -2, "new"); 14 | 15 | return 1; 16 | } 17 | 18 | /** 19 | * Create the async metatable in the registry. 20 | * 21 | * [-0, +1, ?] 22 | */ 23 | static int create_async_mt(lua_State *L) { 24 | 25 | static luaL_Reg fns[] = { 26 | { "send", async_send }, 27 | { "stop", async_stop }, 28 | { "start", async_start }, 29 | { NULL, NULL } 30 | }; 31 | luaL_newmetatable(L, ASYNC_MT); 32 | add_watcher_mt(L); 33 | luaL_setfuncs(L, fns, 0); 34 | 35 | return 1; 36 | } 37 | 38 | /** 39 | * Create a new async object. Arguments: 40 | * 1 - callback function. 41 | * 42 | * @see watcher_new() 43 | * 44 | * [+1, -0, ?] 45 | */ 46 | static int async_new(lua_State* L) { 47 | ev_async* async; 48 | 49 | async = watcher_new(L, sizeof(ev_async), ASYNC_MT); 50 | ev_async_init(async, &async_cb ); 51 | return 1; 52 | } 53 | 54 | /** 55 | * @see watcher_cb() 56 | * 57 | * [+0, -0, m] 58 | */ 59 | static void async_cb(struct ev_loop* loop, ev_async* async, int revents) { 60 | watcher_cb(loop, async, revents); 61 | } 62 | 63 | /** 64 | * Sends/signals/activates the given "ev_async" watcher, that is, feeds an 65 | * "EV_ASYNC" event on the watcher into the event loop, and instantly returns. 66 | * 67 | * Usage: 68 | * async:send(loop) 69 | * 70 | * [+0, -0, e] 71 | */ 72 | static int async_send(lua_State *L) { 73 | ev_async* async = check_async(L, 1); 74 | struct ev_loop* loop = *check_loop_and_init(L, 2); 75 | 76 | ev_async_send(loop, async); 77 | 78 | return 0; 79 | } 80 | 81 | /** 82 | * Stops the async so it won't be called by the specified event loop. 83 | * 84 | * Usage: 85 | * async:stop(loop) 86 | * 87 | * [+0, -0, e] 88 | */ 89 | static int async_stop(lua_State *L) { 90 | ev_async* async = check_async(L, 1); 91 | struct ev_loop* loop = *check_loop_and_init(L, 2); 92 | 93 | loop_stop_watcher(L, 2, 1); 94 | ev_async_stop(loop, async); 95 | 96 | return 0; 97 | } 98 | 99 | /** 100 | * Starts the async so it won't be called by the specified event loop. 101 | * 102 | * Usage: 103 | * async:start(loop [, is_daemon]) 104 | * 105 | * [+0, -0, e] 106 | */ 107 | static int async_start(lua_State *L) { 108 | ev_async* async = check_async(L, 1); 109 | struct ev_loop* loop = *check_loop_and_init(L, 2); 110 | int is_daemon = lua_toboolean(L, 3); 111 | 112 | ev_async_start(loop, async); 113 | loop_start_watcher(L, 2, 1, is_daemon); 114 | 115 | return 0; 116 | } 117 | -------------------------------------------------------------------------------- /io_lua_ev.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Create a table for ev.IO that gives access to the constructor for 3 | * io objects. 4 | * 5 | * [-0, +1, ?] 6 | */ 7 | static int luaopen_ev_io(lua_State *L) { 8 | lua_pop(L, create_io_mt(L)); 9 | 10 | lua_createtable(L, 0, 1); 11 | 12 | lua_pushcfunction(L, io_new); 13 | lua_setfield(L, -2, "new"); 14 | 15 | return 1; 16 | } 17 | 18 | /** 19 | * Create the io metatable in the registry. 20 | * 21 | * [-0, +1, ?] 22 | */ 23 | static int create_io_mt(lua_State *L) { 24 | 25 | static luaL_Reg fns[] = { 26 | { "stop", io_stop }, 27 | { "start", io_start }, 28 | { "getfd" , io_getfd }, 29 | { NULL, NULL } 30 | }; 31 | luaL_newmetatable(L, IO_MT); 32 | add_watcher_mt(L); 33 | luaL_setfuncs(L, fns, 0); 34 | 35 | return 1; 36 | } 37 | 38 | /** 39 | * Create a new io object. Arguments: 40 | * 1 - callback function. 41 | * 2 - fd (file descriptor number) 42 | * 3 - READ | WRITE (what operation to watch) 43 | * 44 | * @see watcher_new() 45 | * 46 | * [+1, -0, ?] 47 | */ 48 | static int io_new(lua_State* L) { 49 | #if LUA_VERSION_NUM > 502 50 | int fd = (int)luaL_checkinteger(L, 2); 51 | int events = (int)luaL_checkinteger(L, 3); 52 | #else 53 | int fd = luaL_checkint(L, 2); 54 | int events = luaL_checkint(L, 3); 55 | #endif 56 | ev_io* io; 57 | 58 | io = watcher_new(L, sizeof(ev_io), IO_MT); 59 | ev_io_init(io, &io_cb, fd, events); 60 | return 1; 61 | } 62 | 63 | /** 64 | * @see watcher_cb() 65 | * 66 | * [+0, -0, m] 67 | */ 68 | static void io_cb(struct ev_loop* loop, ev_io* io, int revents) { 69 | watcher_cb(loop, io, revents); 70 | } 71 | 72 | /** 73 | * Stops the io so it won't be called by the specified event loop. 74 | * 75 | * Usage: 76 | * io:stop(loop) 77 | * 78 | * [+0, -0, e] 79 | */ 80 | static int io_stop(lua_State *L) { 81 | ev_io* io = check_io(L, 1); 82 | struct ev_loop* loop = *check_loop_and_init(L, 2); 83 | 84 | loop_stop_watcher(L, 2, 1); 85 | ev_io_stop(loop, io); 86 | 87 | return 0; 88 | } 89 | 90 | /** 91 | * Starts the io so it won't be called by the specified event loop. 92 | * 93 | * Usage: 94 | * io:start(loop [, is_daemon]) 95 | * 96 | * [+0, -0, e] 97 | */ 98 | static int io_start(lua_State *L) { 99 | ev_io* io = check_io(L, 1); 100 | struct ev_loop* loop = *check_loop_and_init(L, 2); 101 | int is_daemon = lua_toboolean(L, 3); 102 | 103 | ev_io_start(loop, io); 104 | loop_start_watcher(L, 2, 1, is_daemon); 105 | 106 | return 0; 107 | } 108 | 109 | /** 110 | * Returns the original file descriptor 111 | * Usage: 112 | * io:getfd ( ) 113 | * 114 | * [+1, -0, e] 115 | */ 116 | static int io_getfd(lua_State *L) { 117 | ev_io* io = check_io(L, 1); 118 | 119 | lua_pushinteger ( L , io->fd ); 120 | 121 | return 1; 122 | } 123 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This cmake file was inspired by: 2 | # Copyright (C) 2007-2009 LuaDist. 3 | # Submitted by David Manura 4 | # Redistribution and use of this file is allowed according to the 5 | # terms of the MIT license. 6 | # For details see the COPYRIGHT file distributed with LuaDist. 7 | # Please note that the package source code is licensed under its own 8 | # license. 9 | 10 | PROJECT(lua-ev C) 11 | CMAKE_MINIMUM_REQUIRED (VERSION 2.6) 12 | 13 | # Basic configurations 14 | SET(INSTALL_CMOD share/lua/cmod CACHE PATH "Directory to install Lua binary modules (configure lua via LUA_CPATH)") 15 | # / configs 16 | 17 | list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/Modules/") 18 | 19 | # Find libev 20 | FIND_LIBRARY (LIBEV_LIBRARY NAMES ev) 21 | FIND_PATH (LIBEV_INCLUDE_DIR ev.h 22 | PATH_SUFFIXES include/ev include 23 | ) # Find header 24 | INCLUDE(FindPackageHandleStandardArgs) 25 | FIND_PACKAGE_HANDLE_STANDARD_ARGS(libev DEFAULT_MSG LIBEV_LIBRARY LIBEV_INCLUDE_DIR) 26 | # / Find libarchive 27 | 28 | # Find lua 29 | FIND_PACKAGE(Lua5X REQUIRED) 30 | # / Find lua 31 | 32 | # Define how to build ev.so: 33 | INCLUDE_DIRECTORIES(${LIBEV_INCLUDE_DIR} ${LUA_INCLUDE_DIR}) 34 | ADD_LIBRARY(cmod_ev MODULE 35 | lua_ev.c 36 | ) 37 | SET_TARGET_PROPERTIES(cmod_ev PROPERTIES PREFIX "") 38 | SET_TARGET_PROPERTIES(cmod_ev PROPERTIES OUTPUT_NAME ev) 39 | TARGET_LINK_LIBRARIES(cmod_ev ${LUA_LIBRARIES} ${LIBEV_LIBRARY}) 40 | # / build ev.so 41 | 42 | # Define how to test ev.so: 43 | INCLUDE(CTest) 44 | FIND_PROGRAM(LUA NAMES lua lua.bat) 45 | ADD_TEST(ev_io ${LUA} ${CMAKE_CURRENT_SOURCE_DIR}/test/test_ev_io.lua ${CMAKE_CURRENT_SOURCE_DIR}/test/ ${CMAKE_CURRENT_BINARY_DIR}/) 46 | ADD_TEST(ev_loop ${LUA} ${CMAKE_CURRENT_SOURCE_DIR}/test/test_ev_loop.lua ${CMAKE_CURRENT_SOURCE_DIR}/test/ ${CMAKE_CURRENT_BINARY_DIR}/) 47 | ADD_TEST(ev_timer ${LUA} ${CMAKE_CURRENT_SOURCE_DIR}/test/test_ev_timer.lua ${CMAKE_CURRENT_SOURCE_DIR}/test/ ${CMAKE_CURRENT_BINARY_DIR}/) 48 | ADD_TEST(ev_idle ${LUA} ${CMAKE_CURRENT_SOURCE_DIR}/test/test_ev_idle.lua ${CMAKE_CURRENT_SOURCE_DIR}/test/ ${CMAKE_CURRENT_BINARY_DIR}/) 49 | ADD_TEST(ev_signal ${LUA} ${CMAKE_CURRENT_SOURCE_DIR}/test/test_ev_signal.lua ${CMAKE_CURRENT_SOURCE_DIR}/test/ ${CMAKE_CURRENT_BINARY_DIR}/) 50 | ADD_TEST(ev_async ${LUA} ${CMAKE_CURRENT_SOURCE_DIR}/test/test_ev_async.lua ${CMAKE_CURRENT_SOURCE_DIR}/test/ ${CMAKE_CURRENT_BINARY_DIR}/) 51 | ADD_TEST(ev_child ${LUA} ${CMAKE_CURRENT_SOURCE_DIR}/test/test_ev_child.lua ${CMAKE_CURRENT_SOURCE_DIR}/test/ ${CMAKE_CURRENT_BINARY_DIR}/) 52 | ADD_TEST(ev_stat ${LUA} ${CMAKE_CURRENT_SOURCE_DIR}/test/test_ev_stat.lua ${CMAKE_CURRENT_SOURCE_DIR}/test/ ${CMAKE_CURRENT_BINARY_DIR}/) 53 | SET_TESTS_PROPERTIES(ev_io ev_loop ev_timer ev_signal ev_idle ev_child ev_stat 54 | PROPERTIES 55 | FAIL_REGULAR_EXPRESSION 56 | "not ok") 57 | 58 | # / test ev.so 59 | 60 | # Where to install stuff 61 | INSTALL (TARGETS cmod_ev DESTINATION ${INSTALL_CMOD}) 62 | # / Where to install. 63 | -------------------------------------------------------------------------------- /contrib/ev-async.lua: -------------------------------------------------------------------------------- 1 | -- WARNING!! WARNING!! WARNING!! WARNING!! WARNING!! WARNING!! WARNING!! WARNING!! 2 | -- 3 | -- Use this code at your own risk. If one Lua VM has a loop or async watcher 4 | -- that goes out of scope and is used by a different Lua VM, your process 5 | -- will crash and/or behave in unexpected ways. 6 | -- 7 | -- WARNING!! WARNING!! WARNING!! WARNING!! WARNING!! WARNING!! WARNING!! WARNING!! 8 | -- 9 | -- Want something safer? Feel free to contribute! 10 | -- 11 | -- One idea is to allocate all ev_loop and ev_async objects into a pool shared 12 | -- shared across all uses of this library (aka a statically allocated array). This 13 | -- pool would also contain reference counts for each object. 14 | -- 15 | -- The userdata objects for loop and async watchers could then be changed to 16 | -- reference the index into that pool. 17 | -- 18 | -- We could then "serialize" an ev_loop or ev_async object by returning the index. 19 | -- 20 | -- The "deserialize" would allocate an userdata that points to this index and 21 | -- increment the reference count (assuming that an object of the correct type 22 | -- exists at this index, otherwise throw an error). 23 | -- 24 | -- Note that all allocations (and deallocations) of loop and async watchers would 25 | -- require a mutex to ensure only one thread modifies this data at one time. 26 | 27 | 28 | 29 | -- Contribution from https://github.com/ImagicTheCat 30 | -- 31 | -- Extension for lua-ev and LuaJIT to trigger async watchers from other threads. 32 | local ev = require("ev") 33 | local ffi = require("ffi") 34 | 35 | ffi.cdef[[ 36 | struct ev_loop; 37 | struct ev_async; 38 | void ev_async_send(struct ev_loop*, struct ev_async*); 39 | ]] 40 | local libev = ffi.load("ev") 41 | local loop_mt = getmetatable(ev.Loop.default) 42 | local async_mt = getmetatable(ev.Async.new(function() end)) 43 | local async_sz_t = ffi.typeof("struct{struct ev_loop *loop; struct ev_async *async;}") 44 | 45 | local function error_arg(index, expected) 46 | error("bad argument #"..index.." ("..expected.." expected)") 47 | end 48 | 49 | local M = {} 50 | 51 | -- Export serialized send for multi-threading signalization. 52 | -- return string 53 | function M.export(loop, async) 54 | if getmetatable(loop) ~= loop_mt then error_arg(1, "loop") end 55 | if getmetatable(async) ~= async_mt then error_arg(2, "async watcher") end 56 | local loop_p = ffi.cast("struct ev_loop**", loop)[0] 57 | local async_p = ffi.cast("struct ev_async*", async) 58 | local async_sz = async_sz_t({loop = loop_p, async = async_p}) 59 | return ffi.string(async_sz, ffi.sizeof(async_sz_t)) 60 | end 61 | 62 | -- Import serialized send for multi-threading signalization. 63 | -- Return a function which will act as `ev_async_send` when called. 64 | -- 65 | -- Calling the function beyond the lifespan of either the exported loop or async 66 | -- object will result in undefined behavior (e.g. crash/corruption). The same applies 67 | -- with an ill-formed string. 68 | function M.import(data) 69 | if type(data) ~= "string" or #data ~= ffi.sizeof(async_sz_t) then 70 | error("invalid payload") 71 | end 72 | local async_sz = async_sz_t() 73 | ffi.copy(async_sz, data, ffi.sizeof(async_sz_t)) 74 | return function() libev.ev_async_send(async_sz.loop, async_sz.async) end 75 | end 76 | 77 | return M 78 | -------------------------------------------------------------------------------- /test/test_ev_io.lua: -------------------------------------------------------------------------------- 1 | local src_dir, build_dir = ... 2 | package.path = src_dir .. "?.lua;" .. package.path 3 | package.cpath = build_dir .. "?.so;" .. package.cpath 4 | 5 | -- This test relies on socket support: 6 | local has_socket, socket = pcall(require, "socket") 7 | if not has_socket then 8 | print('1..0 # Skipped: No socket library available (' .. socket .. ')') 9 | os.exit(0) 10 | end 11 | print '1..??' 12 | 13 | local tap = require("tap") 14 | local ev = require("ev") 15 | local help = require("help") 16 | local ok = tap.ok 17 | local dump = require("dumper").dump 18 | 19 | local noleaks = help.collect_and_assert_no_watchers 20 | local loop = ev.Loop.default 21 | 22 | local function test_stdin() 23 | local io1 = ev.IO.new( 24 | function(loop, io, revents) 25 | ok(true, 'STDIN is writable') 26 | ok(io:getfd() == 1, 'getfd() works') 27 | io:stop(loop) 28 | end, 1, ev.WRITE) 29 | io1:start(loop) 30 | loop:loop() 31 | end 32 | 33 | local function newtry() 34 | local try = {} 35 | setmetatable(try, try) 36 | function try:__call(body) 37 | local is_err, err = pcall(body) 38 | for _, finalizer in ipairs(self) do 39 | -- ignore errors in finalizers: 40 | pcall(finalizer) 41 | end 42 | assert(is_err, err) 43 | end 44 | function try:finally(finalizer) 45 | self[#self + 1] = finalizer 46 | end 47 | return try 48 | end 49 | 50 | local function test_echo() 51 | local got_response 52 | local try = newtry() 53 | try(function() 54 | local server = assert(socket.bind("*", 0)) 55 | try:finally(function() server:close() end) 56 | server:settimeout(0) 57 | ev.IO.new( 58 | function(loop, watcher) 59 | watcher:stop(loop) 60 | local client = assert(server:accept()) 61 | client:settimeout(0) 62 | ev.IO.new( 63 | function(loop, watcher) 64 | watcher:stop(loop) 65 | local buff = assert(client:receive('*a')) 66 | ev.IO.new( 67 | function(loop, watcher) 68 | watcher:stop(loop) 69 | assert(client:send(buff)) 70 | assert(client:shutdown("send")) 71 | end, 72 | client:getfd(), 73 | ev.WRITE):start(loop) 74 | end, 75 | client:getfd(), 76 | ev.READ):start(loop) 77 | end, 78 | server:getfd(), 79 | ev.READ):start(loop) 80 | local port = select(2, server:getsockname()) 81 | local client = assert(socket.connect("127.0.0.1", port)) 82 | try:finally(function() client:close() end) 83 | client:settimeout(0) 84 | ev.IO.new( 85 | function(loop, watcher) 86 | watcher:stop(loop) 87 | local str = "Hello World" 88 | assert(client:send(str)) 89 | assert(client:shutdown("send")) 90 | ev.IO.new( 91 | function(loop, watcher) 92 | watcher:stop(loop) 93 | local response = assert(client:receive("*a")) 94 | ok(response == str, 95 | tostring(response) .. " == " .. tostring(str)) 96 | got_response = true 97 | end, 98 | client:getfd(), 99 | ev.READ):start(loop) 100 | end, 101 | client:getfd(), 102 | ev.WRITE):start(loop) 103 | loop:loop() 104 | end) 105 | ok(got_response, "echo") 106 | end 107 | 108 | noleaks(test_stdin, "test_stdin") 109 | noleaks(test_echo, "test_echo") 110 | 111 | -------------------------------------------------------------------------------- /lua_ev.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "lua_ev.h" 8 | 9 | /* We make everything static, so we just include all *.c files in a 10 | * single compilation unit. */ 11 | #include "obj_lua_ev.c" 12 | #include "loop_lua_ev.c" 13 | #include "watcher_lua_ev.c" 14 | #include "io_lua_ev.c" 15 | #include "timer_lua_ev.c" 16 | #include "signal_lua_ev.c" 17 | #include "idle_lua_ev.c" 18 | #include "async_lua_ev.c" 19 | #include "child_lua_ev.c" 20 | #include "stat_lua_ev.c" 21 | 22 | static const luaL_Reg R[] = { 23 | {"version", version}, 24 | {"object_count", obj_count}, 25 | {NULL, NULL}, 26 | }; 27 | 28 | 29 | /** 30 | * Entry point into the 'ev' lua library. Validates that the 31 | * dynamically linked libev version matches, creates the object 32 | * registry, and creates the table returned by require(). 33 | */ 34 | LUALIB_API int luaopen_ev(lua_State *L) { 35 | 36 | assert(ev_version_major() == EV_VERSION_MAJOR && 37 | ev_version_minor() >= EV_VERSION_MINOR); 38 | 39 | create_obj_registry(L); 40 | 41 | #if LUA_VERSION_NUM > 501 42 | luaL_newlib(L, R); 43 | #else 44 | luaL_register(L, "ev", R); 45 | #endif 46 | 47 | luaopen_ev_loop(L); 48 | lua_setfield(L, -2, "Loop"); 49 | 50 | luaopen_ev_timer(L); 51 | lua_setfield(L, -2, "Timer"); 52 | 53 | luaopen_ev_io(L); 54 | lua_setfield(L, -2, "IO"); 55 | 56 | luaopen_ev_async(L); 57 | lua_setfield(L, -2, "Async"); 58 | 59 | luaopen_ev_signal(L); 60 | lua_setfield(L, -2, "Signal"); 61 | 62 | luaopen_ev_idle(L); 63 | lua_setfield(L, -2, "Idle"); 64 | 65 | luaopen_ev_child(L); 66 | lua_setfield(L, -2, "Child"); 67 | 68 | luaopen_ev_stat(L); 69 | lua_setfield(L, -2, "Stat"); 70 | 71 | #define EV_SETCONST(state, prefix, C) \ 72 | lua_pushnumber(L, prefix ## C); \ 73 | lua_setfield(L, -2, #C) 74 | 75 | EV_SETCONST(L, EV_, CHILD); 76 | EV_SETCONST(L, EV_, IDLE); 77 | EV_SETCONST(L, EV_, ASYNC); 78 | EV_SETCONST(L, EV_, MINPRI); 79 | EV_SETCONST(L, EV_, MAXPRI); 80 | EV_SETCONST(L, EV_, READ); 81 | EV_SETCONST(L, EV_, SIGNAL); 82 | EV_SETCONST(L, EV_, STAT); 83 | EV_SETCONST(L, EV_, TIMEOUT); 84 | EV_SETCONST(L, EV_, WRITE); 85 | 86 | EV_SETCONST(L, , SIGABRT); 87 | EV_SETCONST(L, , SIGALRM); 88 | EV_SETCONST(L, , SIGBUS); 89 | EV_SETCONST(L, , SIGCHLD); 90 | EV_SETCONST(L, , SIGCONT); 91 | EV_SETCONST(L, , SIGFPE); 92 | EV_SETCONST(L, , SIGHUP); 93 | EV_SETCONST(L, , SIGINT); 94 | EV_SETCONST(L, , SIGIO); 95 | EV_SETCONST(L, , SIGIOT); 96 | EV_SETCONST(L, , SIGKILL); 97 | EV_SETCONST(L, , SIGPIPE); 98 | #ifdef SIGPOLL 99 | EV_SETCONST(L, , SIGPOLL); 100 | #endif 101 | EV_SETCONST(L, , SIGPROF); 102 | #ifdef SIGPWR 103 | EV_SETCONST(L, , SIGPWR); 104 | #endif 105 | EV_SETCONST(L, , SIGQUIT); 106 | EV_SETCONST(L, , SIGSEGV); 107 | #ifdef SIGSTKFLT 108 | EV_SETCONST(L, , SIGSTKFLT); 109 | #endif 110 | EV_SETCONST(L, , SIGSYS); 111 | EV_SETCONST(L, , SIGTERM); 112 | EV_SETCONST(L, , SIGTRAP); 113 | EV_SETCONST(L, , SIGTSTP); 114 | EV_SETCONST(L, , SIGTTIN); 115 | EV_SETCONST(L, , SIGTTOU); 116 | EV_SETCONST(L, , SIGURG); 117 | EV_SETCONST(L, , SIGUSR1); 118 | EV_SETCONST(L, , SIGUSR2); 119 | EV_SETCONST(L, , SIGVTALRM); 120 | EV_SETCONST(L, , SIGWINCH); 121 | EV_SETCONST(L, , SIGXCPU); 122 | EV_SETCONST(L, , SIGXFSZ); 123 | 124 | #undef EV_SETCONST 125 | 126 | return 1; 127 | } 128 | 129 | /** 130 | * Push the major and minor version of libev onto the stack. 131 | * 132 | * [+2, -0, -] 133 | */ 134 | static int version(lua_State *L) { 135 | lua_pushnumber(L, ev_version_major()); 136 | lua_pushnumber(L, ev_version_minor()); 137 | return 2; 138 | } 139 | 140 | /** 141 | * Taken from lua.c out of the lua source distribution. Use this 142 | * function when doing lua_pcall(). 143 | */ 144 | static int traceback(lua_State *L) { 145 | if ( !lua_isstring(L, 1) ) return 1; 146 | 147 | lua_getglobal(L, "debug"); 148 | if ( !lua_istable(L, -1) ) { 149 | lua_pop(L, 1); 150 | return 1; 151 | } 152 | 153 | lua_getfield(L, -1, "traceback"); 154 | if ( !lua_isfunction(L, -1) ) { 155 | lua_pop(L, 2); 156 | return 1; 157 | } 158 | 159 | lua_pushvalue(L, 1); /* pass error message */ 160 | lua_pushinteger(L, 2); /* skip this function and traceback */ 161 | lua_call(L, 2, 1); /* call debug.traceback */ 162 | return 1; 163 | } 164 | -------------------------------------------------------------------------------- /test/test_ev_signal.lua: -------------------------------------------------------------------------------- 1 | print '1..18' 2 | 3 | local src_dir, build_dir = ... 4 | package.path = src_dir .. "?.lua;" .. package.path 5 | package.cpath = build_dir .. "?.so;" .. package.cpath 6 | 7 | local tap = require("tap") 8 | local ev = require("ev") 9 | local help = require("help") 10 | local dump = require("dumper").dump 11 | local ok = tap.ok 12 | 13 | local noleaks = help.collect_and_assert_no_watchers 14 | local loop = ev.Loop.default 15 | 16 | -- Simply see if we can do a simple signal handler: 17 | function test_basic() 18 | local sig = ev.Signal.new( 19 | function(loop, sig, revents) 20 | ok(true, 'got SIGALRM') 21 | ok(ev.SIGNAL == revents, 'ev.SIGNAL(' .. ev.SIGNAL .. ') == revents (' .. revents .. ')') 22 | sig:stop(loop) 23 | end, 24 | 14) -- SIGALRM 25 | sig:start(loop) 26 | os.execute('kill -14 $PPID') 27 | loop:loop() 28 | end 29 | 30 | -- Test daemon=true 31 | function test_daemon_true() 32 | local loop_iters = 0 33 | local sig = ev.Signal.new( 34 | function(loop, sig) 35 | ok(loop_iters > 0, 'got SIGALRM after one loop iteration') 36 | sig:stop(loop) 37 | end, 38 | 14) -- SIGALRM 39 | 40 | sig:start(loop, true) 41 | loop:loop() -- Should be a no-op. 42 | loop_iters = 1 43 | os.execute('kill -14 $PPID') 44 | loop:loop() 45 | end 46 | 47 | -- Test stop(), start(), and is_active() 48 | function test_start_stop_active() 49 | local cnt = 0 50 | local sig = ev.Signal.new( 51 | function(loop, sig) 52 | cnt = cnt + 1 53 | ok(cnt == 1, 'get SIGALRM exactly once') 54 | sig:stop(loop) 55 | end, 56 | 14) -- SIGALRM 57 | 58 | ok(not sig:is_active(), 'not active') 59 | 60 | sig:start(loop) 61 | 62 | ok(sig:is_active(), 'active') 63 | sig:stop(loop) 64 | 65 | loop:loop() -- no-op 66 | 67 | sig:start(loop) 68 | os.execute('kill -14 $PPID') 69 | 70 | loop:loop() 71 | 72 | ok(cnt == 1, 'SIGALRM callback got called') 73 | end 74 | 75 | -- Test invoke() 76 | function test_callback() 77 | local cnt = 0 78 | local sig = ev.Signal.new( 79 | function() 80 | cnt = cnt + 1 81 | ok(cnt == 1, 'Signal callback called exactly once') 82 | end, 83 | 14) 84 | 85 | sig:callback()() 86 | ok(cnt == 1, 'Signal callback actually got called') 87 | end 88 | 89 | -- Test is_pending() 90 | function test_is_pending() 91 | local num_pending = 0 92 | local num_called = 0 93 | local sig2 94 | local sig1 = ev.Signal.new( 95 | function(loop, sig) 96 | if ( sig2:is_pending() ) then 97 | num_pending = num_pending + 1 98 | end 99 | num_called = num_called + 1 100 | sig:stop(loop) 101 | end, 102 | 14) 103 | 104 | sig1:start(loop) 105 | 106 | sig2 = ev.Signal.new( 107 | function(loop, sig) 108 | if ( sig1:is_pending() ) then 109 | num_pending = num_pending + 1 110 | end 111 | num_called = num_called + 1 112 | sig:stop(loop) 113 | end, 114 | 14) 115 | 116 | sig2:start(loop) 117 | 118 | os.execute('kill -14 $PPID') 119 | loop:loop() 120 | 121 | ok(num_pending == 1, 'exactly one signal was pending') 122 | ok(num_called == 2, 'both signal handlers got called') 123 | end 124 | 125 | -- Test clear_pending() 126 | function test_clear_pending() 127 | local num_called = 0 128 | local sig2 129 | local sig1 = ev.Signal.new( 130 | function(loop, sig) 131 | if ( sig2:is_pending() ) then 132 | sig2:clear_pending(loop) 133 | end 134 | num_called = num_called + 1 135 | end, 136 | 14) 137 | sig1:start(loop) 138 | 139 | sig2 = ev.Signal.new( 140 | function(loop, sig) 141 | if ( sig1:is_pending() ) then 142 | sig1:clear_pending(loop) 143 | end 144 | num_called = num_called + 1 145 | end, 146 | 14) 147 | sig2:start(loop) 148 | 149 | local timer = ev.Timer.new( 150 | function(loop, sig) 151 | loop:unloop() 152 | end, 153 | 0.01) 154 | timer:start(loop) 155 | 156 | os.execute('kill -14 $PPID') 157 | loop:loop() 158 | sig1:stop(loop) 159 | sig2:stop(loop) 160 | 161 | ok(num_called == 1, 'just one signal handler got called') 162 | end 163 | 164 | 165 | noleaks(test_basic, "test_basic") 166 | noleaks(test_daemon_true, "test_daemon_true") 167 | noleaks(test_start_stop_active, "test_start_stop_active") 168 | noleaks(test_callback, "test_callback") 169 | noleaks(test_is_pending, "test_is_pending") 170 | noleaks(test_clear_pending, "test_clear_pending") 171 | -------------------------------------------------------------------------------- /timer_lua_ev.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Create a table for ev.Timer that gives access to the constructor for 3 | * timer objects. 4 | * 5 | * [-0, +1, ?] 6 | */ 7 | static int luaopen_ev_timer(lua_State *L) { 8 | lua_pop(L, create_timer_mt(L)); 9 | 10 | lua_createtable(L, 0, 1); 11 | 12 | lua_pushcfunction(L, timer_new); 13 | lua_setfield(L, -2, "new"); 14 | 15 | return 1; 16 | } 17 | 18 | /** 19 | * Create the timer metatable in the registry. 20 | * 21 | * [-0, +1, ?] 22 | */ 23 | static int create_timer_mt(lua_State *L) { 24 | 25 | static luaL_Reg fns[] = { 26 | { "again", timer_again }, 27 | { "stop", timer_stop }, 28 | { "start", timer_start }, 29 | { "clear_pending", timer_clear_pending }, 30 | { NULL, NULL } 31 | }; 32 | luaL_newmetatable(L, TIMER_MT); 33 | add_watcher_mt(L); 34 | luaL_setfuncs(L, fns, 0); 35 | 36 | return 1; 37 | } 38 | 39 | /** 40 | * Create a new timer object. Arguments: 41 | * 1 - callback function. 42 | * 2 - after (number of seconds until timer should trigger). 43 | * 3 - repeat (number of seconds to wait for consecutive timeouts). 44 | * 45 | * @see watcher_new() 46 | * 47 | * [+1, -0, ?] 48 | */ 49 | static int timer_new(lua_State* L) { 50 | ev_tstamp after = luaL_checknumber(L, 2); 51 | ev_tstamp repeat = luaL_optnumber(L, 3, 0); 52 | ev_timer* timer; 53 | 54 | if ( repeat < 0.0 ) 55 | luaL_argerror(L, 3, "repeat must be greater than or equal to 0"); 56 | 57 | timer = watcher_new(L, sizeof(ev_timer), TIMER_MT); 58 | ev_timer_init(timer, &timer_cb, after, repeat); 59 | return 1; 60 | } 61 | 62 | /** 63 | * @see watcher_cb() 64 | * 65 | * [+0, -0, m] 66 | */ 67 | static void timer_cb(struct ev_loop* loop, ev_timer* timer, int revents) { 68 | watcher_cb(loop, timer, revents); 69 | } 70 | 71 | /** 72 | * Restart a timer with the specified repeat number of seconds. If no 73 | * repeat was specified, the timer is simply stopped. May optionally 74 | * specify a new value for repeat, otherwise uses the value set when 75 | * the timer was created. 76 | * 77 | * 1 - timer object. 78 | * 2 - loop object. 79 | * 3 - repeat (number of seconds to wait for consecutive timeouts). 80 | * 81 | * Usage: 82 | * timer:again(loop [, repeat_seconds]) 83 | * 84 | * [+0, -0, e] 85 | */ 86 | static int timer_again(lua_State *L) { 87 | ev_timer* timer = check_timer(L, 1); 88 | struct ev_loop* loop = *check_loop_and_init(L, 2); 89 | ev_tstamp repeat = luaL_optnumber(L, 3, 0); 90 | 91 | if ( repeat < 0.0 ) luaL_argerror(L, 3, "repeat must be greater than 0"); 92 | 93 | if ( repeat ) timer->repeat = repeat; 94 | 95 | if ( timer->repeat ) { 96 | ev_timer_again(loop, timer); 97 | loop_start_watcher(L, 2, 1, -1); 98 | } else { 99 | /* Just calling stop instead of again in case the symantics 100 | * change in libev */ 101 | loop_stop_watcher(L, 2, 1); 102 | ev_timer_stop(loop, timer); 103 | } 104 | 105 | return 0; 106 | } 107 | 108 | /** 109 | * Stops the timer so it won't be called by the specified event loop. 110 | * 111 | * Usage: 112 | * timer:stop(loop) 113 | * 114 | * [+0, -0, e] 115 | */ 116 | static int timer_stop(lua_State *L) { 117 | ev_timer* timer = check_timer(L, 1); 118 | struct ev_loop* loop = *check_loop_and_init(L, 2); 119 | 120 | loop_stop_watcher(L, 2, 1); 121 | ev_timer_stop(loop, timer); 122 | 123 | return 0; 124 | } 125 | 126 | /** 127 | * Starts the timer so it won't be called by the specified event loop. 128 | * 129 | * Usage: 130 | * timer:start(loop [, is_daemon]) 131 | * 132 | * [+0, -0, e] 133 | */ 134 | static int timer_start(lua_State *L) { 135 | ev_timer* timer = check_timer(L, 1); 136 | struct ev_loop* loop = *check_loop_and_init(L, 2); 137 | int is_daemon = lua_toboolean(L, 3); 138 | 139 | ev_timer_start(loop, timer); 140 | loop_start_watcher(L, 2, 1, is_daemon); 141 | 142 | return 0; 143 | } 144 | 145 | /** 146 | * If the timer is pending, return the revents and clear the pending 147 | * status (so the timer callback won't be called). 148 | * 149 | * Usage: 150 | * revents = timer:clear_pending(loop) 151 | * 152 | * [+1, -0, e] 153 | */ 154 | static int timer_clear_pending(lua_State *L) { 155 | ev_timer* timer = check_timer(L, 1); 156 | struct ev_loop* loop = *check_loop_and_init(L, 2); 157 | 158 | int revents = ev_clear_pending(loop, timer); 159 | if ( ! timer->repeat && 160 | ( revents & EV_TIMEOUT ) ) 161 | { 162 | loop_stop_watcher(L, 2, 1); 163 | } 164 | 165 | lua_pushnumber(L, revents); 166 | return 1; 167 | } 168 | -------------------------------------------------------------------------------- /obj_lua_ev.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | static char* obj_registry = "ev{obj}"; 4 | 5 | /** 6 | * Create a "registry" of light userdata pointers into the 7 | * fulluserdata so that we can get handles into the lua objects. 8 | */ 9 | static void create_obj_registry(lua_State *L) { 10 | lua_pushlightuserdata(L, &obj_registry); 11 | lua_newtable(L); 12 | 13 | lua_createtable(L, 0, 1); 14 | lua_pushliteral(L, "v"); 15 | lua_setfield(L, -2, "__mode"); 16 | lua_setmetatable(L, -2); 17 | 18 | lua_rawset(L, LUA_REGISTRYINDEX); 19 | } 20 | 21 | /** 22 | * Count the number of registered objects. This exists only so we can 23 | * validate that objects are properly GC'ed. 24 | * 25 | * [-0, +1, e] 26 | */ 27 | static int obj_count(lua_State *L) { 28 | int count = 0; 29 | 30 | lua_pushlightuserdata(L, &obj_registry); 31 | lua_rawget(L, LUA_REGISTRYINDEX); 32 | assert(lua_istable(L, -1) /* create_obj_registry() should have ran */); 33 | 34 | lua_pushnil(L); 35 | while ( lua_next(L, -2) != 0 ) { 36 | count++; 37 | lua_pop(L, 1); 38 | } 39 | lua_pushinteger(L, count); 40 | return 1; 41 | } 42 | 43 | /** 44 | * Create a new "object" with a metatable of tname and allocate size 45 | * bytes for the object. Also create an fenv associated with the 46 | * object. This fenv is used to keep track of lua objects so that the 47 | * garbage collector doesn't prematurely collect lua objects that are 48 | * referenced by the C data structure. 49 | * 50 | * [-0, +1, ?] 51 | */ 52 | static void* obj_new(lua_State* L, size_t size, const char* tname) { 53 | void* obj; 54 | 55 | obj = lua_newuserdata(L, size); 56 | luaL_getmetatable(L, tname); 57 | lua_setmetatable(L, -2); 58 | 59 | /* Optimized for "watcher" creation that does not use a shadow 60 | * table: 61 | */ 62 | lua_createtable(L, 1, 0); 63 | lua_setuservalue(L, -2); 64 | 65 | return obj; 66 | } 67 | 68 | /** 69 | * Lazily create the shadow table, and provide write access to this 70 | * shadow table. 71 | * 72 | * [-0, +0, ?] 73 | */ 74 | static int obj_newindex(lua_State *L) { 75 | lua_getuservalue(L, 1); 76 | lua_rawgeti(L, -1, WATCHER_SHADOW); 77 | 78 | /* fenv, shadow */ 79 | if ( lua_isnil(L, -1) ) { 80 | /* Lazily create the shadow table: */ 81 | lua_pop(L, 1); 82 | lua_newtable(L); 83 | lua_pushvalue(L, -1); 84 | lua_rawseti(L, -3, WATCHER_SHADOW); 85 | } 86 | 87 | /* h(table, key,value) */ 88 | lua_replace(L, 1); 89 | lua_settop(L, 3); 90 | lua_settable(L, 1); 91 | return 0; 92 | } 93 | 94 | /** 95 | * Provide read access to the shadow table. 96 | * 97 | * [-0, +1, ?] 98 | */ 99 | static int obj_index(lua_State *L) { 100 | if ( lua_getmetatable(L, 1) ) { 101 | lua_pushvalue(L, 2); 102 | lua_gettable(L, -2); 103 | if ( ! lua_isnil(L, -1) ) return 1; 104 | lua_pop(L, 1); 105 | } 106 | lua_getuservalue(L, 1); 107 | lua_rawgeti(L, -1, WATCHER_SHADOW); 108 | 109 | if ( lua_isnil(L, -1) ) return 1; 110 | 111 | lua_pushvalue(L, 2); 112 | lua_gettable(L, -2); 113 | return 1; 114 | } 115 | 116 | /** 117 | * Register the lua object at index obj_i so it is keyed off of the 118 | * obj pointer. 119 | * 120 | * [-0, +0, ?] 121 | */ 122 | static void register_obj(lua_State*L, int obj_i, void* obj) { 123 | obj_i = lua_absindex(L, obj_i); 124 | 125 | lua_pushlightuserdata(L, &obj_registry); 126 | lua_rawget(L, LUA_REGISTRYINDEX); 127 | assert(lua_istable(L, -1) /* create_obj_registry() should have ran */); 128 | 129 | lua_pushlightuserdata(L, obj); 130 | lua_pushvalue(L, obj_i); 131 | lua_rawset(L, -3); 132 | lua_pop(L, 1); 133 | } 134 | 135 | /** 136 | * Pushes the lua representation of n objects onto the stack. The 137 | * objs array must be NULL terminated. Returns the number of objects 138 | * pushed onto the stack. 139 | * 140 | * [-0, +objs_len, m] 141 | */ 142 | static int push_objs(lua_State* L, void** objs) { 143 | int obj_count = 0; 144 | int registry_i; 145 | void** cur; 146 | 147 | for ( cur=objs; *cur; ++cur ) obj_count++; 148 | 149 | if ( 0 == obj_count ) return obj_count; 150 | 151 | lua_checkstack(L, obj_count + 1); 152 | 153 | lua_pushlightuserdata(L, &obj_registry); 154 | lua_rawget(L, LUA_REGISTRYINDEX); 155 | assert(lua_istable(L, -1) /* create_obj_registry() should have ran */); 156 | 157 | registry_i = lua_gettop(L); 158 | for ( cur=objs; *cur; ++cur ) { 159 | lua_pushlightuserdata(L, *cur); 160 | lua_rawget(L, registry_i); 161 | } 162 | 163 | lua_remove(L, registry_i); 164 | 165 | return obj_count; 166 | } 167 | -------------------------------------------------------------------------------- /stat_lua_ev.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Create a table for ev.STAT that gives access to the constructor for 3 | * stat objects. 4 | * 5 | * [-0, +1, ?] 6 | */ 7 | static int luaopen_ev_stat(lua_State *L) { 8 | lua_pop(L, create_stat_mt(L)); 9 | 10 | lua_createtable(L, 0, 1); 11 | 12 | lua_pushcfunction(L, stat_new); 13 | lua_setfield(L, -2, "new"); 14 | 15 | return 1; 16 | } 17 | 18 | /** 19 | * Create the stat metatable in the registry. 20 | * 21 | * [-0, +1, ?] 22 | */ 23 | static int create_stat_mt(lua_State *L) { 24 | 25 | static luaL_Reg fns[] = { 26 | { "stop", stat_stop }, 27 | { "start", stat_start }, 28 | { "getdata", stat_getdata }, 29 | { NULL, NULL } 30 | }; 31 | luaL_newmetatable(L, STAT_MT); 32 | add_watcher_mt(L); 33 | luaL_setfuncs(L, fns, 0); 34 | 35 | return 1; 36 | } 37 | 38 | /** 39 | * Create a new stat object. Arguments: 40 | * 1 - callback function. 41 | * 2 - path 42 | * 3 - interval 43 | * 44 | * @see watcher_new() 45 | * 46 | * [+1, -0, ?] 47 | */ 48 | static int stat_new(lua_State* L) { 49 | const char* path = luaL_checkstring(L, 2); 50 | #if LUA_VERSION_NUM > 502 51 | ev_tstamp interval = (int)luaL_optinteger(L, 3, 0); 52 | #else 53 | ev_tstamp interval = luaL_optint(L, 3, 0); 54 | #endif 55 | ev_stat* stat; 56 | 57 | stat = watcher_new(L, sizeof(ev_stat), STAT_MT); 58 | ev_stat_init(stat, &stat_cb, path, interval); 59 | return 1; 60 | } 61 | 62 | /** 63 | * @see watcher_cb() 64 | * 65 | * [+0, -0, m] 66 | */ 67 | static void stat_cb(struct ev_loop* loop, ev_stat* stat, int revents) { 68 | watcher_cb(loop, stat, revents); 69 | } 70 | 71 | /** 72 | * Stops the stat so it won't be called by the specified event loop. 73 | * 74 | * Usage: 75 | * stat:stop(loop) 76 | * 77 | * [+0, -0, e] 78 | */ 79 | static int stat_stop(lua_State *L) { 80 | ev_stat* stat = check_stat(L, 1); 81 | struct ev_loop* loop = *check_loop_and_init(L, 2); 82 | 83 | loop_stop_watcher(L, 2, 1); 84 | ev_stat_stop(loop, stat); 85 | 86 | return 0; 87 | } 88 | 89 | /** 90 | * Starts the stat so it will be called by the specified event loop. 91 | * 92 | * Usage: 93 | * stat:start(loop [, is_daemon]) 94 | * 95 | * [+0, -0, e] 96 | */ 97 | static int stat_start(lua_State *L) { 98 | ev_stat* stat = check_stat(L, 1); 99 | struct ev_loop* loop = *check_loop_and_init(L, 2); 100 | int is_daemon = lua_toboolean(L, 3); 101 | 102 | ev_stat_start(loop, stat); 103 | loop_start_watcher(L, 2, 1, is_daemon); 104 | 105 | return 0; 106 | } 107 | 108 | #define set_attr(value) \ 109 | lua_pushliteral(L, #value); \ 110 | lua_pushinteger(L, stat->attr.st_##value); \ 111 | lua_settable(L, -3) 112 | 113 | #define set_prev(value) \ 114 | lua_pushliteral(L, #value); \ 115 | lua_pushinteger(L, stat->prev.st_##value); \ 116 | lua_settable(L, -3) 117 | 118 | /** 119 | * Returns a table with the following fields: 120 | * - path: the file system path that is being watched; 121 | * - interval: the specified interval; 122 | * - attr: the most-recently detected attributes of the file in a form 123 | * of table with the following fields: dev, ino, mode, nlink, uid, gid, 124 | * rdev, size, atime, mtime, ctime corresponding to struct stat members 125 | * (st_dev, st_ino, etc.); 126 | * - prev: the previous attributes of the file with the same fields as 127 | * attr fields. 128 | * 129 | * Usage: 130 | * stat:getdata() 131 | * 132 | * [+1, -0, e] 133 | */ 134 | static int stat_getdata(lua_State *L) { 135 | ev_stat* stat = check_stat(L, 1); 136 | 137 | lua_newtable(L); 138 | 139 | lua_pushliteral(L, "path"); 140 | lua_pushstring(L, stat->path); 141 | lua_settable(L, -3); 142 | 143 | lua_pushliteral(L, "interval"); 144 | lua_pushinteger(L, stat->interval); 145 | lua_settable(L, -3); 146 | 147 | /* attr table */ 148 | lua_pushliteral(L, "attr"); 149 | lua_newtable(L); 150 | 151 | set_attr(dev); 152 | set_attr(ino); 153 | set_attr(mode); 154 | set_attr(nlink); 155 | set_attr(uid); 156 | set_attr(gid); 157 | set_attr(rdev); 158 | set_attr(size); 159 | set_attr(atime); 160 | set_attr(mtime); 161 | set_attr(ctime); 162 | 163 | lua_settable(L, -3); 164 | 165 | /* prev table */ 166 | lua_pushliteral(L, "prev"); 167 | lua_newtable(L); 168 | 169 | set_prev(dev); 170 | set_prev(ino); 171 | set_prev(mode); 172 | set_prev(nlink); 173 | set_prev(uid); 174 | set_prev(gid); 175 | set_prev(rdev); 176 | set_prev(size); 177 | set_prev(atime); 178 | set_prev(mtime); 179 | set_prev(ctime); 180 | 181 | lua_settable(L, -3); 182 | 183 | return 1; 184 | } 185 | -------------------------------------------------------------------------------- /tapview: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | # tapview - a TAP (Test Anything Protocol) viewer in pure POSIX shell 3 | # 4 | # Copyright by Eric S. Raymond 5 | # 6 | # This code is intended to be embedded in your project. The author 7 | # grants permission for it to be distributed under the prevailing 8 | # license of your project if you choose, provided that license is 9 | # OSD-compliant; otherwise the following SPDX tag incorporates a 10 | # license by reference. 11 | # 12 | # SPDX-License-Identifier: BSD-2-Clause 13 | # 14 | # This is version 1.1 15 | # A newer version may be available at https://gitlab.com/esr/tapview 16 | # 17 | # POSIX allows but does not mandate that -n suppresses emission of a 18 | # trailing newline in echo. Thus, some shell builtin echos don't do 19 | # that. Cope gracefully. 20 | # shellcheck disable=SC2039 21 | if [ "$(echo -n "a"; echo "b")" = "ab" ] 22 | then 23 | ECHO="echo" 24 | elif [ "$(/bin/echo -n "a"; /bin/echo "b")" = "ab" ] 25 | then 26 | ECHO="/bin/echo" 27 | else 28 | echo "tapview: bailing out, your echo lacks -n support." 29 | exit 3 30 | fi 31 | 32 | OK="." 33 | FAIL="F" 34 | SKIP="s" 35 | TODO_NOT_OK="x" 36 | TODO_OK="u" 37 | 38 | ship_char() { 39 | # shellcheck disable=SC2039 40 | "${ECHO}" -n "$1" 41 | } 42 | 43 | ship_line() { 44 | report="${report}${1}\n" 45 | } 46 | 47 | testcount=0 48 | failcount=0 49 | skipcount=0 50 | todocount=0 51 | test_before_plan=no 52 | test_after_plan=no 53 | expect="" 54 | status=0 55 | 56 | report="" 57 | IFS="" 58 | state=start 59 | while read -r line 60 | do 61 | if expr "$line" : "Bail out!" >/dev/null 62 | then 63 | ship_line "$line" 64 | status=2 65 | break 66 | fi 67 | # Process a plan line 68 | if expr "$line" : '1\.\.[0-9][0-9]*' >/dev/null 69 | then 70 | if [ "$expect" != "" ] 71 | then 72 | if [ "${testcount}" -gt 0 ] 73 | then 74 | echo "" 75 | fi 76 | ship_line "tapview: cannot have more than one plan line." 77 | echo "${report}" 78 | exit 1 79 | fi 80 | if expr "$line" : ".* *SKIP" >/dev/null || expr "$line" : ".* *skip" >/dev/null 81 | then 82 | ship_line "$line" 83 | echo "${report}" 84 | exit 1 # Not specified in the standard whether this should exit 1 or 0 85 | fi 86 | expect=$(expr "$line" : '1\.\.\([0-9][0-9]*\)') 87 | continue 88 | fi 89 | # Process an ok line 90 | if expr "$line" : "ok" >/dev/null 91 | then 92 | testcount=$((testcount + 1)) 93 | if [ "$expect" = "" ] 94 | then 95 | test_before_plan=yes 96 | else 97 | test_after_plan=yes 98 | fi 99 | if expr "$line" : ".*# *TODO" >/dev/null || expr "$line" : ".*# *todo" >/dev/null 100 | then 101 | ship_char ${TODO_OK} 102 | ship_line "$line" 103 | todocount=$((todocount + 1)) 104 | elif expr "$line" : ".*# *SKIP" >/dev/null || expr "$line" : ".*# *skip" >/dev/null 105 | then 106 | ship_char ${SKIP} 107 | ship_line "$line" 108 | skipcount=$((skipcount + 1)) 109 | else 110 | ship_char ${OK} 111 | fi 112 | state=ok 113 | continue 114 | fi 115 | # Process a not-ok line 116 | if expr "$line" : "not ok" >/dev/null 117 | then 118 | testcount=$((testcount + 1)) 119 | if [ "$expect" = "" ] 120 | then 121 | test_before_plan=yes 122 | else 123 | test_after_plan=yes 124 | fi 125 | if expr "$line" : ".*# *SKIP" >/dev/null || expr "$line" : ".*# *skip" >/dev/null 126 | then 127 | ship_char "${SKIP}" 128 | state=ok 129 | skipcount=$((skipcount + 1)) 130 | continue 131 | fi 132 | if expr "$line" : ".*# *TODO" >/dev/null || expr "$line" : ".*# *todo" >/dev/null 133 | then 134 | ship_char ${TODO_NOT_OK} 135 | state=ok 136 | todocount=$((todocount + 1)) 137 | continue 138 | fi 139 | ship_char "${FAIL}" 140 | ship_line "$line" 141 | state=not_ok 142 | failcount=$((failcount + 1)) 143 | status=1 144 | continue 145 | fi 146 | # shellcheck disable=SC2166 147 | if [ "${state}" = "yaml" ] 148 | then 149 | ship_line "$line" 150 | if expr "$line" : '[ ]*\.\.\.' >/dev/null 151 | then 152 | state=ok 153 | fi 154 | elif expr "$line" : "[ ]*---" >/dev/null 155 | then 156 | ship_line "$line" 157 | state=yaml 158 | fi 159 | done 160 | 161 | /bin/echo "" 162 | 163 | if [ -z "$expect" ] 164 | then 165 | ship_line "Missing a plan." 166 | status=1 167 | elif [ "$test_before_plan" = "yes" ] && [ "$test_after_plan" = "yes" ] 168 | then 169 | ship_line "A plan line may only be placed before or after all tests." 170 | status=1 171 | elif [ "${expect}" -gt "${testcount}" ] 172 | then 173 | ship_line "Expected ${expect} tests but only ${testcount} ran." 174 | status=1 175 | elif [ "${expect}" -lt "${testcount}" ] 176 | then 177 | ship_line "Expected ${expect} tests but ${testcount} ran." 178 | status=1 179 | fi 180 | 181 | report="${report}${testcount} tests, ${failcount} failures" 182 | if [ "$todocount" != 0 ] 183 | then 184 | report="${report}, ${todocount} TODOs" 185 | fi 186 | if [ "$skipcount" != 0 ] 187 | then 188 | report="${report}, ${skipcount} SKIPs" 189 | fi 190 | 191 | echo "${report}." 192 | 193 | exit "${status}" 194 | 195 | # end 196 | -------------------------------------------------------------------------------- /example.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2011 by Ross Anderson 2 | -- 3 | -- Permission is hereby granted, free of charge, to any person obtaining a copy 4 | -- of this software and associated documentation files (the "Software"), to deal 5 | -- in the Software without restriction, including without limitation the rights 6 | -- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | -- copies of the Software, and to permit persons to whom the Software is 8 | -- furnished to do so, subject to the following conditions: 9 | -- 10 | -- The above copyright notice and this permission notice shall be included in 11 | -- all copies or substantial portions of the Software. 12 | -- 13 | -- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | -- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | -- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | -- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | -- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | -- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | -- THE SOFTWARE. 20 | ---------------------------------------------------------------------- 21 | -- 22 | -- RUNNING THIS EXAMPLE: 23 | -- 24 | -- lua example.lua 25 | -- 26 | -- HOW IT WORKS: 27 | -- 28 | -- This example attempts to walk you through a "typical" event loop 29 | -- program. In general, all event loop programs work by registering 30 | -- callback functions with the event loop that are executed when a 31 | -- condition is met and the event loop is executing. 32 | -- 33 | -- In this example, we start by registering an "idol" callback: 34 | -- 35 | -- ev.Idle.new(build_all_timers):start(ev.Loop.default) 36 | -- 37 | -- This callback is ran whenever the event loop has nothing else to 38 | -- do. Note that this callback is not immediately executed when it 39 | -- is registered with the event loop, but instead defers execution 40 | -- until the event loop begins. In this case, the event loop begins 41 | -- executing when this line of code runs: 42 | -- 43 | -- ev.Loop.default:loop() 44 | -- 45 | -- So, after the event loop is running it immediately calls 46 | -- build_all_timers with the event loop and idol watcher as 47 | -- arguments. The first thing we do is cancel this idol watcher with 48 | -- this line of code: 49 | -- 50 | -- idol_event:stop(loop) 51 | -- 52 | -- If we do not cancel this idol watcher, then build_all_timers will 53 | -- run again the next time that the event loop is idol. Try 54 | -- commenting this line out and see what happens :-). 55 | -- 56 | -- In the next line of code we call build_timer which creates a timer 57 | -- that fires every 5 seconds (since interval=5): 58 | -- 59 | -- local timer = ev.Timer.new(callback, interval, interval) 60 | -- timer:start(loop) 61 | -- 62 | -- We then register other times that fire at various other intervals, 63 | -- the smallest interval being every 0.5 seconds. 64 | -- 65 | -- After build_all_timers finishes execution, the function returns 66 | -- back to the event loop so that other callbacks can be executed. 67 | -- In this example, there are 4 active "watchers" all waiting for 68 | -- various time intervals. Naturally, the smallest time interval 69 | -- (callback "3" which fires every 0.5 seconds) will be called by the 70 | -- event loop 3 or 4 times before callback "2" which fires ever 2 71 | -- seconds has a chance to execute. 72 | -- 73 | -- Eventually, after 10 seconds worth of timers getting fired is 74 | -- done, we remove these "watchers" from the event loop and when no 75 | -- more active watchers exist in the event loop, the event loop 76 | -- returns: 77 | -- 78 | -- ev.Loop.default:loop() 79 | -- -- At last the above line of code completes. 80 | 81 | 82 | local ev = require'ev' 83 | 84 | 85 | -- build a timer event loop with a task defined as a callback 86 | function build_timer(loop, number, interval) 87 | local i = 0 88 | local function callback(loop, timer_event) 89 | print(os.date(), "CB " .. tostring(number), "interval: " .. interval) 90 | i = i + interval 91 | if i >= 10 then 92 | timer_event:stop(loop) 93 | end 94 | end 95 | local timer = ev.Timer.new(callback, interval, interval) 96 | timer:start(loop) 97 | return timer 98 | end 99 | 100 | -- our "bootstrapping" callback 101 | function build_all_timers(loop, idol_event) 102 | -- We have "bootstapped" into the event loop and therefore do not 103 | -- care about having this function execute when the event loop is 104 | -- idol: 105 | idol_event:stop(loop) 106 | 107 | print("Run build_all_timers callback") 108 | build_timer(loop, 1, 5.0) 109 | -- By making the 2 second timer a higher priority than the 0.5 110 | -- second timer, we should see the 0.5 timer execute 3 times 111 | -- before the 2.0 timer goes off insead of seeing it execute 4 112 | -- times. 113 | build_timer(loop, 2, 2.0):priority(ev.MAXPRI) 114 | build_timer(loop, 3, 0.5) 115 | build_timer(loop, 4, 10.0) 116 | end 117 | 118 | print("Register build_all_timers callback") 119 | ev.Idle.new(build_all_timers):start(ev.Loop.default) 120 | 121 | print("Run the event loop") 122 | ev.Loop.default:loop() 123 | -------------------------------------------------------------------------------- /test/test_ev_timer.lua: -------------------------------------------------------------------------------- 1 | print '1..20' 2 | 3 | local src_dir, build_dir = ... 4 | package.path = src_dir .. "?.lua;" .. package.path 5 | package.cpath = build_dir .. "?.so;" .. package.cpath 6 | 7 | local tap = require("tap") 8 | local ev = require("ev") 9 | local help = require("help") 10 | local dump = require("dumper").dump 11 | local ok = tap.ok 12 | 13 | local noleaks = help.collect_and_assert_no_watchers 14 | local loop = ev.Loop.default 15 | 16 | -- Simply see if we can do a simple one second timer: 17 | function test_basic() 18 | local timer1 = ev.Timer.new( 19 | function(loop, timer, revents) 20 | ok(true, 'one second timer') 21 | ok(ev.TIMEOUT == revents, 'ev.TIMEOUT(' .. ev.TIMEOUT .. ') == revents (' .. revents .. ')') 22 | end, 23 | 0.01) 24 | timer1:start(loop) 25 | loop:loop() 26 | end 27 | 28 | -- Test daemon=true on timer() 29 | function test_daemon_true() 30 | local timer1_count = 0 31 | local timer1 = ev.Timer.new( 32 | function(loop, timer) 33 | timer1_count = timer1_count + 1 34 | ok(timer1_count == 1, 'once and only once') 35 | end, 0.1) 36 | timer1:start(loop) 37 | 38 | local timer2_count = 0 39 | local timer2 = ev.Timer.new( 40 | function(loop, timer) 41 | timer2_count = timer2_count + 1 42 | ok(timer2_count == 1, 'once and only once') 43 | end, 0.01) 44 | 45 | timer2:start(loop, true) 46 | 47 | loop:loop() 48 | 49 | local timer3 = ev.Timer.new( 50 | function(loop, timer) 51 | ok(false, 'Should never be called!') 52 | end, 0.01) 53 | 54 | timer3:start(loop, true) 55 | 56 | 57 | loop:loop() 58 | 59 | -- TODO: Should we make it so timer3 is automatically stopped if it is never executed in the event loop? 60 | timer3:stop(loop) 61 | end 62 | 63 | -- Test again() method 64 | function test_again() 65 | local timer1_count = 0 66 | local timer1 = ev.Timer.new( 67 | function(loop, timer) 68 | timer1_count = timer1_count + 1 69 | if timer1_count == 3 then 70 | timer:stop(loop) 71 | else 72 | timer:again(loop, 0.1) 73 | end 74 | end, 0.1) 75 | timer1:start(loop) 76 | loop:loop() 77 | ok(timer1_count == 3, 'timer1 called thrice') 78 | end 79 | 80 | -- Test stop(), start(), and is_active() 81 | function test_start_stop_active() 82 | local timer1_count = 0 83 | local timer1 = ev.Timer.new( 84 | function(loop, timer) 85 | timer1_count = timer1_count + 1 86 | ok(timer1_count == 1, 'once and only once') 87 | end, 0.01) 88 | 89 | ok(not timer1:is_active(), 'not active') 90 | 91 | timer1:start(loop) 92 | 93 | ok(timer1:is_active(), 'active') 94 | 95 | timer1:stop(loop) 96 | 97 | loop:loop() 98 | 99 | timer1:start(loop) 100 | 101 | loop:loop() 102 | end 103 | 104 | -- Test invoke() 105 | function test_callback() 106 | local timer1_count1 = 0 107 | local timer1 = ev.Timer.new( 108 | function() 109 | timer1_count1 = timer1_count1 + 1 110 | ok(timer1_count1 == 1, 'once and only once A') 111 | end, 0.01) 112 | 113 | -- Test calling the callback manually: 114 | timer1:callback()() 115 | 116 | local timer1_count2 = 0 117 | 118 | -- Test setting the callback: 119 | timer1:callback( 120 | function(loop, timer) 121 | timer1_count2 = timer1_count2 + 1 122 | ok(timer1_count2 == 1, 'once and only once B') 123 | end) 124 | 125 | -- Register it and have it get called: 126 | timer1:start(loop) 127 | 128 | loop:loop() 129 | end 130 | 131 | -- Test is_pending() 132 | function test_is_pending() 133 | local num_pending = 0 134 | local num_called = 0 135 | local timer2 136 | local timer1 = ev.Timer.new( 137 | function(loop, timer) 138 | if ( timer2:is_pending() ) then 139 | num_pending = num_pending + 1 140 | end 141 | num_called = num_called + 1 142 | end, 0.01) 143 | 144 | timer1:start(loop) 145 | 146 | timer2 = ev.Timer.new( 147 | function(loop, timer) 148 | if ( timer1:is_pending() ) then 149 | num_pending = num_pending + 1 150 | end 151 | num_called = num_called + 1 152 | end, 0.01) 153 | 154 | timer2:start(loop) 155 | 156 | loop:loop() 157 | 158 | ok(num_pending == 1, 'exactly one timer was pending') 159 | ok(num_called == 2, 'both timers got called') 160 | end 161 | 162 | -- Test clear_pending() 163 | function test_clear_pending() 164 | local num_called = 0 165 | local timer2 166 | local timer1 = ev.Timer.new( 167 | function(loop, timer) 168 | if ( timer2:is_pending() ) then 169 | timer2:clear_pending(loop) 170 | end 171 | num_called = num_called + 1 172 | end, 0.01) 173 | 174 | timer1:start(loop) 175 | 176 | timer2 = ev.Timer.new( 177 | function(loop, timer) 178 | if ( timer1:is_pending() ) then 179 | timer1:clear_pending(loop) 180 | end 181 | num_called = num_called + 1 182 | end, 0.01) 183 | 184 | timer2:start(loop) 185 | 186 | loop:loop() 187 | 188 | ok(num_called == 1, 'exactly one timer was called') 189 | end 190 | 191 | 192 | noleaks(test_basic, "test_basic") 193 | noleaks(test_daemon_true, "test_daemon_true") 194 | noleaks(test_again, "test_again") 195 | noleaks(test_start_stop_active, "test_start_stop_active") 196 | noleaks(test_callback, "test_callback") 197 | noleaks(test_is_pending, "test_is_pending") 198 | noleaks(test_clear_pending, "test_clear_pending") 199 | --print(dump("registry", debug.getregistry()[1])); 200 | 201 | -- test_is_pending() 202 | -- noleaks("test_is_pending") 203 | -- test_clear_pending() 204 | -- noleaks("test_clear_pending") 205 | 206 | -------------------------------------------------------------------------------- /child_lua_ev.c: -------------------------------------------------------------------------------- 1 | #ifndef _WIN32 2 | #include 3 | #endif 4 | 5 | #if LUA_VERSION_NUM > 502 6 | #define luaL_checkbool(L, i) (lua_isboolean(L, i) ? lua_toboolean(L, i) : (int)luaL_checkinteger(L, i)) 7 | #else 8 | #define luaL_checkbool(L, i) (lua_isboolean(L, i) ? lua_toboolean(L, i) : luaL_checkint(L, i)) 9 | #endif 10 | 11 | /** 12 | * Create a table for ev.CHILD that gives access to the constructor for 13 | * child objects. 14 | * 15 | * [-0, +1, ?] 16 | */ 17 | static int luaopen_ev_child(lua_State *L) { 18 | lua_pop(L, create_child_mt(L)); 19 | 20 | lua_createtable(L, 0, 1); 21 | 22 | lua_pushcfunction(L, child_new); 23 | lua_setfield(L, -2, "new"); 24 | 25 | return 1; 26 | } 27 | 28 | /** 29 | * Create the child metatable in the registry. 30 | * 31 | * [-0, +1, ?] 32 | */ 33 | static int create_child_mt(lua_State *L) { 34 | static luaL_Reg fns[] = { 35 | { "stop", child_stop }, 36 | { "start", child_start }, 37 | { "getpid" , child_getpid }, 38 | { "getrpid" , child_getrpid }, 39 | { "getstatus" , child_getstatus }, 40 | { NULL, NULL } 41 | }; 42 | luaL_newmetatable(L, CHILD_MT); 43 | add_watcher_mt(L); 44 | luaL_setfuncs(L, fns, 0); 45 | 46 | return 1; 47 | } 48 | 49 | /** 50 | * Create a new child object. Arguments: 51 | * 1 - callback function. 52 | * 2 - pid number (0 for any pid) 53 | * 3 - trace (either false - only activate the watcher when the process 54 | * terminates or true - additionally activate the watcher when the 55 | * process is stopped or continued). 56 | * 57 | * @see watcher_new() 58 | * 59 | * [+1, -0, ?] 60 | */ 61 | static int child_new(lua_State* L) { 62 | #if LUA_VERSION_NUM > 502 63 | int pid = (int)luaL_checkinteger(L, 2); 64 | #else 65 | int pid = luaL_checkint(L, 2); 66 | #endif 67 | int trace = luaL_checkbool(L, 3); 68 | ev_child* child; 69 | 70 | child = watcher_new(L, sizeof(ev_child), CHILD_MT); 71 | ev_child_init(child, &child_cb, pid, trace); 72 | return 1; 73 | } 74 | 75 | /** 76 | * @see watcher_cb() 77 | * 78 | * [+0, -0, m] 79 | */ 80 | static void child_cb(struct ev_loop* loop, ev_child* child, int revents) { 81 | watcher_cb(loop, child, revents); 82 | } 83 | 84 | /** 85 | * Stops the child so it won't be called by the specified event loop. 86 | * 87 | * Usage: 88 | * child:stop(loop) 89 | * 90 | * [+0, -0, e] 91 | */ 92 | static int child_stop(lua_State *L) { 93 | ev_child* child = check_child(L, 1); 94 | struct ev_loop* loop = *check_loop_and_init(L, 2); 95 | 96 | loop_stop_watcher(L, 2, 1); 97 | ev_child_stop(loop, child); 98 | 99 | return 0; 100 | } 101 | 102 | /** 103 | * Starts the child so it will be called by the specified event loop. 104 | * 105 | * Usage: 106 | * child:start(loop [, is_daemon]) 107 | * 108 | * [+0, -0, e] 109 | */ 110 | static int child_start(lua_State *L) { 111 | ev_child* child = check_child(L, 1); 112 | struct ev_loop* loop = *check_loop_and_init(L, 2); 113 | int is_daemon = lua_toboolean(L, 3); 114 | 115 | ev_child_start(loop, child); 116 | loop_start_watcher(L, 2, 1, is_daemon); 117 | 118 | return 0; 119 | } 120 | 121 | /** 122 | * Returns the child pid (the process id this watcher watches out for, or 0, 123 | * meaning any process id). 124 | * Usage: 125 | * child:getpid() 126 | * 127 | * [+1, -0, e] 128 | */ 129 | static int child_getpid(lua_State *L) { 130 | ev_child* child = check_child(L, 1); 131 | 132 | lua_pushinteger (L, child->pid); 133 | 134 | return 1; 135 | } 136 | 137 | /** 138 | * Returns the child rpid (the process id that detected a status change). 139 | * Usage: 140 | * child:getrpid() 141 | * 142 | * [+1, -0, e] 143 | */ 144 | static int child_getrpid(lua_State *L) { 145 | ev_child* child = check_child(L, 1); 146 | 147 | lua_pushinteger (L, child->rpid); 148 | 149 | return 1; 150 | } 151 | 152 | static void populate_child_status_table(ev_child *child, lua_State *L) { 153 | int exited, stopped, signaled; 154 | 155 | exited = WIFEXITED(child->rstatus); 156 | stopped = WIFSTOPPED(child->rstatus); 157 | signaled = WIFSIGNALED(child->rstatus); 158 | 159 | lua_pushstring(L, "status"); 160 | lua_pushinteger(L, child->rstatus); 161 | lua_settable(L, -3); 162 | 163 | lua_pushstring(L, "exited"); 164 | lua_pushboolean(L, exited); 165 | lua_settable(L, -3); 166 | 167 | lua_pushstring(L, "stopped"); 168 | lua_pushboolean(L, stopped); 169 | lua_settable(L, -3); 170 | 171 | lua_pushstring(L, "signaled"); 172 | lua_pushboolean(L, signaled); 173 | lua_settable(L, -3); 174 | 175 | if (exited) { 176 | lua_pushstring(L, "exit_status"); 177 | lua_pushinteger(L, WEXITSTATUS(child->rstatus)); 178 | lua_settable(L, -3); 179 | } 180 | 181 | if (stopped) { 182 | lua_pushstring(L, "stop_signal"); 183 | lua_pushinteger(L, WSTOPSIG(child->rstatus)); 184 | lua_settable(L, -3); 185 | } 186 | 187 | if (signaled) { 188 | lua_pushstring(L, "term_signal"); 189 | lua_pushinteger(L, WTERMSIG(child->rstatus)); 190 | lua_settable(L, -3); 191 | } 192 | } 193 | 194 | /** 195 | * Returns the process exit/trace status caused by "rpid" (see your systems 196 | * "waitpid" and "sys/wait.h" for details). 197 | * Usage: 198 | * child:getstatus() 199 | * 200 | * It returns the table with the following fields: 201 | * - exited: true if status was returned for a child process that terminated 202 | * normally; 203 | * - stopped: true if status was returned for a child process that is currently 204 | * stopped; 205 | * - signaled: true if status was returned for a child process that terminated 206 | * due to receipt of a signal that was not caught; 207 | * - exit_status: (only if exited == true) the low-order 8 bits of the status 208 | * argument that the child process passed to _exit() or exit(), or the value 209 | * the child process returned from main(); 210 | * - stop_signal: (only if stopped == true) the number of the signal that 211 | * caused the child process to stop; 212 | * - term_signal: (only if signaled == true) the number of the signal that 213 | * caused the termination of the child process. 214 | * 215 | * [+1, -0, e] 216 | */ 217 | static int child_getstatus(lua_State *L) { 218 | ev_child* child = check_child(L, 1); 219 | 220 | lua_newtable(L); 221 | #ifndef _WIN32 222 | populate_child_status_table(child, L); 223 | #endif 224 | 225 | return 1; 226 | } 227 | -------------------------------------------------------------------------------- /watcher_lua_ev.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | static const char* watcher_magic = "a string that is used" 4 | " to mark a watcher"; 5 | 6 | /** 7 | * Add watcher specific methods to the table on the top of the lua 8 | * stack. 9 | * 10 | * [-0, +0, ?] 11 | */ 12 | static int add_watcher_mt(lua_State *L) { 13 | 14 | static luaL_Reg fns[] = { 15 | { "is_active", watcher_is_active }, 16 | { "is_pending", watcher_is_pending }, 17 | { "clear_pending", watcher_clear_pending }, 18 | { "callback", watcher_callback }, 19 | { "priority", watcher_priority }, 20 | { "__index", obj_index }, 21 | { "__newindex", obj_newindex }, 22 | { NULL, NULL } 23 | }; 24 | luaL_setfuncs(L, fns, 0); 25 | 26 | /* Mark this as being a watcher: */ 27 | lua_pushliteral(L, "is_watcher__"); 28 | lua_pushlightuserdata(L, (void*)watcher_magic); 29 | lua_rawset(L, -3); 30 | 31 | return 0; 32 | } 33 | 34 | /** 35 | * Checks that we have a watcher at watcher_i index by validating the 36 | * metatable has the is_watcher__ field set to the watcher magic light 37 | * userdata that is simply used to mark a metatable as being a 38 | * "watcher". 39 | * 40 | * [-0, +0, ?] 41 | */ 42 | static struct ev_watcher* check_watcher(lua_State *L, int watcher_i) { 43 | void *watcher = lua_touserdata(L, watcher_i); 44 | if ( watcher != NULL ) { /* Got a userdata? */ 45 | if ( lua_getmetatable(L, watcher_i) ) { /* got a metatable? */ 46 | lua_getfield(L, -1, "is_watcher__"); 47 | lua_pushlightuserdata(L, (void*)watcher_magic); 48 | 49 | if ( lua_rawequal(L, -1, -2) ) { 50 | lua_pop(L, 3); 51 | return (struct ev_watcher*)watcher; 52 | } 53 | } 54 | } 55 | luaL_error(L, "ev{io,timer,signal,idle} expected, got %s", luaL_typename(L, watcher_i)); 56 | return NULL; 57 | } 58 | 59 | /** 60 | * Test if the watcher is active. 61 | * 62 | * Usage: 63 | * bool = watcher:is_active() 64 | * 65 | * [+1, -0, e] 66 | */ 67 | static int watcher_is_active(lua_State *L) { 68 | lua_pushboolean(L, ev_is_active(check_watcher(L, 1))); 69 | return 1; 70 | } 71 | 72 | /** 73 | * Test if the watcher is pending. 74 | * 75 | * Usage: 76 | * bool = watcher:is_pending() 77 | * 78 | * [+1, -0, e] 79 | */ 80 | static int watcher_is_pending(lua_State *L) { 81 | lua_pushboolean(L, ev_is_pending(check_watcher(L, 1))); 82 | return 1; 83 | } 84 | 85 | /** 86 | * If the watcher is pending, return the revents and clear the pending 87 | * status (so the watcher callback won't be called). 88 | * 89 | * Usage: 90 | * revents = watcher:clear_pending(loop) 91 | * 92 | * [+1, -0, e] 93 | */ 94 | static int watcher_clear_pending(lua_State *L) { 95 | lua_pushnumber(L, ev_clear_pending(*check_loop_and_init(L, 2), check_watcher(L, 1))); 96 | return 1; 97 | } 98 | 99 | /** 100 | * Implement the new function on all the watcher objects. The first 101 | * element on the stack must be the callback function. The new 102 | * "watcher" is now at the top of the stack. 103 | * 104 | * [+1, -0, ?] 105 | */ 106 | static void* watcher_new(lua_State* L, size_t size, const char* lua_type) { 107 | void* obj; 108 | 109 | luaL_checktype(L, 1, LUA_TFUNCTION); 110 | 111 | obj = obj_new(L, size, lua_type); 112 | register_obj(L, -1, obj); 113 | 114 | lua_getuservalue(L, -1); 115 | lua_pushvalue(L, 1); 116 | lua_rawseti(L, -2, WATCHER_FN); 117 | 118 | lua_pop(L, 1); 119 | 120 | return obj; 121 | } 122 | 123 | /** 124 | * Implements the callback function on all the watcher objects. This 125 | * will be indirectly called by the libev event loop implementation. 126 | * 127 | * TODO: Custom error handlers? Currently, any error in a callback 128 | * will print the error to stderr and things will "go on". 129 | * 130 | * [+0, -0, m] 131 | */ 132 | static void watcher_cb(struct ev_loop *loop, void *watcher, int revents) { 133 | lua_State* L = ev_userdata(loop); 134 | void* objs[3] = { loop, watcher, NULL }; 135 | int result; 136 | 137 | lua_pushcfunction(L, traceback); 138 | 139 | result = lua_checkstack(L, 5); 140 | assert(result != 0 /* able to allocate enough space on lua stack */); 141 | result = push_objs(L, objs); 142 | assert(result == 2 /* pushed two objects on the lua stack */); 143 | assert(!lua_isnil(L, -2) /* the loop obj was resolved */); 144 | assert(!lua_isnil(L, -1) /* the watcher obj was resolved */); 145 | 146 | /* STACK: , , */ 147 | 148 | if ( !ev_is_active(watcher) ) { 149 | /* Must remove "stop"ed watcher from loop: */ 150 | loop_stop_watcher(L, -2, -1); 151 | } 152 | 153 | lua_getuservalue(L, -1); 154 | assert(lua_istable(L, -1) /* The watcher fenv was found */); 155 | lua_rawgeti(L, -1, WATCHER_FN); 156 | if ( lua_isnil(L, -1) ) { 157 | /* The watcher function was set to nil, so do nothing */ 158 | lua_pop(L, 5); 159 | return; 160 | } 161 | assert(lua_isfunction(L, -1) /* watcher function is a function */); 162 | 163 | /* STACK: , , , , */ 164 | 165 | lua_insert(L, -4); 166 | lua_pop(L, 1); 167 | lua_pushinteger(L, revents); 168 | 169 | /* STACK: , , , , */ 170 | if ( lua_pcall(L, 3, 0, -5) ) { 171 | /* TODO: Enable user-specified error handler! */ 172 | fprintf(stderr, "CALLBACK FAILED: %s\n", 173 | lua_tostring(L, -1)); 174 | lua_pop(L, 2); 175 | } else { 176 | lua_pop(L, 1); 177 | } 178 | } 179 | 180 | /** 181 | * Get/set the watcher callback. If passed a new_callback, then the 182 | * old_callback will be returned. Otherwise, just returns the current 183 | * callback function. 184 | * 185 | * Usage: 186 | * old_callback = watcher:callback([new_callback]) 187 | * 188 | * [+1, -0, e] 189 | */ 190 | static int watcher_callback(lua_State *L) { 191 | int has_fn = lua_gettop(L) > 1; 192 | 193 | check_watcher(L, 1); 194 | if ( has_fn ) luaL_checktype(L, 2, LUA_TFUNCTION); 195 | 196 | lua_getuservalue(L, 1); 197 | assert(lua_istable(L, -1) /* getfenv of watcher worked */); 198 | lua_rawgeti(L, -1, WATCHER_FN); 199 | if ( has_fn ) { 200 | lua_pushvalue(L, 2); 201 | lua_rawseti(L, -3, WATCHER_FN); 202 | } 203 | lua_remove(L, -2); 204 | return 1; 205 | } 206 | 207 | /** 208 | * Get/set the watcher priority. If passed a new_priority, then the 209 | * old_priority will be returned. Otherwise, just returns the current 210 | * priority. 211 | * 212 | * Usage: 213 | * old_priority = watcher:priority([new_priority]) 214 | * 215 | * [+1, -0, e] 216 | */ 217 | static int watcher_priority(lua_State *L) { 218 | int has_pri = lua_gettop(L) > 1; 219 | ev_watcher *w = check_watcher(L, 1); 220 | int old_pri = ev_priority(w); 221 | 222 | #if LUA_VERSION_NUM > 502 223 | if ( has_pri ) ev_set_priority(w, (int)luaL_checkinteger(L, 2)); 224 | #else 225 | if ( has_pri ) ev_set_priority(w, luaL_checkint(L, 2)); 226 | #endif 227 | lua_pushinteger(L, old_pri); 228 | return 1; 229 | } 230 | -------------------------------------------------------------------------------- /lua_ev.h: -------------------------------------------------------------------------------- 1 | /** 2 | * This is a private header file. If you use it outside of this 3 | * package, then you will be surprised by any changes made. 4 | * 5 | * Documentation for functions declared here is with the function 6 | * implementation. 7 | */ 8 | 9 | /** 10 | * Compatibility defines 11 | */ 12 | 13 | #if EV_VERSION_MAJOR < 3 || (EV_VERSION_MAJOR == 3 && EV_VERSION_MINOR < 7) 14 | # warning lua-ev requires version 3.7 or newer of libev 15 | #endif 16 | 17 | #if LUA_VERSION_NUM <= 501 18 | 19 | /** Backwards compatibility shims: */ 20 | #define lua_absindex(L, i) \ 21 | ((i) > 0 || (i) <= LUA_REGISTRYINDEX ? \ 22 | (i) : lua_gettop(L) + (i) + 1) 23 | 24 | #define lua_setuservalue(L, i) lua_setfenv((L), (i)) 25 | 26 | #define lua_getuservalue(L, i) lua_getfenv((L), (i)) 27 | 28 | /* NOTE: this only works if nups == 0! */ 29 | #define luaL_setfuncs(L, fns, nups) luaL_register((L), NULL, (fns)) 30 | 31 | #endif 32 | 33 | /** 34 | * Define the names used for the metatables. 35 | */ 36 | #define LOOP_MT "ev{loop}" 37 | #define IO_MT "ev{io}" 38 | #define ASYNC_MT "ev{async}" 39 | #define TIMER_MT "ev{timer}" 40 | #define SIGNAL_MT "ev{signal}" 41 | #define IDLE_MT "ev{idle}" 42 | #define CHILD_MT "ev{child}" 43 | #define STAT_MT "ev{stat}" 44 | 45 | /** 46 | * Special token to represent the uninitialized default loop. This is 47 | * so we can defer initializing the default loop for as long as 48 | * possible. 49 | */ 50 | #define UNINITIALIZED_DEFAULT_LOOP (struct ev_loop*)1 51 | 52 | /** 53 | * The location in the fenv of the watcher that contains the callback 54 | * function. 55 | */ 56 | #define WATCHER_FN 1 57 | 58 | /** 59 | * The location in the fenv of the shadow table. 60 | */ 61 | #define WATCHER_SHADOW 2 62 | 63 | /** 64 | * Various "check" functions simply call luaL_checkudata() and do the 65 | * appropriate casting, with the exception of check_watcher which is 66 | * implemented as a C function. 67 | */ 68 | 69 | /** 70 | * If there is any chance that the loop is not initialized yet, then 71 | * please use check_loop_and_init() instead! Also note that the 72 | * loop userdata is a pointer, so don't forget to de-reference the 73 | * result. 74 | */ 75 | #define check_loop(L, narg) \ 76 | ((struct ev_loop**) luaL_checkudata((L), (narg), LOOP_MT)) 77 | 78 | #define check_timer(L, narg) \ 79 | ((struct ev_timer*) luaL_checkudata((L), (narg), TIMER_MT)) 80 | 81 | #define check_io(L, narg) \ 82 | ((struct ev_io*) luaL_checkudata((L), (narg), IO_MT)) 83 | 84 | #define check_async(L, narg) \ 85 | ((struct ev_async*) luaL_checkudata((L), (narg), ASYNC_MT)) 86 | 87 | #define check_signal(L, narg) \ 88 | ((struct ev_signal*) luaL_checkudata((L), (narg), SIGNAL_MT)) 89 | 90 | #define check_idle(L, narg) \ 91 | ((struct ev_idle*) luaL_checkudata((L), (narg), IDLE_MT)) 92 | 93 | #define check_child(L, narg) \ 94 | ((struct ev_child*) luaL_checkudata((L), (narg), CHILD_MT)) 95 | 96 | #define check_stat(L, narg) \ 97 | ((struct ev_stat*) luaL_checkudata((L), (narg), STAT_MT)) 98 | 99 | 100 | /** 101 | * Generic functions: 102 | */ 103 | static int version(lua_State *L); 104 | static int traceback(lua_State *L); 105 | 106 | /** 107 | * Loop functions: 108 | */ 109 | static int luaopen_ev_loop(lua_State *L); 110 | static int create_loop_mt(lua_State *L); 111 | static struct ev_loop** loop_alloc(lua_State *L); 112 | static struct ev_loop** check_loop_and_init(lua_State *L, int loop_i); 113 | static int loop_new(lua_State *L); 114 | static int loop_delete(lua_State *L); 115 | static void loop_start_watcher(lua_State* L, int loop_i, int watcher_i, int is_daemon); 116 | static void loop_stop_watcher(lua_State* L, int loop_i, int watcher_i); 117 | static int loop_is_default(lua_State *L); 118 | static int loop_iteration(lua_State *L); 119 | static int loop_depth(lua_State *L); 120 | static int loop_now(lua_State *L); 121 | static int loop_update_now(lua_State *L); 122 | static int loop_loop(lua_State *L); 123 | static int loop_unloop(lua_State *L); 124 | static int loop_backend(lua_State *L); 125 | static int loop_fork(lua_State *L); 126 | 127 | /** 128 | * Object functions: 129 | */ 130 | static void create_obj_registry(lua_State *L); 131 | static int obj_count(lua_State *L); 132 | static void* obj_new(lua_State* L, size_t size, const char* tname); 133 | static int obj_newindex(lua_State *L); 134 | static int obj_index(lua_State *L); 135 | 136 | static int push_objs(lua_State* L, void** objs); 137 | 138 | /** 139 | * Watcher functions: 140 | */ 141 | static int add_watcher_mt(lua_State *L); 142 | static int watcher_is_active(lua_State *L); 143 | static int watcher_is_pending(lua_State *L); 144 | static int watcher_clear_pending(lua_State *L); 145 | static void* watcher_new(lua_State* L, size_t size, const char* lua_type); 146 | static int watcher_callback(lua_State *L); 147 | static int watcher_priority(lua_State *L); 148 | static void watcher_cb(struct ev_loop *loop, void *watcher, int revents); 149 | static struct ev_watcher* check_watcher(lua_State *L, int watcher_i); 150 | 151 | /** 152 | * Timer functions: 153 | */ 154 | static int luaopen_ev_timer(lua_State *L); 155 | static int create_timer_mt(lua_State *L); 156 | static int timer_new(lua_State* L); 157 | static void timer_cb(struct ev_loop* loop, ev_timer* timer, int revents); 158 | static int timer_again(lua_State *L); 159 | static int timer_stop(lua_State *L); 160 | static int timer_start(lua_State *L); 161 | static int timer_clear_pending(lua_State *L); 162 | 163 | /** 164 | * IO functions: 165 | */ 166 | static int luaopen_ev_io(lua_State *L); 167 | static int create_io_mt(lua_State *L); 168 | static int io_new(lua_State* L); 169 | static void io_cb(struct ev_loop* loop, ev_io* io, int revents); 170 | static int io_stop(lua_State *L); 171 | static int io_start(lua_State *L); 172 | static int io_getfd(lua_State *L); 173 | 174 | /** 175 | * Async functions: 176 | */ 177 | static int luaopen_ev_async(lua_State *L); 178 | static int create_async_mt(lua_State *L); 179 | static int async_new(lua_State* L); 180 | static void async_cb(struct ev_loop* loop, ev_async* async, int revents); 181 | static int async_send(lua_State *L); 182 | static int async_stop(lua_State *L); 183 | static int async_start(lua_State *L); 184 | 185 | /** 186 | * Signal functions: 187 | */ 188 | static int luaopen_ev_signal(lua_State *L); 189 | static int create_signal_mt(lua_State *L); 190 | static int signal_new(lua_State* L); 191 | static void signal_cb(struct ev_loop* loop, ev_signal* sig, int revents); 192 | static int signal_stop(lua_State *L); 193 | static int signal_start(lua_State *L); 194 | 195 | /** 196 | * Idle functions: 197 | */ 198 | static int luaopen_ev_idle(lua_State *L); 199 | static int create_idle_mt(lua_State *L); 200 | static int idle_new(lua_State* L); 201 | static void idle_cb(struct ev_loop* loop, ev_idle* idle, int revents); 202 | static int idle_stop(lua_State *L); 203 | static int idle_start(lua_State *L); 204 | 205 | /** 206 | * Child functions: 207 | */ 208 | static int luaopen_ev_child(lua_State *L); 209 | static int create_child_mt(lua_State *L); 210 | static int child_new(lua_State* L); 211 | static void child_cb(struct ev_loop* loop, ev_child* sig, int revents); 212 | static int child_stop(lua_State *L); 213 | static int child_start(lua_State *L); 214 | static int child_getpid(lua_State *L); 215 | static int child_getrpid(lua_State *L); 216 | static int child_getstatus(lua_State *L); 217 | 218 | /** 219 | * Stat functions: 220 | */ 221 | static int luaopen_ev_stat(lua_State *L); 222 | static int create_stat_mt(lua_State *L); 223 | static int stat_new(lua_State* L); 224 | static void stat_cb(struct ev_loop* loop, ev_stat* sig, int revents); 225 | static int stat_stop(lua_State *L); 226 | static int stat_start(lua_State *L); 227 | static int stat_start(lua_State *L); 228 | static int stat_getdata(lua_State *L); 229 | -------------------------------------------------------------------------------- /loop_lua_ev.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Create a table for ev.Loop that gives access to the constructor for 3 | * loop objects and the "default" event loop object instance. 4 | * 5 | * [-0, +1, ?] 6 | */ 7 | static int luaopen_ev_loop(lua_State *L) { 8 | lua_pop(L, create_loop_mt(L)); 9 | 10 | lua_createtable(L, 0, 1); 11 | 12 | lua_pushcfunction(L, loop_new); 13 | lua_setfield(L, -2, "new"); 14 | 15 | *loop_alloc(L) = UNINITIALIZED_DEFAULT_LOOP; 16 | lua_setfield(L, -2, "default"); 17 | 18 | return 1; 19 | } 20 | 21 | /** 22 | * Create the loop metatable in the registry. 23 | * 24 | * [-0, +1, ?] 25 | */ 26 | static int create_loop_mt(lua_State *L) { 27 | 28 | static luaL_Reg fns[] = { 29 | { "is_default", loop_is_default }, 30 | { "count", loop_iteration }, /* old API */ 31 | { "iteration", loop_iteration }, 32 | { "depth", loop_depth }, 33 | { "now", loop_now }, 34 | { "update_now", loop_update_now }, 35 | { "loop", loop_loop }, 36 | { "unloop", loop_unloop }, 37 | { "backend", loop_backend }, 38 | { "fork", loop_fork }, 39 | { "__gc", loop_delete }, 40 | { NULL, NULL } 41 | }; 42 | luaL_newmetatable(L, LOOP_MT); 43 | luaL_setfuncs(L, fns, 0); 44 | lua_pushvalue(L, -1); 45 | lua_setfield(L, -2, "__index"); 46 | 47 | return 1; 48 | } 49 | 50 | /** 51 | * Create a table intended as the loop object, sets the metatable, 52 | * registers it, creates the evlua_loop struct appropriately, and sets 53 | * the userdata fenv to an empty table. This table is used to store 54 | * references to active watchers so the garbage collector does not 55 | * prematurely collect the watchers. 56 | * 57 | * [-0, +1, v] 58 | */ 59 | static struct ev_loop** loop_alloc(lua_State *L) { 60 | struct ev_loop** loop = (struct ev_loop**) 61 | obj_new(L, sizeof(struct ev_loop*), LOOP_MT); 62 | 63 | return loop; 64 | } 65 | 66 | /** 67 | * Validates that loop_i is a loop object and checks if it is the 68 | * special UNINITIALIZED_DEFAULT_LOOP token, and if so it initializes 69 | * the default loop. If everything checks out fine, then a pointer to 70 | * the ev_loop object is returned. 71 | */ 72 | static struct ev_loop** check_loop_and_init(lua_State *L, int loop_i) { 73 | struct ev_loop** loop_r = check_loop(L, loop_i); 74 | if ( UNINITIALIZED_DEFAULT_LOOP == *loop_r ) { 75 | *loop_r = ev_default_loop(EVFLAG_AUTO); 76 | if ( NULL == *loop_r ) { 77 | luaL_error(L, 78 | "libev init failed, perhaps LIBEV_FLAGS environment variable " 79 | " is causing it to select a bad backend?"); 80 | } 81 | register_obj(L, loop_i, *loop_r); 82 | } 83 | return loop_r; 84 | } 85 | 86 | 87 | 88 | /** 89 | * Create a new non-default loop instance. 90 | * 91 | * [-0, +1, ?] 92 | */ 93 | static int loop_new(lua_State *L) { 94 | struct ev_loop** loop_r = loop_alloc(L); 95 | 96 | unsigned int flags = lua_isnumber(L, 1) ? 97 | lua_tointeger(L, 1) : EVFLAG_AUTO; 98 | 99 | *loop_r = ev_loop_new(flags); 100 | 101 | register_obj(L, -1, *loop_r); 102 | 103 | return 1; 104 | } 105 | 106 | /** 107 | * Delete a loop instance. Default event loop is ignored. 108 | */ 109 | static int loop_delete(lua_State *L) { 110 | struct ev_loop* loop = *check_loop(L, 1); 111 | 112 | if ( UNINITIALIZED_DEFAULT_LOOP == loop || 113 | ev_is_default_loop(loop) ) return 0; 114 | 115 | ev_loop_destroy(loop); 116 | return 0; 117 | } 118 | 119 | /** 120 | * Must be called aftert start()ing a watcher. This is necessary so 121 | * that the watcher is not prematurely garbage collected, and if the 122 | * watcher is "marked as a daemon", then ev_unref() is called so that 123 | * this watcher does not prevent the event loop from terminating. 124 | * 125 | * is_daemon may be -1 to specify that if this watcher is already 126 | * registered in the event loop, then use the current is_daemon value, 127 | * otherwise set is_daemon to false. 128 | * 129 | * [-0, +0, m] 130 | */ 131 | static void loop_start_watcher(lua_State* L, int loop_i, int watcher_i, int is_daemon) { 132 | int current_is_daemon = -1; 133 | 134 | loop_i = lua_absindex(L, loop_i); 135 | watcher_i = lua_absindex(L, watcher_i); 136 | 137 | /* Check that watcher isn't already registered: */ 138 | lua_getuservalue(L, loop_i); 139 | lua_pushvalue(L, watcher_i); 140 | lua_rawget(L, -2); 141 | 142 | if ( ! lua_isnil(L, -1) ) { 143 | current_is_daemon = lua_toboolean(L, -1); 144 | } 145 | lua_pop(L, 1); 146 | 147 | if ( is_daemon == -1 ) { 148 | /* Set is_daemon properly for -1 case. */ 149 | if ( current_is_daemon == -1 ) 150 | is_daemon = 0; 151 | else 152 | is_daemon = current_is_daemon; 153 | } 154 | 155 | /* Currently not initialized, or daemon status change? */ 156 | if ( -1 == current_is_daemon || 157 | current_is_daemon ^ is_daemon ) 158 | { 159 | lua_pushvalue(L, watcher_i); 160 | lua_pushboolean(L, is_daemon); 161 | lua_rawset(L, -3); 162 | lua_pop(L, 1); 163 | 164 | if ( ! is_daemon && -1 == current_is_daemon ) { 165 | /* Do nothing, we are initializing a non-daemon */ 166 | } else if ( is_daemon ) { 167 | /* unref() so that we are a "daemon" */ 168 | ev_unref(*check_loop_and_init(L, loop_i)); 169 | } else { 170 | /* ref() so that we are no longer a "daemon" */ 171 | ev_ref(*check_loop_and_init(L, loop_i)); 172 | } 173 | } 174 | } 175 | 176 | /** 177 | * Must be called aftert stop()ing a watcher, or after a watcher is 178 | * automatically stopped (such as a non-repeating timer expiring). 179 | * This is necessary so that the watcher is not prematurely garbage 180 | * collected, and if the watcher is "marked as a daemon", then 181 | * ev_ref() is called in order to "undo" what was done in 182 | * loop_add_watcher(). 183 | * 184 | * [-0, +0, m] 185 | */ 186 | static void loop_stop_watcher(lua_State* L, int loop_i, int watcher_i) { 187 | loop_i = lua_absindex(L, loop_i); 188 | watcher_i = lua_absindex(L, watcher_i); 189 | 190 | lua_getuservalue(L, loop_i); 191 | lua_pushvalue(L, watcher_i); 192 | lua_rawget(L, -2); 193 | 194 | if ( lua_toboolean(L, -1) ) ev_ref(*check_loop_and_init(L, loop_i)); 195 | lua_pop(L, 1); 196 | 197 | lua_pushvalue(L, watcher_i); 198 | lua_pushnil(L); 199 | lua_rawset(L, -3); 200 | lua_pop(L, 1); 201 | } 202 | 203 | /** 204 | * Check if this is the default event loop. 205 | */ 206 | static int loop_is_default(lua_State *L) { 207 | struct ev_loop* loop = *check_loop(L, 1); 208 | lua_pushboolean(L, 209 | UNINITIALIZED_DEFAULT_LOOP == loop || 210 | ev_is_default_loop(loop) ); 211 | return 1; 212 | } 213 | 214 | /** 215 | * How many times have we iterated though the event loop? 216 | */ 217 | static int loop_iteration(lua_State *L) { 218 | struct ev_loop* loop = *check_loop(L, 1); 219 | lua_pushinteger(L, 220 | UNINITIALIZED_DEFAULT_LOOP == loop ? 221 | 0 : ev_loop_count(loop)); 222 | return 1; 223 | } 224 | 225 | /** 226 | * How many times have we iterated though the event loop? 227 | */ 228 | static int loop_depth(lua_State *L) { 229 | struct ev_loop* loop = *check_loop(L, 1); 230 | lua_pushinteger(L, 231 | UNINITIALIZED_DEFAULT_LOOP == loop ? 232 | 0 : ev_loop_depth(loop)); 233 | return 1; 234 | } 235 | 236 | /** 237 | * The current event loop time. 238 | */ 239 | static int loop_now(lua_State *L) { 240 | lua_pushnumber(L, ev_now(*check_loop_and_init(L, 1))); 241 | return 1; 242 | } 243 | 244 | /** 245 | * Sync the event loop time with "real" time and return the "real" 246 | * time. 247 | */ 248 | static int loop_update_now(lua_State *L) { 249 | struct ev_loop* loop = *check_loop_and_init(L, 1); 250 | ev_now_update(loop); 251 | lua_pushnumber(L, ev_now(loop)); 252 | return 1; 253 | } 254 | 255 | /** 256 | * Actually do the event loop. 257 | */ 258 | static int loop_loop(lua_State *L) { 259 | struct ev_loop *loop = *check_loop_and_init(L, 1); 260 | void *old_userdata = ev_userdata(loop); 261 | ev_set_userdata(loop, L); 262 | ev_loop(loop, 0); 263 | ev_set_userdata(loop, old_userdata); 264 | return 0; 265 | } 266 | 267 | /** 268 | * "Quit" out of the event loop. 269 | */ 270 | static int loop_unloop(lua_State *L) { 271 | ev_unloop(*check_loop_and_init(L, 1), EVUNLOOP_ALL); 272 | return 0; 273 | } 274 | 275 | /** 276 | * Determine which backend is implementing the event loop. 277 | * 278 | * [-0, +1, m] 279 | */ 280 | static int loop_backend(lua_State *L) { 281 | lua_pushinteger(L, ev_backend(*check_loop_and_init(L, 1))); 282 | return 1; 283 | } 284 | 285 | /** 286 | * Make it safe to resume an event loop after the fork(2) system call. 287 | * 288 | * [-0, +0, m] 289 | */ 290 | static int loop_fork(lua_State *L) { 291 | struct ev_loop* loop = *check_loop(L, 1); 292 | 293 | if ( UNINITIALIZED_DEFAULT_LOOP == loop ) { 294 | // Do nothing! 295 | } else if ( ev_is_default_loop(loop) ) { 296 | ev_default_fork(); 297 | } else { 298 | ev_loop_fork(loop); 299 | } 300 | 301 | return 0; 302 | } 303 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # lua-ev 2 | 3 | ## Requirements 4 | 5 | * [libev](http://dist.schmorp.de/libev/): 6 | [mirror](http://software.schmorp.de/pkg/libev.html) 7 | * [CMake](http://www.cmake.org/cmake/resources/software.html) for building 8 | 9 | ## Loading the library 10 | 11 | * If you built the library as a loadable package 12 | ```lua 13 | [local] ev = require 'ev' 14 | ``` 15 | * If you compiled the package statically into your application, call 16 | the function `luaopen_ev(L)`. It will create a table with the ev 17 | functions and leave it on the stack. 18 | 19 | ## Choosing a backend: 20 | 21 | Set the `LIBEV_FLAGS=` environment variable to choose a backend: 22 | 23 | 1. select() 24 | 2. poll() 25 | 4. epoll() 26 | 8. kqueue 27 | 16. /dev/poll (not implemented) 28 | 32. Solaris port 29 | 30 | Please see the documentation for libev for more details. 31 | 32 | **WARNING**: 33 | 34 | If your program `fork()`s, then you will need to re-initialize 35 | your event loop(s) in the child process. You can do this 36 | re-initialization using the `loop:fork()` function. 37 | 38 | **NOTE**: 39 | 40 | If you are new to event loop programming, take a look at 41 | [example.lua](example.lua). 42 | 43 | ## ev functions 44 | 45 | ### major, minor = ev.version() 46 | 47 | returns numeric ev version for the major and minor 48 | levels of the version dynamically linked in. 49 | 50 | ### loop = ev.Loop.new() 51 | 52 | Create a new non-default event loop. See ev.Loop object methods 53 | below. 54 | 55 | ### loop = ev.Loop.default 56 | 57 | The "default" event loop. See ev.Loop object methods below. 58 | Note that the default loop is "lazy loaded". 59 | 60 | ### timer = ev.Timer.new(on_timeout, after_seconds [, repeat_seconds]) 61 | 62 | Create a new timer that will call the on_timeout function when the 63 | timer expires. The timer initially expires in after_seconds 64 | (floating point), then it will expire in repeat_seconds (floating 65 | point) over and over again (unless repeat_seconds is zero or is 66 | omitted in which case, this timer will only fire once). 67 | 68 | The returned timer is an ev.Timer object. See below for the 69 | methods on this object. 70 | 71 | **NOTE**: You must explicitly register the timer with an event loop in 72 | order for it to take effect. 73 | 74 | The on_timeout function will be called with these arguments 75 | (return values are ignored): 76 | 77 | ### on_timeout(loop, timer, revents) 78 | 79 | The loop is the event loop for which the timer is registered, 80 | the timer is the ev.Timer object, and revents is ev.TIMEOUT. 81 | 82 | See also `ev_timer_init()` C function. 83 | 84 | ### sig = ev.Signal.new(on_signal, signal_number) 85 | 86 | Create a new signal watcher that will call the on_signal function 87 | when the specified signal_number is delivered. 88 | 89 | The returned sig is an ev.Signal object. See below for the methods on 90 | this object. 91 | 92 | **NOTE**: You must explicitly register the sig with an event loop in 93 | order for it to take effect. 94 | 95 | The on_signal function will be called with these arguments (return 96 | values are ignored): 97 | 98 | ### on_signal(loop, sig, revents) 99 | 100 | The loop is the event loop for which the io object is 101 | registered, the sig parameter is the ev.Signal object, and 102 | revents is ev.SIGNAL. 103 | 104 | See also `ev_signal_init()` C function. 105 | 106 | ### io = ev.IO.new(on_io, file_descriptor, revents) 107 | 108 | Create a new io watcher that will call the on_io function when the 109 | specified file_descriptor is available for read and/or write 110 | depending on the revents. The revents parameter must be either 111 | ev.READ, ev.WRITE, or the bitwise or of ev.READ and ev.WRITE (use 112 | bitlib to do bitwise or). 113 | 114 | The returned io is an ev.IO object. See below for the methods on 115 | this object. 116 | 117 | **NOTE**: You must explicitly register the io with an event loop in 118 | order for it to take effect. 119 | 120 | The on_io function will be called with these arguments (return 121 | values are ignored): 122 | 123 | ### on_io(loop, io, revents) 124 | 125 | The loop is the event loop for which the io object is 126 | registered, the io parameter is the ev.IO object, and revents 127 | is a bit set consisting of ev.READ and/or ev.WRITE and/or 128 | ev.TIMEOUT depending on which event triggered this callback. 129 | Of course ev.TIMEOUT won't be in that set since this is the io 130 | watcher. 131 | 132 | See also `ev_io_init()` C function. 133 | 134 | ### idle = ev.Idle.new(on_idle) 135 | 136 | Create a new io watcher that will call the on_idle function 137 | whenever there is nothing else to do. This means that the loop 138 | will never block while an idle watcher is started. 139 | 140 | The returned io is an ev.Idle object. See below for the methods on 141 | this object. 142 | 143 | **NOTE**: You must explicitly register the idle with an event loop in 144 | order for it to take effect. 145 | 146 | The on_idle function will be called with these arguments (return 147 | values are ignored): 148 | 149 | ### on_idle(loop, idle, revents) 150 | 151 | The loop is the event loop for which the idle object is 152 | registered, the idle parameter is the ev.Idle object, and 153 | revents is ev.IDLE. 154 | 155 | See also `ev_idle_init()` C function. 156 | 157 | ### async = ev.Async.new(on_async) 158 | 159 | Create a new async watcher that will call the on_async function 160 | whenever an application calls ev_async_send() from another context 161 | (this can be another thread or some other context which does not 162 | control the loop this watcher lives on) 163 | 164 | The returned async is an ev.Async object. See below for the methods 165 | on this object. 166 | 167 | NOTE: You must explicitly register the async with an event loop in 168 | order for it to take effect. 169 | 170 | The on_async function will be called with these arguments (return 171 | values are ignored): 172 | 173 | For a LuaJIT/FFI helper about the use of ev_async_send() from another thread, see [contrib/ev-async.lua](contrib/ev-async.lua). 174 | 175 | ### on_async(loop, idle, revents) 176 | 177 | The loop is the event loop for which the idle object is 178 | registered, the idle parameter is the ev.Idle object, and 179 | revents is ev.IDLE. 180 | 181 | See also `ev_idle_init()` C function. 182 | 183 | ### child = ev.Child.new(on_child, pid, trace) 184 | 185 | Create a new child watcher that will call the on_child function 186 | whenever SIGCHLD for registered pid is delivered. 187 | 188 | When pid is set to 0 the watcher will fire for any pid. 189 | 190 | When trace is false the watcher will be activated when the process 191 | terminates. If it's true - it will additionally be activated when 192 | the process is stopped or continued. 193 | 194 | The returned child is an ev.Child object. See below for the methods on 195 | this object. 196 | 197 | NOTE: You must explicitly register the idle with an event loop in 198 | order for it to take effect. 199 | 200 | The on_child function will be called with these arguments (return 201 | values are ignored): 202 | 203 | ### on_child(loop, child, revents) 204 | 205 | The loop is the event loop for which the idle object is 206 | registered, the child parameter is the ev.Child object, and 207 | revents is ev.CHILD. 208 | 209 | See also `ev_child_init()` C function. 210 | 211 | ### stat = ev.Stat.new(on_stat, path [, interval]) 212 | 213 | Configures the watcher to wait for status changes of the given "path". 214 | The "interval" is a hint on how quickly a change is expected to be 215 | detected and may normally be left out to let libev choose a suitable 216 | value. 217 | 218 | The returned stat is an ev.Stat object. See below for the methods on 219 | this object. 220 | 221 | NOTE: You must explicitly register the stat with an event loop in 222 | order for it to take effect. 223 | 224 | The on_stat function will be called with these arguments (return 225 | values are ignored): 226 | 227 | ### on_stat(loop, stat, revents) 228 | 229 | The loop is the event loop for which the idle object is 230 | registered, the stat parameter is the ev.Stat object, and 231 | revents is ev.STAT. 232 | 233 | See also `ev_stat_init()` C function. 234 | 235 | ### ev.READ (constant) 236 | 237 | If this bit is set, the io watcher is ready to read. See also 238 | `EV_READ` C definition. 239 | 240 | ### ev.WRITE (constant) 241 | 242 | If this bit is set, the io watcher is ready to write. See also 243 | `EV_WRITE` C definition. 244 | 245 | ### ev.TIMEOUT (constant) 246 | 247 | If this bit is set, the watcher was triggered by a timeout. See 248 | also `EV_TIMEOUT` C definition. 249 | 250 | ### ev.SIGNAL (constant) 251 | 252 | If this bit is set, the watcher was triggered by a signal. See 253 | also `EV_SIGNAL` C definition. 254 | 255 | ### ev.ASYNC (constant) 256 | 257 | If this bit is set, the watcher has been asynchronously notified. See also 258 | `EV_ASYNC` C definition. 259 | 260 | ### ev.CHILD (constant) 261 | 262 | If this bit is set, the watcher was triggered by a child signal. 263 | See also `EV_CHILD` C definition. 264 | 265 | ### ev.STAT (constant) 266 | 267 | If this bit is set, the watcher was triggered by a change in 268 | attributes of the file system path. See also `EV_STAT` C definition. 269 | 270 | ## ev.Loop object methods 271 | 272 | ### loop:fork() 273 | 274 | You *must* call this function in the child process after `fork(2)` 275 | system call and before the next iteration of the event loop. 276 | 277 | ### loop:loop() 278 | 279 | Run the event loop! Returns when there are no more watchers 280 | registered with the event loop. See special note below about 281 | calling ev_loop() C API. 282 | 283 | See also `ev_loop()` C function. 284 | 285 | ### bool = loop:is_default() 286 | 287 | Returns true if the referenced loop object is the default event 288 | loop. 289 | 290 | See also `ev_is_default_loop()` C function. 291 | 292 | ### num = loop:iteration() 293 | 294 | Returns the number of loop iterations. Note that this function 295 | used to be called loop:count(). 296 | 297 | See also `ev_iterations()` C function. 298 | 299 | ### num = loop:depth() [libev >= 3.7] 300 | 301 | Returns the number of times loop:loop() was entered minus the 302 | number of times loop:loop() was exited, in other words, the 303 | recursion depth. 304 | 305 | This method is available only if lua-ev was linked 306 | with libev 3.7 and higher. 307 | 308 | See also `ev_depth()` C function. 309 | 310 | ### epochs = loop:now() 311 | 312 | Returns the non-integer epoch seconds time at which the current 313 | iteration of the event loop woke up. 314 | 315 | See also `ev_now()` C function. 316 | 317 | ### epochs = loop:update_now() 318 | 319 | Updates the current time returned by loop.now(), and returns that 320 | timestamp. 321 | 322 | See also `ev_now_update()` C function. 323 | 324 | ### loop:unloop() 325 | 326 | Process all outstanding events in the event loop, but do not make 327 | another iteration of the event loop. 328 | 329 | See also `ev_unloop()` C function. 330 | 331 | ### backend_id = loop:backend() 332 | 333 | Returns the identifier of the current backend which is being used 334 | by this event loop. See the libev documentation for what each 335 | number means: 336 | 337 | http://pod.tst.eu/http://cvs.schmorp.de/libev/ev.pod#FUNCTIONS_CONTROLLING_THE_EVENT_LOOP 338 | 339 | ## object methods common to all watcher types 340 | 341 | ### bool = watcher:is_active() 342 | 343 | Returns true if the watcher is active (has been start()ed, but not 344 | stop()ed). 345 | 346 | See also `ev_is_active()` C function. 347 | 348 | ### bool = watcher:is_pending() 349 | 350 | Returns true if the watcher is pending (it has outstanding events 351 | but its callback has not yet been invoked). 352 | 353 | See also `ev_is_pending()` C function. 354 | 355 | ### revents = watcher:clear_pending() 356 | 357 | If the watcher is pending, return the revents of the pending 358 | event, otherwise returns zero. If the event was pending, the 359 | pending flag is cleared (and therefore this watcher event will not 360 | trigger the events callback). 361 | 362 | See also `ev_clear_pending()` C function. 363 | 364 | ### old_priority = watcher:priority([new_priority]) 365 | 366 | Get access to the priority of this watcher, optionally setting a 367 | new priority. The priority should be a small integer between 368 | ev.MINPRI and ev.MAXPRI. The default is 0. 369 | 370 | See also the `ev_priority()` and `ev_set_priority()` C functions. 371 | 372 | ### old_callback = watcher:callback([new_callback]) 373 | 374 | Get access to the callback function associated with this watcher, 375 | optionally setting a new callback function. 376 | 377 | ## ev.Timer object methods 378 | 379 | ### timer:start(loop [, is_daemon]) 380 | 381 | Start the timer in the specified event loop. Optionally make this 382 | watcher a "daemon" watcher which means that the event loop will 383 | terminate even if this watcher has not triggered. 384 | 385 | See also `ev_timer_start()` C function (document as `ev_TYPE_start()`). 386 | 387 | ### timer:stop(loop) 388 | 389 | Unregister this timer from the specified event loop. Ensures that 390 | the watcher is neither active nor pending. 391 | 392 | See also `ev_timer_stop()` C function (document as `ev_TYPE_stop()`). 393 | 394 | ### timer:again(loop [, seconds]) 395 | 396 | Reset the timer so that it doesn't trigger again in the specified 397 | loop until the specified number of seconds from now have elapsed. 398 | If seconds is not specified, uses the repeat_seconds specified 399 | when the timer was created. 400 | 401 | See also `ev_timer_again()` C function. 402 | 403 | ## ev.IO object methods 404 | 405 | ### io:start(loop [, is_daemon]) 406 | 407 | Start the io in the specified event loop. Optionally make this 408 | watcher a "daemon" watcher which means that the event loop will 409 | terminate even if this watcher has not triggered. 410 | 411 | See also `ev_io_start()` C function (document as `ev_TYPE_start()`). 412 | 413 | ### io:stop(loop) 414 | 415 | Unregister this io from the specified event loop. Ensures that 416 | the watcher is neither active nor pending. 417 | 418 | See also `ev_io_stop()` C function (document as `ev_TYPE_stop()`). 419 | 420 | ### fd = io:getfd() 421 | 422 | Returns the file descriptor associated with the IO object. 423 | 424 | ## ev.Idle object methods 425 | 426 | idle:start(loop [, is_daemon]) 427 | 428 | Start the idle watcher in the specified event loop. Optionally 429 | make this watcher a "daemon" watcher which means that the event 430 | loop will terminate even if this watcher has not triggered. 431 | 432 | See also `ev_idle_start()` C function (document as `ev_TYPE_start()`). 433 | 434 | ### idle:stop(loop) 435 | 436 | Unregister this idle watcher from the specified event loop. 437 | Ensures that the watcher is neither active nor pending. 438 | 439 | See also `ev_io_stop()` C function (document as `ev_TYPE_stop()`). 440 | 441 | ## ev.Async object methods 442 | 443 | ### async:start(loop [, is_daemon]) 444 | 445 | Start the async watcher in the specified event loop. Optionally 446 | make this watcher a "daemon" watcher which means that the event 447 | loop will terminate even if this watcher has not triggered. 448 | 449 | See also `ev_async_start()` C function (document as `ev_TYPE_start()`). 450 | 451 | ### async:stop(loop) 452 | 453 | Unregister this async watcher from the specified event loop. 454 | Ensures that the watcher is neither active nor pending. 455 | 456 | See also `ev_async_stop()` C function (document as `ev_TYPE_stop()`). 457 | 458 | ### async:send(loop) 459 | 460 | Sends/signals/activates the given "ev_async" watcher, that is, feeds an 461 | "EV_ASYNC" event on the watcher into the event loop, and instantly returns. 462 | 463 | See also `ev_async_send()` C function. 464 | 465 | ## ev.Child object methods 466 | 467 | ### child:start(loop [, is_daemon]) 468 | 469 | Start the child watcher in the specified event loop. Optionally 470 | make this watcher a "daemon" watcher which means that the event 471 | loop will terminate even if this watcher has not triggered. 472 | 473 | See also `ev_child_start()` C function (document as `ev_TYPE_start()`). 474 | 475 | ### child:stop(loop) 476 | 477 | Unregister this child watcher from the specified event loop. 478 | Ensures that the watcher is neither active nor pending. 479 | 480 | See also `ev_child_stop()` C function (document as `ev_TYPE_stop()`). 481 | 482 | ### child:getpid() 483 | 484 | Return the process id this watcher watches out for, or 0, meaning 485 | any process id. 486 | 487 | ### child:getrpid() 488 | 489 | Return the process id that detected a status change. 490 | 491 | ### child:getstatus() 492 | 493 | Returns the process exit/trace status caused by "rpid" (see your systems 494 | "waitpid" and "sys/wait.h" for details). 495 | 496 | It returns the table with the following fields: 497 | 498 | - `exited`: true if status was returned for a child process that terminated 499 | normally; 500 | - `stopped`: true if status was returned for a child process that is 501 | currently stopped; 502 | - `signaled`: true if status was returned for a child process that terminated 503 | due to receipt of a signal that was not caught; 504 | - `exit_status`: (only if exited == true) the low-order 8 bits of the status 505 | argument that the child process passed to _exit() or exit(), or the value 506 | the child process returned from main(); 507 | - stop_signal`: (only if stopped == true) the number of the signal that 508 | caused the child process to stop; 509 | - `term_signal`: (only if signaled == true) the number of the signal that 510 | caused the termination of the child process. 511 | 512 | ## ev.Stat object methods 513 | 514 | ### stat:start(loop [, is_daemon]) 515 | 516 | Start the stat watcher in the specified event loop. Optionally 517 | make this watcher a "daemon" watcher which means that the event 518 | loop will terminate even if this watcher has not triggered. 519 | 520 | See also `ev_stat_start()` C function (document as `ev_TYPE_start()`). 521 | 522 | ### stat:stop(loop) 523 | 524 | Unregister this stat watcher from the specified event loop. 525 | Ensures that the watcher is neither active nor pending. 526 | 527 | See also `ev_stat_stop()` C function (document as `ev_TYPE_stop()`). 528 | 529 | ### stat:getdata() 530 | 531 | Returns a table with the following fields: 532 | 533 | * - path: the file system path that is being watched; 534 | * - interval: the specified interval; 535 | * - attr: the most-recently detected attributes of the file in a form 536 | * of table with the following fields: dev, ino, mode, nlink, uid, gid, 537 | * rdev, size, atime, mtime, ctime corresponding to struct stat members 538 | * (st_dev, st_ino, etc.); 539 | * - prev: the previous attributes of the file with the same fields as 540 | * attr fields. 541 | 542 | ### EXCEPTION HANDLING NOTE 543 | 544 | If there is an exception when calling a watcher callback, the error 545 | will be printed to stderr. In the future, it would be cool if 546 | there was a linked list of error handlers, and that if a callback 547 | registers another callback, this linked list of error handlers 548 | would be inherited so that exception handling can be done more 549 | easily. To do this, we just need to add a method for adding an 550 | error handler for the current callback context, and keep calling 551 | the error handlers in the linked list until an error handler 552 | actually handles the exception. If the error handling stack 553 | unwinds, we will probably just resort to printing to stderr. 554 | 555 | ### CALLING ev_loop() C API DIRECTLY: 556 | 557 | If you want to call the `ev_loop()` C API directly, then you **MUST** 558 | set the loop userdata field to the `lua_State` in which you want all 559 | callbacks to be ran in. In the past, the `lua_State` was stored as 560 | a thread local which was set when `loop:loop()` lua function is 561 | called, but this made lua-ev dependent upon pthreads and forced 562 | `ev_loop()` to only be called from lua. Next, I removed the pthreads 563 | dependency and stored the `lua_State` in which the callback was 564 | registered. This doesn't bode well when working with coroutines as 565 | it means that `loop:loop()` may call back into a coroutine that is 566 | long gone. 567 | 568 | The current implementation relies on libev 3.7's ability to set a 569 | userdata field associated with an event loop, and the loop:loop() 570 | implementation simply sets the userdata field for the duration of 571 | `loop:loop()`. This does mean that if you want to call `ev_loop()` 572 | from C then you either need to be sure that no lua watchers are 573 | registered with that loop, or you need to set the userdata field to 574 | the `lua_State*` in which the callbacks should be ran. 575 | 576 | ## TODO 577 | 578 | * [ ] Add support for other watcher types (periodic, embed, etc). 579 | 580 | [MIT License](LICENSE) 581 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | ********************************************************************** 2 | * Author : Brian Maher 3 | * Library : lua-ev - Lua 5.1 interface to libev 4 | * 5 | * The MIT License 6 | * 7 | * Copyright (c) 2009-1010 Brian Maher 8 | * 9 | * Permission is hereby granted, free of charge, to any person obtaining a copy 10 | * of this software and associated documentation files (the "Software"), to deal 11 | * in the Software without restriction, including without limitation the rights 12 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | * copies of the Software, and to permit persons to whom the Software is 14 | * furnished to do so, subject to the following conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be included in 17 | * all copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | * THE SOFTWARE. 26 | ********************************************************************** 27 | 28 | To use this library, you need libev, get it here: 29 | http://dist.schmorp.de/libev/ 30 | http://software.schmorp.de/pkg/libev.html 31 | 32 | To build this library, you need CMake, get it here: 33 | http://www.cmake.org/cmake/resources/software.html 34 | 35 | Loading the library: 36 | 37 | If you built the library as a loadable package 38 | [local] ev = require 'ev' 39 | 40 | If you compiled the package statically into your application, call 41 | the function "luaopen_ev(L)". It will create a table with the ev 42 | functions and leave it on the stack. 43 | 44 | Choosing a backend: 45 | 46 | Set the LIBEV_FLAGS= environment variable to choose a backend: 47 | 48 | 1 - select() 49 | 2 - poll() 50 | 4 - epoll() 51 | 8 - kqueue 52 | 16 - /dev/poll [not implemented] 53 | 32 - Solaris port 54 | 55 | Please see the documentation for libev for more details. 56 | 57 | WARNING: 58 | 59 | If your program fork()s, then you will need to re-initialize 60 | your event loop(s) in the child process. You can do this 61 | re-initialization using the loop:fork() function. 62 | 63 | NOTE: 64 | 65 | If you are new to event loop programming, take a look at example.lua 66 | 67 | -- ev functions -- 68 | 69 | major, minor = ev.version() 70 | 71 | returns numeric ev version for the major and minor 72 | levels of the version dynamically linked in. 73 | 74 | loop = ev.Loop.new() 75 | 76 | Create a new non-default event loop. See ev.Loop object methods 77 | below. 78 | 79 | loop = ev.Loop.default 80 | 81 | The "default" event loop. See ev.Loop object methods below. 82 | Note that the default loop is "lazy loaded". 83 | 84 | timer = ev.Timer.new(on_timeout, after_seconds [, repeat_seconds]) 85 | 86 | Create a new timer that will call the on_timeout function when the 87 | timer expires. The timer initially expires in after_seconds 88 | (floating point), then it will expire in repeat_seconds (floating 89 | point) over and over again (unless repeat_seconds is zero or is 90 | omitted in which case, this timer will only fire once). 91 | 92 | The returned timer is an ev.Timer object. See below for the 93 | methods on this object. 94 | 95 | NOTE: You must explicitly register the timer with an event loop in 96 | order for it to take effect. 97 | 98 | The on_timeout function will be called with these arguments 99 | (return values are ignored): 100 | 101 | on_timeout(loop, timer, revents) 102 | 103 | The loop is the event loop for which the timer is registered, 104 | the timer is the ev.Timer object, and revents is ev.TIMEOUT. 105 | 106 | See also ev_timer_init() C function. 107 | 108 | sig = ev.Signal.new(on_signal, signal_number) 109 | 110 | Create a new signal watcher that will call the on_signal function 111 | when the specified signal_number is delivered. 112 | 113 | The returned sig is an ev.Signal object. See below for the methods on 114 | this object. 115 | 116 | NOTE: You must explicitly register the sig with an event loop in 117 | order for it to take effect. 118 | 119 | The on_signal function will be called with these arguments (return 120 | values are ignored): 121 | 122 | on_signal(loop, sig, revents) 123 | 124 | The loop is the event loop for which the io object is 125 | registered, the sig parameter is the ev.Signal object, and 126 | revents is ev.SIGNAL. 127 | 128 | See also ev_signal_init() C function. 129 | 130 | io = ev.IO.new(on_io, file_descriptor, revents) 131 | 132 | Create a new io watcher that will call the on_io function when the 133 | specified file_descriptor is available for read and/or write 134 | depending on the revents. The revents parameter must be either 135 | ev.READ, ev.WRITE, or the bitwise or of ev.READ and ev.WRITE (use 136 | bitlib to do bitwise or). 137 | 138 | The returned io is an ev.IO object. See below for the methods on 139 | this object. 140 | 141 | NOTE: You must explicitly register the io with an event loop in 142 | order for it to take effect. 143 | 144 | The on_io function will be called with these arguments (return 145 | values are ignored): 146 | 147 | on_io(loop, io, revents) 148 | 149 | The loop is the event loop for which the io object is 150 | registered, the io parameter is the ev.IO object, and revents 151 | is a bit set consisting of ev.READ and/or ev.WRITE and/or 152 | ev.TIMEOUT depending on which event triggered this callback. 153 | Of course ev.TIMEOUT won't be in that set since this is the io 154 | watcher. 155 | 156 | See also ev_io_init() C function. 157 | 158 | idle = ev.Idle.new(on_idle) 159 | 160 | Create a new io watcher that will call the on_idle function 161 | whenever there is nothing else to do. This means that the loop 162 | will never block while an idle watcher is started. 163 | 164 | The returned io is an ev.Idle object. See below for the methods on 165 | this object. 166 | 167 | NOTE: You must explicitly register the idle with an event loop in 168 | order for it to take effect. 169 | 170 | The on_idle function will be called with these arguments (return 171 | values are ignored): 172 | 173 | on_idle(loop, idle, revents) 174 | 175 | The loop is the event loop for which the idle object is 176 | registered, the idle parameter is the ev.Idle object, and 177 | revents is ev.IDLE. 178 | 179 | See also ev_idle_init() C function. 180 | 181 | async = ev.Async.new(on_async) 182 | 183 | Create a new async watcher that will call the on_async function 184 | whenever an application calls ev_async_send() from another context 185 | (this can be another thread or some other context which does not 186 | control the loop this watcher lives on) 187 | 188 | The returned async is an ev.Async object. See below for the methods 189 | on this object. 190 | 191 | NOTE: You must explicitly register the async with an event loop in 192 | order for it to take effect. 193 | 194 | The on_async function will be called with these arguments (return 195 | values are ignored): 196 | 197 | on_async(loop, idle, revents) 198 | 199 | The loop is the event loop for which the idle object is 200 | registered, the idle parameter is the ev.Idle object, and 201 | revents is ev.IDLE. 202 | 203 | See also ev_idle_init() C function. 204 | 205 | child = ev.Child.new(on_child, pid, trace) 206 | 207 | Create a new child watcher that will call the on_child function 208 | whenever SIGCHLD for registered pid is delivered. 209 | 210 | When pid is set to 0 the watcher will fire for any pid. 211 | 212 | When trace is false the watcher will be activated when the process 213 | terminates. If it's true - it will additionally be activated when 214 | the process is stopped or continued. 215 | 216 | The returned child is an ev.Child object. See below for the methods on 217 | this object. 218 | 219 | NOTE: You must explicitly register the idle with an event loop in 220 | order for it to take effect. 221 | 222 | The on_child function will be called with these arguments (return 223 | values are ignored): 224 | 225 | on_child(loop, child, revents) 226 | 227 | The loop is the event loop for which the idle object is 228 | registered, the child parameter is the ev.Child object, and 229 | revents is ev.CHILD. 230 | 231 | See also ev_child_init() C function. 232 | 233 | stat = ev.Stat.new(on_stat, path [, interval]) 234 | 235 | Configures the watcher to wait for status changes of the given "path". 236 | The "interval" is a hint on how quickly a change is expected to be 237 | detected and may normally be left out to let libev choose a suitable 238 | value. 239 | 240 | The returned stat is an ev.Stat object. See below for the methods on 241 | this object. 242 | 243 | NOTE: You must explicitly register the stat with an event loop in 244 | order for it to take effect. 245 | 246 | The on_stat function will be called with these arguments (return 247 | values are ignored): 248 | 249 | on_stat(loop, stat, revents) 250 | 251 | The loop is the event loop for which the idle object is 252 | registered, the stat parameter is the ev.Stat object, and 253 | revents is ev.STAT. 254 | 255 | See also ev_stat_init() C function. 256 | 257 | ev.READ (constant) 258 | 259 | If this bit is set, the io watcher is ready to read. See also 260 | EV_READ C definition. 261 | 262 | ev.WRITE (constant) 263 | 264 | If this bit is set, the io watcher is ready to write. See also 265 | EV_WRITE C definition. 266 | 267 | ev.TIMEOUT (constant) 268 | 269 | If this bit is set, the watcher was triggered by a timeout. See 270 | also EV_TIMEOUT C definition. 271 | 272 | ev.SIGNAL (constant) 273 | 274 | If this bit is set, the watcher was triggered by a signal. See 275 | also EV_SIGNAL C definition. 276 | 277 | ev.CHILD (constant) 278 | 279 | If this bit is set, the watcher was triggered by a child signal. 280 | See also EV_CHILD C definition. 281 | 282 | ev.STAT (constant) 283 | 284 | If this bit is set, the watcher was triggered by a change in 285 | attributes of the file system path. See also EV_STAT C definition. 286 | 287 | -- ev.Loop object methods -- 288 | 289 | loop:fork() 290 | 291 | You *must* call this function in the child process after fork(2) 292 | system call and before the next iteration of the event loop. 293 | 294 | loop:loop() 295 | 296 | Run the event loop! Returns when there are no more watchers 297 | registered with the event loop. See special note below about 298 | calling ev_loop() C API. 299 | 300 | See also ev_loop() C function. 301 | 302 | bool = loop:is_default() 303 | 304 | Returns true if the referenced loop object is the default event 305 | loop. 306 | 307 | See also ev_is_default_loop() C function. 308 | 309 | num = loop:iteration() 310 | 311 | Returns the number of loop iterations. Note that this function 312 | used to be called loop:count(). 313 | 314 | See also ev_iterations() C function. 315 | 316 | num = loop:depth() [libev >= 3.7] 317 | 318 | Returns the number of times loop:loop() was entered minus the 319 | number of times loop:loop() was exited, in other words, the 320 | recursion depth. 321 | 322 | This method is available only if lua-ev was linked 323 | with libev 3.7 and higher. 324 | 325 | See also ev_depth() C function. 326 | 327 | epochs = loop:now() 328 | 329 | Returns the non-integer epoch seconds time at which the current 330 | iteration of the event loop woke up. 331 | 332 | See also ev_now() C function. 333 | 334 | epochs = loop:update_now() 335 | 336 | Updates the current time returned by loop.now(), and returns that 337 | timestamp. 338 | 339 | See also ev_now_update() C function. 340 | 341 | loop:unloop() 342 | 343 | Process all outstanding events in the event loop, but do not make 344 | another iteration of the event loop. 345 | 346 | See also ev_unloop() C function. 347 | 348 | backend_id = loop:backend() 349 | 350 | Returns the identifier of the current backend which is being used 351 | by this event loop. See the libev documentation for what each 352 | number means: 353 | 354 | http://pod.tst.eu/http://cvs.schmorp.de/libev/ev.pod#FUNCTIONS_CONTROLLING_THE_EVENT_LOOP 355 | 356 | -- object methods common to all watcher types -- 357 | 358 | bool = watcher:is_active() 359 | 360 | Returns true if the watcher is active (has been start()ed, but not 361 | stop()ed). 362 | 363 | See also ev_is_active() C function. 364 | 365 | bool = watcher:is_pending() 366 | 367 | Returns true if the watcher is pending (it has outstanding events 368 | but its callback has not yet been invoked). 369 | 370 | See also ev_is_pending() C function. 371 | 372 | revents = watcher:clear_pending() 373 | 374 | If the watcher is pending, return the revents of the pending 375 | event, otherwise returns zero. If the event was pending, the 376 | pending flag is cleared (and therefore this watcher event will not 377 | trigger the events callback). 378 | 379 | See also ev_clear_pending() C function. 380 | 381 | old_priority = watcher:priority([new_priority]) 382 | 383 | Get access to the priority of this watcher, optionally setting a 384 | new priority. The priority should be a small integer between 385 | ev.MINPRI and ev.MAXPRI. The default is 0. 386 | 387 | See also the ev_priority() and ev_set_priority() C functions. 388 | 389 | old_callback = watcher:callback([new_callback]) 390 | 391 | Get access to the callback function associated with this watcher, 392 | optionally setting a new callback function. 393 | 394 | -- ev.Timer object methods -- 395 | 396 | timer:start(loop [, is_daemon]) 397 | 398 | Start the timer in the specified event loop. Optionally make this 399 | watcher a "daemon" watcher which means that the event loop will 400 | terminate even if this watcher has not triggered. 401 | 402 | See also ev_timer_start() C function (document as ev_TYPE_start()). 403 | 404 | timer:stop(loop) 405 | 406 | Unregister this timer from the specified event loop. Ensures that 407 | the watcher is neither active nor pending. 408 | 409 | See also ev_timer_stop() C function (document as ev_TYPE_stop()). 410 | 411 | timer:again(loop [, seconds]) 412 | 413 | Reset the timer so that it doesn't trigger again in the specified 414 | loop until the specified number of seconds from now have elapsed. 415 | If seconds is not specified, uses the repeat_seconds specified 416 | when the timer was created. 417 | 418 | See also ev_timer_again() C function. 419 | 420 | -- ev.IO object methods -- 421 | 422 | io:start(loop [, is_daemon]) 423 | 424 | Start the io in the specified event loop. Optionally make this 425 | watcher a "daemon" watcher which means that the event loop will 426 | terminate even if this watcher has not triggered. 427 | 428 | See also ev_io_start() C function (document as ev_TYPE_start()). 429 | 430 | io:stop(loop) 431 | 432 | Unregister this io from the specified event loop. Ensures that 433 | the watcher is neither active nor pending. 434 | 435 | See also ev_io_stop() C function (document as ev_TYPE_stop()). 436 | 437 | fd = io:getfd() 438 | 439 | Returns the file descriptor associated with the IO object. 440 | 441 | -- ev.Idle object methods -- 442 | 443 | idle:start(loop [, is_daemon]) 444 | 445 | Start the idle watcher in the specified event loop. Optionally 446 | make this watcher a "daemon" watcher which means that the event 447 | loop will terminate even if this watcher has not triggered. 448 | 449 | See also ev_idle_start() C function (document as ev_TYPE_start()). 450 | 451 | idle:stop(loop) 452 | 453 | Unregister this idle watcher from the specified event loop. 454 | Ensures that the watcher is neither active nor pending. 455 | 456 | See also ev_io_stop() C function (document as ev_TYPE_stop()). 457 | 458 | -- ev.Async object methods -- 459 | 460 | async:start(loop [, is_daemon]) 461 | 462 | Start the async watcher in the specified event loop. Optionally 463 | make this watcher a "daemon" watcher which means that the event 464 | loop will terminate even if this watcher has not triggered. 465 | 466 | See also ev_async_start() C function (document as ev_TYPE_start()). 467 | 468 | async:stop(loop) 469 | 470 | Unregister this async watcher from the specified event loop. 471 | Ensures that the watcher is neither active nor pending. 472 | 473 | See also ev_async_stop() C function (document as ev_TYPE_stop()). 474 | 475 | -- ev.Child object methods -- 476 | 477 | child:start(loop [, is_daemon]) 478 | 479 | Start the child watcher in the specified event loop. Optionally 480 | make this watcher a "daemon" watcher which means that the event 481 | loop will terminate even if this watcher has not triggered. 482 | 483 | See also ev_child_start() C function (document as ev_TYPE_start()). 484 | 485 | child:stop(loop) 486 | 487 | Unregister this child watcher from the specified event loop. 488 | Ensures that the watcher is neither active nor pending. 489 | 490 | See also ev_child_stop() C function (document as ev_TYPE_stop()). 491 | 492 | child:getpid() 493 | 494 | Return the process id this watcher watches out for, or 0, meaning 495 | any process id. 496 | 497 | child:getrpid() 498 | 499 | Return the process id that detected a status change. 500 | 501 | child:getstatus() 502 | 503 | Returns the process exit/trace status caused by "rpid" (see your systems 504 | "waitpid" and "sys/wait.h" for details). 505 | 506 | It returns the table with the following fields: 507 | - exited: true if status was returned for a child process that terminated 508 | normally; 509 | - stopped: true if status was returned for a child process that is 510 | currently stopped; 511 | - signaled: true if status was returned for a child process that terminated 512 | due to receipt of a signal that was not caught; 513 | - exit_status: (only if exited == true) the low-order 8 bits of the status 514 | argument that the child process passed to _exit() or exit(), or the value 515 | the child process returned from main(); 516 | - stop_signal: (only if stopped == true) the number of the signal that 517 | caused the child process to stop; 518 | - term_signal: (only if signaled == true) the number of the signal that 519 | caused the termination of the child process. 520 | 521 | -- ev.Stat object methods -- 522 | 523 | stat:start(loop [, is_daemon]) 524 | 525 | Start the stat watcher in the specified event loop. Optionally 526 | make this watcher a "daemon" watcher which means that the event 527 | loop will terminate even if this watcher has not triggered. 528 | 529 | See also ev_stat_start() C function (document as ev_TYPE_start()). 530 | 531 | stat:stop(loop) 532 | 533 | Unregister this stat watcher from the specified event loop. 534 | Ensures that the watcher is neither active nor pending. 535 | 536 | See also ev_stat_stop() C function (document as ev_TYPE_stop()). 537 | 538 | stat:getdata() 539 | 540 | Returns a table with the following fields: 541 | * - path: the file system path that is being watched; 542 | * - interval: the specified interval; 543 | * - attr: the most-recently detected attributes of the file in a form 544 | * of table with the following fields: dev, ino, mode, nlink, uid, gid, 545 | * rdev, size, atime, mtime, ctime corresponding to struct stat members 546 | * (st_dev, st_ino, etc.); 547 | * - prev: the previous attributes of the file with the same fields as 548 | * attr fields. 549 | 550 | EXCEPTION HANDLING NOTE: 551 | 552 | If there is an exception when calling a watcher callback, the error 553 | will be printed to stderr. In the future, it would be cool if 554 | there was a linked list of error handlers, and that if a callback 555 | registers another callback, this linked list of error handlers 556 | would be inherited so that exception handling can be done more 557 | easily. To do this, we just need to add a method for adding an 558 | error handler for the current callback context, and keep calling 559 | the error handlers in the linked list until an error handler 560 | actually handles the exception. If the error handling stack 561 | unwinds, we will probably just resort to printing to stderr. 562 | 563 | CALLING ev_loop() C API DIRECTLY: 564 | 565 | If you want to call the ev_loop() C API directly, then you *must* 566 | set the loop userdata field to the lua_State in which you want all 567 | callbacks to be ran in. In the past, the lua_State was stored as 568 | a thread local which was set when loop:loop() lua function is 569 | called, but this made lua-ev dependent upon pthreads and forced 570 | ev_loop() to only be called from lua. Next, I removed the pthreads 571 | dependency and stored the lua_State in which the callback was 572 | registered. This doesn't bode well when working with coroutines as 573 | it means that loop:loop() may call back into a coroutine that is 574 | long gone. 575 | 576 | The current implementation relies on libev 3.7's ability to set a 577 | userdata field associated with an event loop, and the loop:loop() 578 | implementation simply sets the userdata field for the duration of 579 | loop:loop(). This does mean that if you want to call ev_loop() 580 | from C then you either need to be sure that no lua watchers are 581 | registered with that loop, or you need to set the userdata field to 582 | the lua_State* in which the callbacks should be ran. 583 | 584 | TODO: 585 | 586 | * Add support for other watcher types (periodic, embed, etc). 587 | 588 | --------------------------------------------------------------------------------