ffi-rzmq 0.6.0 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
data/.bnsignore ADDED
@@ -0,0 +1,22 @@
1
+ # The list of files that should be ignored by Mr Bones.
2
+ # Lines that start with '#' are comments.
3
+ #
4
+ # A .gitignore file can be used instead by setting it as the ignore
5
+ # file in your Rakefile:
6
+ #
7
+ # Bones {
8
+ # ignore_file '.gitignore'
9
+ # }
10
+ #
11
+ # For a project with a C extension, the following would be a good set of
12
+ # exclude patterns (uncomment them if you want to use them):
13
+ # *.[oa]
14
+ # *~
15
+ announcement.txt
16
+ coverage
17
+ doc
18
+ pkg
19
+ *.tmproj
20
+ *.gem
21
+ *.rbc
22
+ *.html
data/History.txt CHANGED
@@ -1,3 +1,39 @@
1
+ == 0.7.0 / 20101222
2
+ * Improved performance of calls to Socket#getsockopt. There are usually
3
+ a lot of calls passing RCVMORE, so we now cache those buffers instead
4
+ of reallocating them every time.
5
+
6
+ * Updated the docs on Poller#poll to warn about a possible busy-loop
7
+ condition.
8
+
9
+ * Fixed some more specs to conform with the 0mq 2.1 requirement that
10
+ all sockets must be closed explicitly otherwise the program may
11
+ hang on exit.
12
+
13
+ == 0.6.1 / 20101127
14
+ * API Change!
15
+ Moved the #version method from the Util module and made it a class
16
+ method instead. Invoke as ZMQ::Util.version. Used for conditionally
17
+ enabling certain features based upon the 0mq version that is loaded.
18
+
19
+ * Preliminary support for the Windows platform. Patches supplied
20
+ by arvicco.
21
+
22
+ * Added support for FD and EVENTS socket options. These were added
23
+ in 0mq 2.1.0. Patches + specs supplied by andrewvc.
24
+
25
+ * Added support for LINGER, RECONNECT_IVL, BACKLOG and
26
+ RECOVERY_IVL_MSEC socket options.
27
+
28
+ * Conditionally re-enable the socket finalizer when we are running
29
+ with 0mq 2.1.0 or later.
30
+
31
+ * Drop support for MRI 1.8.x since the 'ffi' gem has dropped it as a
32
+ supported Ruby runtime with its 1.0 release. No action is taken to
33
+ prevent running with MRI 1.8.x but it won't be supported.
34
+
35
+ * Misc. spec fixes. Need more specs!
36
+
1
37
  == 0.6.0 / 20100911
2
38
  * API Change! Modified ZMQ::Message by removing automatic memory
3
39
  management. While doing some performance tests I saw that
data/README.rdoc CHANGED
@@ -7,10 +7,10 @@ ffi-rzmq
7
7
  This gem wraps the ZeroMQ networking library using the ruby FFI (foreign
8
8
  function interface). It's a pure ruby wrapper so this gem can be loaded
9
9
  and run by any ruby runtime that supports FFI. That's all of them:
10
- MRI 1.8.7+, 1.9.x, Rubinius and JRuby.
10
+ MRI 1.9.x, Rubinius and JRuby.
11
11
 
12
12
  The impetus behind this library was to provide support for ZeroMQ in
13
- JRuby which has native threads. Unlike MRI, MacRuby, IronRuby and
13
+ JRuby which has native threads. Unlike MRI, IronRuby and
14
14
  Rubinius which all have a GIL, JRuby allows for threaded access to ruby
15
15
  code from outside extensions. ZeroMQ is heavily threaded, so until the
16
16
  other runtimes remove their GIL, JRuby will likely be the best
@@ -28,18 +28,9 @@ This gem needs more tests. I'm certain there are a
28
28
  ton of bugs, so please open issues for them here or fork this project,
29
29
  fix them, and send me a pull request.
30
30
 
31
- Running this gem with MRI 1.8.x will likely exhibit a LOT of problems in
32
- the form of hangs, segmentation faults and bus errors. The threading support
33
- in MRI 1.8.x is irrevocably broken (according to the brains behind the
34
- Ruby FFI project) so tread carefully.
35
-
36
- Running this gem with MRI 1.9.x may also exhibit some hangs particularly at
37
- shutdown. This is due to a signaling interaction between the 0mq library
38
- and MRI. If the 0mq library is blocked on a call and the program receives
39
- a signal (e.g. SIGINT), the Ruby runtime has no opportunity to run its signal
40
- handler and process it. The 2.1.x branch of 0mq resolves this problem by
41
- interrupting the blocking call and returning EINTR. The 2.0.x branch will
42
- not get this fix because the API change breaks backward compatibility.
31
+ The 'ffi' gem has dropped support for MRI 1.8.x. Since this project relies
32
+ on that gem to load and run this code, then this project also no longer
33
+ supports MRI 1.8.x.
43
34
 
