ffi-rxs 1.0.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/.gitignore +7 -0
- data/AUTHORS.txt +21 -0
- data/Gemfile +3 -0
- data/README.rdoc +86 -0
- data/Rakefile +6 -0
- data/ext/README +5 -0
- data/ffi-rxs.gemspec +25 -0
- data/lib/ffi-rxs/constants.rb +104 -0
- data/lib/ffi-rxs/constants.rb~ +100 -0
- data/lib/ffi-rxs/context.rb +153 -0
- data/lib/ffi-rxs/context.rb~ +155 -0
- data/lib/ffi-rxs/device.rb~ +28 -0
- data/lib/ffi-rxs/exceptions.rb +47 -0
- data/lib/ffi-rxs/exceptions.rb~ +47 -0
- data/lib/ffi-rxs/libc.rb +19 -0
- data/lib/ffi-rxs/libc.rb~ +19 -0
- data/lib/ffi-rxs/libxs.rb +156 -0
- data/lib/ffi-rxs/libxs.rb~ +156 -0
- data/lib/ffi-rxs/message.rb +282 -0
- data/lib/ffi-rxs/message.rb~ +282 -0
- data/lib/ffi-rxs/poll.rb +212 -0
- data/lib/ffi-rxs/poll.rb~ +212 -0
- data/lib/ffi-rxs/poll_items.rb +120 -0
- data/lib/ffi-rxs/poll_items.rb~ +120 -0
- data/lib/ffi-rxs/socket.rb +659 -0
- data/lib/ffi-rxs/socket.rb~ +659 -0
- data/lib/ffi-rxs/util.rb +105 -0
- data/lib/ffi-rxs/util.rb~ +105 -0
- data/lib/ffi-rxs/version.rb +3 -0
- data/lib/ffi-rxs/version.rb~ +3 -0
- data/lib/ffi-rxs.rb +74 -0
- data/spec/context_spec.rb +138 -0
- data/spec/message_spec.rb +128 -0
- data/spec/multipart_spec.rb +108 -0
- data/spec/nonblocking_recv_spec.rb +309 -0
- data/spec/poll_spec.rb +168 -0
- data/spec/pushpull_spec.rb +113 -0
- data/spec/reqrep_spec.rb +66 -0
- data/spec/socket_spec.rb +496 -0
- data/spec/spec_helper.rb +57 -0
- metadata +126 -0
@@ -0,0 +1,659 @@
|
|
1
|
+
|
2
|
+
module XS
|
3
|
+
|
4
|
+
module CommonSocketBehavior
|
5
|
+
include XS::Util
|
6
|
+
|
7
|
+
attr_reader :socket, :name
|
8
|
+
|
9
|
+
# Allocates a socket of type +type+ for sending and receiving data.
|
10
|
+
#
|
11
|
+
# +type+ can be one of XS::REQ, XS::REP, XS::PUB,
|
12
|
+
# XS::SUB, XS::PAIR, XS::PULL, XS::PUSH, XS::XREQ, XS::REP,
|
13
|
+
# XS::DEALER or XS::ROUTER.
|
14
|
+
#
|
15
|
+
# By default, this class uses XS::Message for manual
|
16
|
+
# memory management. For automatic garbage collection of received messages,
|
17
|
+
# it is possible to override the :receiver_class to use XS::ManagedMessage.
|
18
|
+
#
|
19
|
+
# sock = Socket.create(Context.create, XS::REQ, :receiver_class => XS::ManagedMessage)
|
20
|
+
#
|
21
|
+
# Advanced users may want to replace the receiver class with their
|
22
|
+
# own custom class. The custom class must conform to the same public API
|
23
|
+
# as XS::Message.
|
24
|
+
#
|
25
|
+
# Creation of a new Socket object can return nil when socket creation
|
26
|
+
# fails.
|
27
|
+
#
|
28
|
+
# if (socket = Socket.new(context.pointer, XS::REQ))
|
29
|
+
# ...
|
30
|
+
# else
|
31
|
+
# STDERR.puts "Socket creation failed"
|
32
|
+
# end
|
33
|
+
#
|
34
|
+
def self.create context_ptr, type, opts = {:receiver_class => XS::Message}
|
35
|
+
new(context_ptr, type, opts) rescue nil
|
36
|
+
end
|
37
|
+
|
38
|
+
# To avoid rescuing exceptions, use the factory method #create for
|
39
|
+
# all socket creation.
|
40
|
+
#
|
41
|
+
# Allocates a socket of type +type+ for sending and receiving data.
|
42
|
+
#
|
43
|
+
# +type+ can be one of XS::REQ, XS::REP, XS::PUB,
|
44
|
+
# XS::SUB, XS::PAIR, XS::PULL, XS::PUSH, XS::XREQ, XS::REP,
|
45
|
+
# XS::DEALER or XS::ROUTER.
|
46
|
+
#
|
47
|
+
# By default, this class uses XS::Message for manual
|
48
|
+
# memory management. For automatic garbage collection of received messages,
|
49
|
+
# it is possible to override the :receiver_class to use XS::ManagedMessage.
|
50
|
+
#
|
51
|
+
# sock = Socket.new(Context.new, XS::REQ, :receiver_class => XS::ManagedMessage)
|
52
|
+
#
|
53
|
+
# Advanced users may want to replace the receiver class with their
|
54
|
+
# own custom class. The custom class must conform to the same public API
|
55
|
+
# as XS::Message.
|
56
|
+
#
|
57
|
+
# Creation of a new Socket object can raise an exception. This occurs when the
|
58
|
+
# +context_ptr+ is null or when the allocation of the Crossroads socket within the
|
59
|
+
# context fails.
|
60
|
+
#
|
61
|
+
# begin
|
62
|
+
# socket = Socket.new(context.pointer, XS::REQ)
|
63
|
+
# rescue ContextError => e
|
64
|
+
# # error handling
|
65
|
+
# end
|
66
|
+
#
|
67
|
+
def initialize context_ptr, type, opts = {:receiver_class => XS::Message}
|
68
|
+
# users may override the classes used for receiving; class must conform to the
|
69
|
+
# same public API as XS::Message
|
70
|
+
@receiver_klass = opts[:receiver_class]
|
71
|
+
|
72
|
+
context_ptr = context_ptr.pointer if context_ptr.kind_of?(XS::Context)
|
73
|
+
|
74
|
+
unless context_ptr.null?
|
75
|
+
@socket = LibXS.xs_socket context_ptr, type
|
76
|
+
if @socket && !@socket.null?
|
77
|
+
@name = SocketTypeNameMap[type]
|
78
|
+
else
|
79
|
+
raise ContextError.new 'xs_socket', 0, ETERM, "Socket pointer was null"
|
80
|
+
end
|
81
|
+
else
|
82
|
+
raise ContextError.new 'xs_socket', 0, ETERM, "Context pointer was null"
|
83
|
+
end
|
84
|
+
|
85
|
+
@longlong_cache = @int_cache = nil
|
86
|
+
@more_parts_array = []
|
87
|
+
@option_lookup = []
|
88
|
+
populate_option_lookup
|
89
|
+
|
90
|
+
define_finalizer
|
91
|
+
end
|
92
|
+
|
93
|
+
# Set the queue options on this socket.
|
94
|
+
#
|
95
|
+
# Valid +name+ values that take a numeric +value+ are:
|
96
|
+
# XS::HWM
|
97
|
+
# XS::SWAP (version 2 only)
|
98
|
+
# XS::AFFINITY
|
99
|
+
# XS::RATE
|
100
|
+
# XS::RECOVERY_IVL
|
101
|
+
# XS::MCAST_LOOP (version 2 only)
|
102
|
+
# XS::LINGER
|
103
|
+
# XS::RECONNECT_IVL
|
104
|
+
# XS::BACKLOG
|
105
|
+
# XS::RECOVER_IVL_MSEC (version 2 only)
|
106
|
+
# XS::RECONNECT_IVL_MAX (version 3 only)
|
107
|
+
# XS::MAXMSGSIZE (version 3 only)
|
108
|
+
# XS::SNDHWM (version 3 only)
|
109
|
+
# XS::RCVHWM (version 3 only)
|
110
|
+
# XS::MULTICAST_HOPS (version 3 only)
|
111
|
+
# XS::RCVTIMEO (version 3 only)
|
112
|
+
# XS::SNDTIMEO (version 3 only)
|
113
|
+
#
|
114
|
+
# Valid +name+ values that take a string +value+ are:
|
115
|
+
# XS::IDENTITY (version 2/3 only)
|
116
|
+
# XS::SUBSCRIBE
|
117
|
+
# XS::UNSUBSCRIBE
|
118
|
+
#
|
119
|
+
# Returns 0 when the operation completed successfully.
|
120
|
+
# Returns -1 when this operation failed.
|
121
|
+
#
|
122
|
+
# With a -1 return code, the user must check XS.errno to determine the
|
123
|
+
# cause.
|
124
|
+
#
|
125
|
+
# rc = socket.setsockopt(XS::LINGER, 1_000)
|
126
|
+
# XS::Util.resultcode_ok?(rc) ? puts("succeeded") : puts("failed")
|
127
|
+
#
|
128
|
+
def setsockopt name, value, length = nil
|
129
|
+
if 1 == @option_lookup[name]
|
130
|
+
length = 8
|
131
|
+
pointer = LibC.malloc length
|
132
|
+
pointer.write_long_long value
|
133
|
+
|
134
|
+
elsif 0 == @option_lookup[name]
|
135
|
+
length = 4
|
136
|
+
pointer = LibC.malloc length
|
137
|
+
pointer.write_int value
|
138
|
+
|
139
|
+
elsif 2 == @option_lookup[name]
|
140
|
+
length ||= value.size
|
141
|
+
|
142
|
+
# note: not checking errno for failed memory allocations :(
|
143
|
+
pointer = LibC.malloc length
|
144
|
+
pointer.write_string value
|
145
|
+
end
|
146
|
+
|
147
|
+
rc = LibXS.xs_setsockopt @socket, name, pointer, length
|
148
|
+
LibC.free(pointer) unless pointer.nil? || pointer.null?
|
149
|
+
rc
|
150
|
+
end
|
151
|
+
|
152
|
+
# Convenience method for checking on additional message parts.
|
153
|
+
#
|
154
|
+
# Equivalent to calling Socket#getsockopt with XS::RCVMORE.
|
155
|
+
#
|
156
|
+
# Warning: if the call to #getsockopt fails, this method will return
|
157
|
+
# false and swallow the error.
|
158
|
+
#
|
159
|
+
# message_parts = []
|
160
|
+
# message = Message.new
|
161
|
+
# rc = socket.recvmsg(message)
|
162
|
+
# if XS::Util.resultcode_ok?(rc)
|
163
|
+
# message_parts << message
|
164
|
+
# while more_parts?
|
165
|
+
# message = Message.new
|
166
|
+
# rc = socket.recvmsg(message)
|
167
|
+
# message_parts.push(message) if resulcode_ok?(rc)
|
168
|
+
# end
|
169
|
+
# end
|
170
|
+
#
|
171
|
+
def more_parts?
|
172
|
+
rc = getsockopt XS::RCVMORE, @more_parts_array
|
173
|
+
|
174
|
+
Util.resultcode_ok?(rc) ? @more_parts_array.at(0) : false
|
175
|
+
end
|
176
|
+
|
177
|
+
# Binds the socket to an +address+.
|
178
|
+
#
|
179
|
+
# socket.bind("tcp://127.0.0.1:5555")
|
180
|
+
#
|
181
|
+
def bind address
|
182
|
+
LibXS.xs_bind @socket, address
|
183
|
+
end
|
184
|
+
|
185
|
+
# Connects the socket to an +address+.
|
186
|
+
#
|
187
|
+
# rc = socket.connect("tcp://127.0.0.1:5555")
|
188
|
+
#
|
189
|
+
def connect address
|
190
|
+
rc = LibXS.xs_connect @socket, address
|
191
|
+
end
|
192
|
+
|
193
|
+
# Closes the socket. Any unprocessed messages in queue are sent or dropped
|
194
|
+
# depending upon the value of the socket option XS::LINGER.
|
195
|
+
#
|
196
|
+
# Returns 0 upon success *or* when the socket has already been closed.
|
197
|
+
# Returns -1 when the operation fails. Check XS.errno for the error code.
|
198
|
+
#
|
199
|
+
# rc = socket.close
|
200
|
+
# puts("Given socket was invalid!") unless 0 == rc
|
201
|
+
#
|
202
|
+
def close
|
203
|
+
if @socket
|
204
|
+
remove_finalizer
|
205
|
+
rc = LibXS.xs_close @socket
|
206
|
+
@socket = nil
|
207
|
+
release_cache
|
208
|
+
rc
|
209
|
+
else
|
210
|
+
0
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
# Queues the message for transmission. Message is assumed to conform to the
|
215
|
+
# same public API as #Message.
|
216
|
+
#
|
217
|
+
# +flags+ may take two values:
|
218
|
+
# * 0 (default) - blocking operation
|
219
|
+
# * XS::NonBlocking - non-blocking operation
|
220
|
+
# * XS::SNDMORE - this message is part of a multi-part message
|
221
|
+
#
|
222
|
+
# Returns 0 when the message was successfully enqueued.
|
223
|
+
# Returns -1 under two conditions.
|
224
|
+
# 1. The message could not be enqueued
|
225
|
+
# 2. When +flags+ is set with XS::NonBlocking and the socket returned EAGAIN.
|
226
|
+
#
|
227
|
+
# With a -1 return code, the user must check XS.errno to determine the
|
228
|
+
# cause.
|
229
|
+
#
|
230
|
+
def sendmsg message, flags = 0
|
231
|
+
__sendmsg__(@socket, message.address, flags)
|
232
|
+
end
|
233
|
+
|
234
|
+
# Helper method to make a new #Message instance out of the +string+ passed
|
235
|
+
# in for transmission.
|
236
|
+
#
|
237
|
+
# +flags+ may be XS::NonBlocking and XS::SNDMORE.
|
238
|
+
#
|
239
|
+
# Returns 0 when the message was successfully enqueued.
|
240
|
+
# Returns -1 under two conditions.
|
241
|
+
# 1. The message could not be enqueued
|
242
|
+
# 2. When +flags+ is set with XS::NonBlocking and the socket returned EAGAIN.
|
243
|
+
#
|
244
|
+
# With a -1 return code, the user must check XS.errno to determine the
|
245
|
+
# cause.
|
246
|
+
#
|
247
|
+
def send_string string, flags = 0
|
248
|
+
message = Message.new string
|
249
|
+
send_and_close message, flags
|
250
|
+
end
|
251
|
+
|
252
|
+
# Send a sequence of strings as a multipart message out of the +parts+
|
253
|
+
# passed in for transmission. Every element of +parts+ should be
|
254
|
+
# a String.
|
255
|
+
#
|
256
|
+
# +flags+ may be XS::NonBlocking.
|
257
|
+
#
|
258
|
+
# Returns 0 when the messages were successfully enqueued.
|
259
|
+
# Returns -1 under two conditions.
|
260
|
+
# 1. A message could not be enqueued
|
261
|
+
# 2. When +flags+ is set with XS::NonBlocking and the socket returned EAGAIN.
|
262
|
+
#
|
263
|
+
# With a -1 return code, the user must check XS.errno to determine the
|
264
|
+
# cause.
|
265
|
+
#
|
266
|
+
def send_strings parts, flags = 0
|
267
|
+
return -1 if !parts || parts.empty?
|
268
|
+
flags = NonBlocking if dontwait?(flags)
|
269
|
+
|
270
|
+
parts[0..-2].each do |part|
|
271
|
+
rc = send_string part, (flags | XS::SNDMORE)
|
272
|
+
return rc unless Util.resultcode_ok?(rc)
|
273
|
+
end
|
274
|
+
|
275
|
+
send_string parts[-1], flags
|
276
|
+
end
|
277
|
+
|
278
|
+
# Send a sequence of messages as a multipart message out of the +parts+
|
279
|
+
# passed in for transmission. Every element of +parts+ should be
|
280
|
+
# a Message (or subclass).
|
281
|
+
#
|
282
|
+
# +flags+ may be XS::NonBlocking.
|
283
|
+
#
|
284
|
+
# Returns 0 when the messages were successfully enqueued.
|
285
|
+
# Returns -1 under two conditions.
|
286
|
+
# 1. A message could not be enqueued
|
287
|
+
# 2. When +flags+ is set with XS::NonBlocking and the socket returned EAGAIN.
|
288
|
+
#
|
289
|
+
# With a -1 return code, the user must check XS.errno to determine the
|
290
|
+
# cause.
|
291
|
+
#
|
292
|
+
def sendmsgs parts, flags = 0
|
293
|
+
return -1 if !parts || parts.empty?
|
294
|
+
flags = NonBlocking if dontwait?(flags)
|
295
|
+
|
296
|
+
parts[0..-2].each do |part|
|
297
|
+
rc = sendmsg part, (flags | XS::SNDMORE)
|
298
|
+
return rc unless Util.resultcode_ok?(rc)
|
299
|
+
end
|
300
|
+
|
301
|
+
sendmsg parts[-1], flags
|
302
|
+
end
|
303
|
+
|
304
|
+
# Sends a message. This will automatically close the +message+ for both successful
|
305
|
+
# and failed sends.
|
306
|
+
#
|
307
|
+
# Returns 0 when the message was successfully enqueued.
|
308
|
+
# Returns -1 under two conditions.
|
309
|
+
# 1. The message could not be enqueued
|
310
|
+
# 2. When +flags+ is set with XS::NonBlocking and the socket returned EAGAIN.
|
311
|
+
#
|
312
|
+
# With a -1 return code, the user must check XS.errno to determine the
|
313
|
+
# cause.
|
314
|
+
#
|
315
|
+
def send_and_close message, flags = 0
|
316
|
+
rc = sendmsg message, flags
|
317
|
+
message.close
|
318
|
+
rc
|
319
|
+
end
|
320
|
+
|
321
|
+
# Dequeues a message from the underlying queue. By default, this is a blocking operation.
|
322
|
+
#
|
323
|
+
# +flags+ may take two values:
|
324
|
+
# 0 (default) - blocking operation
|
325
|
+
# XS::NonBlocking - non-blocking operation
|
326
|
+
#
|
327
|
+
# Returns 0 when the message was successfully dequeued.
|
328
|
+
# Returns -1 under two conditions.
|
329
|
+
# 1. The message could not be dequeued
|
330
|
+
# 2. When +flags+ is set with XS::NonBlocking and the socket returned EAGAIN.
|
331
|
+
#
|
332
|
+
# With a -1 return code, the user must check XS.errno to determine the
|
333
|
+
# cause.
|
334
|
+
#
|
335
|
+
# The application code is responsible for handling the +message+ object lifecycle
|
336
|
+
# when #recv returns an error code.
|
337
|
+
#
|
338
|
+
def recvmsg message, flags = 0
|
339
|
+
#LibXS.xs_recvmsg @socket, message.address, flags
|
340
|
+
__recvmsg__(@socket, message.address, flags)
|
341
|
+
end
|
342
|
+
|
343
|
+
# Helper method to make a new #Message instance and convert its payload
|
344
|
+
# to a string.
|
345
|
+
#
|
346
|
+
# +flags+ may be XS::NonBlocking.
|
347
|
+
#
|
348
|
+
# Returns 0 when the message was successfully dequeued.
|
349
|
+
# Returns -1 under two conditions.
|
350
|
+
# 1. The message could not be dequeued
|
351
|
+
# 2. When +flags+ is set with XS::NonBlocking and the socket returned EAGAIN.
|
352
|
+
#
|
353
|
+
# With a -1 return code, the user must check XS.errno to determine the
|
354
|
+
# cause.
|
355
|
+
#
|
356
|
+
# The application code is responsible for handling the +message+ object lifecycle
|
357
|
+
# when #recv returns an error code.
|
358
|
+
#
|
359
|
+
def recv_string string, flags = 0
|
360
|
+
message = @receiver_klass.new
|
361
|
+
rc = recvmsg message, flags
|
362
|
+
string.replace(message.copy_out_string) if Util.resultcode_ok?(rc)
|
363
|
+
message.close
|
364
|
+
rc
|
365
|
+
end
|
366
|
+
|
367
|
+
# Receive a multipart message as a list of strings.
|
368
|
+
#
|
369
|
+
# +flag+ may be XS::NonBlocking. Any other flag will be
|
370
|
+
# removed.
|
371
|
+
#
|
372
|
+
def recv_strings list, flag = 0
|
373
|
+
array = []
|
374
|
+
rc = recvmsgs array, flag
|
375
|
+
|
376
|
+
if Util.resultcode_ok?(rc)
|
377
|
+
array.each do |message|
|
378
|
+
list << message.copy_out_string
|
379
|
+
message.close
|
380
|
+
end
|
381
|
+
end
|
382
|
+
|
383
|
+
rc
|
384
|
+
end
|
385
|
+
|
386
|
+
# Receive a multipart message as an array of objects
|
387
|
+
# (by default these are instances of Message).
|
388
|
+
#
|
389
|
+
# +flag+ may be XS::NonBlocking. Any other flag will be
|
390
|
+
# removed.
|
391
|
+
#
|
392
|
+
def recvmsgs list, flag = 0
|
393
|
+
flag = NonBlocking if dontwait?(flag)
|
394
|
+
|
395
|
+
message = @receiver_klass.new
|
396
|
+
rc = recvmsg message, flag
|
397
|
+
|
398
|
+
if Util.resultcode_ok?(rc)
|
399
|
+
list << message
|
400
|
+
|
401
|
+
# check rc *first*; necessary because the call to #more_parts? can reset
|
402
|
+
# the xs_errno to a weird value, so the xs_errno that was set on the
|
403
|
+
# call to #recv gets lost
|
404
|
+
while Util.resultcode_ok?(rc) && more_parts?
|
405
|
+
message = @receiver_klass.new
|
406
|
+
rc = recvmsg message, flag
|
407
|
+
|
408
|
+
if Util.resultcode_ok?(rc)
|
409
|
+
list << message
|
410
|
+
else
|
411
|
+
message.close
|
412
|
+
list.each { |msg| msg.close }
|
413
|
+
list.clear
|
414
|
+
end
|
415
|
+
end
|
416
|
+
else
|
417
|
+
message.close
|
418
|
+
end
|
419
|
+
|
420
|
+
rc
|
421
|
+
end
|
422
|
+
|
423
|
+
# Should only be used for XREQ, XREP, DEALER and ROUTER type sockets. Takes
|
424
|
+
# a +list+ for receiving the message body parts and a +routing_envelope+
|
425
|
+
# for receiving the message parts comprising the 0mq routing information.
|
426
|
+
#
|
427
|
+
def recv_multipart list, routing_envelope, flag = 0
|
428
|
+
parts = []
|
429
|
+
rc = recvmsgs parts, flag
|
430
|
+
|
431
|
+
if Util.resultcode_ok?(rc)
|
432
|
+
routing = true
|
433
|
+
parts.each do |part|
|
434
|
+
if routing
|
435
|
+
routing_envelope << part
|
436
|
+
routing = part.size > 0
|
437
|
+
else
|
438
|
+
list << part
|
439
|
+
end
|
440
|
+
end
|
441
|
+
end
|
442
|
+
|
443
|
+
rc
|
444
|
+
end
|
445
|
+
|
446
|
+
|
447
|
+
private
|
448
|
+
|
449
|
+
def __getsockopt__ name, array
|
450
|
+
# a small optimization so we only have to determine the option
|
451
|
+
# type a single time; gives approx 5% speedup to do it this way.
|
452
|
+
option_type = @option_lookup[name]
|
453
|
+
|
454
|
+
value, length = sockopt_buffers option_type
|
455
|
+
|
456
|
+
rc = LibXS.xs_getsockopt @socket, name, value, length
|
457
|
+
|
458
|
+
if Util.resultcode_ok?(rc)
|
459
|
+
array[0] = if 1 == option_type
|
460
|
+
value.read_long_long
|
461
|
+
elsif 0 == option_type
|
462
|
+
value.read_int
|
463
|
+
elsif 2 == option_type
|
464
|
+
value.read_string(length.read_int)
|
465
|
+
end
|
466
|
+
end
|
467
|
+
|
468
|
+
rc
|
469
|
+
end
|
470
|
+
|
471
|
+
# Calls to XS.getsockopt require us to pass in some pointers. We can cache and save those buffers
|
472
|
+
# for subsequent calls. This is a big perf win for calling RCVMORE which happens quite often.
|
473
|
+
# Cannot save the buffer for the IDENTITY.
|
474
|
+
def sockopt_buffers option_type
|
475
|
+
if 1 == option_type
|
476
|
+
# int64_t or uint64_t
|
477
|
+
unless @longlong_cache
|
478
|
+
length = FFI::MemoryPointer.new :size_t
|
479
|
+
length.write_int 8
|
480
|
+
@longlong_cache = [FFI::MemoryPointer.new(:int64), length]
|
481
|
+
end
|
482
|
+
|
483
|
+
@longlong_cache
|
484
|
+
|
485
|
+
elsif 0 == option_type
|
486
|
+
# int, Crossroads assumes int is 4-bytes
|
487
|
+
unless @int_cache
|
488
|
+
length = FFI::MemoryPointer.new :size_t
|
489
|
+
length.write_int 4
|
490
|
+
@int_cache = [FFI::MemoryPointer.new(:int32), length]
|
491
|
+
end
|
492
|
+
|
493
|
+
@int_cache
|
494
|
+
|
495
|
+
elsif 2 == option_type
|
496
|
+
length = FFI::MemoryPointer.new :size_t
|
497
|
+
# could be a string of up to 255 bytes
|
498
|
+
length.write_int 255
|
499
|
+
[FFI::MemoryPointer.new(255), length]
|
500
|
+
|
501
|
+
else
|
502
|
+
# uh oh, someone passed in an unknown option; use a slop buffer
|
503
|
+
unless @int_cache
|
504
|
+
length = FFI::MemoryPointer.new :size_t
|
505
|
+
length.write_int 4
|
506
|
+
@int_cache = [FFI::MemoryPointer.new(:int32), length]
|
507
|
+
end
|
508
|
+
|
509
|
+
@int_cache
|
510
|
+
end
|
511
|
+
end
|
512
|
+
|
513
|
+
def populate_option_lookup
|
514
|
+
# integer options
|
515
|
+
[EVENTS, LINGER, RECONNECT_IVL, FD, TYPE, BACKLOG].each { |option| @option_lookup[option] = 0 }
|
516
|
+
|
517
|
+
# long long options
|
518
|
+
[RCVMORE, AFFINITY].each { |option| @option_lookup[option] = 1 }
|
519
|
+
|
520
|
+
# string options
|
521
|
+
[SUBSCRIBE, UNSUBSCRIBE].each { |option| @option_lookup[option] = 2 }
|
522
|
+
end
|
523
|
+
|
524
|
+
def release_cache
|
525
|
+
@longlong_cache = nil
|
526
|
+
@int_cache = nil
|
527
|
+
end
|
528
|
+
|
529
|
+
def dontwait?(flags)
|
530
|
+
(NonBlocking & flags) == NonBlocking
|
531
|
+
end
|
532
|
+
alias :noblock? :dontwait?
|
533
|
+
end # module CommonSocketBehavior
|
534
|
+
|
535
|
+
|
536
|
+
module IdentitySupport
|
537
|
+
|
538
|
+
# Convenience method for getting the value of the socket IDENTITY.
|
539
|
+
#
|
540
|
+
def identity
|
541
|
+
array = []
|
542
|
+
getsockopt IDENTITY, array
|
543
|
+
array.at(0)
|
544
|
+
end
|
545
|
+
|
546
|
+
# Convenience method for setting the value of the socket IDENTITY.
|
547
|
+
#
|
548
|
+
def identity=(value)
|
549
|
+
setsockopt IDENTITY, value.to_s
|
550
|
+
end
|
551
|
+
|
552
|
+
|
553
|
+
private
|
554
|
+
|
555
|
+
def populate_option_lookup
|
556
|
+
super()
|
557
|
+
|
558
|
+
# string options
|
559
|
+
[IDENTITY].each { |option| @option_lookup[option] = 2 }
|
560
|
+
end
|
561
|
+
|
562
|
+
end # module IdentitySupport
|
563
|
+
|
564
|
+
class Socket
|
565
|
+
include CommonSocketBehavior
|
566
|
+
include IdentitySupport
|
567
|
+
|
568
|
+
# Get the options set on this socket.
|
569
|
+
#
|
570
|
+
# +name+ determines the socket option to request
|
571
|
+
# +array+ should be an empty array; a result of the proper type
|
572
|
+
# (numeric, string, boolean) will be inserted into
|
573
|
+
# the first position.
|
574
|
+
#
|
575
|
+
# Valid +option_name+ values:
|
576
|
+
# XS::RCVMORE - true or false
|
577
|
+
# XS::HWM - integer
|
578
|
+
# XS::SWAP - integer
|
579
|
+
# XS::AFFINITY - bitmap in an integer
|
580
|
+
# XS::IDENTITY - string
|
581
|
+
# XS::RATE - integer
|
582
|
+
# XS::RECOVERY_IVL - integer
|
583
|
+
# XS::SNDBUF - integer
|
584
|
+
# XS::RCVBUF - integer
|
585
|
+
# XS::FD - fd in an integer
|
586
|
+
# XS::EVENTS - bitmap integer
|
587
|
+
# XS::LINGER - integer measured in milliseconds
|
588
|
+
# XS::RECONNECT_IVL - integer measured in milliseconds
|
589
|
+
# XS::BACKLOG - integer
|
590
|
+
# XS::RECOVER_IVL_MSEC - integer measured in milliseconds
|
591
|
+
#
|
592
|
+
# Returns 0 when the operation completed successfully.
|
593
|
+
# Returns -1 when this operation failed.
|
594
|
+
#
|
595
|
+
# With a -1 return code, the user must check XS.errno to determine the
|
596
|
+
# cause.
|
597
|
+
#
|
598
|
+
# # retrieve high water mark
|
599
|
+
# array = []
|
600
|
+
# rc = socket.getsockopt(XS::HWM, array)
|
601
|
+
# hwm = array.first if XS::Util.resultcode_ok?(rc)
|
602
|
+
#
|
603
|
+
def getsockopt name, array
|
604
|
+
rc = __getsockopt__ name, array
|
605
|
+
|
606
|
+
if Util.resultcode_ok?(rc) && (RCVMORE == name)
|
607
|
+
# convert to boolean
|
608
|
+
array[0] = 1 == array[0]
|
609
|
+
end
|
610
|
+
|
611
|
+
rc
|
612
|
+
end
|
613
|
+
|
614
|
+
|
615
|
+
private
|
616
|
+
|
617
|
+
def __sendmsg__(socket, address, flags)
|
618
|
+
LibXS.zmq_sendmsg(socket, address, flags)
|
619
|
+
end
|
620
|
+
|
621
|
+
def __recvmsg__(socket, address, flags)
|
622
|
+
LibXS.zmq_recvmsg(socket, address, flags)
|
623
|
+
end
|
624
|
+
|
625
|
+
def int_option? name
|
626
|
+
super(name) ||
|
627
|
+
RECONNECT_IVL_MAX == name ||
|
628
|
+
RCVHWM == name ||
|
629
|
+
SNDHWM == name ||
|
630
|
+
RATE == name ||
|
631
|
+
RECOVERY_IVL == name ||
|
632
|
+
SNDBUF == name ||
|
633
|
+
RCVBUF == name
|
634
|
+
end
|
635
|
+
|
636
|
+
def populate_option_lookup
|
637
|
+
super()
|
638
|
+
|
639
|
+
# integer options
|
640
|
+
[RECONNECT_IVL_MAX, RCVHWM, SNDHWM, RATE, RECOVERY_IVL, SNDBUF, RCVBUF].each { |option| @option_lookup[option] = 0 }
|
641
|
+
end
|
642
|
+
|
643
|
+
# these finalizer-related methods cannot live in the CommonSocketBehavior
|
644
|
+
# module; they *must* be in the class definition directly
|
645
|
+
|
646
|
+
def define_finalizer
|
647
|
+
ObjectSpace.define_finalizer(self, self.class.close(@socket))
|
648
|
+
end
|
649
|
+
|
650
|
+
def remove_finalizer
|
651
|
+
ObjectSpace.undefine_finalizer self
|
652
|
+
end
|
653
|
+
|
654
|
+
def self.close socket
|
655
|
+
Proc.new { LibXS.zmq_close socket }
|
656
|
+
end
|
657
|
+
end
|
658
|
+
|
659
|
+
end # module XS
|