rbzmq 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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