44
35
  All features are implemented with the exception of the 0mq devices
45
36
  (forwarder, queue, streamer). For implementations of these devices, see
@@ -104,10 +95,18 @@ Server code:
104
95
  msg = s.recv_string 0
105
96
  raise "Message size doesn't match, expected [#{message_size}] but received [#{msg.size}]" if message_size != msg.size
106
97
  end
98
+
99
+ == Better Examples
100
+
101
+ I highly recommend visiting the Learn Ruby 0mq project for a bunch of good code examples.
102
+
103
+ http://github.com/andrewvc/learn-ruby-zeromq
104
+
105
+
107
106
 
108
107
  == REQUIREMENTS:
109
108
 
110
- * 0mq 2.0.8 or 2.0.9
109
+ * 0mq 2.0.10 or 2.1+
111
110
 
112
111
  The ZeroMQ library must be installed on your system in a well-known location
113
112
  like /usr/local/lib. This is the default for new ZeroMQ installs.
@@ -115,15 +114,12 @@ like /usr/local/lib. This is the default for new ZeroMQ installs.
115
114
  Future releases may include the library as a C extension built at
116
115
  time of installation.
117
116
 
118
- * ffi (> 0.6.3)
117
+ * ffi (>= 1.0.0)
119
118
 
120
- Install the current master version of FFI that fixes several threading problems
121
- under MRI 1.9.x. Unfortunately, MRI 1.8.x is irretrievably broken so it isn't
122
- recommended if you plan to use threads or callbacks.
123
-
124
- Code and installation instructions can be found on github:
119
+ This is a requirement for MRI only. Both Rubinius and JRuby have FFI support built
120
+ in as a standard component. Do *not* run this gem under MRI with an old 'ffi' gem.
121
+ It will crash randomly and you will be sad.
125
122
 
126
- http://github.com/ffi/ffi
127
123
 
128
124
  == INSTALL:
129
125
 
data/Rakefile CHANGED
@@ -1,4 +1,3 @@
1
-
2
1
  begin
3
2
  require 'bones'
4
3
  rescue LoadError
@@ -8,12 +7,30 @@ end
8
7
  task :default => 'test:run'
9
8
  task 'gem:release' => 'test:run'
10
9
 
10
+ namespace :win do
11
+
12
+ desc 'Build and install gem under Windows. Mr Bones just has to break things using tar.'
13
+ task :install do
14
+ PKG_PATH = File.join(File.dirname(__FILE__), 'pkg')
15
+ NAME = File.basename(File.dirname(__FILE__))
16
+ rm_rf PKG_PATH
17
+ system "gem build #{NAME}.gemspec"
18
+ mkdir_p PKG_PATH
19
+ mv "#{NAME}-0.6.0.gem", PKG_PATH
20
+ system "gem install #{PKG_PATH}/#{NAME}-0.6.0.gem"
21
+ end
22
+ end
23
+
11
24
  Bones {
12
- name 'ffi-rzmq'
13
- authors 'Chuck Remes'
14
- email 'cremes@mac.com'
15
- url 'http://github.com/chuckremes/ffi-rzmq'
25
+ name 'ffi-rzmq'
26
+ authors 'Chuck Remes'
27
+ email 'cremes@mac.com'
28
+ url 'http://github.com/chuckremes/ffi-rzmq'
16
29
  readme_file 'README.rdoc'
17
30
  ruby_opts.clear # turn off warnings
31
+
32
+ # necessary for MRI; unnecessary for JRuby and RBX
33
+ # can't enable this until JRuby & RBX have a way of dealing with it cleanly
34
+ #depend_on 'ffi', '>= 1.0.0'
18
35
  }
19
36
 
