ffi-rxs 1.1.0 → 1.2.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.
@@ -1,282 +0,0 @@
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
data/lib/ffi-rxs/poll.rb~ DELETED
@@ -1,212 +0,0 @@
1
-
2
- module XS
3
-
4
- class Poller
5
- include XS::Util
6
-
7
- attr_reader :readables, :writables
8
-
9
- def initialize
10
- @items = XS::PollItems.new
11
- @raw_to_socket = {}
12
- @sockets = []
13
- @readables = []
14
- @writables = []
15
- end
16
-
17
- # Checks each registered socket for selectability based on the poll items'
18
- # registered +events+. Will block for up to +timeout+ milliseconds
19
- # A millisecond is 1/1000 of a second, so to block for 1 second
20
- # pass the value "1000" to #poll.
21
- #
22
- # Pass "-1" or +:blocking+ for +timeout+ for this call to block
23
- # indefinitely.
24
- #
25
- # This method will return *immediately* when there are no registered
26
- # sockets. In that case, the +timeout+ parameter is not honored. To
27
- # prevent a CPU busy-loop, the caller of this method should detect
28
- # this possible condition (via #size) and throttle the call
29
- # frequency.
30
- #
31
- # Returns 0 when there are no registered sockets that are readable
32
- # or writable.
33
- #
34
- # Return 1 (or greater) to indicate the number of readable or writable
35
- # sockets. These sockets should be processed using the #readables and
36
- # #writables accessors.
37
- #
38
- # Returns -1 when there is an error. Use ZMQ::Util.errno to get the related
39
- # error number.
40
- #
41
- def poll timeout = :blocking
42
- unless @items.empty?
43
- timeout = adjust timeout
44
- items_triggered = LibXS.xs_poll @items.address, @items.size, timeout
45
-
46
- if Util.resultcode_ok?(items_triggered)
47
- update_selectables
48
- end
49
-
50
- items_triggered
51
- else
52
- 0
53
- end
54
- end
55
-
56
- # The non-blocking version of #poll. See the #poll description for
57
- # potential exceptions.
58
- #
59
- # May return -1 when an error is encounted. Check ZMQ::Util.errno
60
- # to determine the underlying cause.
61
- #
62
- def poll_nonblock
63
- poll 0
64
- end
65
-
66
- # Register the +sock+ for +events+. This method is idempotent meaning
67
- # it can be called multiple times with the same data and the socket
68
- # will only get registered at most once. Calling multiple times with
69
- # different values for +events+ will OR the event information together.
70
- #
71
- def register sock, events = XS::POLLIN | XS::POLLOUT, fd = 0
72
- return false if (sock.nil? && fd.zero?) || events.zero?
73
-
74
- item = @items.get(@sockets.index(sock))
75
-
76
- unless item
77
- @sockets << sock
78
- item = LibXS::PollItem.new
79
-
80
- if sock.kind_of?(XS::Socket) || sock.kind_of?(Socket)
81
- item[:socket] = sock.socket
82
- item[:fd] = 0
83
- else
84
- item[:socket] = FFI::MemoryPointer.new(0)
85
- item[:fd] = fd
86
- end
87
-
88
- @raw_to_socket[item.socket.address] = sock
89
- @items << item
90
- end
91
-
92
- item[:events] |= events
93
- end
94
-
95
- # Deregister the +sock+ for +events+. When there are no events left,
96
- # this also deletes the socket from the poll items.
97
- #
98
- def deregister sock, events, fd = 0
99
- return unless sock || !fd.zero?
100
-
101
- item = @items.get(@sockets.index(sock))
102
-
103
- if item && (item[:events] & events) > 0
104
- # change the value in place
105
- item[:events] ^= events
106
-
107
- delete sock if item[:events].zero?
108
- true
109
- else
110
- false
111
- end
112
- end
113
-
114
- # A helper method to register a +sock+ as readable events only.
115
- #
116
- def register_readable sock
117
- register sock, XS::POLLIN, 0
118
- end
119
-
120
- # A helper method to register a +sock+ for writable events only.
121
- #
122
- def register_writable sock
123
- register sock, XS::POLLOUT, 0
124
- end
125
-
126
- # A helper method to deregister a +sock+ for readable events.
127
- #
128
- def deregister_readable sock
129
- deregister sock, XS::POLLIN, 0
130
- end
131
-
132
- # A helper method to deregister a +sock+ for writable events.
133
- #
134
- def deregister_writable sock
135
- deregister sock, XS::POLLOUT, 0
136
- end
137
-
138
- # Deletes the +sock+ for all subscribed events. Called internally
139
- # when a socket has been deregistered and has no more events
140
- # registered anywhere.
141
- #
142
- # Can also be called directly to remove the socket from the polling
143
- # array.
144
- #
145
- def delete sock
146
- unless (size = @sockets.size).zero?
147
- @sockets.delete_if { |socket| socket.socket.address == sock.socket.address }
148
- socket_deleted = size != @sockets.size
149
-
150
- item_deleted = @items.delete sock
151
-
152
- raw_deleted = @raw_to_socket.delete(sock.socket.address)
153
-
154
- socket_deleted && item_deleted && raw_deleted
155
-
156
- else
157
- false
158
- end
159
- end
160
-
161
- def size(); @items.size; end
162
-
163
- def inspect
164
- @items.inspect
165
- end
166
-
167
- def to_s(); inspect; end
168
-
169
-
170
- private
171
-
172
- def items_hash hash
173
- @items.each do |poll_item|
174
- hash[@raw_to_socket[poll_item.socket.address]] = poll_item
175
- end
176
- end
177
-
178
- def update_selectables
179
- @readables.clear
180
- @writables.clear
181
-
182
- @items.each do |poll_item|
183
- #FIXME: spec for sockets *and* file descriptors
184
- if poll_item.readable?
185
- @readables << (poll_item.socket.address.zero? ? poll_item.fd : @raw_to_socket[poll_item.socket.address])
186
- end
187
-
188
- if poll_item.writable?
189
- @writables << (poll_item.socket.address.zero? ? poll_item.fd : @raw_to_socket[poll_item.socket.address])
190
- end
191
- end
192
- end
193
-
194
- # Convert the timeout value to something usable by
195
- # the library.
196
- #
197
- # -1 or :blocking should be converted to -1.
198
- #
199
- # Users will pass in values measured as
200
- # milliseconds, so we need to convert that value to
201
- # microseconds for the library.
202
-
203
- def adjust timeout
204
- if :blocking == timeout || -1 == timeout
205
- -1
206
- else
207
- timeout.to_i
208
- end
209
- end
210
- end
211
-
212
- end # module XS
@@ -1,120 +0,0 @@
1
-
2
- module ZMQ
3
- class PollItems
4
- include Enumerable
5
-
6
- def initialize
7
- @element_size = LibZMQ::PollItem.size
8
- @store = nil
9
- @items = []
10
- end
11
-
12
- def size; @items.size; end
13
-
14
- def empty?; @items.empty?; end
15
-
16
- def address
17
- clean
18
- @store
19
- end
20
-
21
- def get index
22
- unless @items.empty? || index.nil?
23
- clean
24
-
25
- # pointer arithmetic in ruby! whee!
26
- pointer = @store + (@element_size * index)
27
-
28
- # cast the memory to a PollItem
29
- LibZMQ::PollItem.new pointer
30
- end
31
- end
32
- alias :[] :get
33
-
34
- def <<(obj)
35
- @dirty = true
36
- @items << obj
37
- end
38
- alias :push :<<
39
-
40
- def delete sock
41
- address = sock.socket.address
42
- found = false
43
-
44
- each_with_index do |item, index|
45
- if address == item[:socket].address
46
- @items.delete_at index
47
- found = true
48
- @dirty = true
49
- clean
50
- break
51
- end
52
- end
53
-
54
- # these semantics are different from the usual Array#delete; returns a
55
- # boolean instead of the actual item or nil
56
- found
57
- end
58
-
59
- def delete_at index
60
- value = nil
61
- unless @items.empty?
62
- value = @items.delete_at index
63
- @dirty = true
64
- clean
65
- end
66
-
67
- value
68
- end
69
-
70
- def each &blk
71
- clean
72
- index = 0
73
- until index >= @items.size do
74
- struct = get index
75
- yield struct
76
- index += 1
77
- end
78
- end
79
-
80
- def each_with_index &blk
81
- clean
82
- index = 0
83
- until index >= @items.size do
84
- struct = get index
85
- yield struct, index
86
- index += 1
87
- end
88
- end
89
-
90
- def inspect
91
- clean
92
- str = ""
93
- each { |item| str << "ptr [#{item[:socket]}], events [#{item[:events]}], revents [#{item[:revents]}], " }
94
- str.chop.chop
95
- end
96
-
97
- def to_s(); inspect; end
98
-
99
- private
100
-
101
- # Allocate a contiguous chunk of memory and copy over the PollItem structs
102
- # to this block. Note that the old +@store+ value goes out of scope so when
103
- # it is garbage collected that native memory should be automatically freed.
104
- def clean
105
- if @dirty
106
- @store = FFI::MemoryPointer.new @element_size, @items.size, true
107
-
108
- # copy over
109
- offset = 0
110
- @items.each do |item|
111
- LibC.memcpy(@store + offset, item.pointer, @element_size)
112
- offset += @element_size
113
- end
114
-
115
- @dirty = false
116
- end
117
- end
118
-
119
- end # class PollItems
120
- end # module ZMQ