rbzmq 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,41 @@
1
+ module RbZMQ
2
+ #
3
+ # = RbZMQ::Message
4
+ #
5
+ class Message
6
+ #
7
+ attr_reader :data
8
+
9
+ def initialize(str = '')
10
+ if str.is_a?(ZMQ::Message)
11
+ @data = str.copy_out_string
12
+ str.close
13
+ else
14
+ @data = str.to_s
15
+ end
16
+ end
17
+
18
+ def to_s
19
+ data
20
+ end
21
+
22
+ def to_zmq
23
+ ZMQ::Message.new(data)
24
+ end
25
+
26
+ class << self
27
+ #
28
+ # Create new {RbZMQ::Message}.
29
+ #
30
+ # If first argument is a {RbZMQ::Message} object it will
31
+ # be returned instead of a new one.
32
+ #
33
+ # @return [RbZMQ::Message] Newly created message.
34
+ #
35
+ def new(*args)
36
+ return args[0] if args[0].is_a?(self)
37
+ super
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,198 @@
1
+ module RbZMQ
2
+ #
3
+ # = RbZMQ::Poller
4
+ #
5
+ # The {Poller} allows to poll on one or more ZMQ sockets
6
+ # or file descriptors simultaneously.
7
+ #
8
+ # @example
9
+ # poller = RbZMQ::Poller.new
10
+ # poller.register(socket, ZMQ::POLLIN)
11
+ # poller.poll(10_000) do |socket|
12
+ # # Do something with socket
13
+ # end
14
+ #
15
+ class Poller
16
+ #
17
+ # Create a new poller.
18
+ #
19
+ def initialize
20
+ @poll_items = ZMQ::PollItems.new
21
+ @mutex = Mutex.new
22
+ end
23
+
24
+ # Poll on all registered objects.
25
+ #
26
+ # If a block is given it will be invoked for each ready
27
+ # pollable object. Without a block an enumerator of
28
+ # ready pollables will be returned.
29
+ #
30
+ # If not selectable is registered {#poll} will return
31
+ # without blocking.
32
+ #
33
+ # @example Poll with block
34
+ # poller.poll(10_000) do |io|
35
+ # io.readable? || io.writable? #=> true
36
+ # end
37
+ #
38
+ # @param timeout [Integer, Symbol] A timeout in milliseconds.
39
+ # The values `-1`, `:blocking` and `:infinity` will
40
+ # block indefinitely.
41
+ #
42
+ # @yield [pollable] Yield each ready object.
43
+ # @yieldparam pollable [RbZMQ::Socket, IO, Object] Registered
44
+ # pollable object.
45
+ #
46
+ # @return [Enumerator, Boolean, Nil] The return value is
47
+ # determined by the following rules:
48
+ # 1. Nil is returned when no objects are registered.
49
+ # 2. An Enumerator will be returned when no block
50
+ # is given. The enumerator will have no elements if
51
+ # call timed out.
52
+ # 3. If a block is given true will be returned when
53
+ # objects were ready, false if times out.
54
+ #
55
+ def poll(timeout, &block)
56
+ mutex.synchronize do
57
+ if @poll_items.any?
58
+ ready_items = do_poll(convert_timeout(timeout))
59
+
60
+ if block_given?
61
+ ready_items > 0 ? each_ready_item(&block) : false
62
+ else
63
+ if ready_items > 0
64
+ to_enum(:each_ready_item)
65
+ else
66
+ Array.new.to_enum(:each)
67
+ end
68
+ end
69
+ else
70
+ nil
71
+ end
72
+ end
73
+ end
74
+
75
+ # Return number of registered pollables.
76
+ #
77
+ # @return [Integer] Number of registered objects.
78
+ #
79
+ def size
80
+ mutex.synchronize { @poll_items.size }
81
+ end
82
+
83
+ # Register given socket or IO to be watched on given
84
+ # event list.
85
+ #
86
+ # This method is idempotent.
87
+ #
88
+ # @example Watch socket to read
89
+ # socket = RbZMQ::Socket.new(ZMQ::DEALER)
90
+ # poller.register(socket, ZMQ::POLLIN)
91
+ #
92
+ # @example Watch IO to write
93
+ # reader, writer = IO.pipe
94
+ # poller.register(writer, ZMQ::POLLOUT)
95
+ #
96
+ # @param pollable [RbZMQ::Socket, IO] Watchable socket or
97
+ # IO object.
98
+ #
99
+ # @param events [Integer] ZMQ events. Calling multiple
100
+ # times with different events will OR the events together.
101
+ # Allowed values are ZMQ::POLLIN and ZMQ::POLLOUT.
102
+ #
103
+ # @return [Integer] Registered events for pollable.
104
+ #
105
+ def register(pollable, events = ZMQ::POLLIN)
106
+ return if pollable.nil? || events.zero?
107
+
108
+ mutex.synchronize do
109
+ item = @poll_items[pollable]
110
+ unless item
111
+ item = ::ZMQ::PollItem.from_pollable(pollable)
112
+ @poll_items << item
113
+ end
114
+
115
+ item.events |= events
116
+ end
117
+ end
118
+
119
+ # Deregister events from pollable.
120
+ #
121
+ # When no events are left or socket or IO object has been
122
+ # closed it will also be remove from watched objects.
123
+ #
124
+ # @param pollable [RbZMQ::Socket, IO] Watchable socket
125
+ # or IO object.
126
+ #
127
+ # @param events [Integer] ZMQ events.
128
+ # Allowed values are ZMQ::POLLIN and ZMQ::POLLOUT.
129
+ #
130
+ # @return [Boolean] False if pollable was removed
131
+ # because all events where removed or it was closed,
132
+ # nil if pollable was not registered or an Integer
133
+ # with the leaving events.
134
+ #
135
+ def deregister(pollable, events = ZMQ::POLLIN | ZMQ::POLLOUT)
136
+ return unless pollable
137
+
138
+ mutex.synchronize do
139
+ item = @poll_items[pollable]
140
+ if item && (item.events & events) > 0
141
+ item.events ^= events
142
+
143
+ if item.events.zero? || item.closed?
144
+ @poll_items.delete pollable
145
+ false
146
+ else
147
+ item.events
148
+ end
149
+ else
150
+ nil
151
+ end
152
+ end
153
+ end
154
+
155
+ # Remove socket or IO object from poller.
156
+ #
157
+ # @param pollable [RbZMQ::Socket, IO] Watched object to remove.
158
+ #
159
+ # @return [Boolean] True if pollable was successfully
160
+ # removed, false otherwise.
161
+ #
162
+ def delete(pollable)
163
+ mutex.synchronize do
164
+ return false if @poll_items.empty?
165
+
166
+ @poll_items.delete pollable
167
+ end
168
+ end
169
+
170
+ private
171
+
172
+ attr_reader :mutex
173
+
174
+ def do_poll(timeout)
175
+ rc = LibZMQ.zmq_poll @poll_items.address,
176
+ @poll_items.size,
177
+ timeout
178
+ RbZMQ::ZMQError.error! rc
179
+ end
180
+
181
+ def each_ready_item(&block)
182
+ @poll_items.each do |item|
183
+ yield item.pollable if item.readable? || item.writable?
184
+ end
185
+
186
+ true
187
+ end
188
+
189
+ def convert_timeout(timeout)
190
+ case timeout
191
+ when :blocking, :infinity, -1
192
+ -1
193
+ else
194
+ Integer timeout
195
+ end
196
+ end
197
+ end
198
+ end
@@ -0,0 +1,287 @@
1
+ module RbZMQ
2
+ #
3
+ # = RbZMQ::Socket
4
+ #
5
+ class Socket
6
+ #
7
+ # Default timeout.
8
+ #
9
+ DEFAULT_TIMEOUT = 5_000
10
+
11
+ # @!visibility private
12
+ #
13
+ # Internal ZMQ::Context reference.
14
+ #
15
+ attr_reader :zmq_ctx
16
+
17
+ # @!visibility private
18
+ #
19
+ # Internal ZMQ::Socket reference.
20
+ #
21
+ attr_reader :zmq_socket
22
+
23
+ # Allocates a socket of given type for sending and receiving data.
24
+ #
25
+ # @param type [Integer] ZMQ socket type, on if ZMQ::REQ, ZMQ::REP,
26
+ # ZMQ::PUB, ZMQ::SUB, ZMQ::PAIR, ZMQ::PULL, ZMQ::PUSH,
27
+ # ZMQ::XREQ, ZMQ::REP, ZMQ::DEALER or ZMQ::ROUTER.
28
+ #
29
+ # @param opts [Hash] Option hash. :ctx will be removed, all other
30
+ # options will be passed to ZMQ::Socket.new.
31
+ #
32
+ # @option opts [Context] :ctx ZMQ context used to initialize socket.
33
+ # By default {Context.global} is used. Must be {RbZMQ::Context},
34
+ # ZMQ::Context or an FFI::Pointer.
35
+ #
36
+ # @raise [ZMQError] On error.
37
+ #
38
+ # @return [Socket] Created socket object.
39
+ #
40
+ def initialize(type, opts = {})
41
+ ctx = opts.fetch(:ctx) { RbZMQ::Context.global }
42
+ ctx = ctx.pointer if ctx.respond_to? :pointer
43
+
44
+ unless ctx.is_a?(FFI::Pointer)
45
+ raise ArgumentError.new "Context must be ZMQ::Context or " \
46
+ "RbZMQ::Context (respond to #pointer) or must be a FFI::Pointer, "\
47
+ "but #{ctx.inspect} given."
48
+ end
49
+
50
+ @zmq_ctx = ctx
51
+ @zmq_socket = ZMQ::Socket.new ctx, type
52
+ rescue ZMQ::ZeroMQError => err
53
+ raise ZMQError.new err
54
+ end
55
+
56
+ # Return ZMQ socket pointer. Required interface for ZMQ::Poller.
57
+ #
58
+ def socket
59
+ @zmq_socket.socket
60
+ end
61
+
62
+ # Bind this socket to given address.
63
+ #
64
+ # @example
65
+ # socket = RbZMQ::Socket.new ZMQ::PUB
66
+ # socket.bind "tcp://127.0.0.1:5555"
67
+ #
68
+ # @param address [String] Address to bind. Must be a supported protocol.
69
+ #
70
+ # @raise [ZMQError] On error.
71
+ #
72
+ # @return [RbZMQ::Socket] Self.
73
+ #
74
+ def bind(address)
75
+ ZMQError.error! zmq_socket.bind address
76
+ self
77
+ end
78
+
79
+ # Connect to given address.
80
+ #
81
+ # @example Bind to single remote address
82
+ # socket = RbZMQ::Socket.new ZMQ::PUSH
83
+ # socket.connect "tcp://127.0.0.1:5555"
84
+ #
85
+ # @example Bind to multiple endpoints
86
+ # socket = RbZMQ::Socket.new ZMQ::ROUTER
87
+ # socket.connect "tcp://127.0.0.1:5555"
88
+ # socket.connect "tcp://127.0.0.1:6666"
89
+ #
90
+ # @raise [ZMQError] On error.
91
+ #
92
+ # @return [RbZMQ::Socket] Self.
93
+ #
94
+ def connect(address)
95
+ ZMQError.error! zmq_socket.connect address
96
+ self
97
+ end
98
+
99
+ # Closes the socket. Any unprocessed messages in queue are sent or dropped
100
+ # depending upon the value of the socket option ZMQ::LINGER.
101
+ #
102
+ # @example
103
+ # socket = RbZMQ::Socket.new ZMQ::PULL
104
+ # socket.close
105
+ #
106
+ # @return [Boolean] Return true upon success *or* when the socket has
107
+ # already been closed, false otherwise. Use {#close!} to raise an error
108
+ # on failure.
109
+ #
110
+ def close
111
+ ZMQError.ok? zmq_socket.close
112
+ end
113
+
114
+ # Closes the socket. Any unprocessed messages in queue are sent or dropped
115
+ # depending upon the value of the socket option ZMQ::LINGER.
116
+ #
117
+ # @example
118
+ # socket = RbZMQ::Socket.new ZMQ::PULL
119
+ # socket.close!
120
+ #
121
+ # @raise [ZMQError] Error raised on failure.
122
+ #
123
+ # @return [Boolean] True.
124
+ #
125
+ def close!
126
+ ZMQError.error! zmq_socket.close
127
+ true
128
+ end
129
+
130
+ # Set a ZMQ socket object.
131
+ #
132
+ # @return [Boolean] True if success, false otherwise.
133
+ #
134
+ # @see zmq_setsockopt
135
+ #
136
+ def setsockopt(opt, val)
137
+ ZMQError.ok? zmq_socket.setsockopt(opt, val)
138
+ end
139
+
140
+ # Queues one or more messages for transmission.
141
+ #
142
+ # @example Send single message or string
143
+ # begin
144
+ # message = RbZMQ::Message.new
145
+ # socket.send message
146
+ # rescue RbZMQ::ZMQError => err
147
+ # puts 'Send failed.'
148
+ # end
149
+ #
150
+ # @example Send multiple messages
151
+ # socket.send ["A", "B", "C 2"]
152
+ #
153
+ # @param messages [RbZMQ::Message, String, #each] A {RbZMQ::Message} or
154
+ # string message to send, or a list of messages responding to `#each`.
155
+ #
156
+ # @param flags [Integer] May contains of the following flags:
157
+ # * 0 (default) - blocking operation
158
+ # * ZMQ::DONTWAIT - non-blocking operation
159
+ # * ZMQ::SNDMORE - this message or all messages
160
+ # are part of a multi-part message
161
+ #
162
+ # @param opts [Hash] Options.
163
+ #
164
+ # @option opts [Boolean] :block If method call should block. Will set
165
+ # ZMQ::DONTWAIT flag if false. Defaults to true.
166
+ #
167
+ # @option opts [Boolean] :more If this message or all messages
168
+ # are part of a multi-part message
169
+ #
170
+ # @raise [ZMQError] Raises an error under two conditions:
171
+ # 1. The message(s) could not be enqueued
172
+ # 2. When flags is set with ZMQ::DONTWAIT and the socket
173
+ # returned EAGAIN.
174
+ #
175
+ # @return [RbZMQ::Socket] Self.
176
+ #
177
+ def send(messages, flags = 0, opts = {})
178
+ opts, flags = flags, 0 if flags.is_a?(Hash)
179
+ flags = convert_flags(opts, flags)
180
+
181
+ if messages.respond_to?(:each)
182
+ send_multiple(messages, flags)
183
+ else
184
+ send_single(messages, flags)
185
+ end
186
+
187
+ self
188
+ end
189
+
190
+ # Dequeues a message from the underlying queue.
191
+ #
192
+ # By default, this is a blocking operation.
193
+ #
194
+ # @example
195
+ # message = socket.recv
196
+ #
197
+ # @param flags [Integer] Can be ZMQ::DONTWAIT.
198
+ #
199
+ # @param opts [Hash] Options.
200
+ #
201
+ # @option opts [Boolean] :block If false operation will be non-blocking.
202
+ # Defaults to true.
203
+ #
204
+ # @option opts [Integer] :timeout Raise a EAGAIN error if nothing was
205
+ # received within given amount of milliseconds. Defaults
206
+ # to {DEFAULT_TIMEOUT}. The values `:blocking`, `:infinity`
207
+ # or `-1` will wait forever.
208
+ #
209
+ # @raise [ZMQError] Raise error under two conditions.
210
+ # 1. The message could not be dequeued
211
+ # 2. When mode is non-blocking and the socket returned EAGAIN.
212
+ #
213
+ # @raise [Errno::EAGAIN] When timeout was reached without receiving
214
+ # a message.
215
+ #
216
+ # @return [RbZMQ::Message] Received message.
217
+ #
218
+ def recv(flags = 0, opts = {})
219
+ opts, flags = flags, 0 if flags.is_a?(Hash)
220
+
221
+ with_recv_timeout(opts) do
222
+ rc = zmq_socket.recvmsg((message = ZMQ::Message.new),
223
+ convert_flags(opts, flags, [:block]))
224
+ ZMQError.error! rc
225
+ RbZMQ::Message.new(message)
226
+ end
227
+ end
228
+
229
+ private
230
+
231
+ def send_multiple(messages, flags)
232
+ flgs = flags | ZMQ::SNDMORE
233
+ last = messages.to_enum(:each).reduce(nil) do |memo, msg|
234
+ send_single(memo, flgs) if memo
235
+ RbZMQ::Message.new(msg)
236
+ end
237
+
238
+ send_single(last, flags) if last
239
+ end
240
+
241
+ def send_single(message, flags)
242
+ zmqmsg = RbZMQ::Message.new(message).to_zmq
243
+ ZMQError.error! zmq_socket.sendmsg(zmqmsg, flags)
244
+ end
245
+
246
+ # Convert option hash to ZMQ flag list
247
+ # * :block (! DONTWAIT) defaults to true
248
+ # * :more (SNDMORE) defaults to false
249
+ def convert_flags(opts, flags = 0, allowed = [:block, :more])
250
+ if !opts.fetch(:block, true) && allowed.include?(:block)
251
+ flags |= ZMQ::DONTWAIT
252
+ end
253
+ if opts.fetch(:more, false) && allowed.include?(:more)
254
+ flags |= ZMQ::SNDMORE
255
+ end
256
+
257
+ flags
258
+ end
259
+
260
+ def poll
261
+ @poll ||= RbZMQ::Poller.new.tap do |poll|
262
+ poll.register @zmq_socket, ZMQ::POLLIN
263
+ end
264
+ end
265
+
266
+ # RECV timeout using ZMQ::POLLER
267
+ def with_recv_timeout(opts)
268
+ timeout = parse_timeout opts[:timeout]
269
+
270
+ unless poll.poll(timeout){ return yield }
271
+ raise Errno::EAGAIN.new "ZMQ socket did not receive anything " \
272
+ "within #{timeout}ms."
273
+ end
274
+ end
275
+
276
+ def parse_timeout(timeout)
277
+ case timeout
278
+ when :blocking, :infinity
279
+ -1
280
+ when nil
281
+ DEFAULT_TIMEOUT
282
+ else
283
+ Integer(timeout)
284
+ end
285
+ end
286
+ end
287
+ end