@@ -0,0 +1,54 @@
1
+ require 'rubygems'
2
+ require 'ffi-rzmq'
3
+
4
+ if ARGV.length < 3
5
+ puts "usage: local_lat <connect-to> <message-size> <roundtrip-count>"
6
+ exit
7
+ end
8
+
9
+ link = ARGV[0]
10
+ message_size = ARGV[1].to_i
11
+ roundtrip_count = ARGV[2].to_i
12
+
13
+ #link = "tcp://127.0.0.1:5555"
14
+
15
+ ctx = ZMQ::Context.new
16
+ s1 = ctx.socket ZMQ::REQ
17
+ s2 = ctx.socket ZMQ::REP
18
+
19
+ s1.connect link
20
+ s2.bind link
21
+
22
+ poller = ZMQ::Poller.new
23
+ poller.register_readable s2
24
+ poller.register_readable s1
25
+
26
+
27
+ start_time = Time.now
28
+
29
+ # kick it off
30
+ message = ZMQ::Message.new("a" * message_size)
31
+ s1.send message, ZMQ::NOBLOCK
32
+ i = roundtrip_count
33
+
34
+ until i.zero?
35
+ i -= 1
36
+
37
+ begin
38
+ poller.poll_nonblock
39
+ rescue ZMQ::PollError => e
40
+ puts "efault? [#{e.efault?}]"
41
+ raise
42
+ end
43
+
44
+ poller.readables.each do |socket|
45
+ received_message = socket.recv_string ZMQ::NOBLOCK
46
+ socket.send ZMQ::Message.new(received_message), ZMQ::NOBLOCK
47
+ end
48
+ end
49
+
50
+ elapsed_usecs = (Time.now.to_f - start_time.to_f) * 1_000_000
51
+ latency = elapsed_usecs / roundtrip_count / 2
52
+
53
+ puts "mean latency: %.3f [us]" % latency
54
+ puts "received all messages in %.3f seconds" % (elapsed_usecs / 1_000_000)
data/ffi-rzmq.gemspec CHANGED
@@ -2,42 +2,42 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = %q{ffi-rzmq}
5
- s.version = "0.6.0"
5
+ s.version = "0.7.0"
6
6
 
7
7
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
8
  s.authors = ["Chuck Remes"]
9
- s.date = %q{2010-09-21}
9
+ s.date = %q{2010-12-22}
10
10
  s.description = %q{This gem wraps the ZeroMQ networking library using the ruby FFI (foreign
11
11
  function interface). It's a pure ruby wrapper so this gem can be loaded
12
12
  and run by any ruby runtime that supports FFI. That's all of them:
13
- MRI 1.8.7+, 1.9.x, Rubinius and JRuby.
13
+ MRI 1.9.x, Rubinius and JRuby.
14
14
 
15
15
  The impetus behind this library was to provide support for ZeroMQ in
16
- JRuby which has native threads. Unlike MRI, MacRuby, IronRuby and
16
+ JRuby which has native threads. Unlike MRI, IronRuby and
17
17
  Rubinius which all have a GIL, JRuby allows for threaded access to ruby
18
18
  code from outside extensions. ZeroMQ is heavily threaded, so until the
19
19
  other runtimes remove their GIL, JRuby will likely be the best
20
20
  environment to run this library.}
21
21
  s.email = %q{cremes@mac.com}
22
22
  s.extra_rdoc_files = ["History.txt", "README.rdoc", "examples/README.rdoc", "version.txt"]
