ffi-rzmq 0.5.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.
- data/History.txt +92 -0
- data/README.rdoc +162 -0
- data/Rakefile +19 -0
- data/examples/local_lat.rb +43 -0
- data/examples/local_lat_zerocopy.rb +24 -0
- data/examples/publish_subscribe.rb +52 -0
- data/examples/remote_lat.rb +53 -0
- data/examples/remote_lat_zerocopy.rb +35 -0
- data/examples/reqrep_poll.rb +49 -0
- data/examples/request_response.rb +23 -0
- data/ffi-rzmq.gemspec +43 -0
- data/lib/ffi-rzmq.rb +71 -0
- data/lib/ffi-rzmq/context.rb +99 -0
- data/lib/ffi-rzmq/exceptions.rb +145 -0
- data/lib/ffi-rzmq/message.rb +210 -0
- data/lib/ffi-rzmq/poll.rb +186 -0
- data/lib/ffi-rzmq/poll_items.rb +98 -0
- data/lib/ffi-rzmq/socket.rb +344 -0
- data/lib/ffi-rzmq/wrapper.rb +120 -0
- data/lib/ffi-rzmq/zmq.rb +147 -0
- data/spec/context_spec.rb +96 -0
- data/spec/reqrep_spec.rb +56 -0
- data/spec/socket_spec.rb +111 -0
- data/spec/spec_helper.rb +38 -0
- data/version.txt +1 -0
- metadata +113 -0
@@ -0,0 +1,344 @@
|
|
1
|
+
|
2
|
+
module ZMQ
|
3
|
+
|
4
|
+
ZMQ_SOCKET_STR = 'zmq_socket'.freeze unless defined? ZMQ_SOCKET_STR
|
5
|
+
ZMQ_SETSOCKOPT_STR = 'zmq_setsockopt'.freeze
|
6
|
+
ZMQ_GETSOCKOPT_STR = 'zmq_getsockopt'.freeze
|
7
|
+
ZMQ_BIND_STR = 'zmq_bind'.freeze
|
8
|
+
ZMQ_CONNECT_STR = 'zmq_connect'.freeze
|
9
|
+
ZMQ_CLOSE_STR = 'zmq_close'.freeze
|
10
|
+
ZMQ_SEND_STR = 'zmq_send'.freeze
|
11
|
+
ZMQ_RECV_STR = 'zmq_recv'.freeze
|
12
|
+
|
13
|
+
class Socket
|
14
|
+
include ZMQ::Util
|
15
|
+
|
16
|
+
attr_reader :socket
|
17
|
+
|
18
|
+
# By default, this class uses ZMQ::Message for regular Ruby
|
19
|
+
# memory management.
|
20
|
+
#
|
21
|
+
# +type+ can be one of ZMQ::REQ, ZMQ::REP, ZMQ::PUB,
|
22
|
+
# ZMQ::SUB, ZMQ::PAIR, ZMQ::UPSTREAM, ZMQ::DOWNSTREAM,
|
23
|
+
# ZMQ::XREQ or ZMQ::XREP.
|
24
|
+
#
|
25
|
+
# Can raise two kinds of exceptions depending on the error.
|
26
|
+
# ContextError:: Raised when a socket operation is attempted on a terminated
|
27
|
+
# #Context. See #ContextError.
|
28
|
+
# SocketError:: See all of the possibilities in the docs for #SocketError.
|
29
|
+
#
|
30
|
+
def initialize context_ptr, type
|
31
|
+
# maybe at some point we'll want to allow users to override this with their
|
32
|
+
# own classes? Or is this a YAGNI mistake?
|
33
|
+
@sender_klass = ZMQ::Message
|
34
|
+
@receiver_klass = ZMQ::Message
|
35
|
+
|
36
|
+
unless context_ptr.null?
|
37
|
+
@socket = LibZMQ.zmq_socket context_ptr, type
|
38
|
+
error_check ZMQ_SOCKET_STR, @socket.null? ? 1 : 0
|
39
|
+
else
|
40
|
+
raise ContextError.new ZMQ_SOCKET_STR, 0, ETERM, "Context pointer was null"
|
41
|
+
end
|
42
|
+
|
43
|
+
define_finalizer
|
44
|
+
end
|
45
|
+
|
46
|
+
# Set the queue options on this socket.
|
47
|
+
#
|
48
|
+
# Valid +option_name+ values that take a numeric +option_value+ are:
|
49
|
+
# ZMQ::HWM
|
50
|
+
# ZMQ::SWAP
|
51
|
+
# ZMQ::AFFINITY
|
52
|
+
# ZMQ::RATE
|
53
|
+
# ZMQ::RECOVERY_IVL
|
54
|
+
# ZMQ::MCAST_LOOP
|
55
|
+
#
|
56
|
+
# Valid +option_name+ values that take a string +option_value+ are:
|
57
|
+
# ZMQ::IDENTITY
|
58
|
+
# ZMQ::SUBSCRIBE
|
59
|
+
# ZMQ::UNSUBSCRIBE
|
60
|
+
#
|
61
|
+
# Can raise two kinds of exceptions depending on the error.
|
62
|
+
# ContextError:: Raised when a socket operation is attempted on a terminated
|
63
|
+
# #Context. See #ContextError.
|
64
|
+
# SocketError:: See all of the possibilities in the docs for #SocketError.
|
65
|
+
#
|
66
|
+
def setsockopt option_name, option_value, option_len = nil
|
67
|
+
begin
|
68
|
+
case option_name
|
69
|
+
when HWM, SWAP, AFFINITY, RATE, RECOVERY_IVL, MCAST_LOOP
|
70
|
+
option_value_ptr = LibC.malloc option_value.size
|
71
|
+
option_value_ptr.write_long option_value
|
72
|
+
|
73
|
+
when IDENTITY, SUBSCRIBE, UNSUBSCRIBE
|
74
|
+
# note: not checking errno for failed memory allocations :(
|
75
|
+
option_value_ptr = LibC.malloc option_value.size
|
76
|
+
option_value_ptr.write_string option_value
|
77
|
+
|
78
|
+
else
|
79
|
+
# we didn't understand the passed option argument
|
80
|
+
# will force a raise due to EINVAL being non-zero
|
81
|
+
error_check ZMQ_SETSOCKOPT_STR, EINVAL
|
82
|
+
end
|
83
|
+
|
84
|
+
result_code = LibZMQ.zmq_setsockopt @socket, option_name, option_value_ptr, option_len || option_value.size
|
85
|
+
error_check ZMQ_SETSOCKOPT_STR, result_code
|
86
|
+
ensure
|
87
|
+
LibC.free option_value_ptr unless option_value_ptr.nil? || option_value_ptr.null?
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
# Get the options set on this socket. Returns a value dependent upon
|
92
|
+
# the +option_name+ requested.
|
93
|
+
#
|
94
|
+
# Valid +option_name+ values and their return types:
|
95
|
+
# ZMQ::RCVMORE - boolean
|
96
|
+
# ZMQ::HWM - integer
|
97
|
+
# ZMQ::SWAP - integer
|
98
|
+
# ZMQ::AFFINITY - bitmap in an integer
|
99
|
+
# ZMQ::IDENTITY - string
|
100
|
+
# ZMQ::RATE - integer
|
101
|
+
# ZMQ::RECOVERY_IVL - integer
|
102
|
+
# ZMQ::MCAST_LOOP - boolean
|
103
|
+
# ZMQ::SNDBUF - integer
|
104
|
+
# ZMQ::RCVBUF - integer
|
105
|
+
#
|
106
|
+
# Can raise two kinds of exceptions depending on the error.
|
107
|
+
# ContextError:: Raised when a socket operation is attempted on a terminated
|
108
|
+
# #Context. See #ContextError.
|
109
|
+
# SocketError:: See all of the possibilities in the docs for #SocketError.
|
110
|
+
#
|
111
|
+
def getsockopt option_name
|
112
|
+
begin
|
113
|
+
option_value = FFI::MemoryPointer.new :pointer
|
114
|
+
option_length = FFI::MemoryPointer.new :size_t
|
115
|
+
|
116
|
+
unless [RCVMORE, HWM, SWAP, AFFINITY, RATE, RECOVERY_IVL, MCAST_LOOP,
|
117
|
+
IDENTITY, SNDBUF, RCVBUF].include? option_name
|
118
|
+
# we didn't understand the passed option argument
|
119
|
+
# will force a raise
|
120
|
+
error_check ZMQ_SETSOCKOPT_STR, -1
|
121
|
+
end
|
122
|
+
|
123
|
+
option_value, option_length = alloc_temp_sockopt_buffers option_name
|
124
|
+
|
125
|
+
result_code = LibZMQ.zmq_getsockopt @socket, option_name, option_value, option_length
|
126
|
+
error_check ZMQ_GETSOCKOPT_STR, result_code
|
127
|
+
ret = 0
|
128
|
+
|
129
|
+
case option_name
|
130
|
+
when RCVMORE, MCAST_LOOP
|
131
|
+
# boolean return
|
132
|
+
ret = option_value.read_long_long != 0
|
133
|
+
when HWM, SWAP, AFFINITY, RATE, RECOVERY_IVL, SNDBUF, RCVBUF
|
134
|
+
ret = option_value.read_long_long
|
135
|
+
when IDENTITY
|
136
|
+
ret = option_value.read_string(option_length.read_long_long)
|
137
|
+
end
|
138
|
+
|
139
|
+
ret
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
# Convenience method for checking on additional message parts.
|
144
|
+
#
|
145
|
+
# Equivalent to Socket#getsockopt ZMQ::RCVMORE
|
146
|
+
#
|
147
|
+
def more_parts?
|
148
|
+
getsockopt ZMQ::RCVMORE
|
149
|
+
end
|
150
|
+
|
151
|
+
# Convenience method for getting the value of the socket IDENTITY.
|
152
|
+
#
|
153
|
+
def identity
|
154
|
+
getsockopt ZMQ::IDENTITY
|
155
|
+
end
|
156
|
+
|
157
|
+
# Convenience method for setting the value of the socket IDENTITY.
|
158
|
+
#
|
159
|
+
def identity= value
|
160
|
+
setsockopt ZMQ::IDENTITY, value.to_s
|
161
|
+
end
|
162
|
+
|
163
|
+
# Can raise two kinds of exceptions depending on the error.
|
164
|
+
# ContextError:: Raised when a socket operation is attempted on a terminated
|
165
|
+
# #Context. See #ContextError.
|
166
|
+
# SocketError:: See all of the possibilities in the docs for #SocketError.
|
167
|
+
#
|
168
|
+
def bind address
|
169
|
+
result_code = LibZMQ.zmq_bind @socket, address
|
170
|
+
error_check ZMQ_BIND_STR, result_code
|
171
|
+
end
|
172
|
+
|
173
|
+
# Can raise two kinds of exceptions depending on the error.
|
174
|
+
# ContextError:: Raised when a socket operation is attempted on a terminated
|
175
|
+
# #Context. See #ContextError.
|
176
|
+
# SocketError:: See all of the possibilities in the docs for #SocketError.
|
177
|
+
#
|
178
|
+
def connect address
|
179
|
+
result_code = LibZMQ.zmq_connect @socket, address
|
180
|
+
error_check ZMQ_CONNECT_STR, result_code
|
181
|
+
end
|
182
|
+
|
183
|
+
# Closes the socket. Any unprocessed messages in queue are dropped.
|
184
|
+
#
|
185
|
+
def close
|
186
|
+
remove_finalizer
|
187
|
+
result_code = LibZMQ.zmq_close @socket
|
188
|
+
error_check ZMQ_CLOSE_STR, result_code
|
189
|
+
end
|
190
|
+
|
191
|
+
# Queues the message for transmission. Message is assumed to be an instance or
|
192
|
+
# subclass of #Message.
|
193
|
+
#
|
194
|
+
# +flags+ may take two values:
|
195
|
+
# * 0 (default) - blocking operation
|
196
|
+
# * ZMQ::NOBLOCK - non-blocking operation
|
197
|
+
# * ZMQ::SNDMORE - this message is part of a multi-part message
|
198
|
+
#
|
199
|
+
# Returns true when the message was successfully enqueued.
|
200
|
+
# Returns false when the message could not be enqueued *and* +flags+ is set
|
201
|
+
# with ZMQ::NOBLOCK.
|
202
|
+
#
|
203
|
+
# The application code is *not* responsible for handling the +message+ object
|
204
|
+
# lifecycle when #send return ZMQ::NOBLOCK or it raises an exception. The
|
205
|
+
# #send method takes ownership of the +message+ and its associated buffers.
|
206
|
+
# A failed call will release the +message+ data buffer.
|
207
|
+
#
|
208
|
+
# Again, once a +message+ object has been passed to this method,
|
209
|
+
# do not try to access its #data buffer anymore. The 0mq library now owns it.
|
210
|
+
#
|
211
|
+
# Can raise two kinds of exceptions depending on the error.
|
212
|
+
# ContextError:: Raised when a socket operation is attempted on a terminated
|
213
|
+
# #Context. See #ContextError.
|
214
|
+
# SocketError:: See all of the possibilities in the docs for #SocketError.
|
215
|
+
#
|
216
|
+
def send message, flags = 0
|
217
|
+
begin
|
218
|
+
result_code = LibZMQ.zmq_send @socket, message.address, flags
|
219
|
+
|
220
|
+
# when the flag isn't set, do a normal error check
|
221
|
+
# when set, check to see if the message was successfully queued
|
222
|
+
queued = flags != NOBLOCK ? error_check(ZMQ_SEND_STR, result_code) : error_check_nonblock(result_code)
|
223
|
+
ensure
|
224
|
+
message.close
|
225
|
+
end
|
226
|
+
|
227
|
+
# true if sent, false if failed/EAGAIN
|
228
|
+
queued
|
229
|
+
end
|
230
|
+
|
231
|
+
# Helper method to make a new #Message instance out of the +message_string+ passed
|
232
|
+
# in for transmission.
|
233
|
+
#
|
234
|
+
# +flags+ may be ZMQ::NOBLOCK.
|
235
|
+
#
|
236
|
+
# Can raise two kinds of exceptions depending on the error.
|
237
|
+
# ContextError:: Raised when a socket operation is attempted on a terminated
|
238
|
+
# #Context. See #ContextError.
|
239
|
+
# SocketError:: See all of the possibilities in the docs for #SocketError.
|
240
|
+
#
|
241
|
+
def send_string message_string, flags = 0
|
242
|
+
message = @sender_klass.new
|
243
|
+
message.copy_in_string message_string
|
244
|
+
result = send message, flags
|
245
|
+
result
|
246
|
+
end
|
247
|
+
|
248
|
+
# Dequeues a message from the underlying queue. By default, this is a blocking operation.
|
249
|
+
#
|
250
|
+
# +flags+ may take two values:
|
251
|
+
# 0 (default) - blocking operation
|
252
|
+
# ZMQ::NOBLOCK - non-blocking operation
|
253
|
+
#
|
254
|
+
# Returns a true when it successfully dequeues one from the queue. Also, the +message+
|
255
|
+
# object is populated by the library with a data buffer containing the received
|
256
|
+
# data.
|
257
|
+
#
|
258
|
+
# Returns nil when a message could not be dequeued *and* +flags+ is set
|
259
|
+
# with ZMQ::NOBLOCK. The +message+ object is not modified in this situation.
|
260
|
+
#
|
261
|
+
# The application code is *not* responsible for handling the +message+ object lifecycle
|
262
|
+
# when #recv raises an exception. The #recv method takes ownership of the
|
263
|
+
# +message+ and its associated buffers. A failed call will
|
264
|
+
# release the data buffers assigned to the +message+.
|
265
|
+
#
|
266
|
+
# Can raise two kinds of exceptions depending on the error.
|
267
|
+
# ContextError:: Raised when a socket operation is attempted on a terminated
|
268
|
+
# #Context. See #ContextError.
|
269
|
+
# SocketError:: See all of the possibilities in the docs for #SocketError.
|
270
|
+
#
|
271
|
+
def recv message, flags = 0
|
272
|
+
begin
|
273
|
+
dequeued = _recv message, flags
|
274
|
+
rescue ZeroMQError
|
275
|
+
message.close
|
276
|
+
raise
|
277
|
+
end
|
278
|
+
|
279
|
+
dequeued ? true : nil
|
280
|
+
end
|
281
|
+
|
282
|
+
# Helper method to make a new #Message instance and convert its payload
|
283
|
+
# to a string.
|
284
|
+
#
|
285
|
+
# +flags+ may be ZMQ::NOBLOCK.
|
286
|
+
#
|
287
|
+
# Can raise two kinds of exceptions depending on the error.
|
288
|
+
# ContextError:: Raised when a socket operation is attempted on a terminated
|
289
|
+
# #Context. See #ContextError.
|
290
|
+
# SocketError:: See all of the possibilities in the docs for #SocketError.
|
291
|
+
#
|
292
|
+
def recv_string flags = 0
|
293
|
+
message = @receiver_klass.new
|
294
|
+
|
295
|
+
begin
|
296
|
+
dequeued = _recv message, flags
|
297
|
+
|
298
|
+
if dequeued
|
299
|
+
message.copy_out_string
|
300
|
+
else
|
301
|
+
nil
|
302
|
+
end
|
303
|
+
ensure
|
304
|
+
message.close
|
305
|
+
end
|
306
|
+
end
|
307
|
+
|
308
|
+
private
|
309
|
+
|
310
|
+
def _recv message, flags = 0
|
311
|
+
result_code = LibZMQ.zmq_recv @socket, message.address, flags
|
312
|
+
|
313
|
+
flags != NOBLOCK ? error_check(ZMQ_RECV_STR, result_code) : error_check_nonblock(result_code)
|
314
|
+
end
|
315
|
+
|
316
|
+
def alloc_temp_sockopt_buffers option_name
|
317
|
+
length = FFI::MemoryPointer.new :int64
|
318
|
+
|
319
|
+
case option_name
|
320
|
+
when RCVMORE, MCAST_LOOP, HWM, SWAP, AFFINITY, RATE, RECOVERY_IVL, SNDBUF, RCVBUF
|
321
|
+
# int64_t
|
322
|
+
length.write_long_long 8
|
323
|
+
[FFI::MemoryPointer.new(:int64), length]
|
324
|
+
when IDENTITY
|
325
|
+
# could be a string of up to 255 bytes
|
326
|
+
length.write_long_long 255
|
327
|
+
[FFI::MemoryPointer.new(255), length]
|
328
|
+
end
|
329
|
+
end
|
330
|
+
|
331
|
+
def define_finalizer
|
332
|
+
ObjectSpace.define_finalizer(self, self.class.close(@socket))
|
333
|
+
end
|
334
|
+
|
335
|
+
def remove_finalizer
|
336
|
+
ObjectSpace.undefine_finalizer self
|
337
|
+
end
|
338
|
+
|
339
|
+
def self.close socket
|
340
|
+
Proc.new { LibZMQ.zmq_close socket }
|
341
|
+
end
|
342
|
+
end # class Socket
|
343
|
+
|
344
|
+
end # module ZMQ
|
@@ -0,0 +1,120 @@
|
|
1
|
+
require 'ffi' unless RBX # external gem
|
2
|
+
|
3
|
+
module LibC
|
4
|
+
extend FFI::Library
|
5
|
+
# figures out the correct libc for each platform including Windows
|
6
|
+
ffi_lib FFI::Library::LIBC unless RBX
|
7
|
+
|
8
|
+
# memory allocators
|
9
|
+
attach_function :malloc, [:size_t], :pointer
|
10
|
+
attach_function :calloc, [:size_t], :pointer
|
11
|
+
attach_function :valloc, [:size_t], :pointer
|
12
|
+
attach_function :realloc, [:pointer, :size_t], :pointer
|
13
|
+
attach_function :free, [:pointer], :void
|
14
|
+
|
15
|
+
# memory movers
|
16
|
+
attach_function :memcpy, [:pointer, :pointer, :size_t], :pointer
|
17
|
+
attach_function :bcopy, [:pointer, :pointer, :size_t], :void
|
18
|
+
|
19
|
+
end # module LibC
|
20
|
+
|
21
|
+
module LibZMQ
|
22
|
+
extend FFI::Library
|
23
|
+
LINUX = ["libzmq", "/usr/local/lib/libzmq", "/opt/local/lib/libzmq"]
|
24
|
+
OSX = ["libzmq", "/usr/local/lib/libzmq", "/opt/local/lib/libzmq"]
|
25
|
+
WINDOWS = []
|
26
|
+
RBX ? ffi_lib(*(LINUX + OSX + WINDOWS)) : ffi_lib(LINUX + OSX + WINDOWS)
|
27
|
+
|
28
|
+
# Misc
|
29
|
+
attach_function :zmq_version, [:pointer, :pointer, :pointer], :void
|
30
|
+
|
31
|
+
# Context and misc api
|
32
|
+
attach_function :zmq_init, [:int], :pointer
|
33
|
+
attach_function :zmq_socket, [:pointer, :int], :pointer
|
34
|
+
attach_function :zmq_term, [:pointer], :int
|
35
|
+
attach_function :zmq_errno, [], :int
|
36
|
+
attach_function :zmq_strerror, [:int], :pointer
|
37
|
+
|
38
|
+
# Message api
|
39
|
+
attach_function :zmq_msg_init, [:pointer], :int
|
40
|
+
attach_function :zmq_msg_init_size, [:pointer, :size_t], :int
|
41
|
+
attach_function :zmq_msg_init_data, [:pointer, :pointer, :size_t, :pointer, :pointer], :int
|
42
|
+
attach_function :zmq_msg_close, [:pointer], :int
|
43
|
+
attach_function :zmq_msg_data, [:pointer], :pointer
|
44
|
+
attach_function :zmq_msg_size, [:pointer], :size_t
|
45
|
+
attach_function :zmq_msg_copy, [:pointer, :pointer], :int
|
46
|
+
attach_function :zmq_msg_move, [:pointer, :pointer], :int
|
47
|
+
|
48
|
+
unless RBX
|
49
|
+
MessageDeallocator = FFI::Function.new(:void, [:pointer, :pointer]) do |data_ptr, hint_ptr|
|
50
|
+
LibC.free data_ptr
|
51
|
+
end
|
52
|
+
MessageDeallocator.autorelease = false
|
53
|
+
end
|
54
|
+
|
55
|
+
# Used for casting pointers back to the struct
|
56
|
+
class Msg < FFI::Struct
|
57
|
+
layout :content, :pointer,
|
58
|
+
:flags, :uint8,
|
59
|
+
:vsm_size, :uint8,
|
60
|
+
:vsm_data, [:uint8, 30]
|
61
|
+
end # class Msg
|
62
|
+
|
63
|
+
|
64
|
+
# Socket api
|
65
|
+
# @blocking = true is a hint to FFI that the following (and only the following)
|
66
|
+
# function may block, therefore it should release the GIL before calling it.
|
67
|
+
# This can aid in situations where the function call will/may block and another
|
68
|
+
# thread within the lib may try to call back into the ruby runtime. Failure to
|
69
|
+
# release the GIL will result in a hang; the hint *may* allow things to run
|
70
|
+
# smoothly for Ruby runtimes hampered by a GIL.
|
71
|
+
attach_function :zmq_setsockopt, [:pointer, :int, :pointer, :int], :int
|
72
|
+
attach_function :zmq_getsockopt, [:pointer, :int, :pointer, :pointer], :int
|
73
|
+
attach_function :zmq_bind, [:pointer, :string], :int
|
74
|
+
attach_function :zmq_connect, [:pointer, :string], :int
|
75
|
+
@blocking = true
|
76
|
+
attach_function :zmq_send, [:pointer, :pointer, :int], :int
|
77
|
+
@blocking = true
|
78
|
+
attach_function :zmq_recv, [:pointer, :pointer, :int], :int
|
79
|
+
attach_function :zmq_close, [:pointer], :int
|
80
|
+
|
81
|
+
# Poll api
|
82
|
+
@blocking = true
|
83
|
+
attach_function :zmq_poll, [:pointer, :int, :long], :int
|
84
|
+
|
85
|
+
module PollItemLayout
|
86
|
+
def self.included(base)
|
87
|
+
base.class_eval do
|
88
|
+
layout :socket, :pointer,
|
89
|
+
:fd, :int,
|
90
|
+
:events, :short,
|
91
|
+
:revents, :short
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end # module PollItemLayout
|
95
|
+
|
96
|
+
class PollItem < FFI::Struct
|
97
|
+
include PollItemLayout
|
98
|
+
|
99
|
+
def socket() self[:socket]; end
|
100
|
+
|
101
|
+
def readable?
|
102
|
+
(self[:revents] & ZMQ::POLLIN) > 0
|
103
|
+
end
|
104
|
+
|
105
|
+
def writable?
|
106
|
+
(self[:revents] & ZMQ::POLLOUT) > 0
|
107
|
+
end
|
108
|
+
|
109
|
+
def both_accessible?
|
110
|
+
readable? && writable?
|
111
|
+
end
|
112
|
+
|
113
|
+
def inspect
|
114
|
+
"socket [#{socket}], fd [#{self[:fd]}], events [#{self[:events]}], revents [#{self[:revents]}]"
|
115
|
+
end
|
116
|
+
|
117
|
+
def to_s; inspect; end
|
118
|
+
end # class PollItem
|
119
|
+
|
120
|
+
end # module ZMQ
|