ffi-rxs 1.1.0 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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