23
- s.files = [".gitignore", "History.txt", "README.rdoc", "Rakefile", "examples/README.rdoc", "examples/local_lat.rb", "examples/local_lat_zerocopy.rb", "examples/local_throughput.rb", "examples/publish_subscribe.rb", "examples/remote_lat.rb", "examples/remote_lat_zerocopy.rb", "examples/remote_throughput.rb", "examples/reqrep_poll.rb", "examples/request_response.rb", "examples/xreqxrep_poll.rb", "ffi-rzmq.gemspec", "lib/ffi-rzmq.rb", "lib/ffi-rzmq/context.rb", "lib/ffi-rzmq/exceptions.rb", "lib/ffi-rzmq/message.rb", "lib/ffi-rzmq/poll.rb", "lib/ffi-rzmq/poll_items.rb", "lib/ffi-rzmq/socket.rb", "lib/ffi-rzmq/wrapper.rb", "lib/ffi-rzmq/zmq.rb", "spec/context_spec.rb", "spec/message_spec.rb", "spec/pushpull_spec.rb", "spec/reqrep_spec.rb", "spec/socket_spec.rb", "spec/spec_helper.rb", "version.txt"]
23
+ s.files = [".bnsignore", "History.txt", "README.rdoc", "Rakefile", "examples/README.rdoc", "examples/local_lat.rb", "examples/local_lat_poll.rb", "examples/local_lat_zerocopy.rb", "examples/local_throughput.rb", "examples/publish_subscribe.rb", "examples/remote_lat.rb", "examples/remote_lat_zerocopy.rb", "examples/remote_throughput.rb", "examples/reqrep_poll.rb", "examples/request_response.rb", "ffi-rzmq.gemspec", "lib/ffi-rzmq.rb", "lib/ffi-rzmq/context.rb", "lib/ffi-rzmq/exceptions.rb", "lib/ffi-rzmq/message.rb", "lib/ffi-rzmq/poll.rb", "lib/ffi-rzmq/poll_items.rb", "lib/ffi-rzmq/socket.rb", "lib/ffi-rzmq/wrapper.rb", "lib/ffi-rzmq/zmq.rb", "spec/context_spec.rb", "spec/message_spec.rb", "spec/pushpull_spec.rb", "spec/reqrep_spec.rb", "spec/socket_spec.rb", "spec/spec_helper.rb", "version.txt"]
24
24
  s.homepage = %q{http://github.com/chuckremes/ffi-rzmq}
25
25
  s.rdoc_options = ["--main", "README.rdoc"]
26
26
  s.require_paths = ["lib"]
27
27
  s.rubyforge_project = %q{ffi-rzmq}
28
28
  s.rubygems_version = %q{1.3.7}
29
- s.summary = %q{This gem wraps the ZeroMQ networking library using the ruby FFI (foreign function interface)}
29
+ s.summary = %q{This gem wraps the ZeroMQ networking library using the ruby FFI (foreign function interface).}
30
30
 
31
31
  if s.respond_to? :specification_version then
32
32
  current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
33
33
  s.specification_version = 3
34
34
 
35
35
  if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
36
- s.add_development_dependency(%q<bones>, [">= 3.4.7"])
36
+ s.add_development_dependency(%q<bones>, [">= 3.5.4"])
37
37
  else
38
- s.add_dependency(%q<bones>, [">= 3.4.7"])
38
+ s.add_dependency(%q<bones>, [">= 3.5.4"])
39
39
  end
40
40
  else
41
- s.add_dependency(%q<bones>, [">= 3.4.7"])
41
+ s.add_dependency(%q<bones>, [">= 3.5.4"])
42
42
  end
43
43
  end
@@ -38,7 +38,7 @@ module ZMQ
38
38
  # be allocated.
39
39
  #
40
40
  def initialize io_threads = 1
41
- @sockets ||= []
41
+ @sockets = []
42
42
  @context = LibZMQ.zmq_init io_threads
43
43
  @pointer = @context
44
44
  error_check ZMQ_INIT_STR, @context.null? ? 1 : 0
data/lib/ffi-rzmq/poll.rb CHANGED
@@ -28,6 +28,12 @@ module ZMQ
28
28
  # registered sockets belongs to an application thread in another
29
29
  # Context.
30
30
  #
31
+ # This method will return *immediately* when there are no registered
32
+ # sockets. In that case, the +timeout+ parameter is not honored. To
33
+ # prevent a CPU busy-loop, the caller of this method should detect
34
+ # this possible condition (via #size) and throttle the call
35
+ # frequency.
36
+ #
31
37
  def poll timeout = :blocking
32
38
  unless @items.empty?
33
39
  timeout = adjust timeout
@@ -178,7 +184,7 @@ module ZMQ
178
184
  if :blocking == timeout || -1 == timeout
179
185
  -1
180
186
  else
181
- timeout *= 1000
187
+ (timeout * 1000).to_i
182
188
  end
183
189
  end
184
190
  end
@@ -27,7 +27,7 @@ module ZMQ
27
27
  #
28
28
  # sock = Socket.new(Context.new, ZMQ::REQ, :receiver_class => ZMQ::ManagedMessage)
29
29
  #
30
- # Advanced users may want to replace the receiver class with their
30
+ # Advanced users may want to replace the receiver class with their
31
31
  # own custom class. The custom class must conform to the same public API
32
32
  # as ZMQ::Message.
33
33
  #
@@ -43,13 +43,19 @@ module ZMQ
43
43
 
44
44
  unless context_ptr.null?
45
45
  @socket = LibZMQ.zmq_socket context_ptr, type
46
- error_check ZMQ_SOCKET_STR, @socket.null? ? 1 : 0
47
- @name = SocketTypeNameMap[type]
46
+ if @socket
47
+ error_check ZMQ_SOCKET_STR, @socket.null? ? 1 : 0
48
+ @name = SocketTypeNameMap[type]
49
+ else
50
+ raise ContextError.new ZMQ_SOCKET_STR, 0, ETERM, "Socket pointer was null"
51
+ end
48
52
  else
49
53
  raise ContextError.new ZMQ_SOCKET_STR, 0, ETERM, "Context pointer was null"
50
54
  end
51
55
 
52
- #define_finalizer
56
+ @sockopt_cache = {}
57
+
58
+ define_finalizer
53
59
  end
54
60
 
55
61
  # Set the queue options on this socket.
@@ -61,6 +67,10 @@ module ZMQ
61
67
  # ZMQ::RATE
62
68
  # ZMQ::RECOVERY_IVL
63
69
  # ZMQ::MCAST_LOOP
70
+ # ZMQ::LINGER
71
+ # ZMQ::RECONNECT_IVL
72
+ # ZMQ::BACKLOG
73
+ # ZMQ::RECOVER_IVL_MSEC
64
74
  #
65
75
  # Valid +option_name+ values that take a string +option_value+ are:
66
76
  # ZMQ::IDENTITY
@@ -75,13 +85,18 @@ module ZMQ
75
85
  def setsockopt option_name, option_value, option_len = nil
76
86
  option_value = sanitize_value option_name, option_value
77
87
  option_len ||= option_value.size
78
-
88
+
79
89
  begin
80
90
  case option_name
81
- when HWM, SWAP, AFFINITY, RATE, RECOVERY_IVL, MCAST_LOOP, SNDBUF, RCVBUF
91
+ when HWM, SWAP, AFFINITY, RATE, RECOVERY_IVL, MCAST_LOOP, SNDBUF, RCVBUF, RECOVERY_IVL_MSEC
82
92
  option_value_ptr = LibC.malloc option_len
83
93
  option_value_ptr.write_long option_value
84
94
 
95
+ when LINGER, RECONNECT_IVL, BACKLOG
96
+ option_len = 4 # hard-code "int" length to 4 bytes
97
+ option_value_ptr = LibC.malloc option_len
98
+ option_value_ptr.write_int option_value
99
+
85
100
  when IDENTITY, SUBSCRIBE, UNSUBSCRIBE
86
101
  # note: not checking errno for failed memory allocations :(
87
102
  option_value_ptr = LibC.malloc option_len
@@ -114,6 +129,12 @@ module ZMQ
114
129
  # ZMQ::MCAST_LOOP - boolean
115
130
  # ZMQ::SNDBUF - integer
116
131
  # ZMQ::RCVBUF - integer
132
+ # ZMQ::FD - fd in an integer
133
+ # ZMQ::EVENTS - bitmap integer
134
+ # ZMQ::LINGER - integer measured in milliseconds
135
+ # ZMQ::RECONNECT_IVL - integer measured in milliseconds
136
+ # ZMQ::BACKLOG - integer
137
+ # ZMQ::RECOVER_IVL_MSEC - integer measured in milliseconds
117
138
  #
118
139
  # Can raise two kinds of exceptions depending on the error.
119
140
  # ContextError:: Raised when a socket operation is attempted on a terminated
@@ -125,8 +146,10 @@ module ZMQ
125
146
  option_value = FFI::MemoryPointer.new :pointer
126
147
  option_length = FFI::MemoryPointer.new :size_t
127
148
 
128
- unless [RCVMORE, HWM, SWAP, AFFINITY, RATE, RECOVERY_IVL, MCAST_LOOP,
129
- IDENTITY, SNDBUF, RCVBUF].include? option_name
149
+ unless [
150
+ RCVMORE, HWM, SWAP, AFFINITY, RATE, RECOVERY_IVL, MCAST_LOOP, IDENTITY,
151
+ SNDBUF, RCVBUF, FD, EVENTS, LINGER, RECONNECT_IVL, BACKLOG, RECOVERY_IVL_MSEC
152
+ ].include? option_name
130
153
  # we didn't understand the passed option argument
131
154
  # will force a raise
132
155
  error_check ZMQ_SETSOCKOPT_STR, -1
@@ -142,8 +165,10 @@ module ZMQ
142
165
  when RCVMORE, MCAST_LOOP
143
166
  # boolean return
144
167
  ret = option_value.read_long_long != 0
145
- when HWM, SWAP, AFFINITY, RATE, RECOVERY_IVL, SNDBUF, RCVBUF
168
+ when HWM, SWAP, AFFINITY, RATE, RECOVERY_IVL, SNDBUF, RCVBUF, RECOVERY_IVL_MSEC
146
169
  ret = option_value.read_long_long
170
+ when LINGER, RECONNECT_IVL, BACKLOG, FD, EVENTS
171
+ ret = option_value.read_int
147
172
  when IDENTITY
148
173
  ret = option_value.read_string(option_length.read_long_long)
149
174
  end
@@ -192,12 +217,16 @@ module ZMQ
192
217
  error_check ZMQ_CONNECT_STR, result_code
193
218
  end
194
219
 
195
- # Closes the socket. Any unprocessed messages in queue are dropped.
220
+ # Closes the socket. Any unprocessed messages in queue are sent or dropped
221
+ # depending upon the value of the socket option ZMQ::LINGER.
196
222
  #
197
223
  def close
198
- remove_finalizer
199
- result_code = LibZMQ.zmq_close @socket
200
- error_check ZMQ_CLOSE_STR, result_code
224
+ if @socket
225
+ remove_finalizer
226
+ result_code = LibZMQ.zmq_close @socket
227
+ error_check ZMQ_CLOSE_STR, result_code
228
+ @socket = nil
229
+ end
201
230
  end
202
231
 
203
232
  # Queues the message for transmission. Message is assumed to conform to the
@@ -326,21 +355,37 @@ module ZMQ
326
355
  flags != NOBLOCK ? error_check(ZMQ_RECV_STR, result_code) : error_check_nonblock(result_code)
327
356
  end
328
357
 
358
+ # Calls to ZMQ.getsockopt require us to pass in some pointers. We can cache and save those buffers
359
+ # for subsequent calls. This is a big perf win for calling RCVMORE which happens quite often.
360
+ # Cannot save the buffer for the IDENTITY.
329
361
  def alloc_temp_sockopt_buffers option_name
330
- length = FFI::MemoryPointer.new :int64
331
-
332
362
  case option_name
333
- when RCVMORE, MCAST_LOOP, HWM, SWAP, AFFINITY, RATE, RECOVERY_IVL, SNDBUF, RCVBUF
363
+ when RCVMORE, MCAST_LOOP, HWM, SWAP, AFFINITY, RATE, RECOVERY_IVL, SNDBUF, RCVBUF, RECOVERY_IVL_MSEC
334
364
  # int64_t
335
- length.write_long_long 8
336
- [FFI::MemoryPointer.new(:int64), length]
365
+ unless @sockopt_cache[:int64]
366
+ length = FFI::MemoryPointer.new :int64
367
+ length.write_long_long 8
368
+ @sockopt_cache[:int64] = [FFI::MemoryPointer.new(:int64), length]
369
+ end
370
+ @sockopt_cache[:int64]
371
+
372
+ when LINGER, RECONNECT_IVL, BACKLOG, FD, EVENTS
373
+ # int, 0mq assumes int is 4-bytes
374
+ unless @sockopt_cache[:int32]
375
+ length = FFI::MemoryPointer.new :int32
376
+ length.write_int 4
377
+ @sockopt_cache[:int32] = [FFI::MemoryPointer.new(:int32), length]
378
+ end
379
+ @sockopt_cache[:int32]
380
+
337
381
  when IDENTITY
382
+ length = FFI::MemoryPointer.new :int64
338
383
  # could be a string of up to 255 bytes
339
384
  length.write_long_long 255
340
385
  [FFI::MemoryPointer.new(255), length]
341
386
  end
342
387
  end
343
-
388
+
344
389
  def sanitize_value option_name, option_value
345
390
  case option_name
346
391
  when HWM, AFFINITY, SNDBUF, RCVBUF
@@ -352,8 +397,17 @@ module ZMQ
352
397
  end
353
398
  end
354
399
 
355
- def define_finalizer
356
- ObjectSpace.define_finalizer(self, self.class.close(@socket))
400
+ # require a minimum of 0mq 2.1.0 to support socket finalizers; it contains important
401
+ # fixes for sockets and threads so that a garbage collector thread can successfully
402
+ # reap this resource without crashing
403
+ if Util.minimum_api?([2, 1, 0])
404
+ def define_finalizer
405
+ ObjectSpace.define_finalizer(self, self.class.close(@socket))
406
+ end
407
+ else
408
+ def define_finalizer
409
+ # no op
410
+ end
357
411
  end
358
412
 
359
413
  def remove_finalizer