├── .gitignore ├── ws ├── init.lua ├── stats_tap.lua ├── tap.lua └── dissector.lua ├── rockspecs ├── lua-zmq-threads-scm-0.rockspec ├── lua-zmq-threads-1.0-1.rockspec ├── lua-zmq-threads-1.1-1.rockspec ├── lua-zmq-threads-1.2-1.rockspec ├── lua-zmq-wireshark-scm-0.rockspec ├── lua-zmq-1.0-1.rockspec ├── lua-zmq-1.1-1.rockspec ├── lua-zmq-1.2-1.rockspec ├── lua-zmq-scm-1.rockspec └── lua-zmq-scm-2.rockspec ├── README.regenerate.md ├── README.windows.md ├── examples ├── forward_poll.lua ├── server.lua ├── publisher.lua ├── subscriber.lua ├── server_multipart.lua ├── client.lua ├── client_multipart.lua ├── poller.lua ├── publisher_poll.lua ├── subscriber_poll.lua ├── server_poll.lua ├── client_poll.lua ├── poller │ ├── ev.lua │ └── epoll.lua ├── subscriber_ev.lua └── poll_zsock.lua ├── COPYRIGHT ├── src ├── stopwatch.nobj.lua ├── ctx.nobj.lua ├── threads.lua ├── poller.lua ├── msg.nobj.lua ├── error.nobj.lua ├── poller.nobj.lua └── socket.nobj.lua ├── perf ├── no_msg │ ├── remote_thr.lua │ ├── local_lat.lua │ ├── remote_lat.lua │ └── local_thr.lua ├── remote_push.lua ├── remote_thr.lua ├── remote_multipart.lua ├── local_lat.lua ├── local_pull.lua ├── local_thr.lua ├── remote_lat.lua ├── local_multipart.lua ├── local_thr_poll.lua ├── thread_lat.lua ├── thread_thr.lua └── thread_push_pull.lua ├── cmake ├── LuaNativeObjects.cmake └── CustomMacros.cmake ├── .travis.yml ├── tests └── test_inproc.lua ├── CMakeLists.txt ├── README.md ├── API.md └── zmq.nobj.lua /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | *.lo 3 | *.so 4 | *.swp 5 | -------------------------------------------------------------------------------- /ws/init.lua: -------------------------------------------------------------------------------- 1 | 2 | local prequire = function(name) 3 | local status, result1 = pcall(require, name) 4 | if not status then 5 | debug("Failed to load " .. name .. ": " .. result1) 6 | end 7 | return result1 8 | end 9 | 10 | prequire("zmq.ws.dissector") 11 | 12 | -- if running from wireshark, register all taps. 13 | if gui_enabled() then 14 | prequire("zmq.ws.stats_tap") 15 | end 16 | 17 | -------------------------------------------------------------------------------- /rockspecs/lua-zmq-threads-scm-0.rockspec: -------------------------------------------------------------------------------- 1 | package = "lua-zmq-threads" 2 | version = "scm-0" 3 | source = { 4 | url = "git://github.com/Neopallium/lua-zmq.git", 5 | } 6 | description = { 7 | summary = "Lua bindings to zeromq2, with LuaJIT2 FFI support.", 8 | homepage = "http://github.com/Neopallium/lua-zmq", 9 | license = "MIT/X11", 10 | } 11 | dependencies = { 12 | "lua-zmq = scm-1", 13 | "lua-llthreads = scm-0", 14 | } 15 | build = { 16 | type = "none", 17 | install = { 18 | lua = { 19 | ['zmq.threads'] = "src/threads.lua", 20 | }, 21 | }, 22 | } 23 | -------------------------------------------------------------------------------- /rockspecs/lua-zmq-threads-1.0-1.rockspec: -------------------------------------------------------------------------------- 1 | package = "lua-zmq-threads" 2 | version = "1.0-1" 3 | source = { 4 | url = "git://github.com/Neopallium/lua-zmq.git", 5 | branch = "v1.0", 6 | } 7 | description = { 8 | summary = "Lua bindings to zeromq2, with LuaJIT2 FFI support.", 9 | homepage = "http://github.com/Neopallium/lua-zmq", 10 | license = "MIT/X11" 11 | } 12 | dependencies = { 13 | "lua-zmq", 14 | "lua-llthreads", 15 | } 16 | build = { 17 | type = "none", 18 | install = { 19 | lua = { 20 | ['zmq.threads'] = "src/threads.lua", 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /rockspecs/lua-zmq-threads-1.1-1.rockspec: -------------------------------------------------------------------------------- 1 | package = "lua-zmq-threads" 2 | version = "1.1-1" 3 | source = { 4 | url = "git://github.com/Neopallium/lua-zmq.git", 5 | branch = "v1.1", 6 | } 7 | description = { 8 | summary = "Lua bindings to zeromq2, with LuaJIT2 FFI support.", 9 | homepage = "http://github.com/Neopallium/lua-zmq", 10 | license = "MIT/X11" 11 | } 12 | dependencies = { 13 | "lua-zmq >= 1.1-1", 14 | "lua-llthreads >= 1.1-1", 15 | } 16 | build = { 17 | type = "none", 18 | install = { 19 | lua = { 20 | ['zmq.threads'] = "src/threads.lua", 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /rockspecs/lua-zmq-threads-1.2-1.rockspec: -------------------------------------------------------------------------------- 1 | package = "lua-zmq-threads" 2 | version = "1.2-1" 3 | source = { 4 | url = "git://github.com/Neopallium/lua-zmq.git", 5 | branch = "v1.2", 6 | } 7 | description = { 8 | summary = "Lua bindings to zeromq2, with LuaJIT2 FFI support.", 9 | homepage = "http://github.com/Neopallium/lua-zmq", 10 | license = "MIT/X11" 11 | } 12 | dependencies = { 13 | "lua-zmq >= 1.2-1", 14 | "lua-llthreads >= 1.3-1", 15 | } 16 | build = { 17 | type = "none", 18 | install = { 19 | lua = { 20 | ['zmq.threads'] = "src/threads.lua", 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /README.regenerate.md: -------------------------------------------------------------------------------- 1 | To re-generating the bindings 2 | ----------------------------- 3 | 4 | You will need to install LuaNativeObjects and set the CMake variable `USE_PRE_GENERATED_BINDINGS` to FALSE. 5 | By default CMake will use the pre-generated bindings that are include in the project. 6 | 7 | Build Dependencies 8 | ------------------ 9 | 10 | Optional dependency for re-generating Lua bindings from `*.nobj.lua` files: 11 | 12 | * [LuaNativeObjects](https://github.com/Neopallium/LuaNativeObjects), this is the bindings generator used to convert the `*.nobj.lua` files into a native Lua module. 13 | 14 | -------------------------------------------------------------------------------- /rockspecs/lua-zmq-wireshark-scm-0.rockspec: -------------------------------------------------------------------------------- 1 | package = "lua-zmq-wireshark" 2 | version = "scm-0" 3 | source = { 4 | url = "git://github.com/Neopallium/lua-zmq.git", 5 | } 6 | description = { 7 | summary = "Lua Wireshark dissector for the ZeroMQ protocol.", 8 | homepage = "http://github.com/Neopallium/lua-zmq", 9 | -- Wireshark requires dissectors to be licensed under the GPL. 10 | license = "GPL", 11 | } 12 | dependencies = {} 13 | build = { 14 | type = "none", 15 | install = { 16 | lua = { 17 | ['zmq.ws.dissector'] = "ws/dissector.lua", 18 | ['zmq.ws.tap'] = "ws/tap.lua", 19 | ['zmq.ws.stats_tap'] = "ws/stats_tap.lua", 20 | }, 21 | }, 22 | } 23 | -------------------------------------------------------------------------------- /rockspecs/lua-zmq-1.0-1.rockspec: -------------------------------------------------------------------------------- 1 | package = "lua-zmq" 2 | version = "1.0-1" 3 | source = { 4 | url = "git://github.com/Neopallium/lua-zmq.git", 5 | branch = "v1.0", 6 | } 7 | description = { 8 | summary = "Lua bindings to zeromq2, with LuaJIT2 FFI support.", 9 | homepage = "http://github.com/Neopallium/lua-zmq", 10 | license = "MIT/X11", 11 | } 12 | dependencies = { 13 | "lua >= 5.1", 14 | } 15 | external_dependencies = { 16 | ZEROMQ = { 17 | header = "zmq.h", 18 | library = "zmq", 19 | } 20 | } 21 | build = { 22 | type = "builtin", 23 | modules = { 24 | zmq = { 25 | sources = {"src/pre_generated-zmq.nobj.c"}, 26 | incdirs = "$(ZEROMQ_INCDIR)", 27 | libdirs = "$(ZEROMQ_LIBDIR)", 28 | libraries = {"zmq"}, 29 | }, 30 | }, 31 | install = { 32 | lua = { 33 | ['zmq.poller'] = "src/poller.lua", 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /rockspecs/lua-zmq-1.1-1.rockspec: -------------------------------------------------------------------------------- 1 | package = "lua-zmq" 2 | version = "1.1-1" 3 | source = { 4 | url = "git://github.com/Neopallium/lua-zmq.git", 5 | branch = "v1.1", 6 | } 7 | description = { 8 | summary = "Lua bindings to zeromq2, with LuaJIT2 FFI support.", 9 | homepage = "http://github.com/Neopallium/lua-zmq", 10 | license = "MIT/X11", 11 | } 12 | dependencies = { 13 | "lua >= 5.1", 14 | } 15 | external_dependencies = { 16 | ZEROMQ = { 17 | header = "zmq.h", 18 | library = "zmq", 19 | } 20 | } 21 | build = { 22 | type = "builtin", 23 | modules = { 24 | zmq = { 25 | sources = {"src/pre_generated-zmq.nobj.c"}, 26 | incdirs = "$(ZEROMQ_INCDIR)", 27 | libdirs = "$(ZEROMQ_LIBDIR)", 28 | libraries = {"zmq"}, 29 | }, 30 | }, 31 | install = { 32 | lua = { 33 | ['zmq.poller'] = "src/poller.lua", 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /rockspecs/lua-zmq-1.2-1.rockspec: -------------------------------------------------------------------------------- 1 | package = "lua-zmq" 2 | version = "1.2-1" 3 | source = { 4 | url = "git://github.com/Neopallium/lua-zmq.git", 5 | branch = "v1.2", 6 | } 7 | description = { 8 | summary = "Lua bindings to zeromq2, with LuaJIT2 FFI support.", 9 | homepage = "http://github.com/Neopallium/lua-zmq", 10 | license = "MIT/X11", 11 | } 12 | dependencies = { 13 | "lua >= 5.1, < 5.5", 14 | } 15 | external_dependencies = { 16 | ZEROMQ = { 17 | header = "zmq.h", 18 | library = "zmq", 19 | } 20 | } 21 | build = { 22 | type = "builtin", 23 | modules = { 24 | zmq = { 25 | sources = {"src/pre_generated-zmq.nobj.c"}, 26 | incdirs = "$(ZEROMQ_INCDIR)", 27 | libdirs = "$(ZEROMQ_LIBDIR)", 28 | libraries = {"zmq"}, 29 | }, 30 | }, 31 | install = { 32 | lua = { 33 | ['zmq.poller'] = "src/poller.lua", 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /README.windows.md: -------------------------------------------------------------------------------- 1 | Steps to install on Windows XP (using MinGW+MSYS) 2 | ------------------------------------------------- 3 | 4 | 1. Download source code of ZeroMQ-2.1.7 5 | 6 | 2. Use mingw+msys to compile 7 | 8 | $ sh configure --prefix=c:/zeromq 9 | 10 | $ make 11 | 12 | $ make install 13 | 14 | copy the header & lib to c:\mingw\include_or_lib 15 | 16 | 3. Download source code of luajit-2.0.0-beta7 17 | 18 | $ make 19 | 20 | $ make install 21 | 22 | copy the header & lib to c:\mingw\include_or_lib 23 | 24 | 4. Install cmake-2.8.4-win32-x86 25 | 26 | 5. Download Neopallium-lua-zmq-1.1 27 | 28 | 6. Use cmake+mingw+msys to build 29 | 30 | $ mkdir build 31 | 32 | $ cd build 33 | 34 | $ cmake -G "MSYS Makefiles" -D ZMQ_PATH=c:/zeromq .. 35 | 36 | $ make 37 | 38 | $ make install 39 | 40 | Files zmq.dll, poller.lua and threads.lua are installed in c:\program files\lua-zmq 41 | 42 | Author 43 | ------ 44 | xumingyong@gmail.com 45 | xumingyong@ehdc.com.cn 46 | 47 | 48 | -------------------------------------------------------------------------------- /examples/forward_poll.lua: -------------------------------------------------------------------------------- 1 | local zmq = require'zmq' 2 | local poller = require"examples.poller" 3 | local poll_zsock = require"examples.poll_zsock" 4 | 5 | local poll = poller.new() 6 | poll_zsock.set_poller(poll) 7 | 8 | local c = zmq.init(1) 9 | local xreq = poll_zsock(c:socket(zmq.XREQ)) 10 | xreq:bind('tcp://127.0.0.1:13333') 11 | local xrep = poll_zsock(c:socket(zmq.XREP)) 12 | xrep:bind('tcp://127.0.0.1:13334') 13 | 14 | local max_recv = 10 15 | 16 | local function forward_io(src,dst) 17 | src.on_data = function() 18 | for i=1,max_recv do 19 | repeat 20 | local data, err = src:recv(zmq.NOBLOCK) 21 | if not data then 22 | if err == 'timeout' then 23 | return 24 | else 25 | error("socket recv error:" .. err) 26 | end 27 | end 28 | local more = src:getopt(zmq.RCVMORE) > 0 29 | dst:send(data,more and zmq.SNDMORE or 0) 30 | until not more 31 | end 32 | end 33 | end 34 | 35 | forward_io(xrep,xreq) 36 | forward_io(xreq,xrep) 37 | 38 | poll:start() 39 | 40 | -------------------------------------------------------------------------------- /rockspecs/lua-zmq-scm-1.rockspec: -------------------------------------------------------------------------------- 1 | package = "lua-zmq" 2 | version = "scm-1" 3 | source = { 4 | url = "git://github.com/Neopallium/lua-zmq.git", 5 | } 6 | description = { 7 | summary = "Lua bindings to zeromq2, with LuaJIT2 FFI support.", 8 | homepage = "http://github.com/Neopallium/lua-zmq", 9 | license = "MIT/X11", 10 | } 11 | dependencies = { 12 | "lua >= 5.1, < 5.5", 13 | } 14 | external_dependencies = { 15 | platforms = { 16 | windows = { 17 | ZEROMQ = { 18 | library = "libzmq", 19 | } 20 | }, 21 | }, 22 | ZEROMQ = { 23 | header = "zmq.h", 24 | library = "zmq", 25 | } 26 | } 27 | build = { 28 | platforms = { 29 | windows = { 30 | modules = { 31 | zmq = { 32 | libraries = {"libzmq"}, 33 | } 34 | } 35 | }, 36 | }, 37 | type = "builtin", 38 | modules = { 39 | zmq = { 40 | sources = {"src/pre_generated-zmq.nobj.c"}, 41 | incdirs = "$(ZEROMQ_INCDIR)", 42 | libdirs = "$(ZEROMQ_LIBDIR)", 43 | libraries = {"zmq"}, 44 | }, 45 | }, 46 | install = { 47 | lua = { 48 | ['zmq.poller'] = "src/poller.lua", 49 | }, 50 | }, 51 | } 52 | -------------------------------------------------------------------------------- /rockspecs/lua-zmq-scm-2.rockspec: -------------------------------------------------------------------------------- 1 | package = "lua-zmq" 2 | version = "scm-2" 3 | source = { 4 | url = "git://github.com/Neopallium/lua-zmq.git", 5 | } 6 | description = { 7 | summary = "Lua bindings to zeromq2, with LuaJIT2 FFI support.", 8 | homepage = "http://github.com/Neopallium/lua-zmq", 9 | license = "MIT/X11", 10 | } 11 | dependencies = { 12 | "lua >= 5.1, < 5.5", 13 | } 14 | external_dependencies = { 15 | platforms = { 16 | windows = { 17 | ZEROMQ = { 18 | library = "libzmq", 19 | } 20 | }, 21 | }, 22 | ZEROMQ = { 23 | header = "zmq.h", 24 | library = "zmq", 25 | } 26 | } 27 | build = { 28 | platforms = { 29 | windows = { 30 | modules = { 31 | zmq = { 32 | libraries = {"libzmq"}, 33 | } 34 | } 35 | }, 36 | }, 37 | type = "builtin", 38 | modules = { 39 | zmq = { 40 | sources = {"src/pre_generated-zmq.nobj.c"}, 41 | incdirs = "$(ZEROMQ_INCDIR)", 42 | libdirs = "$(ZEROMQ_LIBDIR)", 43 | libraries = {"zmq"}, 44 | }, 45 | }, 46 | install = { 47 | lua = { 48 | ['zmq.poller'] = "src/poller.lua", 49 | }, 50 | }, 51 | } 52 | -------------------------------------------------------------------------------- /COPYRIGHT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011 by Robert G. Jakabosky 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 | -------------------------------------------------------------------------------- /ws/stats_tap.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2011, Robert G. Jakabosky All rights reserved. 2 | 3 | local tap = require"zmq.ws.tap" 4 | 5 | local format = string.format 6 | 7 | local stats_tap_mt = {} 8 | stats_tap_mt.__index = stats_tap_mt 9 | 10 | function stats_tap_mt:packet(pinfo, tvb, tree, data) 11 | -- count all ZeroMQ packets 12 | self.count = self.count + 1 13 | data = data or pinfo.tap_data 14 | if not data then 15 | return 16 | end 17 | -- frames 18 | self.frames = self.frames + (data.frames or 0) 19 | -- frames with more flag set 20 | self.more = self.more + (data.more or 0) 21 | -- whole messages. 22 | self.msgs = self.msgs + (data.msgs or 0) 23 | -- total bytes in frame bodies. 24 | self.body_bytes = self.body_bytes + (data.body_bytes or 0) 25 | end 26 | 27 | function stats_tap_mt:draw() 28 | return format([[ 29 | ZeroMQ Packets: %d 30 | Frames: %d 31 | Messages: %d 32 | Flags: More: %d 33 | Payload bytes: %d 34 | ]], 35 | self.count, 36 | self.frames, 37 | self.msgs, 38 | self.more, 39 | self.body_bytes) 40 | end 41 | 42 | function stats_tap_mt:reset() 43 | self.count = 0 44 | self.frames = 0 45 | self.msgs = 0 46 | self.more = 0 47 | self.body_bytes = 0 48 | end 49 | 50 | local function create_stats_tap() 51 | local tap = setmetatable({}, stats_tap_mt) 52 | 53 | tap:reset() -- initialize tap. 54 | return tap, 'zmq', 'lua' 55 | end 56 | 57 | tap("ZeroMQ stats tap", create_stats_tap) 58 | 59 | -------------------------------------------------------------------------------- /examples/server.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2010 Aleksey Yeschenko 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 | local zmq = require"zmq" 22 | 23 | local ctx = zmq.init() 24 | local s = ctx:socket(zmq.REP) 25 | 26 | s:bind("tcp://lo:5555") 27 | 28 | while true do 29 | print(string.format("Received query: '%s'", s:recv())) 30 | s:send("OK") 31 | end 32 | -------------------------------------------------------------------------------- /examples/publisher.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2010 Aleksey Yeschenko 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 | local zmq = require"zmq" 22 | 23 | local ctx = zmq.init() 24 | local s = ctx:socket(zmq.PUB) 25 | 26 | s:bind("tcp://lo:5555") 27 | 28 | local msg_id = 1 29 | while true do 30 | s:send(tostring(msg_id)) 31 | msg_id = msg_id + 1 32 | end 33 | -------------------------------------------------------------------------------- /examples/subscriber.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2010 Aleksey Yeschenko 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 | local zmq = require"zmq" 22 | 23 | local ctx = zmq.init() 24 | local s = ctx:socket(zmq.SUB) 25 | s:setopt(zmq.SUBSCRIBE, "") 26 | s:connect("tcp://localhost:5555") 27 | while true do 28 | local msg = s:recv() 29 | local msg_id = tonumber(msg) 30 | if math.mod(msg_id, 10000) == 0 then print(msg_id) end 31 | end 32 | -------------------------------------------------------------------------------- /examples/server_multipart.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2010 Aleksey Yeschenko 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 | local zmq = require"zmq" 22 | 23 | local ctx = zmq.init() 24 | local s = ctx:socket(zmq.REP) 25 | 26 | s:bind("tcp://lo:5555") 27 | 28 | while true do 29 | local query = s:recv() 30 | while s:getopt(zmq.RCVMORE) == 1 do 31 | query = query .. s:recv() 32 | end 33 | print(string.format("Received query: '%s'", query)) 34 | s:send("OK") 35 | end 36 | -------------------------------------------------------------------------------- /examples/client.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2010 Aleksey Yeschenko 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 | local zmq = require"zmq" 22 | 23 | local N=tonumber(arg[1] or 100) 24 | 25 | local ctx = zmq.init() 26 | local s = ctx:socket(zmq.REQ) 27 | 28 | s:connect("tcp://localhost:5555") 29 | 30 | for i=1,N do 31 | s:send("SELECT * FROM mytable") 32 | local data, err = s:recv() 33 | if data then 34 | print(data) 35 | else 36 | print("s:recv() error:", err) 37 | end 38 | end 39 | 40 | s:close() 41 | ctx:term() 42 | -------------------------------------------------------------------------------- /src/stopwatch.nobj.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2011 by Robert G. Jakabosky 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 | object "ZMQ_StopWatch" { 22 | c_source[[ 23 | #if (ZMQ_VERSION_MAJOR <= 4) && (ZMQ_VERSION_MINOR <= 1) 24 | #include "zmq_utils.h" 25 | #endif 26 | typedef struct ZMQ_StopWatch ZMQ_StopWatch; 27 | ]], 28 | constructor "start" { 29 | c_call "ZMQ_StopWatch *" "zmq_stopwatch_start" {}, 30 | }, 31 | destructor "stop" { 32 | c_method_call { "unsigned long", "usecs", ffi_wrap = 'tonumber' } "zmq_stopwatch_stop" {}, 33 | }, 34 | } 35 | 36 | -------------------------------------------------------------------------------- /examples/client_multipart.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2010 Aleksey Yeschenko 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 | local zmq = require"zmq" 22 | 23 | local N=tonumber(arg[1] or 100) 24 | 25 | local ctx = zmq.init() 26 | local s = ctx:socket(zmq.REQ) 27 | 28 | s:connect("tcp://localhost:5555") 29 | 30 | for i=1,N do 31 | s:send("SELECT * FROM mytable ", zmq.SNDMORE) 32 | s:send("WHERE library = 'zmq'") 33 | 34 | local data, err = s:recv() 35 | if data then 36 | print(data) 37 | else 38 | print("s:recv() error:", err) 39 | end 40 | end 41 | 42 | s:close() 43 | ctx:term() 44 | -------------------------------------------------------------------------------- /examples/poller.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2012 Robert G. Jakabosky 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 | -- safe require. 22 | local require = require 23 | local function safe_require(...) 24 | return pcall(require, ...) 25 | end 26 | 27 | local mod_name = ... 28 | 29 | local backends = { 30 | "epoll", 31 | "ev", 32 | } 33 | 34 | for i=1,#backends do 35 | local backend = backends[i] 36 | local name = mod_name .. '.' .. backend 37 | local status, mod = safe_require(name) 38 | if status then 39 | --print("Loaded backend:", name) 40 | return mod 41 | end 42 | end 43 | 44 | error("Failed to load backend for: " .. mod_name) 45 | 46 | -------------------------------------------------------------------------------- /perf/no_msg/remote_thr.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2010 Aleksey Yeschenko 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 | if not arg[3] then 22 | print("usage: lua remote_thr.lua ") 23 | os.exit() 24 | end 25 | 26 | local connect_to = arg[1] 27 | local message_size = tonumber(arg[2]) 28 | local message_count = tonumber(arg[3]) 29 | 30 | local zmq = require'zmq' 31 | 32 | local ctx = zmq.init(1) 33 | local s = ctx:socket(zmq.PUB) 34 | s:connect(connect_to) 35 | 36 | local msg = ("0"):rep(message_size) 37 | 38 | for i = 1, message_count do 39 | s:send(msg) 40 | end 41 | 42 | --os.execute("sleep " .. 10) 43 | 44 | s:close() 45 | ctx:term() 46 | -------------------------------------------------------------------------------- /perf/no_msg/local_lat.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2010 Aleksey Yeschenko 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 | if not arg[3] then 22 | print("usage: lua local_lat.lua ") 23 | os.exit() 24 | end 25 | 26 | local bind_to = arg[1] 27 | local message_size = tonumber(arg[2]) 28 | local roundtrip_count = tonumber(arg[3]) 29 | 30 | local zmq = require'zmq' 31 | 32 | local ctx = zmq.init(1) 33 | local s = ctx:socket(zmq.REP) 34 | s:bind(bind_to) 35 | 36 | local msg 37 | 38 | for i = 1, roundtrip_count do 39 | msg = s:recv() 40 | assert(#msg == message_size, "Invalid message size") 41 | s:send(msg) 42 | end 43 | 44 | s:close() 45 | ctx:term() 46 | -------------------------------------------------------------------------------- /perf/remote_push.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2010 Aleksey Yeschenko 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 | if not arg[3] then 22 | print("usage: lua remote_thr.lua ") 23 | os.exit() 24 | end 25 | 26 | local connect_to = arg[1] 27 | local message_size = tonumber(arg[2]) 28 | local message_count = tonumber(arg[3]) 29 | 30 | local zmq = require"zmq" 31 | 32 | local ctx = zmq.init(1) 33 | local s = assert(ctx:socket(zmq.PUSH)) 34 | assert(s:connect(connect_to)) 35 | 36 | local data = ("0"):rep(message_size) 37 | local msg = zmq.zmq_msg_t.init_size(message_size) 38 | 39 | for i = 1, message_count do 40 | msg:set_data(data) 41 | assert(s:send_msg(msg)) 42 | end 43 | 44 | --os.execute("sleep " .. 10) 45 | 46 | s:close() 47 | ctx:term() 48 | -------------------------------------------------------------------------------- /perf/remote_thr.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2010 Aleksey Yeschenko 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 | if not arg[3] then 22 | print("usage: lua remote_thr.lua ") 23 | os.exit() 24 | end 25 | 26 | local connect_to = arg[1] 27 | local message_size = tonumber(arg[2]) 28 | local message_count = tonumber(arg[3]) 29 | 30 | local zmq = require"zmq" 31 | 32 | local ctx = zmq.init(1) 33 | local s = assert(ctx:socket(zmq.PUSH)) 34 | assert(s:connect(connect_to)) 35 | 36 | zmq.sleep(1) 37 | 38 | local data = ("0"):rep(message_size) 39 | local msg_data = zmq.zmq_msg_t.init_data(data) 40 | local msg = zmq.zmq_msg_t.init() 41 | 42 | for i = 1, message_count do 43 | msg:copy(msg_data) 44 | assert(s:send_msg(msg)) 45 | end 46 | 47 | s:close() 48 | ctx:term() 49 | 50 | -------------------------------------------------------------------------------- /src/ctx.nobj.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2011 by Robert G. Jakabosky 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 | object "ZMQ_Ctx" { 22 | sys_include"string.h", 23 | error_on_null = "get_zmq_strerror()", 24 | c_source [[ 25 | typedef struct ZMQ_Ctx ZMQ_Ctx; 26 | ]], 27 | destructor "term" { 28 | c_method_call "ZMQ_Error" "zmq_term" {} 29 | }, 30 | method "lightuserdata" { 31 | var_out{ "void *", "ptr" }, 32 | c_source[[ 33 | ${ptr} = ${this}; 34 | ]] 35 | }, 36 | method "socket" { 37 | c_method_call "!ZMQ_Socket *" "zmq_socket" { "int", "type"} 38 | }, 39 | method "set" { 40 | if_defs = { "VERSION_3_2", "VERSION_4_0" }, 41 | c_method_call "int" "zmq_ctx_set" { "int", "flag", "int", "value" } 42 | }, 43 | method "get" { 44 | if_defs = { "VERSION_3_2", "VERSION_4_0" }, 45 | c_method_call "int" "zmq_ctx_get" { "int", "flag" } 46 | }, 47 | } 48 | 49 | -------------------------------------------------------------------------------- /perf/remote_multipart.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2010 Aleksey Yeschenko 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 | if not arg[3] then 22 | print("usage: lua remote_thr.lua ") 23 | os.exit() 24 | end 25 | 26 | local connect_to = arg[1] 27 | local message_size = tonumber(arg[2]) 28 | local message_count = tonumber(arg[3]) 29 | 30 | local zmq = require"zmq" 31 | local z_SNDMORE = zmq.SNDMORE 32 | 33 | local ctx = zmq.init(1) 34 | local s = assert(ctx:socket(zmq.PUSH)) 35 | assert(s:connect(connect_to)) 36 | 37 | local data = ("0"):rep(message_size/2) 38 | local msg = zmq.zmq_msg_t.init_size(message_size/2) 39 | 40 | for i = 1, message_count do 41 | msg:set_data(data) 42 | assert(s:send_msg(msg, z_SNDMORE)) 43 | msg:set_data(data) 44 | assert(s:send_msg(msg)) 45 | end 46 | 47 | os.execute("sleep " .. 1) 48 | 49 | s:close() 50 | ctx:term() 51 | -------------------------------------------------------------------------------- /cmake/LuaNativeObjects.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # Lua Native Objects 3 | # 4 | 5 | find_program(LUA_NATIVE_OBJECTS_EXECUTABLE native_objects.lua 6 | PATHS ${CMAKE_SOURCE_DIR}/../LuaNativeObjects 7 | DOC "LuaNativeObjects executable path") 8 | set(USE_PRE_GENERATED_BINDINGS TRUE CACHE BOOL 9 | "Set this to FALSE to re-generate bindings using LuaNativeObjects") 10 | set(GENERATE_LUADOCS TRUE CACHE BOOL 11 | "Set this to FALSE to avoid generation of docs using LuaDoc") 12 | 13 | macro(GenLuaNativeObjects _src_files_var) 14 | set(_new_src_files) 15 | foreach(_src_file ${${_src_files_var}}) 16 | if(_src_file MATCHES ".nobj.lua") 17 | string(REGEX REPLACE ".nobj.lua" ".nobj.c" _src_file_out ${_src_file}) 18 | string(REGEX REPLACE ".nobj.lua" ".nobj.ffi.lua" _ffi_file_out ${_src_file}) 19 | add_custom_command(OUTPUT ${_src_file_out} ${_ffi_file_out} 20 | COMMAND ${LUA_NATIVE_OBJECTS_EXECUTABLE} -outpath ${CMAKE_CURRENT_BINARY_DIR} -gen lua ${_src_file} 21 | WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} 22 | DEPENDS ${_src_file} 23 | ) 24 | set_source_files_properties(${_src_file_out} PROPERTIES GENERATED TRUE) 25 | set_source_files_properties(${_ffi_file_out} PROPERTIES GENERATED TRUE) 26 | if (${GENERATE_LUADOCS}) 27 | string(REGEX REPLACE ".nobj.lua" "" _doc_base ${_src_file}) 28 | string(REGEX REPLACE ".nobj.lua" ".luadoc" _doc_file_out ${_src_file}) 29 | add_custom_target(${_doc_file_out} ALL 30 | COMMAND ${LUA_NATIVE_OBJECTS_EXECUTABLE} -outpath docs -gen luadoc ${_src_file} 31 | COMMAND luadoc -nofiles -d docs docs 32 | WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} 33 | DEPENDS ${_src_file} 34 | ) 35 | endif() 36 | set_source_files_properties(${_doc_file_out} PROPERTIES GENERATED TRUE) 37 | set(_new_src_files ${_new_src_files} ${_src_file_out}) 38 | else(_src_file MATCHES ".nobj.lua") 39 | set(_new_src_files ${_new_src_files} ${_src_file}) 40 | endif(_src_file MATCHES ".nobj.lua") 41 | endforeach(_src_file) 42 | set(${_src_files_var} ${_new_src_files}) 43 | endmacro(GenLuaNativeObjects _src_files_var) 44 | 45 | -------------------------------------------------------------------------------- /perf/no_msg/remote_lat.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2010 Aleksey Yeschenko 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 | if not arg[3] then 22 | print("usage: lua remote_lat.lua ") 23 | os.exit() 24 | end 25 | 26 | local connect_to = arg[1] 27 | local message_size = tonumber(arg[2]) 28 | local roundtrip_count = tonumber(arg[3]) 29 | 30 | local zmq = require'zmq' 31 | 32 | local ctx = zmq.init(1) 33 | local s = ctx:socket(zmq.REQ) 34 | s:connect(connect_to) 35 | 36 | local msg = ("0"):rep(message_size) 37 | 38 | local timer = zmq.stopwatch_start() 39 | 40 | for i = 1, roundtrip_count do 41 | s:send(msg) 42 | msg = s:recv() 43 | assert(#msg == message_size, "Invalid message size") 44 | end 45 | 46 | local elapsed = timer:stop() 47 | 48 | s:close() 49 | ctx:term() 50 | 51 | local latency = elapsed / roundtrip_count / 2 52 | 53 | print(string.format("message size: %i [B]", message_size)) 54 | print(string.format("roundtrip count: %i", roundtrip_count)) 55 | print(string.format("mean latency: %.3f [us]", latency)) 56 | -------------------------------------------------------------------------------- /perf/local_lat.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2010 Aleksey Yeschenko 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 | if not arg[3] then 22 | print("usage: lua local_lat.lua ") 23 | os.exit() 24 | end 25 | 26 | local bind_to = arg[1] 27 | local message_size = tonumber(arg[2]) 28 | local roundtrip_count = tonumber(arg[3]) 29 | 30 | local zmq = require"zmq" 31 | 32 | local ctx = zmq.init(1) 33 | local s = assert(ctx:socket(zmq.REP)) 34 | assert(s:bind(bind_to)) 35 | 36 | local msg = zmq.zmq_msg_t() 37 | 38 | local timer 39 | 40 | for i = 1, roundtrip_count do 41 | assert(s:recv_msg(msg)) 42 | if not timer then 43 | timer = zmq.stopwatch_start() 44 | end 45 | assert(msg:size() == message_size, "Invalid message size") 46 | assert(s:send_msg(msg)) 47 | end 48 | 49 | local elapsed = timer:stop() 50 | 51 | s:close() 52 | ctx:term() 53 | 54 | local latency = elapsed / roundtrip_count / 2 55 | 56 | print(string.format("mean latency: %.3f [us]", latency)) 57 | local secs = elapsed / (1000 * 1000) 58 | print(string.format("elapsed = %f", secs)) 59 | print(string.format("msg/sec = %f", roundtrip_count / secs)) 60 | 61 | -------------------------------------------------------------------------------- /cmake/CustomMacros.cmake: -------------------------------------------------------------------------------- 1 | ## Orignal Macros copied from lighttpd 2.0 2 | 3 | ## modules are without the "lib" prefix 4 | 5 | macro(setup_static_modules TARGET) 6 | file(WRITE ${TARGET}_builtins.h "/* auto-generated by CMake build do not edit */\n\n") 7 | endmacro(setup_static_modules) 8 | 9 | macro(add_module TARGET MODNAME) 10 | set(_static_mod ${BUILD_STATIC}) 11 | 12 | ## create list of module source files. 13 | set(_srcfiles) 14 | set(_def_module_src ${CMAKE_CURRENT_SOURCE_DIR}/modules/${MODNAME}.c) 15 | if(EXISTS ${_def_module_src}) 16 | set(_srcfiles ${_def_module_src}) 17 | endif(EXISTS ${_def_module_src}) 18 | foreach(_srcfile ${ARGN}) 19 | if(_srcfile STREQUAL "STATIC") 20 | set(_static_mod TRUE) 21 | else(_srcfile STREQUAL "STATIC") 22 | set(_srcfiles ${_srcfiles} ${_srcfile}) 23 | endif(_srcfile STREQUAL "STATIC") 24 | endforeach(_srcfile) 25 | 26 | if(_static_mod) 27 | set(STATIC_MODULE_SRC ${STATIC_MODULE_SRC} ${_srcfiles}) 28 | file(APPEND ${TARGET}_builtins.h "STATIC_MOD(${MODNAME})\n") 29 | else(_static_mod) 30 | add_library(${MODNAME} MODULE ${_srcfiles}) 31 | set(MODULE_TARGETS ${MODULE_TARGETS} ${MODNAME}) 32 | 33 | add_target_properties(${MODNAME} LINK_FLAGS ${COMMON_LDFLAGS}) 34 | add_target_properties(${MODNAME} COMPILE_FLAGS ${COMMON_CFLAGS}) 35 | set_target_properties(${MODNAME} PROPERTIES CMAKE_INSTALL_PREFIX ${CMAKE_INSTALL_PREFIX}) 36 | 37 | ## Windows likes to link it this way back to app! 38 | if(WIN32) 39 | set_target_properties(${MODNAME} PROPERTIES LINK_FLAGS ${TARGET}.lib) 40 | endif(WIN32) 41 | 42 | if(APPLE) 43 | set_target_properties(${MODNAME} PROPERTIES LINK_FLAGS "-flat_namespace -undefined suppress") 44 | endif(APPLE) 45 | endif(_static_mod) 46 | endmacro(add_module) 47 | 48 | macro(add_target_properties _target _name) 49 | set(_properties) 50 | foreach(_prop ${ARGN}) 51 | set(_properties "${_properties} ${_prop}") 52 | endforeach(_prop) 53 | get_target_property(_old_properties ${_target} ${_name}) 54 | ##message(STATUS "adding property to ${_target} ${_name}:" ${_properties}) 55 | if(NOT _old_properties) 56 | # in case it's NOTFOUND 57 | set(_old_properties) 58 | endif(NOT _old_properties) 59 | set_target_properties(${_target} PROPERTIES ${_name} "${_old_properties} ${_properties}") 60 | endmacro(add_target_properties) 61 | 62 | -------------------------------------------------------------------------------- /perf/no_msg/local_thr.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2010 Aleksey Yeschenko 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 | if not arg[3] then 22 | print("usage: lua local_thr.lua ") 23 | os.exit() 24 | end 25 | 26 | local bind_to = arg[1] 27 | local message_size = tonumber(arg[2]) 28 | local message_count = tonumber(arg[3]) 29 | 30 | local zmq = require'zmq' 31 | 32 | local ctx = zmq.init(1) 33 | local s = ctx:socket(zmq.SUB) 34 | s:setopt(zmq.SUBSCRIBE, ""); 35 | s:bind(bind_to) 36 | 37 | local msg 38 | msg = s:recv() 39 | 40 | local timer = zmq.stopwatch_start() 41 | 42 | for i = 1, message_count - 1 do 43 | msg = s:recv() 44 | assert(#msg == message_size, "Invalid message size") 45 | end 46 | 47 | local elapsed = timer:stop() 48 | 49 | s:close() 50 | ctx:term() 51 | 52 | if elapsed == 0 then elapsed = 1 end 53 | 54 | local throughput = message_count / (elapsed / 1000000) 55 | local megabits = throughput * message_size * 8 / 1000000 56 | 57 | print(string.format("message size: %i [B]", message_size)) 58 | print(string.format("message count: %i", message_count)) 59 | print(string.format("mean throughput: %i [msg/s]", throughput)) 60 | print(string.format("mean throughput: %.3f [Mb/s]", megabits)) 61 | -------------------------------------------------------------------------------- /perf/local_pull.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2011 Robert G. Jakabosky 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 | if not arg[3] then 22 | print("usage: lua local_thr.lua ") 23 | os.exit() 24 | end 25 | 26 | local bind_to = arg[1] 27 | local message_size = tonumber(arg[2]) 28 | local message_count = tonumber(arg[3]) 29 | 30 | local zmq = require"zmq" 31 | 32 | local ctx = zmq.init(1) 33 | local s = assert(ctx:socket(zmq.PULL)) 34 | assert(s:bind(bind_to)) 35 | 36 | print(string.format("message size: %i [B]", message_size)) 37 | print(string.format("message count: %i", message_count)) 38 | 39 | local msg 40 | msg = zmq.zmq_msg_t() 41 | assert(s:recv_msg(msg)) 42 | 43 | local timer = zmq.stopwatch_start() 44 | 45 | for i = 1, message_count - 1 do 46 | assert(s:recv_msg(msg)) 47 | assert(msg:size() == message_size, "Invalid message size") 48 | end 49 | 50 | local elapsed = timer:stop() 51 | 52 | s:close() 53 | ctx:term() 54 | 55 | if elapsed == 0 then elapsed = 1 end 56 | 57 | local throughput = message_count / (elapsed / 1000000) 58 | local megabits = throughput * message_size * 8 / 1000000 59 | 60 | print(string.format("mean throughput: %i [msg/s]", throughput)) 61 | print(string.format("mean throughput: %.3f [Mb/s]", megabits)) 62 | 63 | -------------------------------------------------------------------------------- /perf/local_thr.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2010 Aleksey Yeschenko 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 | if not arg[3] then 22 | print("usage: lua local_thr.lua ") 23 | os.exit() 24 | end 25 | 26 | local bind_to = arg[1] 27 | local message_size = tonumber(arg[2]) 28 | local message_count = tonumber(arg[3]) 29 | 30 | local zmq = require"zmq" 31 | 32 | local ctx = zmq.init(1) 33 | local s = assert(ctx:socket(zmq.PULL)) 34 | assert(s:bind(bind_to)) 35 | 36 | print(string.format("message size: %i [B]", message_size)) 37 | print(string.format("message count: %i", message_count)) 38 | 39 | local msg 40 | msg = zmq.zmq_msg_t() 41 | assert(s:recv_msg(msg)) 42 | 43 | local timer = zmq.stopwatch_start() 44 | 45 | for i = 1, message_count - 1 do 46 | assert(s:recv_msg(msg)) 47 | assert(msg:size() == message_size, "Invalid message size") 48 | end 49 | 50 | local elapsed = timer:stop() 51 | 52 | s:close() 53 | ctx:term() 54 | 55 | if elapsed == 0 then elapsed = 1 end 56 | 57 | local throughput = message_count / (elapsed / 1000000) 58 | local megabits = throughput * message_size * 8 / 1000000 59 | 60 | print(string.format("mean throughput: %i [msg/s]", throughput)) 61 | print(string.format("mean throughput: %.3f [Mb/s]", megabits)) 62 | 63 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: c 2 | 3 | env: 4 | matrix: 5 | - LUA=lua5.1 LIBLUA=liblua5.1-dev LUA_INCDIR=/usr/include/lua5.1 LUA_LIB=lua5.1 6 | - LUA=lua5.2 LIBLUA=liblua5.2-dev LUA_INCDIR=/usr/include/lua5.2 LUA_LIB=lua5.2 7 | - LUA=luajit LIBLUA=libluajit-5.1-dev LUA_INCDIR=/usr/include/luajit-2.0 LUA_LIB=luajit-5.1 8 | 9 | branches: 10 | only: 11 | - master 12 | 13 | compiler: 14 | - gcc 15 | 16 | before_install: 17 | - if [ $LUA = "luajit" ]; then 18 | sudo add-apt-repository ppa:mwild1/ppa -y && sudo apt-get update -y; 19 | fi 20 | 21 | install: 22 | - sudo apt-get install libzmq3-dev -y 23 | - sudo apt-get install $LUA -y 24 | - sudo apt-get install $LIBLUA -y 25 | - LUA_LIBDIR=`pkg-config $LUA --variable=libdir` 26 | - INSTALL_LMOD=`pkg-config $LUA --variable=INSTALL_LMOD` 27 | - INSTALL_CMOD=`pkg-config $LUA --variable=INSTALL_CMOD` 28 | ## make sure there is a 'lua' command. 29 | - if [ ! -x /usr/bin/lua ]; then 30 | sudo ln -s `which $LUA` /usr/bin/lua; 31 | fi 32 | ## install lua-llthreads 33 | - git clone git://github.com/Neopallium/lua-llthreads.git 34 | - cd lua-llthreads ; mkdir build ; cd build 35 | - cmake .. -DLUA_LIBRARIES=$LUA_LIBDIR -DLUA_INCLUDE_DIR=$LUA_INCDIR 36 | -DINSTALL_LMOD=$INSTALL_LMOD -DINSTALL_CMOD=$INSTALL_CMOD 37 | - make 38 | - sudo make install 39 | - cd ../.. 40 | 41 | script: 42 | #### build using pre-generated bindings. 43 | - mkdir build; cd build 44 | - cmake .. -DLUA_LIBRARIES=$LUA_LIBDIR -DLUA_INCLUDE_DIR=$LUA_INCDIR 45 | -DINSTALL_LMOD=$INSTALL_LMOD -DINSTALL_CMOD=$INSTALL_CMOD 46 | - make 47 | - sudo make install 48 | # Run tests. 49 | - $LUA ../tests/test_inproc.lua 50 | - $LUA ../perf/thread_lat.lua 1 1000 51 | - cd .. ; rm -rf build 52 | #### Re-Generate bindings. 53 | - git clone git://github.com/Neopallium/LuaNativeObjects.git; 54 | - mkdir build; cd build 55 | - cmake .. -DLUA_LIBRARIES=$LUA_LIBDIR -DLUA_INCLUDE_DIR=$LUA_INCDIR 56 | -DLUA_NATIVE_OBJECTS_PATH=$TRAVIS_BUILD_DIR/LuaNativeObjects 57 | -DUSE_PRE_GENERATED_BINDINGS=OFF -DGENERATE_LUADOCS=OFF 58 | -DINSTALL_LMOD=$INSTALL_LMOD -DINSTALL_CMOD=$INSTALL_CMOD 59 | - make 60 | - sudo make install 61 | # Run tests. 62 | - $LUA ../tests/test_inproc.lua 63 | - $LUA ../perf/thread_lat.lua 1 1000 64 | 65 | notifications: 66 | email: 67 | on_failure: always 68 | on_success: change 69 | -------------------------------------------------------------------------------- /perf/remote_lat.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2010 Aleksey Yeschenko 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 | if not arg[3] then 22 | print("usage: lua remote_lat.lua ") 23 | os.exit() 24 | end 25 | 26 | local connect_to = arg[1] 27 | local message_size = tonumber(arg[2]) 28 | local roundtrip_count = tonumber(arg[3]) 29 | 30 | local zmq = require"zmq" 31 | 32 | local ctx = zmq.init(1) 33 | local s = assert(ctx:socket(zmq.REQ)) 34 | assert(s:connect(connect_to)) 35 | 36 | local data = ("0"):rep(message_size) 37 | local msg = zmq.zmq_msg_t.init_size(message_size) 38 | 39 | local timer = zmq.stopwatch_start() 40 | 41 | for i = 1, roundtrip_count do 42 | assert(s:send_msg(msg)) 43 | assert(s:recv_msg(msg)) 44 | assert(msg:size() == message_size, "Invalid message size") 45 | end 46 | 47 | local elapsed = timer:stop() 48 | 49 | s:close() 50 | ctx:term() 51 | 52 | local latency = elapsed / roundtrip_count / 2 53 | 54 | print(string.format("message size: %i [B]", message_size)) 55 | print(string.format("roundtrip count: %i", roundtrip_count)) 56 | print(string.format("mean latency: %.3f [us]", latency)) 57 | local secs = elapsed / (1000 * 1000) 58 | print(string.format("Elapsed: %f secs", secs)) 59 | print(string.format("throughput %f msg/secs", roundtrip_count / secs)) 60 | 61 | -------------------------------------------------------------------------------- /ws/tap.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2011, Robert G. Jakabosky All rights reserved. 2 | 3 | local gui_enabled = gui_enabled 4 | local register_menu = register_menu 5 | local MENU_TOOLS_UNSORTED = MENU_TOOLS_UNSORTED 6 | 7 | local win_instances = 0 8 | 9 | local function create_window_tap(name, create) 10 | win_instances = win_instances + 1 11 | 12 | local td, tap_filter, tap_type = create() 13 | 14 | -- tap's output window. 15 | local win = TextWindow.new(name .. " " .. win_instances) 16 | 17 | -- this tap will be local to the menu_function that called it 18 | local tap = Listener.new(tap_type, tap_filter) 19 | 20 | -- callback to remove the tap when the text window closes 21 | function remove_tap() 22 | if tap and tap.remove then 23 | tap:remove() 24 | end 25 | end 26 | 27 | -- make sure the tap doesn't hang around after the window was closed 28 | win:set_atclose(remove_tap) 29 | 30 | -- this function will be called for every packet 31 | function tap.packet(pinfo,tvb, tree, tapdata) 32 | return td:packet(pinfo, tvb, tree, tapdata) 33 | end 34 | 35 | -- this function will be called once every few seconds to redraw the window 36 | function tap.draw() 37 | local text = td:draw() 38 | win:set(text) 39 | end 40 | 41 | -- this function will be called at the end of the capture run. 42 | function tap.reset() 43 | return td:reset() 44 | end 45 | end 46 | 47 | local function create_tshark_tap(name, create) 48 | 49 | local td, tap_filter, tap_type = create() 50 | 51 | -- this tap will be local to the menu_function that called it 52 | local tap = Listener.new(tap_type, tap_filter) 53 | 54 | -- this function will be called for every packet 55 | function tap.packet(pinfo,tvb,tapdata) 56 | return td:packet(pinfo, tvb, tapdata) 57 | end 58 | 59 | -- this function will be called once every few seconds to redraw the window 60 | function tap.draw() 61 | local text = td:draw() 62 | debug(name .. " results:\n" .. text) 63 | end 64 | 65 | -- this function will be called at the end of the capture run. 66 | function tap.reset() 67 | return td:reset() 68 | end 69 | end 70 | 71 | return function (name, create) 72 | if gui_enabled() then 73 | -- menu callback. 74 | local create_tap = function() 75 | create_window_tap(name, create) 76 | end 77 | -- register menu item if running from wireshark 78 | register_menu(name, create_tap, MENU_TOOLS_UNSORTED) 79 | else 80 | -- we are running from tshark, create a non-gui tap now. 81 | create_tshark_tap(name, create) 82 | end 83 | end 84 | 85 | -------------------------------------------------------------------------------- /perf/local_multipart.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2010 Aleksey Yeschenko 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 | if not arg[3] then 22 | print("usage: lua local_thr.lua ") 23 | os.exit() 24 | end 25 | 26 | local bind_to = arg[1] 27 | local message_size = tonumber(arg[2]) 28 | local message_count = tonumber(arg[3]) 29 | 30 | local zmq = require"zmq" 31 | 32 | local ctx = zmq.init(1) 33 | local s = assert(ctx:socket(zmq.PULL)) 34 | --s:setopt(zmq.SUBSCRIBE, ""); 35 | assert(s:bind(bind_to)) 36 | 37 | local function recv_msg(s,msg) 38 | assert(s:recv_msg(msg)) 39 | assert(msg:size() == message_size/2, "Invalid message size") 40 | assert(s:getopt(zmq.RCVMORE) == 1, "Message not multipart") 41 | assert(s:recv_msg(msg)) 42 | assert(msg:size() == message_size/2, "Invalid message size") 43 | assert(s:getopt(zmq.RCVMORE) == 0, "Message has too many parts") 44 | end 45 | 46 | local msg 47 | msg = zmq.zmq_msg_t() 48 | recv_msg(s, msg) 49 | 50 | local timer = zmq.stopwatch_start() 51 | 52 | for i = 1, message_count - 1 do 53 | recv_msg(s, msg) 54 | end 55 | 56 | local elapsed = timer:stop() 57 | 58 | s:close() 59 | ctx:term() 60 | 61 | if elapsed == 0 then elapsed = 1 end 62 | 63 | local throughput = message_count / (elapsed / 1000000) 64 | local megabits = throughput * message_size * 8 / 1000000 65 | 66 | print(string.format("message size: %i [B]", message_size)) 67 | print(string.format("message count: %i", message_count)) 68 | print(string.format("mean throughput: %i [msg/s]", throughput)) 69 | print(string.format("mean throughput: %.3f [Mb/s]", megabits)) 70 | -------------------------------------------------------------------------------- /perf/local_thr_poll.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2010 Aleksey Yeschenko 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 | if not arg[3] then 22 | print("usage: lua local_thr.lua ") 23 | os.exit() 24 | end 25 | 26 | local bind_to = arg[1] 27 | local message_size = tonumber(arg[2]) 28 | local message_count = tonumber(arg[3]) 29 | 30 | local zmq = require"zmq" 31 | local z_poller = require"zmq.poller" 32 | local z_NOBLOCK = zmq.NOBLOCK 33 | 34 | local poller = z_poller(64) 35 | 36 | local ctx = zmq.init(1) 37 | local s = assert(ctx:socket(zmq.SUB)) 38 | assert(s:setopt(zmq.SUBSCRIBE, "")) 39 | assert(s:bind(bind_to)) 40 | 41 | print(string.format("message size: %i [B]", message_size)) 42 | print(string.format("message count: %i", message_count)) 43 | 44 | local msg 45 | msg = zmq.zmq_msg_t() 46 | 47 | local cnt = 0 48 | 49 | poller:add(s, zmq.POLLIN, function(sock) 50 | while s:recv_msg(msg, z_NOBLOCK) do 51 | --assert(msg:size() == message_size, "Invalid message size") 52 | cnt = cnt + 1 53 | if cnt == message_count then 54 | poller:stop() 55 | end 56 | end 57 | end) 58 | 59 | -- wait for first message 60 | assert(s:recv_msg(msg)) 61 | cnt = 1 62 | 63 | local timer = zmq.stopwatch_start() 64 | poller:start() 65 | local elapsed = timer:stop() 66 | 67 | s:close() 68 | ctx:term() 69 | 70 | if elapsed == 0 then elapsed = 1 end 71 | 72 | local throughput = message_count / (elapsed / 1000000) 73 | local megabits = throughput * message_size * 8 / 1000000 74 | 75 | print(string.format("mean throughput: %i [msg/s]", throughput)) 76 | print(string.format("mean throughput: %.3f [Mb/s]", megabits)) 77 | 78 | -------------------------------------------------------------------------------- /tests/test_inproc.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2011 Robert G. Jakabosky 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 | if #arg < 1 then 22 | print("usage: lua " .. arg[0] .. " [message-size] [roundtrip-count] [bind-to] [connect-to]") 23 | end 24 | 25 | local message_size = tonumber(arg[1] or 1) 26 | local roundtrip_count = tonumber(arg[2] or 100) 27 | local bind_to = arg[3] or 'inproc://thread_lat_test' 28 | local connect_to = arg[4] or 'inproc://thread_lat_test' 29 | 30 | local zmq = require"zmq" 31 | 32 | local ctx = zmq.init(1) 33 | local server = assert(ctx:socket(zmq.REQ)) 34 | assert(server:bind(bind_to)) 35 | 36 | local client = ctx:socket(zmq.REP) 37 | client:connect(connect_to) 38 | 39 | local data = ("0"):rep(message_size) 40 | local msg = zmq.zmq_msg_t.init_size(message_size) 41 | local client_msg = zmq.zmq_msg_t() 42 | 43 | print(string.format("message size: %i [B]", message_size)) 44 | print(string.format("roundtrip count: %i", roundtrip_count)) 45 | 46 | local timer = zmq.stopwatch_start() 47 | 48 | for i = 1, roundtrip_count do 49 | -- server send 50 | assert(server:send_msg(msg)) 51 | 52 | -- client recv 53 | assert(client:recv_msg(client_msg)) 54 | assert(client_msg:size() == message_size, "Invalid message size") 55 | -- client send 56 | assert(client:send_msg(client_msg)) 57 | 58 | -- server recv 59 | assert(server:recv_msg(msg)) 60 | assert(msg:size() == message_size, "Invalid message size") 61 | end 62 | 63 | local elapsed = timer:stop() 64 | 65 | server:close() 66 | client:close() 67 | ctx:term() 68 | 69 | local latency = elapsed / roundtrip_count / 2 70 | 71 | print(string.format("mean latency: %.3f [us]", latency)) 72 | local secs = elapsed / (1000 * 1000) 73 | print(string.format("elapsed = %f", secs)) 74 | print(string.format("msg/sec = %f", roundtrip_count / secs)) 75 | 76 | -------------------------------------------------------------------------------- /src/threads.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2011 by Robert G. Jakabosky 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 | -- zmq.thread wraps the low-level threads object & a zmq context. 23 | -- 24 | 25 | local zmq = require"zmq" 26 | local llthreads = require"llthreads" 27 | 28 | local bootstrap_pre = [[ 29 | local action, action_arg, parent_ctx = ... 30 | local func 31 | ]] 32 | 33 | local bootstrap_post = [[ 34 | 35 | -- copy parent ZeroMQ context to this child thread. 36 | local zmq = require"zmq" 37 | local zthreads = require"zmq.threads" 38 | if parent_ctx then 39 | zthreads.set_parent_ctx(zmq.init_ctx(parent_ctx)) 40 | end 41 | 42 | -- create global 'arg' 43 | arg = { select(4, ...) } 44 | 45 | -- load Lua code. 46 | if action == 'runfile' then 47 | func = assert(loadfile(action_arg)) 48 | -- script name 49 | arg[0] = action_arg 50 | elseif action == 'runstring' then 51 | func = assert(loadstring(action_arg)) 52 | -- fake script name 53 | arg[0] = '=(loadstring)' 54 | end 55 | 56 | -- run loaded code. 57 | return func(select(4, ...)) 58 | ]] 59 | 60 | local bootstrap_code = bootstrap_pre..bootstrap_post 61 | 62 | local function new_thread(ctx, action, action_arg, ...) 63 | -- convert ZMQ_Ctx to lightuserdata. 64 | if ctx then 65 | ctx = ctx:lightuserdata() 66 | end 67 | return llthreads.new(bootstrap_code, action, action_arg, ctx, ...) 68 | end 69 | 70 | local M = {} 71 | 72 | function M.set_bootstrap_prelude(code) 73 | bootstrap_code = bootstrap_pre .. code .. bootstrap_post 74 | end 75 | 76 | function M.runfile(ctx, file, ...) 77 | return new_thread(ctx, 'runfile', file, ...) 78 | end 79 | 80 | function M.runstring(ctx, code, ...) 81 | return new_thread(ctx, 'runstring', code, ...) 82 | end 83 | 84 | local parent_ctx = nil 85 | function M.set_parent_ctx(ctx) 86 | parent_ctx = ctx 87 | end 88 | 89 | function M.get_parent_ctx(ctx) 90 | return parent_ctx 91 | end 92 | 93 | zmq.threads = M 94 | return M 95 | -------------------------------------------------------------------------------- /src/poller.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2011 by Robert G. Jakabosky 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 | -- zmq.poller wraps the low-level zmq.ZMQ_Poller object. 23 | -- 24 | -- This wrapper simplifies the event polling loop. 25 | -- 26 | 27 | local zmq = require"zmq" 28 | 29 | local setmetatable = setmetatable 30 | local tonumber = tonumber 31 | local assert = assert 32 | 33 | local poller_mt = {} 34 | poller_mt.__index = poller_mt 35 | 36 | function poller_mt:add(sock, events, cb) 37 | local id = self.poller:add(sock, events) 38 | self.callbacks[id] = function(revents) return cb(sock, revents) end 39 | end 40 | 41 | function poller_mt:modify(sock, events, cb) 42 | local id 43 | if events ~= 0 and cb then 44 | id = self.poller:modify(sock, events) 45 | self.callbacks[id] = function(revents) return cb(sock, revents) end 46 | else 47 | id = self:remove(sock) 48 | self.callbacks[id] = nil 49 | end 50 | end 51 | 52 | function poller_mt:remove(sock) 53 | local id = self.poller:remove(sock) 54 | self.callbacks[id] = nil 55 | end 56 | 57 | function poller_mt:poll(timeout) 58 | local poller = self.poller 59 | local count, err = poller:poll(timeout) 60 | if not count then 61 | return nil, err 62 | end 63 | local callbacks = self.callbacks 64 | for i=1,count do 65 | local id, revents = poller:next_revents_idx() 66 | callbacks[id](revents) 67 | end 68 | return count 69 | end 70 | 71 | function poller_mt:start() 72 | self.is_running = true 73 | while self.is_running do 74 | status, err = self:poll(-1) 75 | if not status then 76 | return false, err 77 | end 78 | end 79 | return true 80 | end 81 | 82 | function poller_mt:stop() 83 | self.is_running = false 84 | end 85 | 86 | local M = {} 87 | 88 | function M.new(pre_alloc) 89 | return setmetatable({ 90 | poller = zmq.ZMQ_Poller(pre_alloc), 91 | callbacks = {}, 92 | }, poller_mt) 93 | end 94 | 95 | zmq.poller = M 96 | return setmetatable(M, {__call = function(tab, ...) return M.new(...) end}) 97 | 98 | -------------------------------------------------------------------------------- /examples/publisher_poll.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2012 Robert G. Jakabosky 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 | local poller = require"examples.poller" 22 | local poll = poller.new() 23 | 24 | local zmq = require"zmq" 25 | local z_NOBLOCK = zmq.NOBLOCK 26 | local z_EVENTS = zmq.EVENTS 27 | local z_POLLIN = zmq.POLLIN 28 | local z_POLLOUT = zmq.POLLOUT 29 | local z_POLLIN_OUT = z_POLLIN + z_POLLOUT 30 | 31 | local ctx = zmq.init() 32 | local s = ctx:socket(zmq.PUB) 33 | local s_FD = s:getopt(zmq.FD) 34 | 35 | s:bind("tcp://lo:5555") 36 | 37 | -- current socket state 38 | local blocked_state 39 | local blocked_event 40 | local on_sock_recv 41 | local on_sock_send 42 | 43 | -- IO event callback when socket was blocked 44 | local function on_sock_io() 45 | local events = s:getopt(z_EVENTS) 46 | local unblocked = false 47 | if events == blocked_event then 48 | -- got the event the socket was blocked on. 49 | unblocked = true 50 | elseif events == z_POLLIN_OUT then 51 | -- got both in & out events 52 | unblocked = true 53 | end 54 | if unblocked then 55 | -- got the event we are blocked on resume. 56 | blocked_event = nil 57 | blocked_state() 58 | -- check if blocked event was processed. 59 | if not blocked_event then 60 | poll:remove_read(s_FD) 61 | end 62 | end 63 | end 64 | local function sock_blocked(state, event) 65 | if not blocked_event then 66 | -- need to register socket's fd with event loop 67 | poll:add_read(s_FD, on_sock_io) 68 | end 69 | blocked_state = state 70 | blocked_event = event 71 | end 72 | 73 | -- sock state functions 74 | local msg_id = 1 75 | function on_sock_send() 76 | local sent, err = s:send(tostring(msg_id), z_NOBLOCK) 77 | if not sent then 78 | assert(err == 'timeout', "Bad error on zmq socket.") 79 | return sock_blocked(on_sock_send, z_POLLOUT) 80 | end 81 | -- message sent, inc. id 82 | msg_id = msg_id + 1 83 | -- yield back to event loop 84 | poll:add_work(on_sock_send) 85 | end 86 | 87 | -- start processing of the socket. 88 | poll:add_work(on_sock_send) 89 | 90 | -- start event loop 91 | poll:start() 92 | 93 | s:close() 94 | ctx:term() 95 | 96 | -------------------------------------------------------------------------------- /examples/subscriber_poll.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2012 Robert G. Jakabosky 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 | local poller = require"examples.poller" 22 | local poll = poller.new() 23 | 24 | local zmq = require"zmq" 25 | local z_NOBLOCK = zmq.NOBLOCK 26 | local z_EVENTS = zmq.EVENTS 27 | local z_POLLIN = zmq.POLLIN 28 | local z_POLLOUT = zmq.POLLOUT 29 | local z_POLLIN_OUT = z_POLLIN + z_POLLOUT 30 | 31 | local N=tonumber(arg[1] or 100) 32 | 33 | local ctx = zmq.init() 34 | local s = ctx:socket(zmq.SUB) 35 | local s_FD = s:getopt(zmq.FD) 36 | 37 | s:setopt(zmq.SUBSCRIBE, "") 38 | s:connect("tcp://localhost:5555") 39 | 40 | -- current socket state 41 | local blocked_state 42 | local blocked_event 43 | local on_sock_recv 44 | local on_sock_send 45 | 46 | -- IO event callback when socket was blocked 47 | local function on_sock_io() 48 | local events = s:getopt(z_EVENTS) 49 | local unblocked = false 50 | if events == blocked_event then 51 | -- got the event the socket was blocked on. 52 | unblocked = true 53 | elseif events == z_POLLIN_OUT then 54 | -- got both in & out events 55 | unblocked = true 56 | end 57 | if unblocked then 58 | -- got the event we are blocked on resume. 59 | blocked_event = nil 60 | blocked_state() 61 | -- check if blocked event was processed. 62 | if not blocked_event then 63 | poll:remove_read(s_FD) 64 | end 65 | end 66 | end 67 | local function sock_blocked(state, event) 68 | if not blocked_event then 69 | -- need to register socket's fd with event loop 70 | poll:add_read(s_FD, on_sock_io) 71 | end 72 | blocked_state = state 73 | blocked_event = event 74 | end 75 | 76 | -- sock state functions 77 | function on_sock_recv() 78 | local data, err = s:recv(z_NOBLOCK) 79 | if not data then 80 | assert(err == 'timeout', "Bad error on zmq socket.") 81 | return sock_blocked(on_sock_recv, z_POLLIN) 82 | end 83 | local msg_id = tonumber(data) 84 | if (msg_id % 10000) == 0 then print(data) end 85 | return on_sock_recv() 86 | end 87 | 88 | -- start processing of the socket. 89 | poll:add_work(on_sock_recv) 90 | 91 | -- start event loop 92 | poll:start() 93 | 94 | s:close() 95 | ctx:term() 96 | 97 | -------------------------------------------------------------------------------- /perf/thread_lat.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2011 Robert G. Jakabosky 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 | if #arg < 1 then 22 | print("usage: lua " .. arg[0] .. " [message-size] [roundtrip-count] [bind-to] [connect-to]") 23 | end 24 | 25 | local message_size = tonumber(arg[1] or 1) 26 | local roundtrip_count = tonumber(arg[2] or 100000) 27 | local bind_to = arg[3] or 'inproc://thread_lat_test' 28 | local connect_to = arg[4] or 'inproc://thread_lat_test' 29 | 30 | local zmq = require"zmq" 31 | local zthreads = require"zmq.threads" 32 | 33 | local child_code = [[ 34 | local connect_to, message_size, roundtrip_count = ... 35 | 36 | local zmq = require"zmq" 37 | local zthreads = require"zmq.threads" 38 | 39 | local ctx = zthreads.get_parent_ctx() 40 | local s = ctx:socket(zmq.REP) 41 | s:connect(connect_to) 42 | 43 | local msg = zmq.zmq_msg_t() 44 | 45 | for i = 1, roundtrip_count do 46 | assert(s:recv_msg(msg)) 47 | assert(msg:size() == message_size, "Invalid message size") 48 | assert(s:send_msg(msg)) 49 | end 50 | 51 | s:close() 52 | ]] 53 | 54 | local ctx = zmq.init(1) 55 | local s = assert(ctx:socket(zmq.REQ)) 56 | assert(s:bind(bind_to)) 57 | 58 | local child_thread = zthreads.runstring(ctx, child_code, connect_to, message_size, roundtrip_count) 59 | child_thread:start() 60 | 61 | local data = ("0"):rep(message_size) 62 | local msg = zmq.zmq_msg_t.init_size(message_size) 63 | 64 | print(string.format("message size: %i [B]", message_size)) 65 | print(string.format("roundtrip count: %i", roundtrip_count)) 66 | 67 | zmq.sleep(2) -- wait for child thread to connect. 68 | 69 | local timer = zmq.stopwatch_start() 70 | 71 | for i = 1, roundtrip_count do 72 | assert(s:send_msg(msg)) 73 | assert(s:recv_msg(msg)) 74 | assert(msg:size() == message_size, "Invalid message size") 75 | end 76 | 77 | local elapsed = timer:stop() 78 | 79 | s:close() 80 | child_thread:join() 81 | ctx:term() 82 | 83 | local latency = elapsed / roundtrip_count / 2 84 | 85 | print(string.format("mean latency: %.3f [us]", latency)) 86 | local secs = elapsed / (1000 * 1000) 87 | print(string.format("elapsed = %f", secs)) 88 | print(string.format("msg/sec = %f", roundtrip_count / secs)) 89 | 90 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # 2 | # Lua bindings for 0MQ 3 | # 4 | cmake_minimum_required(VERSION 3.18) 5 | 6 | project(lua-zmq C) 7 | 8 | set(BUILD_SHARED_LIBS TRUE) 9 | 10 | set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake) 11 | 12 | set(INSTALL_LMOD ${CMAKE_INSTALL_PREFIX}/share/lua/ CACHE PATH 13 | "Directory to install Lua source modules (configure lua via LUA_PATH)") 14 | set(INSTALL_CMOD ${CMAKE_INSTALL_PREFIX}/lib/lua/ CACHE PATH 15 | "Directory to install Lua binary modules (configure lua via LUA_CPATH)") 16 | set(ZMQ_PATH "" CACHE PATH 17 | "Directory to libzmq. (by default use pkg-config to detect path)") 18 | 19 | set(COMMON_CFLAGS "${CFLAGS}") 20 | set(COMMON_LDFLAGS) 21 | set(COMMON_LIBS) 22 | 23 | ## Lua 5.x 24 | include(FindLua) 25 | if(NOT ${LUA_FOUND}) 26 | message(FATAL_ERROR "The FindLua module could not find lua :-(") 27 | endif() 28 | set(COMMON_LIBS "${COMMON_LIBS};${LUA_LIBRARIES}") 29 | 30 | if(WIN32) 31 | set(COMMON_CFLAGS "${COMMON_CFLAGS} -I${LUA_INCLUDE_DIR}") 32 | set(COMMON_LDFLAGS "${COMMON_LDFLAGS} ${LUA_LIBRARY}") 33 | if(NOT MSVC) 34 | set(COMMON_LDFLAGS "${COMMON_LDFLAGS} -Wl,--export-all-symbols") 35 | endif() 36 | endif() 37 | ## MAC OSX needs extra linker flags 38 | if(APPLE) 39 | set(COMMON_LDFLAGS "${COMMON_LDFLAGS} -undefined dynamic_lookup") 40 | endif() 41 | 42 | ## LibZMQ 43 | if(WIN32) 44 | ## need ZMQ_PATH 45 | if(IS_DIRECTORY "${ZMQ_PATH}") 46 | else() 47 | message(FATAL_ERROR "Please set the ZMQ_PATH CMake variable.") 48 | endif() 49 | endif() 50 | if(IS_DIRECTORY ${ZMQ_PATH}) 51 | set(COMMON_CFLAGS "${COMMON_CFLAGS} -I${ZMQ_PATH}/include") 52 | if(MSVC) 53 | set(COMMON_LIBS "${COMMON_LIBS};libzmq") 54 | else() 55 | set(COMMON_LDFLAGS "${COMMON_LDFLAGS} -L${ZMQ_PATH}/lib") 56 | set(COMMON_LIBS "${COMMON_LIBS};zmq") 57 | endif() 58 | link_directories(${ZMQ_PATH}/lib) 59 | else() 60 | ## fallback to using pkg-config 61 | include(FindPkgConfig) 62 | pkg_search_module(ZMQ REQUIRED libzmq) 63 | set(COMMON_CFLAGS "${COMMON_CFLAGS} ${ZMQ_CFLAGS}") 64 | set(COMMON_LDFLAGS "${COMMON_LDFLAGS} ${ZMQ_LDFLAGS}") 65 | set(COMMON_LIBS "${COMMON_LIBS};${ZMQ_LIBRARIES}") 66 | endif() 67 | 68 | ## LuaNativeObjects 69 | include(LuaNativeObjects) 70 | include(CustomMacros) 71 | 72 | include_directories(${CMAKE_CURRENT_SOURCE_DIR} 73 | ${CMAKE_CURRENT_BINARY_DIR} 74 | ${LUA_INCLUDE_DIR}) 75 | 76 | ## LuaZMQ 77 | set(LUA_ZMQ_SRC 78 | zmq.nobj.lua 79 | ) 80 | 81 | ## LuaZMQ Lua source modules 82 | set(LUA_ZMQ_SRC_LUA_MODS 83 | src/poller.lua 84 | src/threads.lua 85 | ) 86 | 87 | if(${USE_PRE_GENERATED_BINDINGS}) 88 | set(LUA_ZMQ_SRC src/pre_generated-zmq.nobj.c) 89 | else() 90 | # Generate Lua bindings. 91 | GenLuaNativeObjects(LUA_ZMQ_SRC) 92 | endif() 93 | 94 | add_library(lua-zmq MODULE ${LUA_ZMQ_SRC}) 95 | target_link_libraries(lua-zmq ${COMMON_LIBS}) 96 | set_target_properties(lua-zmq PROPERTIES PREFIX "") 97 | add_target_properties(lua-zmq COMPILE_FLAGS "${COMMON_CFLAGS}") 98 | add_target_properties(lua-zmq LINK_FLAGS "${LD_FLAGS} ${COMMON_LDFLAGS}") 99 | set_target_properties(lua-zmq PROPERTIES OUTPUT_NAME zmq) 100 | 101 | install(TARGETS lua-zmq 102 | DESTINATION "${INSTALL_CMOD}") 103 | 104 | install(FILES ${LUA_ZMQ_SRC_LUA_MODS} 105 | DESTINATION "${INSTALL_LMOD}/zmq") 106 | 107 | -------------------------------------------------------------------------------- /examples/server_poll.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2012 Robert G. Jakabosky 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 | local poller = require"examples.poller" 22 | local poll = poller.new() 23 | 24 | local zmq = require"zmq" 25 | local z_NOBLOCK = zmq.NOBLOCK 26 | local z_EVENTS = zmq.EVENTS 27 | local z_POLLIN = zmq.POLLIN 28 | local z_POLLOUT = zmq.POLLOUT 29 | local z_POLLIN_OUT = z_POLLIN + z_POLLOUT 30 | 31 | local ctx = zmq.init() 32 | local s = ctx:socket(zmq.REP) 33 | local s_FD = s:getopt(zmq.FD) 34 | 35 | s:bind("tcp://lo:5555") 36 | 37 | -- current socket state 38 | local blocked_state 39 | local blocked_event 40 | local on_sock_recv 41 | local on_sock_send 42 | 43 | -- IO event callback when socket was blocked 44 | local function on_sock_io() 45 | local events = s:getopt(z_EVENTS) 46 | local unblocked = false 47 | if events == blocked_event then 48 | -- got the event the socket was blocked on. 49 | unblocked = true 50 | elseif events == z_POLLIN_OUT then 51 | -- got both in & out events 52 | unblocked = true 53 | end 54 | if unblocked then 55 | -- got the event we are blocked on resume. 56 | blocked_event = nil 57 | blocked_state() 58 | -- check if blocked event was processed. 59 | if not blocked_event then 60 | poll:remove_read(s_FD) 61 | end 62 | end 63 | end 64 | local function sock_blocked(state, event) 65 | if not blocked_event then 66 | -- need to register socket's fd with event loop 67 | poll:add_read(s_FD, on_sock_io) 68 | end 69 | blocked_state = state 70 | blocked_event = event 71 | end 72 | 73 | -- sock state functions 74 | function on_sock_recv() 75 | local data, err = s:recv(z_NOBLOCK) 76 | if not data then 77 | assert(err == 'timeout', "Bad error on zmq socket.") 78 | return sock_blocked(on_sock_recv, z_POLLIN) 79 | end 80 | print(string.format("Received query: '%s'", data)) 81 | return on_sock_send() 82 | end 83 | 84 | function on_sock_send() 85 | local sent, err = s:send("OK", z_NOBLOCK) 86 | if not sent then 87 | assert(err == 'timeout', "Bad error on zmq socket.") 88 | return sock_blocked(on_sock_send, z_POLLOUT) 89 | end 90 | -- yield back to event loop 91 | poll:add_work(on_sock_recv) 92 | end 93 | 94 | -- start processing of the socket. 95 | poll:add_work(on_sock_recv) 96 | 97 | -- start event loop 98 | poll:start() 99 | 100 | s:close() 101 | ctx:term() 102 | 103 | -------------------------------------------------------------------------------- /examples/client_poll.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2012 Robert G. Jakabosky 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 | local poller = require"examples.poller" 22 | local poll = poller.new() 23 | 24 | local zmq = require"zmq" 25 | local z_NOBLOCK = zmq.NOBLOCK 26 | local z_EVENTS = zmq.EVENTS 27 | local z_POLLIN = zmq.POLLIN 28 | local z_POLLOUT = zmq.POLLOUT 29 | local z_POLLIN_OUT = z_POLLIN + z_POLLOUT 30 | 31 | local N=tonumber(arg[1] or 100) 32 | 33 | local ctx = zmq.init() 34 | local s = ctx:socket(zmq.REQ) 35 | local s_FD = s:getopt(zmq.FD) 36 | 37 | s:connect("tcp://localhost:5555") 38 | 39 | -- current socket state 40 | local blocked_state 41 | local blocked_event 42 | local on_sock_recv 43 | local on_sock_send 44 | 45 | -- IO event callback when socket was blocked 46 | local function on_sock_io() 47 | local events = s:getopt(z_EVENTS) 48 | local unblocked = false 49 | if events == blocked_event then 50 | -- got the event the socket was blocked on. 51 | unblocked = true 52 | elseif events == z_POLLIN_OUT then 53 | -- got both in & out events 54 | unblocked = true 55 | end 56 | if unblocked then 57 | -- got the event we are blocked on resume. 58 | blocked_event = nil 59 | blocked_state() 60 | -- check if blocked event was processed. 61 | if not blocked_event then 62 | poll:remove_read(s_FD) 63 | end 64 | end 65 | end 66 | local function sock_blocked(state, event) 67 | if not blocked_event then 68 | -- need to register socket's fd with event loop 69 | poll:add_read(s_FD, on_sock_io) 70 | end 71 | blocked_state = state 72 | blocked_event = event 73 | end 74 | 75 | -- sock state functions 76 | function on_sock_send() 77 | N = N - 1 78 | if N == 0 then 79 | return poll:stop() 80 | end 81 | local sent, err = s:send("SELECT * FROM mytable", z_NOBLOCK) 82 | if not sent then 83 | assert(err == 'timeout', "Bad error on zmq socket.") 84 | return sock_blocked(on_sock_send, z_POLLOUT) 85 | end 86 | -- yield back to event loop 87 | poll:add_work(on_sock_recv) 88 | end 89 | 90 | function on_sock_recv() 91 | local data, err = s:recv(z_NOBLOCK) 92 | if not data then 93 | assert(err == 'timeout', "Bad error on zmq socket.") 94 | return sock_blocked(on_sock_recv, z_POLLIN) 95 | end 96 | print(data) 97 | return on_sock_send() 98 | end 99 | 100 | -- start processing of the socket. 101 | poll:add_work(on_sock_send) 102 | 103 | -- start event loop 104 | poll:start() 105 | 106 | s:close() 107 | ctx:term() 108 | 109 | -------------------------------------------------------------------------------- /perf/thread_thr.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2011 Robert G. Jakabosky 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 | if #arg < 1 then 22 | print("usage: lua " .. arg[0] .. " [message-size] [message-count] [bind-to] [connect-to]") 23 | end 24 | 25 | local message_size = tonumber(arg[1] or 1) 26 | local message_count = tonumber(arg[2] or 100000) 27 | local bind_to = arg[3] or 'inproc://thread_thr_test' 28 | local connect_to = arg[4] or 'inproc://thread_thr_test' 29 | 30 | local zmq = require"zmq" 31 | local zthreads = require"zmq.threads" 32 | 33 | local child_code = [[ 34 | local connect_to, message_size, message_count = ... 35 | 36 | local zmq = require"zmq" 37 | local zthreads = require"zmq.threads" 38 | 39 | local ctx = zthreads.get_parent_ctx() 40 | local s = assert(ctx:socket(zmq.PUSH)) 41 | assert(s:connect(connect_to)) 42 | 43 | local data = ("0"):rep(message_size) 44 | local msg_data = zmq.zmq_msg_t.init_data(data) 45 | local msg = zmq.zmq_msg_t.init() 46 | 47 | local timer = zmq.stopwatch_start() 48 | 49 | for i = 1, message_count do 50 | msg:copy(msg_data) 51 | assert(s:send_msg(msg)) 52 | end 53 | 54 | local elapsed = timer:stop() 55 | 56 | s:close() 57 | 58 | if elapsed == 0 then elapsed = 1 end 59 | 60 | local throughput = message_count / (elapsed / 1000000) 61 | local megabits = throughput * message_size * 8 / 1000000 62 | 63 | print(string.format("Sender mean throughput: %i [msg/s]", throughput)) 64 | print(string.format("Sender mean throughput: %.3f [Mb/s]", megabits)) 65 | 66 | print("sending thread finished.") 67 | ]] 68 | 69 | local ctx = zmq.init(1) 70 | local s = assert(ctx:socket(zmq.PULL)) 71 | assert(s:bind(bind_to)) 72 | 73 | print(string.format("message size: %i [B]", message_size)) 74 | print(string.format("message count: %i", message_count)) 75 | 76 | local child_thread = zthreads.runstring(ctx, child_code, connect_to, message_size, message_count) 77 | child_thread:start() 78 | 79 | local msg 80 | msg = zmq.zmq_msg_t() 81 | assert(s:recv_msg(msg)) 82 | 83 | local timer = zmq.stopwatch_start() 84 | 85 | for i = 1, message_count - 1 do 86 | assert(s:recv_msg(msg)) 87 | assert(msg:size() == message_size, "Invalid message size") 88 | end 89 | 90 | local elapsed = timer:stop() 91 | 92 | s:close() 93 | child_thread:join() 94 | ctx:term() 95 | 96 | if elapsed == 0 then elapsed = 1 end 97 | 98 | local throughput = message_count / (elapsed / 1000000) 99 | local megabits = throughput * message_size * 8 / 1000000 100 | 101 | print(string.format("mean throughput: %i [msg/s]", throughput)) 102 | print(string.format("mean throughput: %.3f [Mb/s]", megabits)) 103 | 104 | -------------------------------------------------------------------------------- /perf/thread_push_pull.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2011 Robert G. Jakabosky 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 | if #arg < 1 then 22 | print("usage: lua " .. arg[0] .. " [message-size] [message-count] [bind-to] [connect-to]") 23 | end 24 | 25 | local message_size = tonumber(arg[1] or 1) 26 | local message_count = tonumber(arg[2] or 100000) 27 | local bind_to = arg[3] or 'inproc://thread_lat_test' 28 | local connect_to = arg[4] or 'inproc://thread_lat_test' 29 | 30 | local zmq = require"zmq" 31 | local zthreads = require"zmq.threads" 32 | 33 | local child_code = [[ 34 | local connect_to, message_size, message_count = ... 35 | 36 | local zmq = require"zmq" 37 | local zthreads = require"zmq.threads" 38 | 39 | local ctx = zthreads.get_parent_ctx() 40 | local s = assert(ctx:socket(zmq.PUSH)) 41 | assert(s:setopt(zmq.HWM, message_count/4)) 42 | assert(s:connect(connect_to)) 43 | 44 | local data = ("0"):rep(message_size) 45 | local msg_data = zmq.zmq_msg_t.init_data(data) 46 | local msg = zmq.zmq_msg_t.init() 47 | 48 | local timer = zmq.stopwatch_start() 49 | 50 | for i = 1, message_count do 51 | msg:copy(msg_data) 52 | assert(s:send_msg(msg)) 53 | end 54 | 55 | local elapsed = timer:stop() 56 | 57 | s:close() 58 | 59 | if elapsed == 0 then elapsed = 1 end 60 | 61 | local throughput = message_count / (elapsed / 1000000) 62 | local megabits = throughput * message_size * 8 / 1000000 63 | 64 | print(string.format("Sender mean throughput: %i [msg/s]", throughput)) 65 | print(string.format("Sender mean throughput: %.3f [Mb/s]", megabits)) 66 | 67 | print("sending thread finished.") 68 | ]] 69 | 70 | local ctx = zmq.init(1) 71 | local s = assert(ctx:socket(zmq.PULL)) 72 | assert(s:bind(bind_to)) 73 | 74 | print(string.format("message size: %i [B]", message_size)) 75 | print(string.format("message count: %i", message_count)) 76 | 77 | local child_thread = zthreads.runstring(ctx, child_code, connect_to, message_size, message_count) 78 | child_thread:start() 79 | 80 | local msg 81 | msg = zmq.zmq_msg_t() 82 | assert(s:recv_msg(msg)) 83 | 84 | local timer = zmq.stopwatch_start() 85 | 86 | for i = 1, message_count - 1 do 87 | assert(s:recv_msg(msg)) 88 | assert(msg:size() == message_size, "Invalid message size") 89 | end 90 | 91 | local elapsed = timer:stop() 92 | 93 | s:close() 94 | child_thread:join() 95 | ctx:term() 96 | 97 | if elapsed == 0 then elapsed = 1 end 98 | 99 | local throughput = message_count / (elapsed / 1000000) 100 | local megabits = throughput * message_size * 8 / 1000000 101 | 102 | print(string.format("mean throughput: %i [msg/s]", throughput)) 103 | print(string.format("mean throughput: %.3f [Mb/s]", megabits)) 104 | 105 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | About 2 | ===== 3 | 4 | [![travis-ci status](https://secure.travis-ci.org/Neopallium/lua-zmq.png?branch=master)](http://travis-ci.org/Neopallium/lua-zmq/builds) 5 | 6 | Lua bindings to zeromq2. Check out the [ZeroMQ Guide with Lua examples](http://zguide.zeromq.org/lua:all). 7 | 8 | Windows 9 | ======= 10 | 11 | Download a compiled version of [LuaJIT 2.0.0-beta11 + lua-zmq + zeromq2.2.0](https://github.com/downloads/Neopallium/lua-zmq/luajit2.0_beta11_zmq2.2_llthreads.zip) 32bit & 64bit. 12 | 13 | API 14 | === 15 | 16 | See [API.md](https://github.com/Neopallium/lua-zmq/blob/master/API.md) and 17 | [ØMQ docs](http://api.zero.mq/2-1-3:_start). 18 | 19 | Requirements 20 | ============ 21 | 22 | * ZeroMQ version 2.1, 2.2 or 3.2. 23 | * Might work with some 2.0.x versions (2.0.6 and lower are not supported). 24 | 25 | For Ubuntu 10.10 users: 26 | 27 | * The version of ZeroMQ (2.0.6beta) that comes with Ubuntu 10.10 will not work with these bindings. Please upgrade to [version 2.1](http://fanf42.blogspot.com/2011/02/installing-zeromq-and-java-bindings-on.html). 28 | 29 | Installation 30 | ============ 31 | 32 | It is recommended to either compile Lua with the "-pthread" flag or preload libpthread.so on Linux when using this module ([see this glibc bug report](http://sourceware.org/bugzilla/show_bug.cgi?id=10652)): 33 | 34 | $ LD_PRELOAD=/lib/libpthread.so lua 35 | 36 | 37 | Release 1.0 38 | ----------- 39 | 40 | lua-zmq: 41 | 42 | $ sudo luarocks install lua-zmq 43 | 44 | lua-zmq-threads: 45 | 46 | $ sudo luarocks install lua-llthreads 47 | $ sudo luarocks install lua-zmq-threads 48 | 49 | 50 | Latest Git revision 51 | ------------------- 52 | 53 | With LuaRocks 2.0.4.1: 54 | 55 | $ sudo luarocks install https://raw.github.com/Neopallium/lua-zmq/master/rockspecs/lua-zmq-scm-1.rockspec 56 | 57 | For threads support: 58 | 59 | $ sudo luarocks install https://raw.github.com/Neopallium/lua-llthreads/master/rockspecs/lua-llthreads-scm-0.rockspec 60 | $ sudo luarocks install https://raw.github.com/Neopallium/lua-zmq/master/rockspecs/lua-zmq-threads-scm-0.rockspec 61 | 62 | With CMake: 63 | 64 | $ git clone git://github.com/Neopallium/lua-zmq.git 65 | $ cd lua-zmq ; mkdir build ; cd build 66 | $ cmake .. 67 | $ make 68 | $ sudo make install 69 | 70 | Throughput benchmark 71 | ==================== 72 | 73 | Throughput benchmark using the tcp transport over localhost: 74 | message size: 30 [B] 75 | message count: 100000000 76 | 77 | Using send/recv functions running under Lua 5.1.4: 78 | mean throughput: 1577407 [msg/s] 79 | mean throughput: 378.578 [Mb/s] 80 | 81 | Using send/recv functions running under LuaJIT2 (git HEAD): 82 | mean throughput: 5112158 [msg/s] 83 | mean throughput: 1226.918 [Mb/s] 84 | 85 | Using send_msg/recv_msg functions running under LuaJIT2 (git HEAD): 86 | mean throughput: 6160911 [msg/s] 87 | mean throughput: 1478.619 [Mb/s] 88 | 89 | C++ code: 90 | mean throughput: 6241452 [msg/s] 91 | mean throughput: 1497.948 [Mb/s] 92 | 93 | 94 | Running benchmarks 95 | ================== 96 | 97 | When running the benchmarks you will need run two different scripts (one 'local' and one 'remote'). Both scripts can be run on the same computer or on different computers. Make sure to start the 'local' script first. 98 | 99 | Throughput benchmark: 100 | 101 | # first start local script 102 | $ luajit-2 perf/local_thr.lua "tcp://lo:5555" 30 1000000 103 | 104 | # then in another window start remote script 105 | $ luajit-2 perf/remote_thr.lua "tcp://localhost:5555" 30 1000000 106 | 107 | Latency benchmark: 108 | 109 | # first start local script 110 | $ luajit-2 perf/local_lat.lua "tcp://lo:5555" 1 100000 111 | 112 | # then in another window start remote script 113 | $ luajit-2 perf/remote_lat.lua "tcp://localhost:5555" 1 100000 114 | 115 | 116 | -------------------------------------------------------------------------------- /examples/poller/ev.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2012 Robert G. Jakabosky 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 | local ev = require'ev' 22 | local ev_READ = ev.READ 23 | local ev_WRITE = ev.WRITE 24 | local loop = ev.Loop.default 25 | 26 | assert(ev.Idle,"Need version > 1.3 of lua-ev that supports Idle watchers.") 27 | 28 | local poller_meths = {} 29 | local poller_mt = {__index = poller_meths} 30 | 31 | local function poller_new() 32 | local self = { 33 | work_cur = {}, 34 | work_last = {}, 35 | io_events = 0, 36 | reads = {}, 37 | idle_enabled = false, 38 | } 39 | 40 | self.idle = ev.Idle.new(function() 41 | local tasks = #self.work_cur 42 | -- check if there is any work 43 | if tasks > 0 then 44 | -- swap work queues. 45 | local last, cur = self.work_cur, self.work_last 46 | self.work_cur, self.work_last = cur, last 47 | for i=1,tasks do 48 | local task = last[i] 49 | last[i] = nil 50 | task() 51 | end 52 | -- check if there is more work. 53 | if #cur > 0 then 54 | return -- don't disable idle watcher, when we have work. 55 | end 56 | end 57 | --print("STOP IDLE:", #self.work_cur, #self.work_last) 58 | -- stop idle watcher, no work. 59 | self.idle_enabled = false 60 | self.idle:stop(loop) 61 | end) 62 | -- set priority to max, to make sure the work queue is processed on each loop. 63 | self.idle:priority(ev.MAXPRI) 64 | 65 | return setmetatable(self, poller_mt) 66 | end 67 | 68 | function poller_meths:add_work(task) 69 | local idx = #self.work_cur + 1 70 | -- add task to current work queue. 71 | self.work_cur[idx] = task 72 | -- make sure the idle watcher is enabled. 73 | if not self.idle_enabled then 74 | self.idle_enabled = true 75 | self.idle:start(loop) 76 | end 77 | end 78 | 79 | function poller_meths:add_read(fd, cb) 80 | local io_read = self.reads[fd] 81 | -- make sure read event hasn't been registered yet. 82 | if not io_read then 83 | self.io_events = self.io_events + 1 84 | io_read = ev.IO.new(function() 85 | cb(fd) 86 | end, fd, ev_READ) 87 | self.reads[fd] = io_read 88 | io_read:start(loop) 89 | else 90 | -- update read callback? 91 | io_read:callback(cb) 92 | -- need to re-start watcher? 93 | if not io_read:is_active() then 94 | io_read:start(loop) 95 | end 96 | end 97 | end 98 | 99 | function poller_meths:remove_read(fd) 100 | local io_read = self.reads[fd] 101 | -- make sure there was a read event registered. 102 | if io_read then 103 | self.io_events = self.io_events - 1 104 | io_read:stop(loop) 105 | end 106 | end 107 | 108 | function poller_meths:start() 109 | return loop:loop() 110 | end 111 | 112 | function poller_meths:stop() 113 | return loop:unloop() 114 | end 115 | 116 | -- module only exports a 'new' function. 117 | return { 118 | new = poller_new, 119 | } 120 | -------------------------------------------------------------------------------- /examples/poller/epoll.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2012 Robert G. Jakabosky 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 | local epoll = require"epoll" 22 | local EPOLLIN = epoll.EPOLLIN 23 | local EPOLLOUT = epoll.EPOLLOUT 24 | 25 | local poller_meths = {} 26 | local poller_mt = {__index = poller_meths} 27 | 28 | local function poller_new() 29 | local reads = {} 30 | -- create closure for epoll io_event callback. 31 | local function do_io_event(fd, ev) 32 | local cb = reads[fd] 33 | return cb(fd, ev) 34 | end 35 | 36 | return setmetatable({ 37 | work_cur = {}, 38 | work_last = {}, 39 | reads = reads, 40 | io_events = 0, 41 | do_io_event = do_io_event, 42 | poller = epoll.new(), 43 | }, poller_mt) 44 | end 45 | 46 | function poller_meths:add_work(task) 47 | -- add task to current work queue. 48 | self.work_cur[#self.work_cur + 1] = task 49 | end 50 | 51 | function poller_meths:add_read(fd, cb) 52 | -- make sure read event hasn't been registered yet. 53 | if not self.reads[fd] then 54 | self.io_events = self.io_events + 1 55 | self.reads[fd] = cb 56 | return self.poller:add(fd, EPOLLIN, fd) 57 | else 58 | -- update read callback? 59 | self.reads[fd] = cb 60 | end 61 | end 62 | 63 | function poller_meths:remove_read(fd) 64 | -- make sure there was a read event registered. 65 | if self.reads[fd] then 66 | self.io_events = self.io_events - 1 67 | self.reads[fd] = nil 68 | return self.poller:del(fd) 69 | end 70 | end 71 | 72 | local function poller_do_work(self) 73 | local tasks = #self.work_cur 74 | -- check if there is any work 75 | if tasks > 0 then 76 | -- swap work queues. 77 | local last, cur = self.work_cur, self.work_last 78 | self.work_cur, self.work_last = cur, last 79 | for i=1,tasks do 80 | local task = last[i] 81 | last[i] = nil 82 | task() 83 | end 84 | -- return new work queue length. 85 | return #cur 86 | end 87 | return tasks 88 | end 89 | 90 | function poller_meths:start() 91 | local do_io_event = self.do_io_event 92 | local poller = self.poller 93 | self.is_running = true 94 | while self.is_running do 95 | -- run work task 96 | local new_work = poller_do_work(self) 97 | -- wait == 0, if there is work to do, else wait == -1 98 | local wait = (new_work > 0) and 0 or -1 99 | -- poll for fd events, if there are events to poll for. 100 | --print("poller:step()", new_work, self.io_events) 101 | if self.io_events > 0 then 102 | assert(poller:wait_callback(do_io_event, wait)) 103 | else 104 | -- no io events to poll, do we still have work? 105 | if #self.work_cur == 0 then 106 | -- nothing to do, exit event loop 107 | self.is_running = false 108 | return 109 | end 110 | end 111 | end 112 | end 113 | 114 | function poller_meths:stop() 115 | self.is_running = false 116 | end 117 | 118 | -- module only exports a 'new' function. 119 | return { 120 | new = poller_new, 121 | } 122 | -------------------------------------------------------------------------------- /examples/subscriber_ev.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2012 Robert G. Jakabosky 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 | local zmq = require"zmq" 22 | local z_NOBLOCK = zmq.NOBLOCK 23 | local z_EVENTS = zmq.EVENTS 24 | local z_POLLIN = zmq.POLLIN 25 | local z_POLLOUT = zmq.POLLOUT 26 | local z_POLLIN_OUT = z_POLLIN + z_POLLOUT 27 | local ev = require'ev' 28 | local loop = ev.Loop.default 29 | 30 | -- define a sub_worker class 31 | local sub_worker_mt = {} 32 | function sub_worker_mt:close(...) 33 | self.s_io_idle:stop(self.loop) 34 | self.s_io_read:stop(self.loop) 35 | return self.socket:close(...) 36 | end 37 | function sub_worker_mt:bind(...) 38 | return self.socket:bind(...) 39 | end 40 | function sub_worker_mt:connect(...) 41 | return self.socket:connect(...) 42 | end 43 | function sub_worker_mt:sub(topic) 44 | return self.socket:setopt(zmq.SUBSCRIBE, topic) 45 | end 46 | function sub_worker_mt:unsub(topic) 47 | return self.socket:setopt(zmq.UNSUBSCRIBE, topic) 48 | end 49 | sub_worker_mt.__index = sub_worker_mt 50 | 51 | local function sub_worker(loop, ctx, msg_cb) 52 | local s = ctx:socket(zmq.SUB) 53 | local self = { loop = loop, socket = s, msg_cb = msg_cb } 54 | setmetatable(self, sub_worker_mt) 55 | -- create ev callbacks for recving data. 56 | -- need idle watcher since ZeroMQ sockets are edge-triggered instead of level-triggered 57 | local s_io_idle 58 | local s_io_read 59 | local max_recvs = 10 60 | local function s_recv(recv_cnt) 61 | local msg, err = s:recv(z_NOBLOCK) 62 | if err == 'timeout' then 63 | -- need to block on read IO 64 | return false 65 | end 66 | self:msg_cb(msg) 67 | if recv_cnt > 1 then 68 | return s_recv(recv_cnt - 1) 69 | end 70 | return true 71 | end 72 | s_io_idle = ev.Idle.new(function() 73 | if not s_recv(max_recvs) then 74 | -- need to block on read IO 75 | s_io_idle:stop(loop) 76 | s_io_read:start(loop) 77 | end 78 | end) 79 | s_io_idle:start(loop) 80 | s_io_read = ev.IO.new(function() 81 | local events = s:getopt(z_EVENTS) 82 | if events == z_POLLIN or events == z_POLLIN_OUT then 83 | if s_recv(max_recvs) then 84 | -- read IO is not block, enable idle watcher to handle reads. 85 | s_io_idle:start(loop) 86 | s_io_read:stop(loop) 87 | end 88 | end 89 | end, s:getopt(zmq.FD), ev.READ) 90 | self.s_io_idle = s_io_idle 91 | self.s_io_read = s_io_read 92 | return self 93 | end 94 | 95 | local ctx = zmq.init() 96 | 97 | -- message handling function. 98 | local function handle_msg(worker, msg) 99 | local msg_id = tonumber(msg) 100 | if math.mod(msg_id, 10000) == 0 then print(worker.id, msg_id) end 101 | end 102 | 103 | local sub1 = sub_worker(loop, ctx, handle_msg) 104 | sub1.id = 'sub1' 105 | sub1:sub('') 106 | sub1:connect("tcp://localhost:5555") 107 | local sub2 = sub_worker(loop, ctx, handle_msg) 108 | sub2.id = 'sub2' 109 | sub2:sub('') 110 | sub2:connect("tcp://localhost:5555") 111 | 112 | loop:loop() 113 | 114 | -------------------------------------------------------------------------------- /examples/poll_zsock.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2012 Robert G. Jakabosky 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 | local zmq = require"zmq" 22 | 23 | local z_EVENTS = zmq.EVENTS 24 | 25 | local z_POLLIN = zmq.POLLIN 26 | local z_POLLOUT = zmq.POLLOUT 27 | local z_POLLIN_OUT = z_POLLIN + z_POLLOUT 28 | 29 | local poll 30 | 31 | local meths = {} 32 | local zsock_mt = { __index=meths } 33 | 34 | local function zsock_check_events(self) 35 | if not self.check_enabled then 36 | -- enable 'on_work' callback to handle checking for socket events. 37 | self.check_enabled = true 38 | poll:add_work(self.on_work) 39 | end 40 | end 41 | 42 | function meths:events() 43 | zsock_check_events(self) 44 | return self.sock:events() 45 | end 46 | 47 | function meths:getopt(opt) 48 | if (opt == z_EVENTS) then 49 | zsock_check_events(self) 50 | end 51 | return self.sock:getopt(opt) 52 | end 53 | 54 | function meths:setopt(opt,val) 55 | return self.sock:setopt(opt,val) 56 | end 57 | 58 | function meths:sub(topic) 59 | return self.sock:sub(topic) 60 | end 61 | 62 | function meths:unsub(topic) 63 | return self.sock:unsub(topic) 64 | end 65 | 66 | function meths:identity(id) 67 | return self.sock:identity(id) 68 | end 69 | 70 | function meths:bind(addr) 71 | return self.sock:bind(addr) 72 | end 73 | 74 | function meths:connect(addr) 75 | return self.sock:connect(addr) 76 | end 77 | 78 | function meths:close() 79 | return self.sock:close() 80 | end 81 | 82 | function meths:send(msg, flags) 83 | zsock_check_events(self) 84 | local sent, err = self.sock:send(msg, flags) 85 | if not sent and err == 'timeout' then 86 | self.send_blocked = true 87 | end 88 | return sent, err 89 | end 90 | 91 | function meths:send_msg(msg, flags) 92 | zsock_check_events(self) 93 | local sent, err = self.sock:send_msg(msg, flags) 94 | if not sent and err == 'timeout' then 95 | self.send_blocked = true 96 | end 97 | return sent, err 98 | end 99 | 100 | function meths:recv(flags) 101 | zsock_check_events(self) 102 | local msg, err = self.sock:recv(flags) 103 | if not msg and err == 'timeout' then 104 | self.recv_blocked = true 105 | end 106 | return msg, err 107 | end 108 | 109 | function meths:recv_msg(msg, flags) 110 | zsock_check_events(self) 111 | local stat, err = self.sock:recv_msg(msg, flags) 112 | if not stat and err == 'timeout' then 113 | self.recv_blocked = true 114 | end 115 | return stat, err 116 | end 117 | 118 | local function nil_cb() 119 | end 120 | 121 | local function wrap_zsock(sock, on_data, on_drain) 122 | local self = setmetatable({ 123 | sock = sock, 124 | on_data = on_data or nil_cb, 125 | on_drain = on_drain or nil_cb, 126 | recv_blocked = false, 127 | send_blocked = false, 128 | check_enabled = false, 129 | }, zsock_mt) 130 | 131 | local function on_work() 132 | self.check_enabled = false 133 | local events = sock:events() 134 | local read = false 135 | local write = false 136 | if events == z_POLLIN_OUT then 137 | read = true 138 | write = true 139 | elseif events == z_POLLIN then 140 | read = true 141 | elseif events == z_POLLOUT then 142 | write = true 143 | else 144 | return 145 | end 146 | if read then 147 | self.recv_blocked = false 148 | self:on_data(sock) 149 | -- there might be more messages to read. 150 | if not self.recv_blocked then 151 | zsock_check_events(self) 152 | end 153 | end 154 | if write and self.send_blocked then 155 | self:on_drain(sock) 156 | end 157 | end 158 | self.on_work = on_work 159 | 160 | -- listen for read events to enable socket. 161 | poll:add_read(sock:fd(), function() 162 | on_work() 163 | end) 164 | 165 | zsock_check_events(self) 166 | return self 167 | end 168 | 169 | return setmetatable({ 170 | set_poller = function(poller) 171 | local old = poll 172 | poll = poller 173 | return old 174 | end, 175 | wrap_zsock = wrap_zsock, 176 | }, { __call = function(tab, ...) return wrap_zsock(...) end}) 177 | 178 | -------------------------------------------------------------------------------- /src/msg.nobj.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2011 by Robert G. Jakabosky 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 | object "zmq_msg_t" { 22 | -- store the `zmq_msg_t` structure in Lua userdata object 23 | userdata_type = "embed", 24 | implements "Buffer" { 25 | implement_method "const_data" { 26 | c_function = "zmq_msg_data" 27 | }, 28 | implement_method "get_size" { 29 | c_function = "zmq_msg_size" 30 | }, 31 | }, 32 | implements "MutableBuffer" { 33 | implement_method "data" { 34 | c_function = "zmq_msg_data" 35 | }, 36 | implement_method "get_size" { 37 | c_function = "zmq_msg_size" 38 | }, 39 | }, 40 | -- 41 | -- Define zmq_msq_t type & function API for FFI 42 | -- 43 | ffi_cdef[[ 44 | 45 | struct zmq_msg_t 46 | { 47 | unsigned char _ [64]; 48 | }; 49 | 50 | int zmq_msg_init (zmq_msg_t *msg); 51 | int zmq_msg_init_size (zmq_msg_t *msg, size_t size); 52 | 53 | ]], 54 | constructor "init" { 55 | c_method_call "ZMQ_Error" "zmq_msg_init" {}, 56 | }, 57 | constructor "init_size" { 58 | c_method_call "ZMQ_Error" "zmq_msg_init_size" { "size_t", "size" }, 59 | }, 60 | constructor "init_data" { 61 | var_in{ "const char *", "data" }, 62 | c_method_call { "ZMQ_Error", "err" } "zmq_msg_init_size" { "size_t", "#data" }, 63 | c_source[[ 64 | if(0 == ${err}) { 65 | /* fill message */ 66 | memcpy(zmq_msg_data(${this}), ${data}, ${data_len}); 67 | } 68 | ]], 69 | ffi_source[[ 70 | if(0 == ${err}) then 71 | -- fill message 72 | ffi.copy(C.zmq_msg_data(${this}), ${data}, ${data_len}) 73 | end 74 | ]], 75 | }, 76 | destructor { 77 | c_method_call "ZMQ_Error" "zmq_msg_close" {} 78 | }, 79 | method "close" { 80 | c_method_call "ZMQ_Error" "zmq_msg_close" {} 81 | }, 82 | method "move" { 83 | c_method_call "ZMQ_Error" "zmq_msg_move" { "zmq_msg_t *", "src" } 84 | }, 85 | method "copy" { 86 | c_method_call "ZMQ_Error" "zmq_msg_copy" { "zmq_msg_t *", "src" } 87 | }, 88 | method "set_data" { 89 | var_in{ "const char *", "data" }, 90 | var_out{ "ZMQ_Error", "err" }, 91 | c_source[[ 92 | /* check message data size. */ 93 | if(zmq_msg_size(${this}) != ${data_len}) { 94 | /* need to resize message. */ 95 | zmq_msg_close(${this}); /* close old message, to free old data. */ 96 | ${err} = zmq_msg_init_size(${this}, ${data_len}); /* re-initialize message. */ 97 | if(0 != ${err}) { 98 | luaL_error(L, "set_data() failed: %s", get_zmq_strerror()); 99 | } 100 | } 101 | /* copy data into message */ 102 | memcpy(zmq_msg_data(${this}), ${data}, ${data_len}); 103 | ]], 104 | ffi_source[[ 105 | -- check message data size. 106 | if (C.zmq_msg_size(${this}) ~= ${data_len}) then 107 | -- need to resize message. 108 | C.zmq_msg_close(${this}); -- close old message, to free old data. 109 | ${err} = C.zmq_msg_init_size(${this}, ${data_len}); -- re-initialize message. 110 | if (0 ~= ${err}) then 111 | error("set_data() failed: " .. get_zmq_strerror()); 112 | end 113 | end 114 | -- copy data into message 115 | ffi.copy(C.zmq_msg_data(${this}), ${data}, ${data_len}); 116 | ]], 117 | }, 118 | method "data" { 119 | c_method_call "void *" "zmq_msg_data" {} 120 | }, 121 | method "set_size" { 122 | var_in{ "size_t", "size" }, 123 | var_out{ "ZMQ_Error", "err" }, 124 | c_source[[ 125 | /* check message data size. */ 126 | if(zmq_msg_size(${this}) != ${size}) { 127 | /* need to resize message. */ 128 | zmq_msg_close(${this}); /* close old message, to free old data. */ 129 | ${err} = zmq_msg_init_size(${this}, ${size}); /* re-initialize message. */ 130 | if(0 != ${err}) { 131 | luaL_error(L, "set_size() failed: %s", get_zmq_strerror()); 132 | } 133 | } 134 | ]], 135 | ffi_source[[ 136 | -- check message data size. 137 | if (C.zmq_msg_size(${this}) ~= ${size}) then 138 | -- need to resize message. 139 | C.zmq_msg_close(${this}); -- close old message, to free old data. 140 | ${err} = C.zmq_msg_init_size(${this}, ${size}); -- re-initialize message. 141 | if (0 ~= ${err}) then 142 | error("set_size() failed: " .. get_zmq_strerror()); 143 | end 144 | end 145 | ]], 146 | }, 147 | method "size" { 148 | c_method_call { "size_t", "size", ffi_wrap = "tonumber"} "zmq_msg_size" {} 149 | }, 150 | method "__tostring" { 151 | var_out{ "const char *", "data", has_length = true }, 152 | c_source[[ 153 | ${data} = zmq_msg_data(${this}); 154 | ${data_len} = zmq_msg_size(${this}); 155 | ]], 156 | ffi_source[[ 157 | ${data} = C.zmq_msg_data(${this}); 158 | ${data_len} = C.zmq_msg_size(${this}); 159 | ]], 160 | }, 161 | } 162 | 163 | -------------------------------------------------------------------------------- /API.md: -------------------------------------------------------------------------------- 1 | ## constants 2 | 3 | ZMQ_CONSTANT_NAME in the C API turns into zmq.CONSTANT_NAME in Lua. 4 | 5 | ## version() 6 | 7 | Reports 0MQ library version. 8 | See [zmq_version(3)](http://api.zeromq.org/zmq_version.html). 9 | 10 | zmq.version() 11 | 12 | ## init(io_threads) 13 | 14 | Initialises ØMQ context. 15 | See [zmq_init(3)](http://api.zeromq.org/zmq_init.html). 16 | 17 | zmq.init(io_threads) 18 | 19 | # ZMQ Context methods 20 | 21 | ## term() 22 | 23 | Terminates ØMQ context. 24 | See [zmq_term(3)](http://api.zeromq.org/zmq_term.html). 25 | 26 | ctx:term() 27 | 28 | ## socket(type) 29 | 30 | Creates ØMQ socket. 31 | See [zmq_socket(3)](http://api.zeromq.org/zmq_socket.html). 32 | 33 | ctx:socket(type) 34 | 35 | # ZMQ Socket methods 36 | 37 | ## close() 38 | 39 | Destroys ØMQ socket. 40 | See [zmq_close(3)](http://api.zeromq.org/zmq_close.html). 41 | 42 | sock:close() 43 | 44 | ## setopt(option, optval) 45 | 46 | Sets a specified option on a ØMQ socket. 47 | See [zmq_setsockopt(3)](http://api.zeromq.org/zmq_setsockopt.html). 48 | 49 | sock:setopt(option, optval) 50 | 51 | ## getopt(option) 52 | 53 | Gets a specified option of a ØMQ socket. 54 | See [zmq_getsockopt(3)](http://api.zeromq.org/zmq_getsockopt.html). 55 | 56 | sock:getopt(option) 57 | 58 | ## bind(addr) 59 | 60 | Binds the socket to the specified address. 61 | See [zmq_bind(3)](http://api.zeromq.org/zmq_bind.html). 62 | 63 | sock:bind(addr) 64 | 65 | ## connect(addr) 66 | 67 | Connect the socket to the specified address. 68 | See [zmq_connect(3)](http://api.zeromq.org/zmq_connect.html). 69 | 70 | sock:connect(addr) 71 | 72 | ## send(msg [, flags]) 73 | 74 | Sends a message(Lua string). 75 | See [zmq_send(3)](http://api.zeromq.org/zmq_send.html). 76 | 77 | sock:send(msg) 78 | sock:send(msg, flags) 79 | 80 | ## recv([flags]) 81 | 82 | Retrieves a message(a Lua string) from the socket. 83 | See [zmq_recv(3)](http://api.zeromq.org/zmq_recv.html). 84 | 85 | msg = sock:recv() 86 | msg = sock:recv(flags) 87 | 88 | # Zero-copy send_msg/recv_msg methods 89 | 90 | These methods allow Zero-copy transfer of a message from one ZMQ socket to another. 91 | 92 | ## send_msg(msg [, flags]) 93 | 94 | Sends a message from a zmq_msg_t object. 95 | See [zmq_send(3)](http://api.zeromq.org/zmq_send.html). 96 | 97 | sock:send_msg(msg) 98 | sock:send_msg(msg, flags) 99 | 100 | ## recv_msg(msg [, flags]) 101 | 102 | Retrieves a message from the socket into a zmq_msg_t object. 103 | See [zmq_recv(3)](http://api.zeromq.org/zmq_recv.html). 104 | 105 | sock:recv_msg(msg) 106 | sock:recv_msg(msg, flags) 107 | 108 | # zmq_msg_t object constructors 109 | 110 | ## zmq_msg_t.init() 111 | 112 | Create an empty zmq_msg_t object. 113 | See [zmq_msg_init(3)](http://api.zeromq.org/zmq_msg_init.html). 114 | 115 | local msg = zmq_msg_t.init() 116 | 117 | ## zmq_msg_t.init_size(size) 118 | 119 | Create an empty zmq_msg_t object and allow space for a message of `size` bytes. 120 | See [zmq_msg_init_size(3)](http://api.zeromq.org/zmq_msg_init_size.html). 121 | 122 | local msg = zmq_msg_t.init_size(size) 123 | msg:set_data(data) -- if (#data ~= size) then the message will be re-sized. 124 | 125 | ## zmq_msg_t.init_data(data) 126 | 127 | Create an zmq_msg_t object initialized with the content of `data`. 128 | 129 | local msg = zmq_msg_t.init_data(data) 130 | -- that is the same as: 131 | local msg = zmq_msg_t.init_size(#data) 132 | msg:set_data(data) 133 | 134 | # zmq_msg_t object methods 135 | 136 | ## move(src) 137 | 138 | Move the contents of one message into another. 139 | See [zmq_msg_move(3)](http://api.zeromq.org/zmq_msg_move.html). 140 | 141 | msg1:move(msg2) -- move contents from msg2 -> msg1 142 | 143 | ## copy(src) 144 | 145 | Copy the contents of one message into another. 146 | See [zmq_msg_copy(3)](http://api.zeromq.org/zmq_msg_copy.html). 147 | 148 | msg1:copy(msg2) -- copy contents from msg2 -> msg1 149 | 150 | ## set_size(size) 151 | 152 | Re-initialize the message with a new size. The current contents will be lost. 153 | See [zmq_msg_init_size(3)](http://api.zeromq.org/zmq_msg_init_size.html). 154 | 155 | msg:set_size(size) -- re-initialize message if size is different from current size. 156 | local buf = msg:data() -- get buffer to fill message with new contents. 157 | 158 | ## set_data(data) 159 | 160 | Change the message contents. 161 | See [zmq_msg_data(3)](http://api.zeromq.org/zmq_msg_data.html). 162 | 163 | msg:set_data(data) -- replace/set the message contents to `data` 164 | 165 | ## data() 166 | 167 | Get a lightuserdata pointer to the message contents. 168 | See [zmq_msg_data(3)](http://api.zeromq.org/zmq_msg_data.html). 169 | 170 | local data = msg:data() -- get the message contents 171 | 172 | ## size() 173 | 174 | Get the size of the message contents. 175 | See [zmq_msg_size(3)](http://api.zeromq.org/zmq_msg_size.html). 176 | 177 | local size = msg:size() 178 | 179 | ## close() 180 | 181 | Free the message contents and invalid the zmq_msg_t userdata object. 182 | See [zmq_msg_close(3)](http://api.zeromq.org/zmq_msg_close.html). 183 | 184 | msg:close() -- free message contents and invalid `msg` 185 | 186 | # FD/ZMQ Socket poller object 187 | 188 | The poller object wraps [zmq_poll()](http://api.zeromq.org/zmq_poll.html) to allow polling 189 | of events from multiple ZMQ Sockets and/or normal sockets. 190 | 191 | ## zmq.poller([pre_alloc]) 192 | 193 | Construct a new poller object. The optional `pre_alloc` parameter is to pre-size the poller 194 | for the number of sockets it will handle (the size can grow dynamically as-needed). 195 | 196 | local poller = zmq.poller(64) 197 | 198 | ## add(socket|fd, events, callback) 199 | 200 | Add a ZMQ Socket or fd to the poller. `callback` will be called when one of the events 201 | in `events` is raised. 202 | 203 | poller:add(sock, zmq.POLLIN, function(sock) print(sock, " is readable.") end) 204 | poller:add(sock, zmq.POLLOUT, function(sock) print(sock, " is writable.") end) 205 | poller:add(sock, zmq.POLLIN+zmq.POLLOUT, function(sock, revents) 206 | print(sock, " has events:", revents) 207 | end) 208 | 209 | ## modify(socket|fd, events, callback) 210 | 211 | Change the `events` or `callback` for a socket/fd. 212 | 213 | -- first wait for read event. 214 | poller:add(sock, zmq.POLLIN, function(sock) print(sock, " is readable.") end) 215 | -- now wait for write event. 216 | poller:modify(sock, zmq.POLLOUT, function(sock) print(sock, " is writable.") end) 217 | 218 | ## remove(socket|fd) 219 | 220 | Remove a socket/fd from the poller. 221 | 222 | -- first wait for read event. 223 | poller:add(sock, zmq.POLLIN, function(sock) print(sock, " is readable.") end) 224 | -- remove socket from poller. 225 | poller:remove(sock) 226 | 227 | ## poll(timeout) 228 | 229 | Wait `timeout` milliseconds [1] for events on the registered sockets (timeout = -1, means 230 | wait indefinitely). If any events happen, then those events are dispatched. 231 | 232 | poller:poll(1000) -- wait 1 second for events. 233 | 234 | [1] For zmq 2.x `timeout` is in microseconds. For versions 3.x or higher `timeout` will be in milliseconds. 235 | 236 | poller:poll(1000 * zmq.POLL_MSEC) -- backwards/forwards compatible 237 | 238 | ## start() 239 | 240 | Start an event loop waiting for and dispatching events. 241 | 242 | poller:start() 243 | 244 | ## stop() 245 | 246 | Stop the event loop. 247 | 248 | poller:stop() 249 | 250 | -------------------------------------------------------------------------------- /ws/dissector.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2011, Robert G. Jakabosky All rights reserved. 2 | 3 | -- cache globals to local for speed. 4 | local format=string.format 5 | local tostring=tostring 6 | local tonumber=tonumber 7 | local sqrt=math.sqrt 8 | local pairs=pairs 9 | 10 | -- wireshark API globals 11 | local Pref = Pref 12 | local Proto = Proto 13 | local ProtoField = ProtoField 14 | local DissectorTable = DissectorTable 15 | local ByteArray = ByteArray 16 | local PI_MALFORMED = PI_MALFORMED 17 | local PI_ERROR = PI_ERROR 18 | 19 | -- zmq protocol example 20 | -- declare our protocol 21 | local zmq_proto = Proto("zmq","ZMQ","ZeroMQ Protocol") 22 | 23 | -- setup preferences 24 | zmq_proto.prefs["tcp_port_start"] = 25 | Pref.string("TCP port range start", "5555", "First TCP port to decode as this protocol") 26 | zmq_proto.prefs["tcp_port_end"] = 27 | Pref.string("TCP port range end", "5555", "Last TCP port to decode as this protocol") 28 | -- current preferences settings. 29 | local current_settings = { 30 | tcp_port_start = -1, 31 | tcp_port_end = -1, 32 | } 33 | 34 | -- setup protocol fields. 35 | zmq_proto.fields = {} 36 | local fds = zmq_proto.fields 37 | fds.frame = ProtoField.new("Frame", "zmq.frame", "ftypes.BYTES", nil, "base.NONE") 38 | fds.length = ProtoField.new("Frame Length", "zmq.frame.len", "ftypes.UINT64", nil, "base.DEC") 39 | fds.length8 = ProtoField.new("Frame 8bit Length", "zmq.frame.len8", "ftypes.UINT8", nil, "base.DEC") 40 | fds.length64 = ProtoField.new("Frame 64bit Length", "zmq.frame.len64", "ftypes.UINT64", nil, "base.DEC") 41 | fds.flags = ProtoField.new("Frame Flags", "zmq.frame.flags", "ftypes.UINT8", nil, "base.HEX", "0xFF") 42 | fds.flags_more = ProtoField.new("More", "zmq.frame.flags.more", "ftypes.UINT8", nil, "base.HEX", "0x01") 43 | fds.body = ProtoField.new("Frame body", "zmq.frame.body", "ftypes.BYTES", nil, "base.NONE") 44 | 45 | -- un-register zmq to handle tcp port range 46 | local function unregister_tcp_port_range(start_port, end_port) 47 | if not start_port or start_port <= 0 or not end_port or end_port <= 0 then 48 | return 49 | end 50 | local tcp_port_table = DissectorTable.get("tcp.port") 51 | for port = start_port,end_port do 52 | tcp_port_table:remove(port,zmq_proto) 53 | end 54 | end 55 | 56 | -- register zmq to handle tcp port range 57 | local function register_tcp_port_range(start_port, end_port) 58 | if not start_port or start_port <= 0 or not end_port or end_port <= 0 then 59 | return 60 | end 61 | local tcp_port_table = DissectorTable.get("tcp.port") 62 | for port = start_port,end_port do 63 | tcp_port_table:add(port,zmq_proto) 64 | end 65 | end 66 | 67 | -- handle preferences changes. 68 | function zmq_proto.init(arg1, arg2) 69 | local old_start, old_end 70 | local new_start, new_end 71 | -- check if preferences have changed. 72 | for pref_name,old_v in pairs(current_settings) do 73 | local new_v = zmq_proto.prefs[pref_name] 74 | if new_v ~= old_v then 75 | if pref_name == "tcp_port_start" then 76 | old_start = old_v 77 | new_start = new_v 78 | elseif pref_name == "tcp_port_end" then 79 | old_end = old_v 80 | new_end = new_v 81 | end 82 | -- save new value. 83 | current_settings[pref_name] = new_v 84 | end 85 | end 86 | -- un-register old port range 87 | if old_start and old_end then 88 | unregister_tcp_port_range(tonumber(old_start), tonumber(old_end)) 89 | end 90 | -- register new port range. 91 | if new_start and new_end then 92 | register_tcp_port_range(tonumber(new_start), tonumber(new_end)) 93 | end 94 | end 95 | 96 | -- parse flag bits. 97 | local BITS = { 98 | MORE = 0x01, 99 | RESERVED = 0x7E, 100 | } 101 | local flag_names = {"MORE"} 102 | local bits_lookup = {} 103 | local bits_list = { 104 | {}, 105 | {MORE = true}, 106 | {MORE = true, RESERVED = true}, 107 | {RESERVED = true}, 108 | } 109 | local function parse_flags(flags) 110 | return bits_lookup[flags] or bits_lookup[1] 111 | end 112 | 113 | -- make bits object 114 | local function make_bits(bits) 115 | local proxy = newproxy(true) 116 | local meta = getmetatable(proxy) 117 | meta.__index = bits 118 | meta.__tostring = function() 119 | return bits.flags 120 | end 121 | -- combind bits into string description. 122 | local flags = nil 123 | for i=1,#flag_names do 124 | local name = flag_names[i] 125 | if bits[name] then 126 | if flags then 127 | flags = flags .. ',' .. name 128 | else 129 | flags = name 130 | end 131 | end 132 | end 133 | -- combind bits into one byte value. 134 | local byte = 0x00 135 | for k,v in pairs(bits) do 136 | local bit = assert(BITS[k], "Invalid bit name.") 137 | byte = byte + BITS[k] 138 | end 139 | bits.flags = flags or '' 140 | bits.byte = byte 141 | return proxy 142 | end 143 | -- make bits objects in bis_lookup 144 | for i=1,#bits_list do 145 | local bits = bits_list[i] 146 | bits = make_bits(bits) 147 | bits_lookup[bits.byte] = bits 148 | end 149 | 150 | local function zmq_dissect_frame(buffer, pinfo, frame_tree, tap) 151 | local rang,offset 152 | -- Frame length 153 | offset = 0 154 | local len_off = offset 155 | local len8_rang = buffer(offset,1) 156 | local len_rang = len8_rang 157 | local frame_len = len8_rang:uint() 158 | -- 8bit length field 159 | local ti = frame_tree:add(fds.length8, len8_rang) 160 | ti:set_hidden() 161 | offset = offset + 1 162 | if frame_len == 255 then 163 | local len64_rang = buffer(offset, 8) 164 | len_rang = buffer(len_off, 9) 165 | frame_len = tonumber(tostring(len64_rang:uint64())) 166 | -- 64bit length field. 167 | local ti = frame_tree:add(fds.length64, len64_rang) 168 | ti:set_hidden() 169 | offset = offset + 8 170 | local ti = frame_tree:add(fds.length, len_rang) 171 | ti:set_text(format("Frame Length: %d", frame_length)) 172 | else 173 | frame_tree:add(fds.length, len_rang) 174 | end 175 | -- Frame flags 176 | rang = buffer(offset,1) 177 | local flags = rang:uint() 178 | local flags_bits = parse_flags(flags) 179 | local flags_list = flags_bits.flags 180 | local flags_tree = frame_tree:add(fds.flags, rang) 181 | flags_tree:set_text(format('Flags: 0x%02X (%s)', flags, flags_list)) 182 | flags_tree:add(fds.flags_more, rang) 183 | offset = offset + 1 184 | if flags_bits.MORE then 185 | tap.more = tap.more + 1 186 | else 187 | -- if the 'more' flag is not set then this is the last frame in a message. 188 | tap.msgs = tap.msgs + 1 189 | end 190 | -- Frame body 191 | local body_len = frame_len - 1 192 | local body = '' 193 | if body_len > 0 then 194 | tap.body_bytes = tap.body_bytes + body_len 195 | rang = buffer(offset, body_len) 196 | local ti = frame_tree:add_le(fds.body, rang) 197 | if body_len <= 4 then 198 | body = format("%08x", rang:uint()) 199 | else 200 | body = tostring(rang) 201 | end 202 | ti:set_text(format("%s", body)) 203 | end 204 | offset = offset + body_len 205 | -- frame summary 206 | if body_len > 0 then 207 | if flags_bits.MORE then 208 | frame_tree:set_text(format("Frame: [MORE] Body[%u]=%s", body_len, body)) 209 | else 210 | frame_tree:set_text(format("Frame: Body[%u]=%s", body_len, body)) 211 | end 212 | else 213 | if flags_bits.MORE then 214 | frame_tree:set_text(format("Frame: [MORE] No data")) 215 | else 216 | frame_tree:set_text(format("Frame: No data")) 217 | end 218 | end 219 | end 220 | 221 | local DESEGMENT_ONE_MORE_SEGMENT = 0x0fffffff 222 | local DESEGMENT_UNTIL_FIN = 0x0ffffffe 223 | 224 | -- packet dissector 225 | function zmq_proto.dissector(tvb,pinfo,tree) 226 | local offset = 0 227 | local tvb_length = tvb:len() 228 | local reported_length = tvb:reported_len() 229 | local length_remaining 230 | local zmq_tree 231 | local rang 232 | local frames = 0 233 | local tap = {} 234 | 235 | tap.frames = 0 236 | tap.msgs = 0 237 | tap.more = 0 238 | tap.body_bytes = 0 239 | 240 | while(offset < reported_length and offset < tvb_length) do 241 | length_remaining = tvb_length - offset 242 | -- check for fixed part of PDU 243 | if length_remaining < 2 then 244 | pinfo.desegment_offset = offset 245 | pinfo.desegment_len = DESEGMENT_ONE_MORE_SEGMENT 246 | break 247 | end 248 | -- decode frame length 249 | -- decode single byte frame length 250 | rang = tvb(offset, 1) 251 | local frame_len = rang:le_uint() 252 | local pdu_len = frame_len + 1 253 | if frame_len == 255 then 254 | -- make sure there is enough bytes 255 | if length_remaining < 10 then 256 | pinfo.desegment_offset = offset 257 | pinfo.desegment_len = DESEGMENT_ONE_MORE_SEGMENT 258 | break 259 | end 260 | -- decode extra long frame length. 261 | rang = tvb(offset + 1, 8) 262 | frame_len = tonumber(tostring(rang:uint64())) 263 | pdu_len = frame_len + 9 264 | end 265 | -- provide hints to tcp 266 | if not pinfo.visited then 267 | local remaining_bytes = reported_length - offset 268 | if pdu_len > remaining_bytes then 269 | pinfo.want_pdu_tracking = 2 270 | pinfo.bytes_until_next_pdu = pdu_len - remaining_bytes 271 | end 272 | end 273 | -- check if we need more bytes to dissect this frame. 274 | if length_remaining < pdu_len then 275 | pinfo.desegment_offset = offset 276 | pinfo.desegment_len = (pdu_len - length_remaining) 277 | break 278 | end 279 | -- dissect zmq frame 280 | if not zmq_tree then 281 | zmq_tree = tree:add(zmq_proto,tvb(),"ZMQ frames") 282 | end 283 | rang = tvb(offset, pdu_len) 284 | local frame_tree = zmq_tree:add(fds.frame, rang) 285 | zmq_dissect_frame(rang:tvb(), pinfo, frame_tree, tap) 286 | frames = frames + 1 287 | -- step to next frame. 288 | local offset_before = offset 289 | offset = offset + pdu_len 290 | if offset < offset_before then break end 291 | end 292 | if zmq_tree then 293 | zmq_tree:set_text(format("ZMQ frames=%u", frames)) 294 | end 295 | if frames > 0 then 296 | tap.frames = frames 297 | pinfo.tap_data = tap 298 | end 299 | -- Info column 300 | pinfo.cols.protocol = "ZMQ" 301 | pinfo.cols.info = format('ZMQ frames=%u',frames) 302 | end 303 | 304 | -- register zmq to handle tcp ports 5550-5560 305 | register_tcp_port_range(5550,5560) 306 | 307 | -------------------------------------------------------------------------------- /src/error.nobj.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2011 by Robert G. Jakabosky 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 | -- E* error values. 22 | meta_object "ZErrors" { 23 | export_definitions { 24 | -- Native 0MQ error codes. 25 | "EFSM", 26 | "ENOCOMPATPROTO", 27 | "ETERM", 28 | "EMTHREAD", 29 | 30 | "EPERM", -- Operation not permitted 31 | "ENOENT", -- No such file or directory 32 | "ESRCH", -- No such process 33 | "EINTR", -- Interrupted system call 34 | "EIO", -- I/O error 35 | "ENXIO", -- No such device or address 36 | "E2BIG", -- Argument list too long 37 | "ENOEXEC", -- Exec format error 38 | "EBADF", -- Bad file number 39 | "ECHILD", -- No child processes 40 | "EAGAIN", -- Try again 41 | "ENOMEM", -- Out of memory 42 | "EACCES", -- Permission denied 43 | "EFAULT", -- Bad address 44 | "ENOTBLK", -- Block device required 45 | "EBUSY", -- Device or resource busy 46 | "EEXIST", -- File exists 47 | "EXDEV", -- Cross-device link 48 | "ENODEV", -- No such device 49 | "ENOTDIR", -- Not a directory 50 | "EISDIR", -- Is a directory 51 | "EINVAL", -- Invalid argument 52 | "ENFILE", -- File table overflow 53 | "EMFILE", -- Too many open files 54 | "ENOTTY", -- Not a typewriter 55 | "ETXTBSY", -- Text file busy 56 | "EFBIG", -- File too large 57 | "ENOSPC", -- No space left on device 58 | "ESPIPE", -- Illegal seek 59 | "EROFS", -- Read-only file system 60 | "EMLINK", -- Too many links 61 | "EPIPE", -- Broken pipe 62 | "EDOM", -- Math argument out of domain of func 63 | "ERANGE", -- Math result not representable 64 | 65 | "EDEADLK", -- Resource deadlock would occur 66 | "EDEADLOCK", -- EDEADLK 67 | "ENAMETOOLONG", -- File name too long 68 | "ENOLCK", -- No record locks available 69 | "ENOSYS", -- Function not implemented 70 | "ENOTEMPTY", -- Directory not empty 71 | "ELOOP", -- Too many symbolic links encountered 72 | "EWOULDBLOCK", -- Operation would block 73 | "ENOMSG", -- No message of desired type 74 | "EIDRM", -- Identifier removed 75 | "ECHRNG", -- Channel number out of range 76 | "EL2NSYNC", -- Level 2 not synchronized 77 | "EL3HLT", -- Level 3 halted 78 | "EL3RST", -- Level 3 reset 79 | "ELNRNG", -- Link number out of range 80 | "EUNATCH", -- Protocol driver not attached 81 | "ENOCSI", -- No CSI structure available 82 | "EL2HLT", -- Level 2 halted 83 | "EBADE", -- Invalid exchange 84 | "EBADR", -- Invalid request descriptor 85 | "EXFULL", -- Exchange full 86 | "ENOANO", -- No anode 87 | "EBADRQC", -- Invalid request code 88 | "EBADSLT", -- Invalid slot 89 | 90 | "EBFONT", -- Bad font file format 91 | "ENOSTR", -- Device not a stream 92 | "ENODATA", -- No data available 93 | "ETIME", -- Timer expired 94 | "ENOSR", -- Out of streams resources 95 | "ENONET", -- Machine is not on the network 96 | "ENOPKG", -- Package not installed 97 | "EREMOTE", -- Object is remote 98 | "ENOLINK", -- Link has been severed 99 | "EADV", -- Advertise error 100 | "ESRMNT", -- Srmount error 101 | "ECOMM", -- Communication error on send 102 | "EPROTO", -- Protocol error 103 | "EMULTIHOP", -- Multihop attempted 104 | "EDOTDOT", -- RFS specific error 105 | "EBADMSG", -- Not a data message 106 | "EOVERFLOW", -- Value too large for defined data type 107 | "ENOTUNIQ", -- Name not unique on network 108 | "EBADFD", -- File descriptor in bad state 109 | "EREMCHG", -- Remote address changed 110 | "ELIBACC", -- Can not access a needed shared library 111 | "ELIBBAD", -- Accessing a corrupted shared library 112 | "ELIBSCN", -- .lib section in a.out corrupted 113 | "ELIBMAX", -- Attempting to link in too many shared libraries 114 | "ELIBEXEC", -- Cannot exec a shared library directly 115 | "EILSEQ", -- Illegal byte sequence 116 | "ERESTART", -- Interrupted system call should be restarted 117 | "ESTRPIPE", -- Streams pipe error 118 | "EUSERS", -- Too many users 119 | "ENOTSOCK", -- Socket operation on non-socket 120 | "EDESTADDRREQ", -- Destination address required 121 | "EMSGSIZE", -- Message too long 122 | "EPROTOTYPE", -- Protocol wrong type for socket 123 | "ENOPROTOOPT", -- Protocol not available 124 | "EPROTONOSUPPORT", -- Protocol not supported 125 | "ESOCKTNOSUPPORT", -- Socket type not supported 126 | "EOPNOTSUPP", -- Operation not supported on transport endpoint 127 | "EPFNOSUPPORT", -- Protocol family not supported 128 | "EAFNOSUPPORT", -- Address family not supported by protocol 129 | "EADDRINUSE", -- Address already in use 130 | "EADDRNOTAVAIL", -- Cannot assign requested address 131 | "ENETDOWN", -- Network is down 132 | "ENETUNREACH", -- Network is unreachable 133 | "ENETRESET", -- Network dropped connection because of reset 134 | "ECONNABORTED", -- Software caused connection abort 135 | "ECONNRESET", -- Connection reset by peer 136 | "ENOBUFS", -- No buffer space available 137 | "EISCONN", -- Transport endpoint is already connected 138 | "ENOTCONN", -- Transport endpoint is not connected 139 | "ESHUTDOWN", -- Cannot send after transport endpoint shutdown 140 | "ETOOMANYREFS", -- Too many references: cannot splice 141 | "ETIMEDOUT", -- Connection timed out 142 | "ECONNREFUSED", -- Connection refused 143 | "EHOSTDOWN", -- Host is down 144 | "EHOSTUNREACH", -- No route to host 145 | "EALREADY", -- Operation already in progress 146 | "EINPROGRESS", -- Operation now in progress 147 | "ESTALE", -- Stale NFS file handle 148 | "EUCLEAN", -- Structure needs cleaning 149 | "ENOTNAM", -- Not a XENIX named type file 150 | "ENAVAIL", -- No XENIX semaphores available 151 | "EISNAM", -- Is a named type file 152 | "EREMOTEIO", -- Remote I/O error 153 | "EDQUOT", -- Quota exceeded 154 | 155 | "ENOMEDIUM", -- No medium found 156 | "EMEDIUMTYPE", -- Wrong medium type 157 | "ECANCELED", -- Operation Canceled 158 | "ENOKEY", -- Required key not available 159 | "EKEYEXPIRED", -- Key has expired 160 | "EKEYREVOKED", -- Key has been revoked 161 | "EKEYREJECTED", -- Key was rejected by service 162 | 163 | -- for robust mutexes 164 | "EOWNERDEAD", -- Owner died 165 | "ENOTRECOVERABLE", -- State not recoverable 166 | 167 | "ERFKILL", -- Operation not possible due to RF-kill 168 | }, 169 | 170 | method "description" { 171 | var_in{ "", "err" }, 172 | var_out{ "const char *", "msg" }, 173 | c_source "pre" [[ 174 | int err_type; 175 | int err_num = -1; 176 | ]], 177 | c_source[[ 178 | err_type = lua_type(L, ${err::idx}); 179 | if(err_type == LUA_TSTRING) { 180 | lua_pushvalue(L, ${err::idx}); 181 | lua_rawget(L, ${this::idx}); 182 | if(lua_isnumber(L, -1)) { 183 | err_num = lua_tointeger(L, -1); 184 | } 185 | lua_pop(L, 1); 186 | } else if(err_type == LUA_TNUMBER) { 187 | err_num = lua_tointeger(L, ${err::idx}); 188 | } else { 189 | return luaL_argerror(L, ${err::idx}, "expected string/number"); 190 | } 191 | if(err_num < 0) { 192 | lua_pushnil(L); 193 | lua_pushliteral(L, "UNKNOWN ERROR"); 194 | return 2; 195 | } 196 | ${msg} = strerror(err_num); 197 | ]], 198 | }, 199 | 200 | method "__index" { 201 | var_in{ "int", "err" }, 202 | var_out{ "const char *", "msg" }, 203 | c_source[[ 204 | switch(${err}) { 205 | case EAGAIN: 206 | ${msg} = "timeout"; 207 | break; 208 | case EINTR: 209 | ${msg} = "interrupted"; 210 | break; 211 | #if defined(ETERM) 212 | case ETERM: 213 | ${msg} = "closed"; 214 | break; 215 | #endif 216 | default: 217 | ${msg} = zmq_strerror(${err}); 218 | break; 219 | } 220 | lua_pushvalue(L, ${err::idx}); 221 | lua_pushstring(L, ${msg}); 222 | lua_rawset(L, ${this::idx}); 223 | ]], 224 | }, 225 | } 226 | 227 | ffi_cdef[[ 228 | int zmq_errno (void); 229 | ]] 230 | 231 | ffi_source "ffi_src" [[ 232 | -- get ZErrors table to map errno to error name. 233 | local ZError_names = _M.ZErrors 234 | 235 | local function get_zmq_strerror() 236 | return ZError_names[C.zmq_errno()] 237 | end 238 | ]] 239 | 240 | c_source "extra_code" [[ 241 | static char *zmq_ZErrors_key = "zmq_ZErrors_key"; 242 | /* 243 | * This wrapper function is to make the EAGAIN/ETERM error messages more like 244 | * what is returned by LuaSocket. 245 | */ 246 | static const char *get_zmq_strerror() { 247 | int err = zmq_errno(); 248 | switch(err) { 249 | case EAGAIN: 250 | return "timeout"; 251 | break; 252 | case EINTR: 253 | return "interrupted"; 254 | break; 255 | #if defined(ETERM) 256 | case ETERM: 257 | return "closed"; 258 | break; 259 | #endif 260 | default: 261 | break; 262 | } 263 | return zmq_strerror(err); 264 | } 265 | 266 | ]] 267 | 268 | c_source "module_init_src" [[ 269 | /* Cache reference to zmq.ZErrors table for errno->string convertion. */ 270 | lua_pushlightuserdata(L, zmq_ZErrors_key); 271 | lua_getfield(L, -2, "ZErrors"); 272 | lua_rawset(L, LUA_REGISTRYINDEX); 273 | ]] 274 | 275 | -- Convert ZMQ Error codes into strings. 276 | -- 277 | -- This is an error code wrapper object, it converts C-style 'int' return error code 278 | -- into Lua-style 'nil, "Error message"' return values. 279 | -- 280 | error_code "ZMQ_Error" "int" { 281 | ffi_type = "int", 282 | is_error_check = function(rec) return "(-1 == ${" .. rec.name .. "})" end, 283 | ffi_is_error_check = function(rec) return "(-1 == ${" .. rec.name .. "})" end, 284 | default = "0", 285 | c_source [[ 286 | int num; 287 | if(-1 == err) { 288 | /* get ZErrors table. */ 289 | lua_pushlightuserdata(L, zmq_ZErrors_key); 290 | lua_rawget(L, LUA_REGISTRYINDEX); 291 | /* convert zmq_errno to string. */ 292 | num = zmq_errno(); 293 | lua_pushinteger(L, num); 294 | lua_gettable(L, -2); 295 | /* remove ZErrors table. */ 296 | lua_remove(L, -2); 297 | if(!lua_isnil(L, -1)) { 298 | /* found error. */ 299 | return; 300 | } 301 | /* Unknown error. */ 302 | lua_pop(L, 1); 303 | lua_pushfstring(L, "UNKNOWN ERROR(%d)", num); 304 | return; 305 | } 306 | ]], 307 | ffi_source [[ 308 | if(-1 == err) then 309 | err_str = ZError_names[C.zmq_errno()] 310 | end 311 | ]], 312 | } 313 | 314 | -------------------------------------------------------------------------------- /zmq.nobj.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2011 by Robert G. Jakabosky 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 | -- make generated variable nicer. 22 | set_variable_format "%s%d" 23 | 24 | c_module "zmq" { 25 | -- module settings. 26 | module_globals = true, -- support old code that doesn't do: local zmq = require"zmq" 27 | use_globals = false, 28 | hide_meta_info = true, 29 | luajit_ffi = true, 30 | -- needed for functions exported from module. 31 | luajit_ffi_load_cmodule = true, 32 | 33 | ffi_load { 34 | "zmq", -- default lib name. 35 | Windows = "libzmq", -- lib name for on windows. 36 | }, 37 | 38 | sys_include "string.h", 39 | include "zmq.h", 40 | 41 | c_source "typedefs" [[ 42 | /* detect zmq version */ 43 | #define VERSION_2_0 1 44 | #define VERSION_2_1 0 45 | #define VERSION_2_2 0 46 | #define VERSION_3_0 0 47 | #define VERSION_3_2 0 48 | #define VERSION_4_0 0 49 | #if defined(ZMQ_VERSION_MAJOR) 50 | # if (ZMQ_VERSION_MAJOR == 2) && (ZMQ_VERSION_MINOR == 2) 51 | # undef VERSION_2_2 52 | # define VERSION_2_2 1 53 | # undef VERSION_2_1 54 | # define VERSION_2_1 1 55 | # endif 56 | # if (ZMQ_VERSION_MAJOR == 2) && (ZMQ_VERSION_MINOR == 1) 57 | # undef VERSION_2_1 58 | # define VERSION_2_1 1 59 | # endif 60 | # if (ZMQ_VERSION_MAJOR == 3) && (ZMQ_VERSION_MINOR == 3) 61 | # undef VERSION_2_0 62 | # define VERSION_2_0 0 63 | # undef VERSION_3_2 64 | # define VERSION_3_2 1 65 | # undef VERSION_3_0 66 | # define VERSION_3_0 1 67 | # endif 68 | # if (ZMQ_VERSION_MAJOR == 3) && (ZMQ_VERSION_MINOR == 2) 69 | # undef VERSION_2_0 70 | # define VERSION_2_0 0 71 | # undef VERSION_3_2 72 | # define VERSION_3_2 1 73 | # undef VERSION_3_0 74 | # define VERSION_3_0 1 75 | # endif 76 | # if (ZMQ_VERSION_MAJOR == 3) 77 | # undef VERSION_2_0 78 | # define VERSION_2_0 0 79 | # undef VERSION_3_0 80 | # define VERSION_3_0 1 81 | # endif 82 | # if (ZMQ_VERSION_MAJOR == 4) 83 | # undef VERSION_2_0 84 | # define VERSION_2_0 0 85 | # undef VERSION_3_2 86 | # define VERSION_3_2 0 87 | # undef VERSION_3_0 88 | # define VERSION_3_0 0 89 | # undef VERSION_4_0 90 | # define VERSION_4_0 1 91 | # endif 92 | #endif 93 | 94 | /* make sure ZMQ_DONTWAIT & ZMQ_NOBLOCK are both defined. */ 95 | #ifndef ZMQ_DONTWAIT 96 | # define ZMQ_DONTWAIT ZMQ_NOBLOCK 97 | #endif 98 | #ifndef ZMQ_NOBLOCK 99 | # define ZMQ_NOBLOCK ZMQ_DONTWAIT 100 | #endif 101 | 102 | /* make sure DEALER/ROUTER & XREQ/XREP are all defined. */ 103 | #ifndef ZMQ_DEALER 104 | # define ZMQ_DEALER ZMQ_XREQ 105 | #endif 106 | #ifndef ZMQ_ROUTER 107 | # define ZMQ_ROUTER ZMQ_XREP 108 | #endif 109 | #ifndef ZMQ_XREQ 110 | # define ZMQ_XREQ ZMQ_DEALER 111 | #endif 112 | #ifndef ZMQ_XREP 113 | # define ZMQ_XREP ZMQ_ROUTER 114 | #endif 115 | 116 | #if VERSION_2_0 117 | # define ZMQ_POLL_MSEC 1000 // zmq_poll is usec 118 | #elif VERSION_3_0 || VERSION_4_0 119 | # define ZMQ_POLL_MSEC 1 // zmq_poll is msec 120 | # ifndef ZMQ_HWM 121 | # define ZMQ_HWM 1 // backwards compatibility 122 | # endif 123 | #endif 124 | ]], 125 | 126 | -- 127 | -- Module constants 128 | -- 129 | export_definitions { 130 | MAX_VSM_SIZE = "ZMQ_MAX_VSM_SIZE", 131 | 132 | -- context settings 133 | MAX_SOCKETS = "ZMQ_MAX_SOCKETS", 134 | IO_THREADS = "ZMQ_IO_THREADS", 135 | 136 | -- message types 137 | DELIMITER = "ZMQ_DELIMITER", 138 | VSM = "ZMQ_VSM", 139 | 140 | -- message flags 141 | MSG_MORE = "ZMQ_MSG_MORE", 142 | MSG_SHARED = "ZMQ_MSG_SHARED", 143 | 144 | -- socket types 145 | PAIR = "ZMQ_PAIR", 146 | PUB = "ZMQ_PUB", 147 | SUB = "ZMQ_SUB", 148 | REQ = "ZMQ_REQ", 149 | REP = "ZMQ_REP", 150 | PULL = "ZMQ_PULL", 151 | PUSH = "ZMQ_PUSH", 152 | 153 | DEALER = "ZMQ_DEALER", 154 | ROUTER = "ZMQ_ROUTER", 155 | XREQ = "ZMQ_XREQ", 156 | XREP = "ZMQ_XREP", 157 | 158 | -- new 3.1 socket types 159 | XPUB = "ZMQ_XPUB", 160 | XSUB = "ZMQ_XSUB", 161 | 162 | -- socket options 163 | HWM = "ZMQ_HWM", 164 | SWAP = "ZMQ_SWAP", 165 | AFFINITY = "ZMQ_AFFINITY", 166 | IDENTITY = "ZMQ_IDENTITY", 167 | SUBSCRIBE = "ZMQ_SUBSCRIBE", 168 | UNSUBSCRIBE = "ZMQ_UNSUBSCRIBE", 169 | RATE = "ZMQ_RATE", 170 | RECOVERY_IVL = "ZMQ_RECOVERY_IVL", 171 | MCAST_LOOP = "ZMQ_MCAST_LOOP", 172 | SNDBUF = "ZMQ_SNDBUF", 173 | RCVBUF = "ZMQ_RCVBUF", 174 | RCVMORE = "ZMQ_RCVMORE", 175 | FD = "ZMQ_FD", 176 | EVENTS = "ZMQ_EVENTS", 177 | TYPE = "ZMQ_TYPE", 178 | LINGER = "ZMQ_LINGER", 179 | RECONNECT_IVL = "ZMQ_RECONNECT_IVL", 180 | RECONNECT_IVL_MSEC= "ZMQ_RECONNECT_IVL_MSEC", 181 | BACKLOG = "ZMQ_BACKLOG", 182 | RECONNECT_IVL_MAX = "ZMQ_RECONNECT_IVL_MAX", 183 | MAXMSGSIZE = "ZMQ_MAXMSGSIZE", 184 | SNDHWM = "ZMQ_SNDHWM", 185 | RCVHWM = "ZMQ_RCVHWM", 186 | MULTICAST_HOPS = "ZMQ_MULTICAST_HOPS", 187 | RCVTIMEO = "ZMQ_RCVTIMEO", 188 | SNDTIMEO = "ZMQ_SNDTIMEO", 189 | RCVLABEL = "ZMQ_RCVLABEL", 190 | LAST_ENDPOINT = "ZMQ_LAST_ENDPOINT", 191 | ROUTER_MANDATORY = "ZMQ_ROUTER_MANDATORY", 192 | TCP_KEEPALIVE = "ZMQ_TCP_KEEPALIVE", 193 | TCP_KEEPALIVE_CNT = "ZMQ_TCP_KEEPALIVE_CNT", 194 | TCP_KEEPALIVE_IDLE= "ZMQ_TCP_KEEPALIVE_IDLE", 195 | TCP_KEEPALIVE_INTVL= "ZMQ_TCP_KEEPALIVE_INTVL", 196 | TCP_ACCEPT_FILTER = "ZMQ_TCP_ACCEPT_FILTER", 197 | IMMEDIATE = "ZMQ_IMMEDIATE", 198 | XPUB_VERBOSE = "ZMQ_XPUB_VERBOSE", 199 | ROUTER_RAW = "ZMQ_ROUTER_RAW", 200 | IPV6 = "ZMQ_IPV6", 201 | MECHANISM = "ZMQ_MECHANISM", 202 | PLAIN_SERVER = "ZMQ_PLAIN_SERVER", 203 | PLAIN_USERNAME = "ZMQ_PLAIN_USERNAME", 204 | PLAIN_PASSWORD = "ZMQ_PLAIN_PASSWORD", 205 | CURVE_SERVER = "ZMQ_CURVE_SERVER", 206 | CURVE_PUBLICKEY = "ZMQ_CURVE_PUBLICKEY", 207 | CURVE_SECRETKEY = "ZMQ_CURVE_SECRETKEY", 208 | CURVE_SERVERKEY = "ZMQ_CURVE_SERVERKEY", 209 | PROBE_ROUTER = "ZMQ_PROBE_ROUTER", 210 | REQ_CORRELATE = "ZMQ_REQ_CORRELATE", 211 | REQ_RELAXED = "ZMQ_REQ_RELAXED", 212 | CONFLATE = "ZMQ_CONFLATE", 213 | ZAP_DOMAIN = "ZMQ_ZAP_DOMAIN", 214 | 215 | -- send/recv flags 216 | NOBLOCK = "ZMQ_NOBLOCK", 217 | DONTWAIT = "ZMQ_DONTWAIT", 218 | SNDMORE = "ZMQ_SNDMORE", 219 | SNDLABEL = "ZMQ_SNDLABEL", 220 | 221 | -- Security mechanisms 222 | NULL = "ZMQ_NULL", 223 | PLAIN = "ZMQ_PLAIN", 224 | CURVE = "ZMQ_CURVE", 225 | 226 | -- poll events 227 | POLLIN = "ZMQ_POLLIN", 228 | POLLOUT = "ZMQ_POLLOUT", 229 | POLLERR = "ZMQ_POLLERR", 230 | 231 | -- poll milliseconds. 232 | POLL_MSEC = "ZMQ_POLL_MSEC", 233 | 234 | -- Socket Monitor events. 235 | EVENT_CONNECTED = "ZMQ_EVENT_CONNECTED", 236 | EVENT_CONNECT_DELAYED = "ZMQ_EVENT_CONNECT_DELAYED", 237 | EVENT_CONNECT_RETRIED = "ZMQ_EVENT_CONNECT_RETRIED", 238 | 239 | EVENT_LISTENING = "ZMQ_EVENT_LISTENING", 240 | EVENT_BIND_FAILED = "ZMQ_EVENT_BIND_FAILED", 241 | 242 | EVENT_ACCEPTED = "ZMQ_EVENT_ACCEPTED", 243 | EVENT_ACCEPT_FAILED= "ZMQ_EVENT_ACCEPT_FAILED", 244 | 245 | EVENT_CLOSED = "ZMQ_EVENT_CLOSED", 246 | EVENT_CLOSE_FAILED= "ZMQ_EVENT_CLOSE_FAILED", 247 | EVENT_DISCONNECTED= "ZMQ_EVENT_DISCONNECTED", 248 | EVENT_MONITOR_STOPPED = "ZMQ_EVENT_MONITOR_STOPPED", 249 | 250 | EVENT_ALL = "ZMQ_EVENT_ALL", 251 | 252 | -- devices 253 | STREAMER = "ZMQ_STREAMER", 254 | FORWARDER = "ZMQ_FORWARDER", 255 | QUEUE = "ZMQ_QUEUE", 256 | }, 257 | 258 | subfiles { 259 | "src/error.nobj.lua", 260 | "src/msg.nobj.lua", 261 | "src/socket.nobj.lua", 262 | "src/poller.nobj.lua", 263 | "src/ctx.nobj.lua", 264 | "src/stopwatch.nobj.lua", 265 | }, 266 | 267 | -- 268 | -- Module static functions 269 | -- 270 | c_function "version" { 271 | var_out{ "", "ver" }, 272 | c_source[[ 273 | int major, minor, patch; 274 | zmq_version(&(major), &(minor), &(patch)); 275 | 276 | /* return version as a table: { major, minor, patch } */ 277 | lua_createtable(L, 3, 0); 278 | lua_pushinteger(L, major); 279 | lua_rawseti(L, -2, 1); 280 | lua_pushinteger(L, minor); 281 | lua_rawseti(L, -2, 2); 282 | lua_pushinteger(L, patch); 283 | lua_rawseti(L, -2, 3); 284 | ]], 285 | }, 286 | c_function "init" { 287 | var_in{ "int", "io_threads?", default = "1" }, 288 | c_call "!ZMQ_Ctx *" "zmq_init" { "int", "io_threads" }, 289 | }, 290 | c_function "init_ctx" { 291 | var_in{ "", "ptr" }, 292 | var_out{ "ZMQ_Ctx *", "ctx" }, 293 | c_source[[ 294 | if(lua_isuserdata(L, ${ptr::idx})) { 295 | ${ctx} = lua_touserdata(L, ${ptr::idx}); 296 | } else { 297 | return luaL_argerror(L, ${ptr::idx}, "expected lightuserdata"); 298 | } 299 | ]], 300 | ffi_source[[ 301 | local p_type = type(${ptr}) 302 | if p_type == 'userdata' then 303 | ${ctx} = ffi.cast('ZMQ_Ctx *', ${ptr}); 304 | elseif p_type == 'cdata' and ffi.istype('void *', ${ptr}) then 305 | ${ctx} = ffi.cast('ZMQ_Ctx *', ${ptr}); 306 | else 307 | return error("expected lightuserdata/cdata"); 308 | end 309 | ]], 310 | }, 311 | c_function "device" { if_defs = { "VERSION_2_0", "VERSION_3_2" }, 312 | c_call "ZMQ_Error" "zmq_device" 313 | { "int", "device", "ZMQ_Socket *", "insock", "ZMQ_Socket *", "outsock" }, 314 | }, 315 | c_function "proxy" { if_defs = "VERSION_3_2", 316 | c_call "ZMQ_Error" "zmq_proxy" 317 | { "ZMQ_Socket *", "frontend", "ZMQ_Socket *", "backend", "ZMQ_Socket *", "capture?" }, 318 | }, 319 | 320 | -- 321 | -- utils 322 | -- 323 | c_function "stopwatch_start" { 324 | c_call "!ZMQ_StopWatch *" "zmq_stopwatch_start" {}, 325 | }, 326 | c_function "sleep" { 327 | c_call "void" "zmq_sleep" { "int", "seconds_" }, 328 | }, 329 | } 330 | 331 | -------------------------------------------------------------------------------- /src/poller.nobj.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2011 by Robert G. Jakabosky 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 | local ZMQ_Poller_type = [[ 22 | struct ZMQ_Poller { 23 | zmq_pollitem_t *items; 24 | int next; 25 | int count; 26 | int free_list; 27 | int len; 28 | }; 29 | ]] 30 | 31 | object "ZMQ_Poller" { 32 | -- store the `ZMQ_Poller` structure in Lua userdata object 33 | userdata_type = "embed", 34 | c_source(ZMQ_Poller_type), 35 | c_source[[ 36 | 37 | typedef struct ZMQ_Poller ZMQ_Poller; 38 | 39 | #define FREE_ITEM_EVENTS_TAG ((short)0xFFFF) 40 | 41 | #define ITEM_TO_INDEX(items, item) (item - (items)) 42 | 43 | static int poller_resize_items(ZMQ_Poller *poller, int len) { 44 | int old_len = poller->len; 45 | 46 | /* make sure new length is atleast as large as items count. */ 47 | len = (poller->count <= len) ? len : poller->count; 48 | 49 | /* if the new length is the same as the old length, then don't try to resize. */ 50 | if(old_len == len) return len; 51 | 52 | poller->items = (zmq_pollitem_t *)realloc(poller->items, len * sizeof(zmq_pollitem_t)); 53 | poller->len = len; 54 | if(len > old_len) { 55 | /* clear new space. */ 56 | memset(&(poller->items[old_len]), 0, (len - old_len) * sizeof(zmq_pollitem_t)); 57 | } 58 | return len; 59 | } 60 | 61 | void poller_init(ZMQ_Poller *poller, int length) { 62 | poller->items = (zmq_pollitem_t *)calloc(length, sizeof(zmq_pollitem_t)); 63 | poller->next = -1; 64 | poller->count = 0; 65 | poller->len = length; 66 | poller->free_list = -1; 67 | } 68 | 69 | void poller_cleanup(ZMQ_Poller *poller) { 70 | free(poller->items); 71 | poller->items = NULL; 72 | poller->next = -1; 73 | poller->count = 0; 74 | poller->len = 0; 75 | poller->free_list = -1; 76 | } 77 | 78 | int poller_find_sock_item(ZMQ_Poller *poller, ZMQ_Socket *sock) { 79 | zmq_pollitem_t *items; 80 | int count; 81 | int n; 82 | 83 | /* find ZMQ_Socket */ 84 | items = poller->items; 85 | count = poller->count; 86 | for(n=0; n < count; n++) { 87 | if(items[n].socket == sock) return n; 88 | } 89 | /* not found. */ 90 | return -1; 91 | } 92 | 93 | int poller_find_fd_item(ZMQ_Poller *poller, socket_t fd) { 94 | zmq_pollitem_t *items; 95 | int count; 96 | int n; 97 | 98 | /* find fd */ 99 | items = poller->items; 100 | count = poller->count; 101 | for(n=0; n < count; n++) { 102 | if(items[n].fd == fd) return n; 103 | } 104 | /* not found. */ 105 | return -1; 106 | } 107 | 108 | void poller_remove_item(ZMQ_Poller *poller, int idx) { 109 | zmq_pollitem_t *items; 110 | int free_list; 111 | int count; 112 | 113 | count = poller->count; 114 | /* no item to remove. */ 115 | if(idx >= count || count == 0) return; 116 | 117 | items = poller->items; 118 | free_list = poller->free_list; 119 | 120 | /* link new free slot to head of free list. */ 121 | if(free_list >= 0 && free_list < count) { 122 | /* use socket pointer for free list's 'next' field. */ 123 | items[idx].socket = &(items[free_list]); 124 | } else { 125 | /* free list is empty mark poller slot as the end. */ 126 | items[idx].socket = NULL; 127 | } 128 | poller->free_list = idx; 129 | /* mark poller slot as a free slot. */ 130 | items[idx].events = FREE_ITEM_EVENTS_TAG; 131 | /* clear old revents. */ 132 | items[idx].revents = 0; 133 | } 134 | 135 | int poller_get_free_item(ZMQ_Poller *poller) { 136 | zmq_pollitem_t *curr; 137 | zmq_pollitem_t *next; 138 | int count; 139 | int idx; 140 | 141 | count = poller->count; 142 | idx = poller->free_list; 143 | /* check for a free slot in the free list. */ 144 | if(idx >= 0 && idx < count) { 145 | /* remove free slot from free list. */ 146 | curr = &(poller->items[idx]); 147 | /* valid free slot. */ 148 | assert(curr->events == FREE_ITEM_EVENTS_TAG); 149 | /* is poller the last free slot? */ 150 | next = ((zmq_pollitem_t *)curr->socket); 151 | if(next != NULL) { 152 | /* set next free slot as head of free list. */ 153 | poller->free_list = ITEM_TO_INDEX(poller->items, next); 154 | } else { 155 | /* free list is empty now. */ 156 | poller->free_list = -1; 157 | } 158 | /* clear slot */ 159 | memset(curr, 0, sizeof(zmq_pollitem_t)); 160 | return idx; 161 | } 162 | 163 | idx = count; 164 | poller->count = ++count; 165 | /* make room for new item. */ 166 | if(count >= poller->len) { 167 | poller_resize_items(poller, poller->len + 10); 168 | } 169 | return idx; 170 | } 171 | 172 | static int poller_compact_items(ZMQ_Poller *poller) { 173 | zmq_pollitem_t *items; 174 | int count; 175 | int old_count; 176 | int next; 177 | 178 | count = poller->count; 179 | /* if no free slot, then return. */ 180 | if(poller->free_list < 0) return count; 181 | old_count = count; 182 | 183 | items = poller->items; 184 | next = 0; 185 | /* find first free slot. */ 186 | while(next < count && items[next].events != FREE_ITEM_EVENTS_TAG) { 187 | ++next; 188 | } 189 | 190 | /* move non-free slots into free slot. */ 191 | count = next; 192 | ++next; 193 | while(next < old_count) { 194 | if(items[next].events != FREE_ITEM_EVENTS_TAG) { 195 | /* found non-free slot, move it to the current free slot. */ 196 | items[count] = items[next]; 197 | ++count; 198 | } 199 | ++next; 200 | } 201 | 202 | /* clear old used-space */ 203 | memset(&(items[count]), 0, ((old_count - count) * sizeof(zmq_pollitem_t))); 204 | poller->count = count; 205 | poller->free_list = -1; /* free list is now empty. */ 206 | 207 | assert(count <= poller->len); 208 | return count; 209 | } 210 | 211 | int poller_poll(ZMQ_Poller *poller, long timeout) { 212 | int count; 213 | /* remove free slots from items list. */ 214 | if(poller->free_list >= 0) { 215 | count = poller_compact_items(poller); 216 | } else { 217 | count = poller->count; 218 | } 219 | /* poll for events. */ 220 | return zmq_poll(poller->items, count, timeout); 221 | } 222 | 223 | int poller_next_revents(ZMQ_Poller *poller, int *revents) { 224 | zmq_pollitem_t *items; 225 | int count; 226 | int idx; 227 | int next; 228 | 229 | idx = poller->next; 230 | /* do we need to poll for more events? */ 231 | if(idx < 0) { 232 | return idx; 233 | } 234 | items = poller->items; 235 | count = poller->count; 236 | /* find next item with pending events. */ 237 | for(;idx < count; ++idx) { 238 | /* did we find a pending event? */ 239 | if(items[idx].revents != 0) { 240 | *revents = items[idx].revents; 241 | poller->next = idx+1; 242 | return idx; 243 | } 244 | } 245 | /* processed all pending events. */ 246 | poller->next = -1; 247 | *revents = 0; 248 | return -1; 249 | } 250 | 251 | ]], 252 | -- 253 | -- Define ZMQ_Poller type & function API for FFI 254 | -- 255 | ffi_cdef[[ 256 | typedef int socket_t; 257 | typedef struct zmq_pollitem_t { 258 | ZMQ_Socket *socket; 259 | socket_t fd; 260 | short events; 261 | short revents; 262 | } zmq_pollitem_t; 263 | 264 | int poller_find_sock_item(ZMQ_Poller *poller, ZMQ_Socket *sock); 265 | int poller_find_fd_item(ZMQ_Poller *poller, socket_t fd); 266 | int poller_get_free_item(ZMQ_Poller *poller); 267 | int poller_poll(ZMQ_Poller *poller, long timeout); 268 | void poller_remove_item(ZMQ_Poller *poller, int idx); 269 | 270 | ]], 271 | ffi_cdef(ZMQ_Poller_type), 272 | 273 | constructor "new" { 274 | var_in{ "unsigned int", "length", is_optional = true, default = 10 }, 275 | c_export_method_call "void" "poller_init" { "unsigned int", "length" }, 276 | }, 277 | destructor "close" { 278 | c_export_method_call "void" "poller_cleanup" {}, 279 | }, 280 | method "add" { 281 | var_in{ "", "sock" }, 282 | var_in{ "short", "events" }, 283 | var_out{ "int", "idx" }, 284 | c_source "pre" [[ 285 | zmq_pollitem_t *item; 286 | ZMQ_Socket *sock = NULL; 287 | socket_t fd = 0; 288 | ]], 289 | c_source[[ 290 | if(lua_isuserdata(L, ${sock::idx})) { 291 | sock = obj_type_ZMQ_Socket_check(L, ${sock::idx}); 292 | } else if(lua_isnumber(L, ${sock::idx})) { 293 | fd = lua_tonumber(L, ${sock::idx}); 294 | } else { 295 | return luaL_typerror(L, ${sock::idx}, "number or ZMQ_Socket"); 296 | } 297 | ${idx} = poller_get_free_item(${this}); 298 | item = &(${this}->items[${idx}]); 299 | item->socket = sock; 300 | item->fd = fd; 301 | item->events = ${events}; 302 | ]], 303 | ffi_source[[ 304 | local fd = 0 305 | local sock_type = type(${sock}) 306 | local sock 307 | if sock_type == 'cdata' then 308 | sock = obj_type_ZMQ_Socket_check(${sock}) 309 | elseif sock_type == 'number' then 310 | fd = ${sock} 311 | else 312 | error("expected number or ZMQ_Socket") 313 | end 314 | ${idx} = Cmod.poller_get_free_item(${this}) 315 | local item = ${this}.items[${idx}] 316 | item.socket = sock 317 | item.fd = fd 318 | item.events = ${events} 319 | ]], 320 | }, 321 | method "modify" { 322 | var_in{ "", "sock" }, 323 | var_in{ "short", "events" }, 324 | var_out{ "int", "idx" }, 325 | c_source "pre" [[ 326 | zmq_pollitem_t *item; 327 | ZMQ_Socket *sock = NULL; 328 | socket_t fd = 0; 329 | ]], 330 | c_source[[ 331 | if(lua_isuserdata(L, ${sock::idx})) { 332 | sock = obj_type_ZMQ_Socket_check(L, ${sock::idx}); 333 | /* find sock in items list. */ 334 | ${idx} = poller_find_sock_item(${this}, sock); 335 | } else if(lua_isnumber(L, ${sock::idx})) { 336 | fd = lua_tonumber(L, ${sock::idx}); 337 | /* find fd in items list. */ 338 | ${idx} = poller_find_fd_item(${this}, fd); 339 | } else { 340 | return luaL_typerror(L, ${sock::idx}, "number or ZMQ_Socket"); 341 | } 342 | if(${events} != 0) { 343 | /* add/modify. */ 344 | if(${idx} < 0) { 345 | ${idx} = poller_get_free_item(${this}); 346 | } 347 | item = &(${this}->items[${idx}]); 348 | item->socket = sock; 349 | item->fd = fd; 350 | item->events = ${events}; 351 | } else if(${idx} >= 0) { 352 | /* no events remove socket/fd. */ 353 | poller_remove_item(${this}, ${idx}); 354 | } 355 | ]], 356 | ffi_source[[ 357 | local fd = 0 358 | local sock_type = type(${sock}) 359 | local sock 360 | if sock_type == 'cdata' then 361 | sock = obj_type_ZMQ_Socket_check(${sock}) 362 | -- find sock in items list. 363 | ${idx} = Cmod.poller_find_sock_item(${this}, sock) 364 | elseif sock_type == 'number' then 365 | fd = ${sock} 366 | -- find fd in items list. 367 | ${idx} = Cmod.poller_find_fd_item(${this}, fd); 368 | else 369 | error("expected number or ZMQ_Socket") 370 | end 371 | if ${events} ~= 0 then 372 | local item = ${this}.items[${idx}] 373 | item.socket = sock 374 | item.fd = fd 375 | item.events = ${events} 376 | else 377 | Cmod.poller_remove_item(${this}, ${idx}) 378 | end 379 | ]], 380 | }, 381 | method "remove" { 382 | var_in{ "", "sock" }, 383 | var_out{ "int", "idx" }, 384 | c_source "pre" [[ 385 | ZMQ_Socket *sock; 386 | socket_t fd; 387 | ]], 388 | c_source[[ 389 | /* ZMQ_Socket or fd */ 390 | if(lua_isuserdata(L, ${sock::idx})) { 391 | sock = obj_type_ZMQ_Socket_check(L, ${sock::idx}); 392 | /* find sock in items list. */ 393 | ${idx} = poller_find_sock_item(${this}, sock); 394 | } else if(lua_isnumber(L, ${sock::idx})) { 395 | fd = lua_tonumber(L, ${sock::idx}); 396 | /* find fd in items list. */ 397 | ${idx} = poller_find_fd_item(${this}, fd); 398 | } else { 399 | return luaL_typerror(L, ${sock::idx}, "number or ZMQ_Socket"); 400 | } 401 | /* if sock/fd was found. */ 402 | if(${idx} >= 0) { 403 | poller_remove_item(${this}, ${idx}); 404 | } 405 | ]], 406 | ffi_source[[ 407 | local fd = 0 408 | local sock_type = type(${sock}) 409 | local sock 410 | if sock_type == 'cdata' then 411 | sock = obj_type_ZMQ_Socket_check(${sock}) 412 | -- find sock in items list. 413 | ${idx} = Cmod.poller_find_sock_item(${this}, sock) 414 | elseif sock_type == 'number' then 415 | fd = ${sock} 416 | -- find fd in items list. 417 | ${idx} = Cmod.poller_find_fd_item(${this}, fd); 418 | else 419 | error("expected number or ZMQ_Socket") 420 | end 421 | if ${idx} >= 0 then 422 | Cmod.poller_remove_item(${this}, ${idx}) 423 | end 424 | ]], 425 | }, 426 | method "poll" { 427 | var_out{ "int", "count" }, 428 | -- poll for events 429 | c_export_method_call { "ZMQ_Error", "err>2" } "poller_poll" { "long", "timeout" }, 430 | c_source[[ 431 | if(${err} > 0) { 432 | ${this}->next = 0; 433 | ${count} = ${err}; 434 | } else { 435 | ${this}->next = -1; 436 | ${count} = 0; 437 | } 438 | ]], 439 | ffi_source[[ 440 | if(${err} > 0) then 441 | ${this}.next = 0 442 | ${count} = ${err} 443 | else 444 | ${this}.next = -1 445 | ${count} = 0 446 | end 447 | ]], 448 | }, 449 | method "next_revents_idx" { 450 | c_export_method_call { "int", "idx>1" } "poller_next_revents" { "int", "&revents>2" }, 451 | }, 452 | method "count" { 453 | var_out{ "int", "count" }, 454 | c_source[[ 455 | ${count} = ${this}->count; 456 | ]], 457 | ffi_source[[ 458 | ${count} = ${this}.count; 459 | ]], 460 | }, 461 | } 462 | 463 | -------------------------------------------------------------------------------- /src/socket.nobj.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2011 by Robert G. Jakabosky 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 | -- 23 | -- Generate ZeroMQ socket option code customized for each version of zmq (2.0,2.1,3.x) 24 | -- 25 | ------------------------------------------------------------------------------------- 26 | 27 | local OPT_TYPES = { 28 | NONE = "NONE", 29 | INT = "int", 30 | UINT32 = "uint32_t", 31 | UINT64 = "uint64_t", 32 | INT64 = "int64_t", 33 | BLOB = "const char *", 34 | FD = "int", 35 | } 36 | local get_set_prefix = { 37 | rw = { c_get = "lzmq_socket_", get='', c_set = "lzmq_socket_set_", set='set_' }, 38 | r = { c_get = "lzmq_socket_", get='' }, 39 | w = { c_set = "lzmq_socket_", set='' }, 40 | } 41 | 42 | local socket_options = { 43 | { ver_def = 'VERSION_2_0', major = 2, minor = 0, 44 | [1] = { name="hwm", otype="UINT64", mode="rw", ltype="int" }, 45 | [2] = { }, 46 | [3] = { name="swap", otype="INT64", mode="rw", ltype="int" }, 47 | [4] = { name="affinity", otype="UINT64", mode="rw", ltype="uint64_t" }, 48 | [5] = { name="identity", otype="BLOB", mode="rw", ltype="const char *" }, 49 | [6] = { name="subscribe", otype="BLOB", mode="w", ltype="const char *" }, 50 | [7] = { name="unsubscribe", otype="BLOB", mode="w", ltype="const char *" }, 51 | [8] = { name="rate", otype="INT64", mode="rw", ltype="int" }, 52 | [9] = { name="recovery_ivl", otype="INT64", mode="rw", ltype="int" }, 53 | [10] = { name="mcast_loop", otype="INT64", mode="rw", ltype="int" }, 54 | [11] = { name="sndbuf", otype="UINT64", mode="rw", ltype="int" }, 55 | [12] = { name="rcvbuf", otype="UINT64", mode="rw", ltype="int" }, 56 | [13] = { name="rcvmore", otype="INT64", mode="r", ltype="int" }, 57 | }, 58 | { ver_def = 'VERSION_2_1', major = 2, minor = 1, 59 | [14] = { name="fd", otype="FD", mode="r", ltype="int" }, 60 | [15] = { name="events", otype="UINT32", mode="r", ltype="int" }, 61 | [16] = { name="type", otype="INT", mode="r", ltype="int" }, 62 | [17] = { name="linger", otype="INT", mode="rw", ltype="int" }, 63 | [18] = { name="reconnect_ivl", otype="INT", mode="rw", ltype="int" }, 64 | [19] = { name="backlog", otype="INT", mode="rw", ltype="int" }, 65 | [20] = { name="recovery_ivl_msec", otype="INT64", mode="rw", ltype="int64_t" }, 66 | [21] = { name="reconnect_ivl_max", otype="INT", mode="rw", ltype="int" }, 67 | }, 68 | { ver_def = 'VERSION_2_2', major = 2, minor = 2, 69 | [22] = { }, 70 | [23] = { }, 71 | [24] = { }, 72 | [25] = { }, 73 | [26] = { }, 74 | [27] = { name="rcvtimeo", otype="INT", mode="rw", ltype="int" }, 75 | [28] = { name="sndtimeo", otype="INT", mode="rw", ltype="int" }, 76 | }, 77 | { ver_def = 'VERSION_3_0', major = 3, minor = 0, 78 | [1] = { name="hwm", otype="INT", mode="rw", 79 | custom = [[ 80 | ZMQ_Error lzmq_socket_set_hwm(ZMQ_Socket *sock, int value) { 81 | int val; 82 | int rc; 83 | val = (int)value; 84 | rc = zmq_setsockopt(sock, ZMQ_SNDHWM, &value, sizeof(value)); 85 | if(-1 == rc) return rc; 86 | val = (int)value; 87 | return zmq_setsockopt(sock, ZMQ_RCVHWM, &value, sizeof(value)); 88 | } 89 | ZMQ_Error lzmq_socket_hwm(ZMQ_Socket *sock, int *value) { 90 | size_t val_len; 91 | int rc; 92 | val_len = sizeof(value); 93 | rc = zmq_getsockopt(sock, ZMQ_SNDHWM, value, &val_len); 94 | if(-1 == rc) return rc; 95 | val_len = sizeof(value); 96 | return zmq_getsockopt(sock, ZMQ_RCVHWM, value, &val_len); 97 | } 98 | 99 | ]] }, 100 | [2] = { }, 101 | [3] = { }, 102 | [4] = { name="affinity", otype="UINT64", mode="rw", ltype="uint64_t" }, 103 | [5] = { name="identity", otype="BLOB", mode="rw", ltype="const char *" }, 104 | [6] = { name="subscribe", otype="BLOB", mode="w", ltype="const char *" }, 105 | [7] = { name="unsubscribe", otype="BLOB", mode="w", ltype="const char *" }, 106 | [8] = { name="rate", otype="INT", mode="rw", ltype="int" }, 107 | [9] = { name="recovery_ivl", otype="INT", mode="rw", ltype="int" }, 108 | [10] = { }, 109 | [11] = { name="sndbuf", otype="INT", mode="rw", ltype="int" }, 110 | [12] = { name="rcvbuf", otype="INT", mode="rw", ltype="int" }, 111 | [13] = { name="rcvmore", otype="INT", mode="r", ltype="int" }, 112 | [14] = { name="fd", otype="FD", mode="r", ltype="int" }, 113 | [15] = { name="events", otype="INT", mode="r", ltype="int" }, 114 | [16] = { name="type", otype="INT", mode="r", ltype="int" }, 115 | [17] = { name="linger", otype="INT", mode="rw", ltype="int" }, 116 | [18] = { name="reconnect_ivl", otype="INT", mode="rw", ltype="int" }, 117 | [19] = { name="backlog", otype="INT", mode="rw", ltype="int" }, 118 | [20] = { }, 119 | [21] = { name="reconnect_ivl_max", otype="INT", mode="rw", ltype="int" }, 120 | [22] = { name="maxmsgsize", otype="INT64", mode="rw", ltype="int64_t" }, 121 | [23] = { name="sndhwm", otype="INT", mode="rw", ltype="int" }, 122 | [24] = { name="rcvhwm", otype="INT", mode="rw", ltype="int" }, 123 | [25] = { name="multicast_hops", otype="INT", mode="rw", ltype="int" }, 124 | [26] = { }, 125 | [27] = { name="rcvtimeo", otype="INT", mode="rw", ltype="int" }, 126 | [28] = { name="sndtimeo", otype="INT", mode="rw", ltype="int" }, 127 | [29] = { }, 128 | [30] = { }, 129 | [31] = { name="ipv4only", otype="INT", mode="rw", ltype="int" }, 130 | }, 131 | { ver_def = 'VERSION_4_0', major = 4, minor = 0, 132 | [1] = { name="hwm", otype="INT", mode="rw", 133 | custom = [[ 134 | ZMQ_Error lzmq_socket_set_hwm(ZMQ_Socket *sock, int value) { 135 | int val; 136 | int rc; 137 | val = (int)value; 138 | rc = zmq_setsockopt(sock, ZMQ_SNDHWM, &value, sizeof(value)); 139 | if(-1 == rc) return rc; 140 | val = (int)value; 141 | return zmq_setsockopt(sock, ZMQ_RCVHWM, &value, sizeof(value)); 142 | } 143 | ZMQ_Error lzmq_socket_hwm(ZMQ_Socket *sock, int *value) { 144 | size_t val_len; 145 | int rc; 146 | val_len = sizeof(value); 147 | rc = zmq_getsockopt(sock, ZMQ_SNDHWM, value, &val_len); 148 | if(-1 == rc) return rc; 149 | val_len = sizeof(value); 150 | return zmq_getsockopt(sock, ZMQ_RCVHWM, value, &val_len); 151 | } 152 | 153 | ]] }, 154 | [2] = { }, 155 | [3] = { }, 156 | [4] = { name="affinity", otype="UINT64", mode="rw", ltype="uint64_t" }, 157 | [5] = { name="identity", otype="BLOB", mode="rw", ltype="const char *" }, 158 | [6] = { name="subscribe", otype="BLOB", mode="w", ltype="const char *" }, 159 | [7] = { name="unsubscribe", otype="BLOB", mode="w", ltype="const char *" }, 160 | [8] = { name="rate", otype="INT", mode="rw", ltype="int" }, 161 | [9] = { name="recovery_ivl", otype="INT", mode="rw", ltype="int" }, 162 | [10] = { }, 163 | [11] = { name="sndbuf", otype="INT", mode="rw", ltype="int" }, 164 | [12] = { name="rcvbuf", otype="INT", mode="rw", ltype="int" }, 165 | [13] = { name="rcvmore", otype="INT", mode="r", ltype="int" }, 166 | [14] = { name="fd", otype="FD", mode="r", ltype="int" }, 167 | [15] = { name="events", otype="INT", mode="r", ltype="int" }, 168 | [16] = { name="type", otype="INT", mode="r", ltype="int" }, 169 | [17] = { name="linger", otype="INT", mode="rw", ltype="int" }, 170 | [18] = { name="reconnect_ivl", otype="INT", mode="rw", ltype="int" }, 171 | [19] = { name="backlog", otype="INT", mode="rw", ltype="int" }, 172 | [20] = { }, 173 | [21] = { name="reconnect_ivl_max", otype="INT", mode="rw", ltype="int" }, 174 | [22] = { name="maxmsgsize", otype="INT64", mode="rw", ltype="int64_t" }, 175 | [23] = { name="sndhwm", otype="INT", mode="rw", ltype="int" }, 176 | [24] = { name="rcvhwm", otype="INT", mode="rw", ltype="int" }, 177 | [25] = { name="multicast_hops", otype="INT", mode="rw", ltype="int" }, 178 | [26] = { }, 179 | [27] = { name="rcvtimeo", otype="INT", mode="rw", ltype="int" }, 180 | [28] = { name="sndtimeo", otype="INT", mode="rw", ltype="int" }, 181 | [29] = { }, 182 | [30] = { }, 183 | [31] = { name="ipv4only", otype="INT", mode="rw", ltype="int" }, 184 | -- New to version 4.x 185 | [32] = { name="last_endpoint", otype="BLOB", mode="r", ltype="const char *" }, 186 | [33] = { name="router_mandatory", otype="INT", mode="w", ltype="int" }, 187 | [34] = { name="tcp_keepalive", otype="INT", mode="rw", ltype="int" }, 188 | [35] = { name="tcp_keepalive_cnt", otype="INT", mode="rw", ltype="int" }, 189 | [36] = { name="tcp_keepalive_idle",otype="INT", mode="rw", ltype="int" }, 190 | [37] = { name="tcp_keepalive_intvl",otype="INT", mode="rw", ltype="int" }, 191 | [38] = { name="tcp_accept_filter", otype="BLOB", mode="w", ltype="const char *" }, 192 | [39] = { name="immediate", otype="INT", mode="rw", ltype="int" }, 193 | [40] = { name="xpub_verbose", otype="INT", mode="w", ltype="int" }, 194 | [41] = { name="router_raw", otype="INT", mode="w", ltype="int" }, 195 | [42] = { name="ipv6", otype="INT", mode="rw", ltype="int" }, 196 | [43] = { name="mechanism", otype="INT", mode="r", ltype="int" }, 197 | [44] = { name="plain_server", otype="INT", mode="rw", ltype="int" }, 198 | [45] = { name="plain_username", otype="BLOB", mode="rw", ltype="const char *" }, 199 | [46] = { name="plain_password", otype="BLOB", mode="rw", ltype="const char *" }, 200 | [47] = { name="curve_server", otype="INT", mode="rw", ltype="int" }, 201 | [48] = { name="curve_publickey", otype="BLOB", mode="rw", ltype="const char *" }, 202 | [49] = { name="curve_secretkey", otype="BLOB", mode="rw", ltype="const char *" }, 203 | [50] = { name="curve_serverkey", otype="BLOB", mode="rw", ltype="const char *" }, 204 | [51] = { name="probe_router", otype="INT", mode="w", ltype="int" }, 205 | [52] = { name="req_correlate", otype="INT", mode="w", ltype="int" }, 206 | [53] = { name="req_relaxed", otype="INT", mode="w", ltype="int" }, 207 | [54] = { name="conflate", otype="INT", mode="rw", ltype="int" }, 208 | [55] = { name="zap_domain", otype="BLOB", mode="rw", ltype="const char *" }, 209 | }, 210 | } 211 | local max_options = 60 -- this number must be larger then the highest option value. 212 | 213 | local function foreach_opt(func) 214 | for i=1,#socket_options do 215 | local ver_opts = socket_options[i] 216 | for num=1,max_options do 217 | local opt = ver_opts[num] 218 | if opt then 219 | func(num, opt, ver_opts) 220 | end 221 | end 222 | end 223 | end 224 | local add=function(t,val) return table.insert(t,val) end 225 | local function template(data, templ) 226 | return templ:gsub("%${(.-)}", data) 227 | end 228 | 229 | local socket_methods = {} 230 | local ffi_opt_names = {} 231 | local max_methods = 0 232 | local function get_methods(opt, ver) 233 | local num = opt.num 234 | -- check if methods have been created 235 | local methods = socket_methods[num] 236 | 237 | if not methods then 238 | add(ffi_opt_names, "\t\t[".. num .. "] = '" .. opt.name .. "',\n") 239 | -- need to create methods info. 240 | methods = { 241 | num=num, 242 | name=opt.name, 243 | get=opt.get, set=opt.set, c_get=opt.c_get, c_set=opt.c_set, 244 | ltype=opt.ltype, otype=opt.otype, mode=opt.mode, 245 | versions = {}, 246 | } 247 | 248 | -- initialize all version as not-supported. 249 | for i=1,#socket_options do 250 | local ver_opts = socket_options[i] 251 | methods[ver_opts.ver_def] = false 252 | end 253 | 254 | if num > max_methods then max_methods = num end 255 | 256 | socket_methods[num] = methods 257 | end 258 | 259 | -- mark this version as supporting the option. 260 | methods[ver.ver_def] = true 261 | add(methods.versions, ver) 262 | 263 | return methods 264 | end 265 | 266 | -- do pre-processing of options. 267 | foreach_opt(function(num, opt, ver) 268 | opt.num = num 269 | if not opt.name then 270 | opt.name = 'none' 271 | opt.otype = 'NONE' 272 | opt.DEF = 'unused' 273 | return 274 | end 275 | -- track max option number for each version. 276 | if not ver.max_opt or ver.max_opt < num then 277 | ver.max_opt = num 278 | end 279 | opt.DEF = "ZMQ_" .. opt.name:upper() 280 | -- ctype & ffi_type 281 | local ctype = OPT_TYPES[opt.otype] 282 | opt.ctype = ctype 283 | if opt.otype == 'BLOB' then 284 | opt.ffi_type = 'string' 285 | opt.set_len_param = ', size_t value_len' 286 | opt.set_val_name = 'value' 287 | opt.set_len_name = 'value_len' 288 | elseif ctype ~= 'NONE' then 289 | opt.ffi_type = ctype .. '[1]' 290 | opt.set_len_param = '' 291 | opt.set_val_name = '&value' 292 | opt.set_len_name = 'sizeof(value)' 293 | end 294 | -- getter/setter names 295 | for meth,prefix in pairs(get_set_prefix[opt.mode]) do 296 | opt[meth] = prefix .. opt.name 297 | end 298 | -- create common list of option get/set methods. 299 | local methods = get_methods(opt, ver) 300 | end) 301 | 302 | local options_c_code = {} 303 | local opt_types = {} 304 | 305 | local function if_def(def) 306 | local code = "#if " .. def .. "\n" 307 | add(options_c_code, code) 308 | add(opt_types, code) 309 | end 310 | local function endif(def) 311 | local code = "#endif /* #if " .. def .. " */\n" 312 | add(options_c_code, code) 313 | add(opt_types, code) 314 | end 315 | 316 | -- build C code for socket options setters/getters 317 | local last_ver 318 | foreach_opt(function(num, opt, ver) 319 | if ver ~= last_ver then 320 | if last_ver then 321 | endif(last_ver.ver_def) 322 | end 323 | last_ver = ver 324 | if_def(ver.ver_def) 325 | add(opt_types, template(ver,[[ 326 | #define ${ver_def}_MAX_OPT ${max_opt} 327 | ]])) 328 | end 329 | add(opt_types, template(opt,[[ 330 | OPT_TYPE_${otype}, /* ${num} ${DEF} */ 331 | ]])) 332 | if opt.name == 'none' then return end 333 | -- generate setter 334 | local set = '' 335 | local get = '' 336 | if opt.c_set then 337 | if opt.otype == 'BLOB' then 338 | set = [[ 339 | LUA_NOBJ_API ZMQ_Error ${c_set}(ZMQ_Socket *sock, const char *value, size_t str_len) { 340 | return zmq_setsockopt(sock, ${DEF}, value, str_len); 341 | ]] 342 | elseif opt.ctype == opt.ltype then 343 | set = [[ 344 | LUA_NOBJ_API ZMQ_Error ${c_set}(ZMQ_Socket *sock, ${ltype} value) { 345 | return zmq_setsockopt(sock, ${DEF}, &value, sizeof(value)); 346 | ]] 347 | else 348 | set = [[ 349 | LUA_NOBJ_API ZMQ_Error ${c_set}(ZMQ_Socket *sock, ${ltype} value) { 350 | ${ctype} val = (${ctype})value; 351 | return zmq_setsockopt(sock, ${DEF}, &val, sizeof(val)); 352 | ]] 353 | end 354 | set = set .. "}\n\n" 355 | end 356 | -- generate getter 357 | if opt.c_get then 358 | if opt.otype == 'BLOB' then 359 | get = [[ 360 | LUA_NOBJ_API ZMQ_Error ${c_get}(ZMQ_Socket *sock, char *value, size_t *len) { 361 | return zmq_getsockopt(sock, ${DEF}, value, len); 362 | ]] 363 | elseif opt.ctype == opt.ltype then 364 | get = [[ 365 | LUA_NOBJ_API ZMQ_Error ${c_get}(ZMQ_Socket *sock, ${ltype} *value) { 366 | size_t val_len = sizeof(${ltype}); 367 | return zmq_getsockopt(sock, ${DEF}, value, &val_len); 368 | ]] 369 | else 370 | get = [[ 371 | LUA_NOBJ_API ZMQ_Error ${c_get}(ZMQ_Socket *sock, ${ltype} *value) { 372 | ${ctype} val; 373 | size_t val_len = sizeof(val); 374 | int rc = zmq_getsockopt(sock, ${DEF}, &val, &val_len); 375 | *value = (${ltype})val; 376 | return rc; 377 | ]] 378 | end 379 | get = get .. "}\n\n" 380 | end 381 | local templ 382 | if opt.custom then 383 | templ = opt.custom 384 | else 385 | templ = set .. get 386 | end 387 | add(options_c_code, template(opt,templ)) 388 | end) 389 | endif(last_ver.ver_def) 390 | 391 | add(opt_types, [[ 392 | #if VERSION_4_0 393 | # define MAX_OPTS VERSION_4_0_MAX_OPT 394 | #elif VERSION_3_0 395 | # define MAX_OPTS VERSION_3_0_MAX_OPT 396 | #else 397 | # if VERSION_2_2 398 | # define MAX_OPTS VERSION_2_2_MAX_OPT 399 | # elif VERSION_2_1 400 | # define MAX_OPTS VERSION_2_1_MAX_OPT 401 | # else 402 | # define MAX_OPTS VERSION_2_0_MAX_OPT 403 | # endif 404 | #endif 405 | }; 406 | 407 | ]]) 408 | 409 | options_c_code = table.concat(options_c_code) 410 | opt_types = table.concat(opt_types) 411 | ffi_opt_names = table.concat(ffi_opt_names) 412 | 413 | local function tunpack(tab, idx, max) 414 | if idx == max then return tab[idx] end 415 | return tab[idx], tunpack(tab, idx + 1, max) 416 | end 417 | 418 | local function build_meth_if_def(meth) 419 | local v = {} 420 | for i=1,#socket_options do 421 | local ver_opts = socket_options[i] 422 | if meth[ver_opts.ver_def] then 423 | v[#v+1] = ver_opts.ver_def 424 | end 425 | end 426 | return v 427 | end 428 | 429 | local function build_option_methods() 430 | local m = {} 431 | 432 | for i=1,max_methods do 433 | local meth = socket_methods[i] 434 | if meth then 435 | local ltype = meth.ltype 436 | local name 437 | -- get list of version defs for this method. 438 | local if_defs = build_meth_if_def(meth) 439 | -- generate getter method. 440 | name = meth.get 441 | if name then 442 | local args = { ltype, "&value" } 443 | local val_out = { ltype, "&value" } 444 | if meth.otype == 'BLOB' then 445 | val_out = { 'char *', "value", has_length = true } 446 | args = { 'char *', "value", "size_t", "&#value" } 447 | end 448 | m[#m+1] = method (name) { if_defs = if_defs, 449 | var_out(val_out), 450 | c_export_method_call "ZMQ_Error" (meth.c_get) (args), 451 | } 452 | end 453 | -- generate setter method. 454 | name = meth.set 455 | if name then 456 | local args = { ltype, "value" } 457 | if meth.otype == 'BLOB' then 458 | args = { ltype, "value", "size_t", "#value" } 459 | end 460 | m[#m+1] = method (name) { if_defs = if_defs, 461 | c_export_method_call "ZMQ_Error" (meth.c_set) (args), 462 | } 463 | end 464 | end 465 | end 466 | 467 | return tunpack(m, 1, #m) 468 | end 469 | 470 | ------------------------------------------------------------------------------------- 471 | -- 472 | -- ZeroMQ socket object. 473 | -- 474 | ------------------------------------------------------------------------------------- 475 | 476 | object "ZMQ_Socket" { 477 | error_on_null = "get_zmq_strerror()", 478 | ffi_source "ffi_pre_cdef" [[ 479 | 480 | -- detect zmq version 481 | local VERSION_2_0 = true 482 | local VERSION_2_1 = false 483 | local VERSION_2_2 = false 484 | local VERSION_3_0 = false 485 | local VERSION_4_0 = false 486 | local zver = _M.version() 487 | if zver[1] == 4 then 488 | VERSION_2_0 = false 489 | VERSION_4_0 = true 490 | elseif zver[1] == 3 then 491 | VERSION_2_0 = false 492 | VERSION_3_0 = true 493 | elseif zver[1] == 2 and zver[2] == 2 then 494 | VERSION_2_2 = true 495 | VERSION_2_1 = true 496 | elseif zver[1] == 2 and zver[2] == 1 then 497 | VERSION_2_1 = true 498 | end 499 | 500 | if VERSION_2_0 then 501 | ffi.cdef[==[ 502 | typedef int ZMQ_Error; 503 | typedef struct ZMQ_Socket ZMQ_Socket; 504 | typedef struct zmq_msg_t zmq_msg_t; 505 | 506 | ZMQ_Error zmq_sendmsg(ZMQ_Socket *sock, zmq_msg_t *msg, int flags) __asm__("zmq_send"); 507 | ZMQ_Error zmq_recvmsg(ZMQ_Socket *sock, zmq_msg_t *msg, int flags) __asm__("zmq_recv"); 508 | ]==] 509 | end 510 | ]], 511 | c_source ([[ 512 | 513 | /* detect really old ZeroMQ 2.0.x series. */ 514 | #if !defined(ZMQ_RCVMORE) 515 | # error "Your version of ZeroMQ is too old. Please upgrade to version 2.1 or to the latest 2.0.x" 516 | #endif 517 | 518 | typedef struct ZMQ_Socket ZMQ_Socket; 519 | 520 | #ifdef _WIN32 521 | #include 522 | typedef SOCKET socket_t; 523 | #else 524 | typedef int socket_t; 525 | #endif 526 | 527 | #if VERSION_2_0 528 | # define zmq_sendmsg zmq_send 529 | # define zmq_recvmsg zmq_recv 530 | #endif 531 | 532 | /* socket option types. */ 533 | #define OPT_TYPE_NONE 0 534 | #define OPT_TYPE_INT 1 535 | #define OPT_TYPE_UINT32 2 536 | #define OPT_TYPE_UINT64 3 537 | #define OPT_TYPE_INT64 4 538 | #define OPT_TYPE_BLOB 5 539 | #define OPT_TYPE_FD 6 540 | 541 | static const int opt_types[] = { 542 | OPT_TYPE_NONE, /* 0 unused */ 543 | ]] .. opt_types .. options_c_code), 544 | 545 | destructor "close" { 546 | c_method_call "ZMQ_Error" "zmq_close" {} 547 | }, 548 | method "bind" { 549 | c_method_call "ZMQ_Error" "zmq_bind" { "const char *", "addr" } 550 | }, 551 | method "unbind" { 552 | if_defs = { "VERSION_3_2", "VERSION_4_0" }, 553 | c_method_call "ZMQ_Error" "zmq_unbind" { "const char *", "addr" } 554 | }, 555 | method "connect" { 556 | c_method_call "ZMQ_Error" "zmq_connect" { "const char *", "addr" } 557 | }, 558 | method "disconnect" { 559 | if_defs = { "VERSION_3_2", "VERSION_4_0" }, 560 | c_method_call "ZMQ_Error" "zmq_disconnect" { "const char *", "addr" } 561 | }, 562 | ffi_cdef[[ 563 | int zmq_setsockopt (void *s, int option, const void *optval, size_t optvallen); 564 | int zmq_getsockopt (void *s, int option, void *optval, size_t *optvallen); 565 | ]], 566 | ffi_source([[ 567 | local option_gets = {} 568 | local option_sets = {} 569 | 570 | do 571 | local opt_name 572 | local methods = _meth.${object_name} 573 | setmetatable(option_gets,{__index = function(tab,opt) 574 | local opt_name = opt_name[opt] 575 | if not opt_name then return nil end 576 | local method = methods[opt_name] 577 | rawset(tab, opt, method) 578 | return method 579 | end}) 580 | setmetatable(option_sets,{__index = function(tab,opt) 581 | local opt_name = opt_name[opt] 582 | if not opt_name then return nil end 583 | local method = methods['set_' .. opt_name] or methods[opt_name] 584 | rawset(tab, opt, method) 585 | return method 586 | end}) 587 | opt_name = { 588 | ]] .. ffi_opt_names .. [[} 589 | end 590 | 591 | ]]), 592 | method "setopt" { 593 | var_in{ "uint32_t", "opt" }, 594 | var_in{ "", "val" }, 595 | var_out{ "ZMQ_Error", "err" }, 596 | c_source[[ 597 | size_t val_len; 598 | const void *val; 599 | 600 | #if VERSION_2_1 || VERSION_3_0 || VERSION_4_0 601 | socket_t fd_val; 602 | #endif 603 | int int_val; 604 | uint32_t uint32_val; 605 | uint64_t uint64_val; 606 | int64_t int64_val; 607 | 608 | #if VERSION_3_0 || VERSION_4_0 609 | /* 3.0 backwards compatibility support for HWM. */ 610 | if(${opt} == ZMQ_HWM) { 611 | int_val = luaL_checkinteger(L, ${val::idx}); 612 | val = &int_val; 613 | val_len = sizeof(int_val); 614 | ${err} = zmq_setsockopt(${this}, ZMQ_SNDHWM, val, val_len); 615 | if(-1 != ${err}) { 616 | ${err} = zmq_setsockopt(${this}, ZMQ_RCVHWM, val, val_len); 617 | } 618 | goto finished; 619 | } 620 | #endif 621 | 622 | if(${opt} > MAX_OPTS) { 623 | return luaL_argerror(L, ${opt::idx}, "Invalid socket option."); 624 | } 625 | 626 | switch(opt_types[${opt}]) { 627 | #if VERSION_2_1 || VERSION_3_0 || VERSION_4_0 628 | case OPT_TYPE_FD: 629 | fd_val = luaL_checkinteger(L, ${val::idx}); 630 | val = &fd_val; 631 | val_len = sizeof(fd_val); 632 | break; 633 | #endif 634 | case OPT_TYPE_INT: 635 | int_val = luaL_checkinteger(L, ${val::idx}); 636 | val = &int_val; 637 | val_len = sizeof(int_val); 638 | break; 639 | case OPT_TYPE_UINT32: 640 | uint32_val = luaL_checkinteger(L, ${val::idx}); 641 | val = &uint32_val; 642 | val_len = sizeof(uint32_val); 643 | break; 644 | case OPT_TYPE_UINT64: 645 | uint64_val = luaL_checkinteger(L, ${val::idx}); 646 | val = &uint64_val; 647 | val_len = sizeof(uint64_val); 648 | break; 649 | case OPT_TYPE_INT64: 650 | int64_val = luaL_checkinteger(L, ${val::idx}); 651 | val = &int64_val; 652 | val_len = sizeof(int64_val); 653 | break; 654 | case OPT_TYPE_BLOB: 655 | val = luaL_checklstring(L, ${val::idx}, &(val_len)); 656 | break; 657 | default: 658 | printf("Invalid socket option type, this shouldn't happen.\n"); 659 | abort(); 660 | break; 661 | } 662 | ${err} = zmq_setsockopt(${this}, ${opt}, val, val_len); 663 | finished: 664 | ]], 665 | ffi_source[[ 666 | local set = option_sets[${opt}] 667 | if set then 668 | return set(${this},${val}) 669 | else 670 | error("Invalid socket option.") 671 | end 672 | ]], 673 | }, 674 | ffi_source[[ 675 | local tmp_val_len = ffi.new('size_t[1]', 4) 676 | ]], 677 | method "getopt" { 678 | var_in{ "uint32_t", "opt" }, 679 | var_out{ "", "val" }, 680 | var_out{ "ZMQ_Error", "err" }, 681 | c_source[[ 682 | size_t val_len; 683 | 684 | #if VERSION_2_1 || VERSION_3_0 || VERSION_4_0 685 | socket_t fd_val; 686 | #endif 687 | int int_val; 688 | uint32_t uint32_val; 689 | uint64_t uint64_val; 690 | int64_t int64_val; 691 | #define STR_MAX 255 692 | char str_val[STR_MAX]; 693 | 694 | if(${opt} > MAX_OPTS) { 695 | lua_pushnil(L); 696 | lua_pushliteral(L, "Invalid socket option."); 697 | return 2; 698 | } 699 | 700 | switch(opt_types[${opt}]) { 701 | #if VERSION_2_1 || VERSION_3_0 || VERSION_4_0 702 | case OPT_TYPE_FD: 703 | val_len = sizeof(fd_val); 704 | ${err} = zmq_getsockopt(${this}, ${opt}, &fd_val, &val_len); 705 | if(0 == ${err}) { 706 | lua_pushinteger(L, (lua_Integer)fd_val); 707 | return 1; 708 | } 709 | break; 710 | #endif 711 | case OPT_TYPE_INT: 712 | val_len = sizeof(int_val); 713 | ${err} = zmq_getsockopt(${this}, ${opt}, &int_val, &val_len); 714 | if(0 == ${err}) { 715 | lua_pushinteger(L, (lua_Integer)int_val); 716 | return 1; 717 | } 718 | break; 719 | case OPT_TYPE_UINT32: 720 | val_len = sizeof(uint32_val); 721 | ${err} = zmq_getsockopt(${this}, ${opt}, &uint32_val, &val_len); 722 | if(0 == ${err}) { 723 | lua_pushinteger(L, (lua_Integer)uint32_val); 724 | return 1; 725 | } 726 | break; 727 | case OPT_TYPE_UINT64: 728 | val_len = sizeof(uint64_val); 729 | ${err} = zmq_getsockopt(${this}, ${opt}, &uint64_val, &val_len); 730 | if(0 == ${err}) { 731 | lua_pushinteger(L, (lua_Integer)uint64_val); 732 | return 1; 733 | } 734 | break; 735 | case OPT_TYPE_INT64: 736 | val_len = sizeof(int64_val); 737 | ${err} = zmq_getsockopt(${this}, ${opt}, &int64_val, &val_len); 738 | if(0 == ${err}) { 739 | lua_pushinteger(L, (lua_Integer)int64_val); 740 | return 1; 741 | } 742 | break; 743 | case OPT_TYPE_BLOB: 744 | val_len = STR_MAX; 745 | ${err} = zmq_getsockopt(${this}, ${opt}, str_val, &val_len); 746 | if(0 == ${err}) { 747 | lua_pushlstring(L, str_val, val_len); 748 | return 1; 749 | } 750 | #undef STR_MAX 751 | break; 752 | default: 753 | printf("Invalid socket option type, this shouldn't happen.\n"); 754 | abort(); 755 | break; 756 | } 757 | lua_pushnil(L); 758 | ]], 759 | ffi_source[[ 760 | local get = option_gets[${opt}] 761 | if get then 762 | return get(${this}) 763 | else 764 | error("Invalid socket option.") 765 | end 766 | ]], 767 | }, 768 | -- 769 | -- zmq_send 770 | -- 771 | method "send_msg" { 772 | c_method_call "ZMQ_Error" "zmq_sendmsg" { "zmq_msg_t *", "msg", "int", "flags?" }, 773 | }, 774 | -- create helper function for `zmq_send` 775 | c_source[[ 776 | LUA_NOBJ_API ZMQ_Error simple_zmq_send(ZMQ_Socket *sock, const char *data, size_t data_len, int flags) { 777 | #if VERSION_3_2 778 | return zmq_send(sock, data, data_len, flags); 779 | #else 780 | ZMQ_Error err; 781 | zmq_msg_t msg; 782 | /* initialize message */ 783 | err = zmq_msg_init_size(&msg, data_len); 784 | if(0 == err) { 785 | /* fill message */ 786 | memcpy(zmq_msg_data(&msg), data, data_len); 787 | /* send message */ 788 | err = zmq_sendmsg(sock, &msg, flags); 789 | /* close message */ 790 | zmq_msg_close(&msg); 791 | } 792 | return err; 793 | #endif 794 | } 795 | ]], 796 | method "send" { 797 | c_export_method_call "ZMQ_Error" "simple_zmq_send" 798 | { "const char *", "data", "size_t", "#data", "int", "flags?"} 799 | }, 800 | -- 801 | -- zmq_recv 802 | -- 803 | method "recv_msg" { 804 | c_method_call "ZMQ_Error" "zmq_recvmsg" { "zmq_msg_t *", "msg", "int", "flags?" }, 805 | }, 806 | ffi_source[[ 807 | local tmp_msg = ffi.new('zmq_msg_t') 808 | ]], 809 | method "recv" { 810 | var_in{ "int", "flags?" }, 811 | var_out{ "const char *", "data", has_length = true }, 812 | var_out{ "ZMQ_Error", "err" }, 813 | c_source[[ 814 | zmq_msg_t msg; 815 | /* initialize message */ 816 | ${err} = zmq_msg_init(&msg); 817 | if(0 == ${err}) { 818 | /* receive message */ 819 | ${err} = zmq_recvmsg(${this}, &msg, ${flags}); 820 | if(${err} >= 0) { 821 | ${data} = zmq_msg_data(&msg); 822 | ${data_len} = zmq_msg_size(&msg); 823 | } 824 | } 825 | ]], 826 | c_source "post" [[ 827 | /* close message */ 828 | zmq_msg_close(&msg); 829 | ]], 830 | ffi_source[[ 831 | local msg = tmp_msg 832 | -- initialize blank message. 833 | if C.zmq_msg_init(msg) < 0 then 834 | return nil, get_zmq_strerror() 835 | end 836 | 837 | -- receive message 838 | ${err} = C.zmq_recvmsg(${this}, msg, ${flags}) 839 | if ${err} >= 0 then 840 | local data = ffi.string(C.zmq_msg_data(msg), C.zmq_msg_size(msg)) 841 | -- close message 842 | C.zmq_msg_close(msg) 843 | return data 844 | end 845 | -- close message 846 | C.zmq_msg_close(msg) 847 | ]], 848 | }, 849 | 850 | -- 851 | -- Monitor socket. 852 | -- 853 | method "monitor" { 854 | if_defs = { "VERSION_3_2", "VERSION_4_0" }, 855 | c_method_call "ZMQ_Error" "zmq_socket_monitor" { "const char *", "addr", "int", "events" } 856 | }, 857 | c_source[[ 858 | typedef struct ZMQ_recv_event { 859 | int event_id; 860 | int value; 861 | const char *addr; 862 | size_t addr_len; 863 | const char *err; 864 | } ZMQ_recv_event; 865 | 866 | #if (ZMQ_VERSION_MAJOR == 4) && (ZMQ_VERSION_MINOR >= 1) 867 | typedef struct zmq_event_t { 868 | int16_t event; 869 | int32_t value; 870 | } zmq_event_t; 871 | 872 | #endif 873 | 874 | int monitor_recv_event(ZMQ_Socket *s, zmq_msg_t *msg, int flags, ZMQ_recv_event *ev) 875 | { 876 | int rc ; 877 | zmq_event_t event; 878 | 879 | ev->event_id = 0; 880 | ev->value = 0; 881 | ev->addr = NULL; 882 | ev->err = NULL; 883 | ev->addr_len = 0; 884 | zmq_msg_init(msg); 885 | 886 | /* recv binary event. */ 887 | rc = zmq_recvmsg(s, msg, flags); 888 | if(rc < 0) { 889 | return rc; 890 | } 891 | #if ZMQ_VERSION_MAJOR == 3 892 | if(zmq_msg_size(msg) != sizeof(event)) { 893 | ev->err = "Invalid monitor event. Wrong event size."; 894 | return -1; 895 | } 896 | memcpy(&event, zmq_msg_data(msg), sizeof(event)); 897 | ev->event_id = event.event; 898 | 899 | switch(event.event) { 900 | case ZMQ_EVENT_CONNECTED: 901 | ev->value = event.data.connected.fd; 902 | ev->addr = event.data.connected.addr; 903 | break; 904 | case ZMQ_EVENT_CONNECT_DELAYED: 905 | ev->value = event.data.connect_delayed.err; 906 | ev->addr = event.data.connect_delayed.addr; 907 | break; 908 | case ZMQ_EVENT_CONNECT_RETRIED: 909 | ev->value = event.data.connect_retried.interval; 910 | ev->addr = event.data.connect_retried.addr; 911 | break; 912 | case ZMQ_EVENT_LISTENING: 913 | ev->value = event.data.listening.fd; 914 | ev->addr = event.data.listening.addr; 915 | break; 916 | case ZMQ_EVENT_BIND_FAILED: 917 | ev->value = event.data.bind_failed.err; 918 | ev->addr = event.data.bind_failed.addr; 919 | break; 920 | case ZMQ_EVENT_ACCEPTED: 921 | ev->value = event.data.accepted.fd; 922 | ev->addr = event.data.accepted.addr; 923 | break; 924 | case ZMQ_EVENT_ACCEPT_FAILED: 925 | ev->value = event.data.accept_failed.err; 926 | ev->addr = event.data.accept_failed.addr; 927 | break; 928 | case ZMQ_EVENT_CLOSED: 929 | ev->value = event.data.closed.fd; 930 | ev->addr = event.data.closed.addr; 931 | break; 932 | case ZMQ_EVENT_CLOSE_FAILED: 933 | ev->value = event.data.close_failed.err; 934 | ev->addr = event.data.close_failed.addr; 935 | break; 936 | case ZMQ_EVENT_DISCONNECTED: 937 | ev->value = event.data.disconnected.fd; 938 | ev->addr = event.data.disconnected.addr; 939 | break; 940 | } 941 | if(ev->addr) { 942 | ev->addr_len = strlen(ev->addr); 943 | } 944 | 945 | if(zmq_msg_more(msg) != 0) { 946 | ev->err = "Invalid monitor event. Has too many parts."; 947 | return -1; 948 | } 949 | #else 950 | if(zmq_msg_size(msg) != (sizeof(event.event) + sizeof(event.value))) { 951 | ev->err = "Invalid monitor event. Wrong event size."; 952 | return -1; 953 | } 954 | /* copy binary data to event struct */ 955 | const char* data = (char*)zmq_msg_data(msg); 956 | memcpy(&(event.event), data, sizeof(event.event)); 957 | memcpy(&(event.value), data+sizeof(event.event), sizeof(event.value)); 958 | ev->event_id = event.event; 959 | ev->value = event.value; 960 | 961 | if(zmq_msg_more(msg) == 0) { 962 | ev->err = "Invalid monitor event. Missing address part."; 963 | return -1; 964 | } 965 | ev->value = event.value; 966 | 967 | /* recv address part */ 968 | rc = zmq_recvmsg(s, msg, flags); 969 | if(rc < 0) { 970 | return rc; 971 | } 972 | if(zmq_msg_more(msg) != 0) { 973 | ev->err = "Invalid monitor event. Has too many parts."; 974 | return -1; 975 | } 976 | /* copy address part */ 977 | ev->addr_len = zmq_msg_size(msg) ; 978 | ev->addr = zmq_msg_data(msg); 979 | #endif 980 | 981 | return 1; 982 | } 983 | 984 | ]], 985 | ffi_cdef[[ 986 | typedef struct ZMQ_recv_event { 987 | int event_id; 988 | int value; 989 | const char *addr; 990 | size_t addr_len; 991 | const char *err; 992 | } ZMQ_recv_event; 993 | 994 | int monitor_recv_event(ZMQ_Socket *s, zmq_msg_t *msg, int flags, ZMQ_recv_event *ev); 995 | ]], 996 | ffi_source[[ 997 | local tmp_recv_event = ffi.new('ZMQ_recv_event') 998 | ]], 999 | method "recv_event" { 1000 | if_defs = { "VERSION_3_2", "VERSION_4_0" }, 1001 | var_in{ "int", "flags?" }, 1002 | var_out{ "int", "event_id" }, 1003 | var_out{ "int", "value" }, 1004 | var_out{ "const char *", "addr", has_length = true }, 1005 | var_out{ "ZMQ_Error", "err" }, 1006 | c_source[[ 1007 | zmq_msg_t msg; 1008 | ZMQ_recv_event event; 1009 | 1010 | /* receive monitor event */ 1011 | ${err} = monitor_recv_event(${this}, &msg, ${flags}, &event); 1012 | if(${err} >= 0) { 1013 | ${event_id} = event.event_id; 1014 | ${value} = event.value; 1015 | ${addr} = event.addr; 1016 | ${addr_len} = event.addr_len; //${err}; 1017 | } else if(event.err != NULL) { 1018 | /* error parsing monitor event. */ 1019 | lua_pushnil(L); 1020 | lua_pushstring(L, event.err); 1021 | return 2; 1022 | } 1023 | ]], 1024 | c_source "post" [[ 1025 | /* close message */ 1026 | zmq_msg_close(&msg); 1027 | ]], 1028 | ffi_source[[ 1029 | local msg = tmp_msg 1030 | local event = tmp_recv_event 1031 | local addr 1032 | 1033 | -- receive monitor event 1034 | ${err} = Cmod.monitor_recv_event(${this}, msg, ${flags}, event) 1035 | if ${err} >= 0 then 1036 | addr = ffi.string(event.addr, event.addr_len) 1037 | -- close message 1038 | C.zmq_msg_close(msg) 1039 | return event.event_id, event.value, addr 1040 | end 1041 | -- close message 1042 | C.zmq_msg_close(msg) 1043 | if event.err ~= nil then 1044 | -- error parsing monitor event. 1045 | return nil, ffi.string(event.err) 1046 | end 1047 | ]], 1048 | }, 1049 | 1050 | -- build option set/get methods. THIS MUST BE LAST. 1051 | build_option_methods(), 1052 | } 1053 | 1054 | --------------------------------------------------------------------------------