ffi-rxs 1.0.0 → 1.0.1
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 +2 -0
- data/.travis.yml +10 -0
- data/.yardopts +9 -0
- data/CHANGELOG +11 -0
- data/LICENSE +20 -0
- data/README.textile +86 -0
- data/examples/LICENSE +18 -0
- data/examples/README +2 -0
- data/examples/durable_pub.rb +34 -0
- data/examples/durable_sub.rb +29 -0
- data/examples/latency_measurement.rb +140 -0
- data/examples/reply.rb +25 -0
- data/examples/req_rep_poll.rb +71 -0
- data/examples/request.rb +24 -0
- data/examples/task_sink.rb +31 -0
- data/examples/task_vent.rb +34 -0
- data/examples/task_worker.rb +36 -0
- data/examples/throughput_measurement.rb +169 -0
- data/examples/weather_upd_client.rb +47 -0
- data/examples/weather_upd_server.rb +35 -0
- data/examples/xreq_xrep_poll.rb +101 -0
- data/ffi-rxs.gemspec +3 -5
- data/lib/ffi-rxs/constants.rb +15 -11
- data/lib/ffi-rxs/context.rb +54 -60
- data/lib/ffi-rxs/exceptions.rb +7 -5
- data/lib/ffi-rxs/libc.rb +1 -0
- data/lib/ffi-rxs/libxs.rb +2 -0
- data/lib/ffi-rxs/message.rb +79 -50
- data/lib/ffi-rxs/poll.rb +54 -12
- data/lib/ffi-rxs/poll_items.rb +2 -1
- data/lib/ffi-rxs/socket.rb +243 -184
- data/lib/ffi-rxs/util.rb +25 -16
- data/lib/ffi-rxs/version.rb +3 -1
- data/lib/ffi-rxs.rb +8 -14
- data/spec/socket_spec.rb +63 -0
- metadata +31 -19
- data/README.rdoc +0 -86
data/lib/ffi-rxs/message.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# encoding: utf-8
|
1
2
|
|
2
3
|
module XS
|
3
4
|
|
@@ -28,71 +29,76 @@ module XS
|
|
28
29
|
# When you are done using a *received* message object, call #close to
|
29
30
|
# release the associated buffers.
|
30
31
|
#
|
31
|
-
#
|
32
|
-
#
|
33
|
-
#
|
34
|
-
#
|
35
|
-
#
|
36
|
-
#
|
37
|
-
#
|
38
|
-
#
|
32
|
+
# @example
|
33
|
+
# received_message = Message.create
|
34
|
+
# if received_message
|
35
|
+
# rc = socket.recvmsg(received_message)
|
36
|
+
# if XS::Util.resultcode_ok?(rc)
|
37
|
+
# puts "Message contained: #{received_message.copy_out_string}"
|
38
|
+
# else
|
39
|
+
# STDERR.puts "Error when receiving message: #{XS::Util.error_string}"
|
40
|
+
# end
|
39
41
|
#
|
42
|
+
# @example Define a custom layout for the data sent between Crossroads peers
|
43
|
+
# class MyMessage
|
44
|
+
# class Layout < FFI::Struct
|
45
|
+
# layout :value1, :uint8,
|
46
|
+
# :value2, :uint64,
|
47
|
+
# :value3, :uint32,
|
48
|
+
# :value4, [:char, 30]
|
49
|
+
# end
|
40
50
|
#
|
41
|
-
#
|
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
|
42
60
|
#
|
43
|
-
#
|
44
|
-
# class Layout < FFI::Struct
|
45
|
-
# layout :value1, :uint8,
|
46
|
-
# :value2, :uint64,
|
47
|
-
# :value3, :uint32,
|
48
|
-
# :value4, [:char, 30]
|
49
|
-
# end
|
61
|
+
# def size() @size = @msg_t.size; end
|
50
62
|
#
|
51
|
-
#
|
52
|
-
#
|
53
|
-
#
|
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
|
63
|
+
# def value1
|
64
|
+
# @data[:value1]
|
65
|
+
# end
|
60
66
|
#
|
61
|
-
#
|
67
|
+
# def value4
|
68
|
+
# @data[:value4].to_ptr.read_string
|
69
|
+
# end
|
62
70
|
#
|
63
|
-
#
|
64
|
-
#
|
65
|
-
#
|
71
|
+
# def value1=(val)
|
72
|
+
# @data[:value1] = val
|
73
|
+
# end
|
66
74
|
#
|
67
|
-
#
|
68
|
-
#
|
69
|
-
#
|
75
|
+
# def create_sendable_message
|
76
|
+
# msg = Message.new
|
77
|
+
# msg.copy_in_bytes @pointer, Layout.size
|
78
|
+
# end
|
79
|
+
# end
|
70
80
|
#
|
71
|
-
#
|
72
|
-
#
|
73
|
-
#
|
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}"
|
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
85
|
#
|
86
86
|
class Message
|
87
87
|
include XS::Util
|
88
88
|
|
89
|
-
# Recommended way to create a standard message.
|
90
|
-
# returned upon success, nil when allocation fails.
|
89
|
+
# Recommended way to create a standard message.
|
91
90
|
#
|
91
|
+
# @param message (optional)
|
92
|
+
#
|
93
|
+
# @return [Message] upon success
|
94
|
+
# @return nil when allocation fails
|
92
95
|
def self.create message = nil
|
93
96
|
new(message) rescue nil
|
94
97
|
end
|
95
98
|
|
99
|
+
# Initialize object
|
100
|
+
#
|
101
|
+
# @param message (optional)
|
96
102
|
def initialize message = nil
|
97
103
|
# allocate our own pointer so that we can tell it to *not* zero out
|
98
104
|
# the memory; it's pointless work since the library is going to
|
@@ -114,6 +120,7 @@ module XS
|
|
114
120
|
#
|
115
121
|
# Can only be initialized via #copy_in_string or #copy_in_bytes once.
|
116
122
|
#
|
123
|
+
# @param string
|
117
124
|
def copy_in_string string
|
118
125
|
string_size = string.respond_to?(:bytesize) ? string.bytesize : string.size
|
119
126
|
copy_in_bytes string, string_size if string
|
@@ -124,6 +131,8 @@ module XS
|
|
124
131
|
#
|
125
132
|
# Can only be initialized via #copy_in_string or #copy_in_bytes once.
|
126
133
|
#
|
134
|
+
# @param bytes
|
135
|
+
# @param length
|
127
136
|
def copy_in_bytes bytes, len
|
128
137
|
data_buffer = LibC.malloc len
|
129
138
|
# writes the exact number of bytes, no null byte to terminate string
|
@@ -138,22 +147,26 @@ module XS
|
|
138
147
|
# Provides the memory address of the +xs_msg_t+ struct. Used mostly for
|
139
148
|
# passing to other methods accessing the underlying library that
|
140
149
|
# require a real data address.
|
141
|
-
#
|
142
150
|
def address
|
143
151
|
@pointer
|
144
152
|
end
|
145
153
|
alias :pointer :address
|
146
154
|
|
155
|
+
# Copy content of message to another message
|
156
|
+
#
|
157
|
+
# @param source
|
147
158
|
def copy source
|
148
159
|
LibXS.xs_msg_copy @pointer, source
|
149
160
|
end
|
150
161
|
|
162
|
+
# Move content of message to another message
|
163
|
+
#
|
164
|
+
# @param source
|
151
165
|
def move source
|
152
166
|
LibXS.xs_msg_move @pointer, source
|
153
167
|
end
|
154
168
|
|
155
169
|
# Provides the size of the data buffer for this +xs_msg_t+ C struct.
|
156
|
-
#
|
157
170
|
def size
|
158
171
|
LibXS.xs_msg_size @pointer
|
159
172
|
end
|
@@ -163,6 +176,7 @@ module XS
|
|
163
176
|
# when the +message+ object goes out of scope and gets garbage
|
164
177
|
# collected.
|
165
178
|
#
|
179
|
+
# @return pointer
|
166
180
|
def data
|
167
181
|
LibXS.xs_msg_data @pointer
|
168
182
|
end
|
@@ -171,6 +185,7 @@ module XS
|
|
171
185
|
#
|
172
186
|
# Note: If this is binary data, it won't print very prettily.
|
173
187
|
#
|
188
|
+
# @return string
|
174
189
|
def copy_out_string
|
175
190
|
data.read_string(size)
|
176
191
|
end
|
@@ -181,6 +196,8 @@ module XS
|
|
181
196
|
# Only releases the buffer a single time. Subsequent calls are
|
182
197
|
# no ops.
|
183
198
|
#
|
199
|
+
# @return 0 if successful
|
200
|
+
# @return -1 if unsuccessful
|
184
201
|
def close
|
185
202
|
rc = 0
|
186
203
|
|
@@ -196,6 +213,7 @@ module XS
|
|
196
213
|
# each new instance
|
197
214
|
@msg_size = LibXS::Msg.size
|
198
215
|
|
216
|
+
# Store the message size
|
199
217
|
def self.msg_size() @msg_size; end
|
200
218
|
|
201
219
|
end # class Message
|
@@ -231,6 +249,11 @@ module XS
|
|
231
249
|
# Makes a copy of +len+ bytes from the ruby string +bytes+. Library
|
232
250
|
# handles deallocation of the native memory buffer.
|
233
251
|
#
|
252
|
+
# @param bytes
|
253
|
+
# @param length
|
254
|
+
#
|
255
|
+
# @return 0 if successful
|
256
|
+
# @return -1 if unsuccessful
|
234
257
|
def copy_in_bytes bytes, len
|
235
258
|
rc = super(bytes, len)
|
236
259
|
|
@@ -243,6 +266,8 @@ module XS
|
|
243
266
|
# Manually release the message struct and its associated data
|
244
267
|
# buffer.
|
245
268
|
#
|
269
|
+
# @return 0 if successful
|
270
|
+
# @return -1 if unsuccessful
|
246
271
|
def close
|
247
272
|
rc = super()
|
248
273
|
remove_finalizer
|
@@ -252,10 +277,12 @@ module XS
|
|
252
277
|
|
253
278
|
private
|
254
279
|
|
280
|
+
# Deletes native resources after object has been destroyed
|
255
281
|
def define_finalizer
|
256
282
|
ObjectSpace.define_finalizer(self, self.class.close(@pointer))
|
257
283
|
end
|
258
284
|
|
285
|
+
# Removes all finalizers for object
|
259
286
|
def remove_finalizer
|
260
287
|
ObjectSpace.undefine_finalizer self
|
261
288
|
end
|
@@ -265,6 +292,8 @@ module XS
|
|
265
292
|
# This is intentional. Since this code runs as a finalizer, there is no
|
266
293
|
# way to catch a raised exception anywhere near where the error actually
|
267
294
|
# occurred in the code, so we just ignore deallocation failures here.
|
295
|
+
#
|
296
|
+
# @param pointer
|
268
297
|
def self.close ptr
|
269
298
|
Proc.new do
|
270
299
|
# release the data buffer
|
data/lib/ffi-rxs/poll.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# encoding: utf-8
|
1
2
|
|
2
3
|
module XS
|
3
4
|
|
@@ -28,16 +29,14 @@ module XS
|
|
28
29
|
# this possible condition (via #size) and throttle the call
|
29
30
|
# frequency.
|
30
31
|
#
|
31
|
-
#
|
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.
|
32
|
+
# @param timeout
|
37
33
|
#
|
38
|
-
#
|
34
|
+
# @return 0 when there are no readable/writeable registered sockets
|
35
|
+
# @return number to indicate the number of readable or writable sockets
|
36
|
+
# @return -1 when there is an error
|
37
|
+
|
38
|
+
# When return code -1 use XS::Util.errno to get the related
|
39
39
|
# error number.
|
40
|
-
#
|
41
40
|
def poll timeout = :blocking
|
42
41
|
unless @items.empty?
|
43
42
|
timeout = adjust timeout
|
@@ -56,9 +55,9 @@ module XS
|
|
56
55
|
# The non-blocking version of #poll. See the #poll description for
|
57
56
|
# potential exceptions.
|
58
57
|
#
|
59
|
-
#
|
60
|
-
# to determine the underlying cause.
|
58
|
+
# @return -1 when an error is encountered.
|
61
59
|
#
|
60
|
+
# When return code -1 check XS::Util.errno to determine the underlying cause.
|
62
61
|
def poll_nonblock
|
63
62
|
poll 0
|
64
63
|
end
|
@@ -68,6 +67,12 @@ module XS
|
|
68
67
|
# will only get registered at most once. Calling multiple times with
|
69
68
|
# different values for +events+ will OR the event information together.
|
70
69
|
#
|
70
|
+
# @param socket
|
71
|
+
# @param events
|
72
|
+
# One of @XS::POLLIN@ and @XS::POLLOUT@
|
73
|
+
#
|
74
|
+
# @return true if successful
|
75
|
+
# @return false if not
|
71
76
|
def register sock, events = XS::POLLIN | XS::POLLOUT, fd = 0
|
72
77
|
return false if (sock.nil? && fd.zero?) || events.zero?
|
73
78
|
|
@@ -76,7 +81,6 @@ module XS
|
|
76
81
|
unless item
|
77
82
|
@sockets << sock
|
78
83
|
item = LibXS::PollItem.new
|
79
|
-
|
80
84
|
if sock.kind_of?(XS::Socket) || sock.kind_of?(Socket)
|
81
85
|
item[:socket] = sock.socket
|
82
86
|
item[:fd] = 0
|
@@ -95,6 +99,12 @@ module XS
|
|
95
99
|
# Deregister the +sock+ for +events+. When there are no events left,
|
96
100
|
# this also deletes the socket from the poll items.
|
97
101
|
#
|
102
|
+
# @param socket
|
103
|
+
# @param events
|
104
|
+
# One of @XS::POLLIN@ and @XS::POLLOUT@
|
105
|
+
#
|
106
|
+
# @return true if successful
|
107
|
+
# @return false if not
|
98
108
|
def deregister sock, events, fd = 0
|
99
109
|
return unless sock || !fd.zero?
|
100
110
|
|
@@ -113,24 +123,40 @@ module XS
|
|
113
123
|
|
114
124
|
# A helper method to register a +sock+ as readable events only.
|
115
125
|
#
|
126
|
+
# @param socket
|
127
|
+
#
|
128
|
+
# @return true if successful
|
129
|
+
# @return false if not
|
116
130
|
def register_readable sock
|
117
131
|
register sock, XS::POLLIN, 0
|
118
132
|
end
|
119
133
|
|
120
134
|
# A helper method to register a +sock+ for writable events only.
|
121
135
|
#
|
136
|
+
# @param socket
|
137
|
+
#
|
138
|
+
# @return true if successful
|
139
|
+
# @return false if not
|
122
140
|
def register_writable sock
|
123
141
|
register sock, XS::POLLOUT, 0
|
124
142
|
end
|
125
143
|
|
126
144
|
# A helper method to deregister a +sock+ for readable events.
|
127
145
|
#
|
146
|
+
# @param socket
|
147
|
+
#
|
148
|
+
# @return true if successful
|
149
|
+
# @return false if not
|
128
150
|
def deregister_readable sock
|
129
151
|
deregister sock, XS::POLLIN, 0
|
130
152
|
end
|
131
153
|
|
132
154
|
# A helper method to deregister a +sock+ for writable events.
|
133
155
|
#
|
156
|
+
# @param socket
|
157
|
+
#
|
158
|
+
# @return true if successful
|
159
|
+
# @return false if not
|
134
160
|
def deregister_writable sock
|
135
161
|
deregister sock, XS::POLLOUT, 0
|
136
162
|
end
|
@@ -142,6 +168,10 @@ module XS
|
|
142
168
|
# Can also be called directly to remove the socket from the polling
|
143
169
|
# array.
|
144
170
|
#
|
171
|
+
# @param socket
|
172
|
+
#
|
173
|
+
# @return true if successful
|
174
|
+
# @return false if not
|
145
175
|
def delete sock
|
146
176
|
unless (size = @sockets.size).zero?
|
147
177
|
@sockets.delete_if { |socket| socket.socket.address == sock.socket.address }
|
@@ -158,23 +188,32 @@ module XS
|
|
158
188
|
end
|
159
189
|
end
|
160
190
|
|
191
|
+
# Convenience method to return size of items array
|
161
192
|
def size(); @items.size; end
|
162
193
|
|
194
|
+
# Convenience method to inspect items array
|
163
195
|
def inspect
|
164
196
|
@items.inspect
|
165
197
|
end
|
166
198
|
|
199
|
+
# Convenience method to inspect poller
|
167
200
|
def to_s(); inspect; end
|
168
201
|
|
169
202
|
|
170
203
|
private
|
171
204
|
|
205
|
+
# Create hash of items
|
206
|
+
#
|
207
|
+
# @param empty hash
|
208
|
+
#
|
209
|
+
# @return hash
|
172
210
|
def items_hash hash
|
173
211
|
@items.each do |poll_item|
|
174
212
|
hash[@raw_to_socket[poll_item.socket.address]] = poll_item
|
175
213
|
end
|
176
214
|
end
|
177
215
|
|
216
|
+
# Update readables and writeables
|
178
217
|
def update_selectables
|
179
218
|
@readables.clear
|
180
219
|
@writables.clear
|
@@ -199,7 +238,10 @@ module XS
|
|
199
238
|
# Users will pass in values measured as
|
200
239
|
# milliseconds, so we need to convert that value to
|
201
240
|
# microseconds for the library.
|
202
|
-
|
241
|
+
#
|
242
|
+
# @param timeout
|
243
|
+
#
|
244
|
+
# @return number
|
203
245
|
def adjust timeout
|
204
246
|
if :blocking == timeout || -1 == timeout
|
205
247
|
-1
|
data/lib/ffi-rxs/poll_items.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# encoding: utf-8
|
1
2
|
|
2
3
|
module XS
|
3
4
|
class PollItems
|
@@ -22,7 +23,7 @@ module XS
|
|
22
23
|
unless @items.empty? || index.nil?
|
23
24
|
clean
|
24
25
|
|
25
|
-
# pointer arithmetic in ruby
|
26
|
+
# pointer arithmetic in ruby
|
26
27
|
pointer = @store + (@element_size * index)
|
27
28
|
|
28
29
|
# cast the memory to a PollItem
|