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,282 @@
|
|
1
|
+
|
2
|
+
module XS
|
3
|
+
|
4
|
+
# The factory constructor optionally takes a string as an argument. It will
|
5
|
+
# copy this string to native memory in preparation for transmission.
|
6
|
+
# So, don't pass a string unless you intend to send it. Internally it
|
7
|
+
# calls #copy_in_string.
|
8
|
+
#
|
9
|
+
# Call #close to release buffers when you are done with the data.
|
10
|
+
#
|
11
|
+
# (This class is not really zero-copy. Ruby makes this near impossible
|
12
|
+
# since Ruby objects can be relocated in memory by the GC at any
|
13
|
+
# time. There is no way to peg them to native memory or have them
|
14
|
+
# use non-movable native memory as backing store.)
|
15
|
+
#
|
16
|
+
# Message represents ruby equivalent of the +xs_msg_t+ C struct.
|
17
|
+
# Access the underlying memory buffer and the buffer size using the
|
18
|
+
# #data and #size methods respectively.
|
19
|
+
#
|
20
|
+
# It is recommended that this class be composed inside another class for
|
21
|
+
# access to the underlying buffer. The outer wrapper class can provide
|
22
|
+
# nice accessors for the information in the data buffer; a clever
|
23
|
+
# implementation can probably lazily encode/decode the data buffer
|
24
|
+
# on demand. Lots of protocols send more information than is strictly
|
25
|
+
# necessary, so only decode (copy from the 0mq buffer to Ruby) that
|
26
|
+
# which is necessary.
|
27
|
+
#
|
28
|
+
# When you are done using a *received* message object, call #close to
|
29
|
+
# release the associated buffers.
|
30
|
+
#
|
31
|
+
# received_message = Message.create
|
32
|
+
# if received_message
|
33
|
+
# rc = socket.recvmsg(received_message)
|
34
|
+
# if XS::Util.resultcode_ok?(rc)
|
35
|
+
# puts "Message contained: #{received_message.copy_out_string}"
|
36
|
+
# else
|
37
|
+
# STDERR.puts "Error when receiving message: #{XS::Util.error_string}"
|
38
|
+
# end
|
39
|
+
#
|
40
|
+
#
|
41
|
+
# Define a custom layout for the data sent between Crossroads peers.
|
42
|
+
#
|
43
|
+
# class MyMessage
|
44
|
+
# class Layout < FFI::Struct
|
45
|
+
# layout :value1, :uint8,
|
46
|
+
# :value2, :uint64,
|
47
|
+
# :value3, :uint32,
|
48
|
+
# :value4, [:char, 30]
|
49
|
+
# end
|
50
|
+
#
|
51
|
+
# def initialize msg_struct = nil
|
52
|
+
# if msg_struct
|
53
|
+
# @msg_t = msg_struct
|
54
|
+
# @data = Layout.new(@msg_t.data)
|
55
|
+
# else
|
56
|
+
# @pointer = FFI::MemoryPointer.new :byte, Layout.size, true
|
57
|
+
# @data = Layout.new @pointer
|
58
|
+
# end
|
59
|
+
# end
|
60
|
+
#
|
61
|
+
# def size() @size = @msg_t.size; end
|
62
|
+
#
|
63
|
+
# def value1
|
64
|
+
# @data[:value1]
|
65
|
+
# end
|
66
|
+
#
|
67
|
+
# def value4
|
68
|
+
# @data[:value4].to_ptr.read_string
|
69
|
+
# end
|
70
|
+
#
|
71
|
+
# def value1=(val)
|
72
|
+
# @data[:value1] = val
|
73
|
+
# end
|
74
|
+
#
|
75
|
+
# def create_sendable_message
|
76
|
+
# msg = Message.new
|
77
|
+
# msg.copy_in_bytes @pointer, Layout.size
|
78
|
+
# end
|
79
|
+
#
|
80
|
+
#
|
81
|
+
# message = Message.new
|
82
|
+
# successful_read = socket.recv message
|
83
|
+
# message = MyMessage.new message if successful_read
|
84
|
+
# puts "value1 is #{message.value1}"
|
85
|
+
#
|
86
|
+
class Message
|
87
|
+
include XS::Util
|
88
|
+
|
89
|
+
# Recommended way to create a standard message. A Message object is
|
90
|
+
# returned upon success, nil when allocation fails.
|
91
|
+
#
|
92
|
+
def self.create message = nil
|
93
|
+
new(message) rescue nil
|
94
|
+
end
|
95
|
+
|
96
|
+
def initialize message = nil
|
97
|
+
# allocate our own pointer so that we can tell it to *not* zero out
|
98
|
+
# the memory; it's pointless work since the library is going to
|
99
|
+
# overwrite it anyway.
|
100
|
+
@pointer = FFI::MemoryPointer.new Message.msg_size, 1, false
|
101
|
+
|
102
|
+
if message
|
103
|
+
copy_in_string message
|
104
|
+
else
|
105
|
+
# initialize an empty message structure to receive a message
|
106
|
+
result_code = LibXS.xs_msg_init @pointer
|
107
|
+
raise unless Util.resultcode_ok?(result_code)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
# Makes a copy of the ruby +string+ into a native memory buffer so
|
112
|
+
# that libxs can send it. The underlying library will handle
|
113
|
+
# deallocation of the native memory buffer.
|
114
|
+
#
|
115
|
+
# Can only be initialized via #copy_in_string or #copy_in_bytes once.
|
116
|
+
#
|
117
|
+
def copy_in_string string
|
118
|
+
string_size = string.respond_to?(:bytesize) ? string.bytesize : string.size
|
119
|
+
copy_in_bytes string, string_size if string
|
120
|
+
end
|
121
|
+
|
122
|
+
# Makes a copy of +len+ bytes from the ruby string +bytes+. Library
|
123
|
+
# handles deallocation of the native memory buffer.
|
124
|
+
#
|
125
|
+
# Can only be initialized via #copy_in_string or #copy_in_bytes once.
|
126
|
+
#
|
127
|
+
def copy_in_bytes bytes, len
|
128
|
+
data_buffer = LibC.malloc len
|
129
|
+
# writes the exact number of bytes, no null byte to terminate string
|
130
|
+
data_buffer.write_string bytes, len
|
131
|
+
|
132
|
+
# use libC to call free on the data buffer; earlier versions used an
|
133
|
+
# FFI::Function here that called back into Ruby, but Rubinius won't
|
134
|
+
# support that and there are issues with the other runtimes too
|
135
|
+
LibXS.xs_msg_init_data @pointer, data_buffer, len, LibC::Free, nil
|
136
|
+
end
|
137
|
+
|
138
|
+
# Provides the memory address of the +xs_msg_t+ struct. Used mostly for
|
139
|
+
# passing to other methods accessing the underlying library that
|
140
|
+
# require a real data address.
|
141
|
+
#
|
142
|
+
def address
|
143
|
+
@pointer
|
144
|
+
end
|
145
|
+
alias :pointer :address
|
146
|
+
|
147
|
+
def copy source
|
148
|
+
LibXS.xs_msg_copy @pointer, source
|
149
|
+
end
|
150
|
+
|
151
|
+
def move source
|
152
|
+
LibXS.xs_msg_move @pointer, source
|
153
|
+
end
|
154
|
+
|
155
|
+
# Provides the size of the data buffer for this +xs_msg_t+ C struct.
|
156
|
+
#
|
157
|
+
def size
|
158
|
+
LibXS.xs_msg_size @pointer
|
159
|
+
end
|
160
|
+
|
161
|
+
# Returns a pointer to the data buffer.
|
162
|
+
# This pointer should *never* be freed. It will automatically be freed
|
163
|
+
# when the +message+ object goes out of scope and gets garbage
|
164
|
+
# collected.
|
165
|
+
#
|
166
|
+
def data
|
167
|
+
LibXS.xs_msg_data @pointer
|
168
|
+
end
|
169
|
+
|
170
|
+
# Returns the data buffer as a string.
|
171
|
+
#
|
172
|
+
# Note: If this is binary data, it won't print very prettily.
|
173
|
+
#
|
174
|
+
def copy_out_string
|
175
|
+
data.read_string(size)
|
176
|
+
end
|
177
|
+
|
178
|
+
# Manually release the message struct and its associated data
|
179
|
+
# buffer.
|
180
|
+
#
|
181
|
+
# Only releases the buffer a single time. Subsequent calls are
|
182
|
+
# no ops.
|
183
|
+
#
|
184
|
+
def close
|
185
|
+
rc = 0
|
186
|
+
|
187
|
+
if @pointer
|
188
|
+
rc = LibXS.xs_msg_close @pointer
|
189
|
+
@pointer = nil
|
190
|
+
end
|
191
|
+
|
192
|
+
rc
|
193
|
+
end
|
194
|
+
|
195
|
+
# cache the msg size so we don't have to recalculate it when creating
|
196
|
+
# each new instance
|
197
|
+
@msg_size = LibXS::Msg.size
|
198
|
+
|
199
|
+
def self.msg_size() @msg_size; end
|
200
|
+
|
201
|
+
end # class Message
|
202
|
+
|
203
|
+
|
204
|
+
|
205
|
+
# A subclass of #Message that includes finalizers for deallocating
|
206
|
+
# native memory when this object is garbage collected. Note that on
|
207
|
+
# certain Ruby runtimes the use of finalizers can add 10s of
|
208
|
+
# microseconds of overhead for each message. The convenience comes
|
209
|
+
# at a price.
|
210
|
+
#
|
211
|
+
# The constructor optionally takes a string as an argument. It will
|
212
|
+
# copy this string to native memory in preparation for transmission.
|
213
|
+
# So, don't pass a string unless you intend to send it. Internally it
|
214
|
+
# calls #copy_in_string.
|
215
|
+
#
|
216
|
+
# Call #close to release buffers when you have *not* passed this on
|
217
|
+
# to Socket#send. That method calls #close on your behalf.
|
218
|
+
#
|
219
|
+
# When you are done using a *received* message object, just let it go out of
|
220
|
+
# scope to release the memory. During the next garbage collection run
|
221
|
+
# it will call the equivalent of #LibXS.xs_msg_close to release
|
222
|
+
# all buffers. Obviously, this automatic collection of message objects
|
223
|
+
# comes at the price of a larger memory footprint (for the
|
224
|
+
# finalizer proc object) and lower performance. If you wanted blistering
|
225
|
+
# performance, Ruby isn't there just yet.
|
226
|
+
#
|
227
|
+
# As noted above, for sent objects the underlying library will call close
|
228
|
+
# for you.
|
229
|
+
#
|
230
|
+
class ManagedMessage < Message
|
231
|
+
# Makes a copy of +len+ bytes from the ruby string +bytes+. Library
|
232
|
+
# handles deallocation of the native memory buffer.
|
233
|
+
#
|
234
|
+
def copy_in_bytes bytes, len
|
235
|
+
rc = super(bytes, len)
|
236
|
+
|
237
|
+
# make sure we have a way to deallocate this memory if the object goes
|
238
|
+
# out of scope
|
239
|
+
define_finalizer
|
240
|
+
rc
|
241
|
+
end
|
242
|
+
|
243
|
+
# Manually release the message struct and its associated data
|
244
|
+
# buffer.
|
245
|
+
#
|
246
|
+
def close
|
247
|
+
rc = super()
|
248
|
+
remove_finalizer
|
249
|
+
rc
|
250
|
+
end
|
251
|
+
|
252
|
+
|
253
|
+
private
|
254
|
+
|
255
|
+
def define_finalizer
|
256
|
+
ObjectSpace.define_finalizer(self, self.class.close(@pointer))
|
257
|
+
end
|
258
|
+
|
259
|
+
def remove_finalizer
|
260
|
+
ObjectSpace.undefine_finalizer self
|
261
|
+
end
|
262
|
+
|
263
|
+
# Message finalizer
|
264
|
+
# Note that there is no error checking for the call to #xs_msg_close.
|
265
|
+
# This is intentional. Since this code runs as a finalizer, there is no
|
266
|
+
# way to catch a raised exception anywhere near where the error actually
|
267
|
+
# occurred in the code, so we just ignore deallocation failures here.
|
268
|
+
def self.close ptr
|
269
|
+
Proc.new do
|
270
|
+
# release the data buffer
|
271
|
+
LibXS.xs_msg_close ptr
|
272
|
+
end
|
273
|
+
end
|
274
|
+
|
275
|
+
# cache the msg size so we don't have to recalculate it when creating
|
276
|
+
# each new instance
|
277
|
+
# need to do this again because ivars are not inheritable
|
278
|
+
@msg_size = LibXS::Msg.size
|
279
|
+
|
280
|
+
end # class ManagedMessage
|
281
|
+
|
282
|
+
end # module XS
|
@@ -0,0 +1,282 @@
|
|
1
|
+
|
2
|
+
module XS
|
3
|
+
|
4
|
+
# The factory constructor optionally takes a string as an argument. It will
|
5
|
+
# copy this string to native memory in preparation for transmission.
|
6
|
+
# So, don't pass a string unless you intend to send it. Internally it
|
7
|
+
# calls #copy_in_string.
|
8
|
+
#
|
9
|
+
# Call #close to release buffers when you are done with the data.
|
10
|
+
#
|
11
|
+
# (This class is not really zero-copy. Ruby makes this near impossible
|
12
|
+
# since Ruby objects can be relocated in memory by the GC at any
|
13
|
+
# time. There is no way to peg them to native memory or have them
|
14
|
+
# use non-movable native memory as backing store.)
|
15
|
+
#
|
16
|
+
# Message represents ruby equivalent of the +xs_msg_t+ C struct.
|
17
|
+
# Access the underlying memory buffer and the buffer size using the
|
18
|
+
# #data and #size methods respectively.
|
19
|
+
#
|
20
|
+
# It is recommended that this class be composed inside another class for
|
21
|
+
# access to the underlying buffer. The outer wrapper class can provide
|
22
|
+
# nice accessors for the information in the data buffer; a clever
|
23
|
+
# implementation can probably lazily encode/decode the data buffer
|
24
|
+
# on demand. Lots of protocols send more information than is strictly
|
25
|
+
# necessary, so only decode (copy from the 0mq buffer to Ruby) that
|
26
|
+
# which is necessary.
|
27
|
+
#
|
28
|
+
# When you are done using a *received* message object, call #close to
|
29
|
+
# release the associated buffers.
|
30
|
+
#
|
31
|
+
# received_message = Message.create
|
32
|
+
# if received_message
|
33
|
+
# rc = socket.recvmsg(received_message)
|
34
|
+
# if XS::Util.resultcode_ok?(rc)
|
35
|
+
# puts "Message contained: #{received_message.copy_out_string}"
|
36
|
+
# else
|
37
|
+
# STDERR.puts "Error when receiving message: #{XS::Util.error_string}"
|
38
|
+
# end
|
39
|
+
#
|
40
|
+
#
|
41
|
+
# Define a custom layout for the data sent between Crossroads peers.
|
42
|
+
#
|
43
|
+
# class MyMessage
|
44
|
+
# class Layout < FFI::Struct
|
45
|
+
# layout :value1, :uint8,
|
46
|
+
# :value2, :uint64,
|
47
|
+
# :value3, :uint32,
|
48
|
+
# :value4, [:char, 30]
|
49
|
+
# end
|
50
|
+
#
|
51
|
+
# def initialize msg_struct = nil
|
52
|
+
# if msg_struct
|
53
|
+
# @msg_t = msg_struct
|
54
|
+
# @data = Layout.new(@msg_t.data)
|
55
|
+
# else
|
56
|
+
# @pointer = FFI::MemoryPointer.new :byte, Layout.size, true
|
57
|
+
# @data = Layout.new @pointer
|
58
|
+
# end
|
59
|
+
# end
|
60
|
+
#
|
61
|
+
# def size() @size = @msg_t.size; end
|
62
|
+
#
|
63
|
+
# def value1
|
64
|
+
# @data[:value1]
|
65
|
+
# end
|
66
|
+
#
|
67
|
+
# def value4
|
68
|
+
# @data[:value4].to_ptr.read_string
|
69
|
+
# end
|
70
|
+
#
|
71
|
+
# def value1=(val)
|
72
|
+
# @data[:value1] = val
|
73
|
+
# end
|
74
|
+
#
|
75
|
+
# def create_sendable_message
|
76
|
+
# msg = Message.new
|
77
|
+
# msg.copy_in_bytes @pointer, Layout.size
|
78
|
+
# end
|
79
|
+
#
|
80
|
+
#
|
81
|
+
# message = Message.new
|
82
|
+
# successful_read = socket.recv message
|
83
|
+
# message = MyMessage.new message if successful_read
|
84
|
+
# puts "value1 is #{message.value1}"
|
85
|
+
#
|
86
|
+
class Message
|
87
|
+
include XS::Util
|
88
|
+
|
89
|
+
# Recommended way to create a standard message. A Message object is
|
90
|
+
# returned upon success, nil when allocation fails.
|
91
|
+
#
|
92
|
+
def self.create message = nil
|
93
|
+
new(message) rescue nil
|
94
|
+
end
|
95
|
+
|
96
|
+
def initialize message = nil
|
97
|
+
# allocate our own pointer so that we can tell it to *not* zero out
|
98
|
+
# the memory; it's pointless work since the library is going to
|
99
|
+
# overwrite it anyway.
|
100
|
+
@pointer = FFI::MemoryPointer.new Message.msg_size, 1, false
|
101
|
+
|
102
|
+
if message
|
103
|
+
copy_in_string message
|
104
|
+
else
|
105
|
+
# initialize an empty message structure to receive a message
|
106
|
+
result_code = LibXS.xs_msg_init @pointer
|
107
|
+
raise unless Util.resultcode_ok?(result_code)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
# Makes a copy of the ruby +string+ into a native memory buffer so
|
112
|
+
# that libzmq can send it. The underlying library will handle
|
113
|
+
# deallocation of the native memory buffer.
|
114
|
+
#
|
115
|
+
# Can only be initialized via #copy_in_string or #copy_in_bytes once.
|
116
|
+
#
|
117
|
+
def copy_in_string string
|
118
|
+
string_size = string.respond_to?(:bytesize) ? string.bytesize : string.size
|
119
|
+
copy_in_bytes string, string_size if string
|
120
|
+
end
|
121
|
+
|
122
|
+
# Makes a copy of +len+ bytes from the ruby string +bytes+. Library
|
123
|
+
# handles deallocation of the native memory buffer.
|
124
|
+
#
|
125
|
+
# Can only be initialized via #copy_in_string or #copy_in_bytes once.
|
126
|
+
#
|
127
|
+
def copy_in_bytes bytes, len
|
128
|
+
data_buffer = LibC.malloc len
|
129
|
+
# writes the exact number of bytes, no null byte to terminate string
|
130
|
+
data_buffer.write_string bytes, len
|
131
|
+
|
132
|
+
# use libC to call free on the data buffer; earlier versions used an
|
133
|
+
# FFI::Function here that called back into Ruby, but Rubinius won't
|
134
|
+
# support that and there are issues with the other runtimes too
|
135
|
+
LibXS.zmq_msg_init_data @pointer, data_buffer, len, LibC::Free, nil
|
136
|
+
end
|
137
|
+
|
138
|
+
# Provides the memory address of the +zmq_msg_t+ struct. Used mostly for
|
139
|
+
# passing to other methods accessing the underlying library that
|
140
|
+
# require a real data address.
|
141
|
+
#
|
142
|
+
def address
|
143
|
+
@pointer
|
144
|
+
end
|
145
|
+
alias :pointer :address
|
146
|
+
|
147
|
+
def copy source
|
148
|
+
LibXS.xs_msg_copy @pointer, source
|
149
|
+
end
|
150
|
+
|
151
|
+
def move source
|
152
|
+
LibXS.xs_msg_move @pointer, source
|
153
|
+
end
|
154
|
+
|
155
|
+
# Provides the size of the data buffer for this +zmq_msg_t+ C struct.
|
156
|
+
#
|
157
|
+
def size
|
158
|
+
LibZMQ.xs_msg_size @pointer
|
159
|
+
end
|
160
|
+
|
161
|
+
# Returns a pointer to the data buffer.
|
162
|
+
# This pointer should *never* be freed. It will automatically be freed
|
163
|
+
# when the +message+ object goes out of scope and gets garbage
|
164
|
+
# collected.
|
165
|
+
#
|
166
|
+
def data
|
167
|
+
LibXS.xs_msg_data @pointer
|
168
|
+
end
|
169
|
+
|
170
|
+
# Returns the data buffer as a string.
|
171
|
+
#
|
172
|
+
# Note: If this is binary data, it won't print very prettily.
|
173
|
+
#
|
174
|
+
def copy_out_string
|
175
|
+
data.read_string(size)
|
176
|
+
end
|
177
|
+
|
178
|
+
# Manually release the message struct and its associated data
|
179
|
+
# buffer.
|
180
|
+
#
|
181
|
+
# Only releases the buffer a single time. Subsequent calls are
|
182
|
+
# no ops.
|
183
|
+
#
|
184
|
+
def close
|
185
|
+
rc = 0
|
186
|
+
|
187
|
+
if @pointer
|
188
|
+
rc = LibXS.xs_msg_close @pointer
|
189
|
+
@pointer = nil
|
190
|
+
end
|
191
|
+
|
192
|
+
rc
|
193
|
+
end
|
194
|
+
|
195
|
+
# cache the msg size so we don't have to recalculate it when creating
|
196
|
+
# each new instance
|
197
|
+
@msg_size = LibXS::Msg.size
|
198
|
+
|
199
|
+
def self.msg_size() @msg_size; end
|
200
|
+
|
201
|
+
end # class Message
|
202
|
+
|
203
|
+
|
204
|
+
|
205
|
+
# A subclass of #Message that includes finalizers for deallocating
|
206
|
+
# native memory when this object is garbage collected. Note that on
|
207
|
+
# certain Ruby runtimes the use of finalizers can add 10s of
|
208
|
+
# microseconds of overhead for each message. The convenience comes
|
209
|
+
# at a price.
|
210
|
+
#
|
211
|
+
# The constructor optionally takes a string as an argument. It will
|
212
|
+
# copy this string to native memory in preparation for transmission.
|
213
|
+
# So, don't pass a string unless you intend to send it. Internally it
|
214
|
+
# calls #copy_in_string.
|
215
|
+
#
|
216
|
+
# Call #close to release buffers when you have *not* passed this on
|
217
|
+
# to Socket#send. That method calls #close on your behalf.
|
218
|
+
#
|
219
|
+
# When you are done using a *received* message object, just let it go out of
|
220
|
+
# scope to release the memory. During the next garbage collection run
|
221
|
+
# it will call the equivalent of #LibZMQ.zmq_msg_close to release
|
222
|
+
# all buffers. Obviously, this automatic collection of message objects
|
223
|
+
# comes at the price of a larger memory footprint (for the
|
224
|
+
# finalizer proc object) and lower performance. If you wanted blistering
|
225
|
+
# performance, Ruby isn't there just yet.
|
226
|
+
#
|
227
|
+
# As noted above, for sent objects the underlying library will call close
|
228
|
+
# for you.
|
229
|
+
#
|
230
|
+
class ManagedMessage < Message
|
231
|
+
# Makes a copy of +len+ bytes from the ruby string +bytes+. Library
|
232
|
+
# handles deallocation of the native memory buffer.
|
233
|
+
#
|
234
|
+
def copy_in_bytes bytes, len
|
235
|
+
rc = super(bytes, len)
|
236
|
+
|
237
|
+
# make sure we have a way to deallocate this memory if the object goes
|
238
|
+
# out of scope
|
239
|
+
define_finalizer
|
240
|
+
rc
|
241
|
+
end
|
242
|
+
|
243
|
+
# Manually release the message struct and its associated data
|
244
|
+
# buffer.
|
245
|
+
#
|
246
|
+
def close
|
247
|
+
rc = super()
|
248
|
+
remove_finalizer
|
249
|
+
rc
|
250
|
+
end
|
251
|
+
|
252
|
+
|
253
|
+
private
|
254
|
+
|
255
|
+
def define_finalizer
|
256
|
+
ObjectSpace.define_finalizer(self, self.class.close(@pointer))
|
257
|
+
end
|
258
|
+
|
259
|
+
def remove_finalizer
|
260
|
+
ObjectSpace.undefine_finalizer self
|
261
|
+
end
|
262
|
+
|
263
|
+
# Message finalizer
|
264
|
+
# Note that there is no error checking for the call to #zmq_msg_close.
|
265
|
+
# This is intentional. Since this code runs as a finalizer, there is no
|
266
|
+
# way to catch a raised exception anywhere near where the error actually
|
267
|
+
# occurred in the code, so we just ignore deallocation failures here.
|
268
|
+
def self.close ptr
|
269
|
+
Proc.new do
|
270
|
+
# release the data buffer
|
271
|
+
LibXS.zmq_msg_close ptr
|
272
|
+
end
|
273
|
+
end
|
274
|
+
|
275
|
+
# cache the msg size so we don't have to recalculate it when creating
|
276
|
+
# each new instance
|
277
|
+
# need to do this again because ivars are not inheritable
|
278
|
+
@msg_size = LibXS::Msg.size
|
279
|
+
|
280
|
+
end # class ManagedMessage
|
281
|
+
|
282
|
+
end # module XS
|