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,309 @@
|
|
1
|
+
|
2
|
+
require File.join(File.dirname(__FILE__), %w[spec_helper])
|
3
|
+
|
4
|
+
module XS
|
5
|
+
|
6
|
+
|
7
|
+
describe Socket do
|
8
|
+
before(:all) do
|
9
|
+
@ctx = Context.new
|
10
|
+
end
|
11
|
+
|
12
|
+
after(:all) do
|
13
|
+
@ctx.terminate
|
14
|
+
end
|
15
|
+
|
16
|
+
|
17
|
+
shared_examples_for "any socket" do
|
18
|
+
|
19
|
+
it "returns -1 when there are no messages to read" do
|
20
|
+
array = []
|
21
|
+
rc = @receiver.recvmsgs(array, XS::NonBlocking)
|
22
|
+
Util.resultcode_ok?(rc).should be_false
|
23
|
+
end
|
24
|
+
|
25
|
+
it "gets EAGAIN when there are no messages to read" do
|
26
|
+
array = []
|
27
|
+
rc = @receiver.recvmsgs(array, XS::NonBlocking)
|
28
|
+
XS::Util.errno.should == XS::EAGAIN
|
29
|
+
end
|
30
|
+
|
31
|
+
it "returns the given array unmodified when there are no messages to read" do
|
32
|
+
array = []
|
33
|
+
rc = @receiver.recvmsgs(array, XS::NonBlocking)
|
34
|
+
array.size.should be_zero
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
shared_examples_for "sockets without exposed envelopes" do
|
40
|
+
|
41
|
+
it "read the single message and returns a successful result code" do
|
42
|
+
rc = @sender.send_string('test')
|
43
|
+
Util.resultcode_ok?(rc).should be_true
|
44
|
+
sleep 0.1 # give it time to deliver to the receiver
|
45
|
+
|
46
|
+
array = []
|
47
|
+
rc = @receiver.recvmsgs(array, XS::NonBlocking)
|
48
|
+
Util.resultcode_ok?(rc).should be_true
|
49
|
+
array.size.should == 1
|
50
|
+
end
|
51
|
+
|
52
|
+
it "read all message parts transmitted and returns a successful result code" do
|
53
|
+
strings = Array.new(10, 'test')
|
54
|
+
rc = @sender.send_strings(strings)
|
55
|
+
Util.resultcode_ok?(rc).should be_true
|
56
|
+
sleep 0.1 # give it time to deliver to the sub socket
|
57
|
+
|
58
|
+
array = []
|
59
|
+
rc = @receiver.recvmsgs(array, XS::NonBlocking)
|
60
|
+
Util.resultcode_ok?(rc).should be_true
|
61
|
+
array.size.should == 10
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
|
66
|
+
shared_examples_for "sockets with exposed envelopes" do
|
67
|
+
|
68
|
+
it "read the single message and returns a successful result code" do
|
69
|
+
rc = @sender.send_string('test')
|
70
|
+
Util.resultcode_ok?(rc).should be_true
|
71
|
+
sleep 0.1 # give it time to deliver to the receiver
|
72
|
+
|
73
|
+
array = []
|
74
|
+
rc = @receiver.recvmsgs(array, XS::NonBlocking)
|
75
|
+
Util.resultcode_ok?(rc).should be_true
|
76
|
+
array.size.should == 1 + 1 # extra 1 for envelope
|
77
|
+
end
|
78
|
+
|
79
|
+
it "read all message parts transmitted and returns a successful result code" do
|
80
|
+
strings = Array.new(10, 'test')
|
81
|
+
rc = @sender.send_strings(strings)
|
82
|
+
Util.resultcode_ok?(rc).should be_true
|
83
|
+
sleep 0.1 # give it time to deliver to the sub socket
|
84
|
+
|
85
|
+
array = []
|
86
|
+
rc = @receiver.recvmsgs(array, XS::NonBlocking)
|
87
|
+
Util.resultcode_ok?(rc).should be_true
|
88
|
+
array.size.should == 10 + 1 # add 1 for the envelope
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
92
|
+
|
93
|
+
context "PUB" do
|
94
|
+
|
95
|
+
describe "non-blocking #recvmsgs where sender binds & receiver connects" do
|
96
|
+
include APIHelper
|
97
|
+
|
98
|
+
before(:each) do
|
99
|
+
@receiver = @ctx.socket XS::SUB
|
100
|
+
port = connect_to_random_tcp_port(@receiver)
|
101
|
+
assert_ok(@receiver.setsockopt(XS::SUBSCRIBE, ''))
|
102
|
+
@sender = @ctx.socket XS::PUB
|
103
|
+
assert_ok(@sender.bind("tcp://127.0.0.1:#{port}"))
|
104
|
+
sleep 0.3
|
105
|
+
end
|
106
|
+
|
107
|
+
after(:each) do
|
108
|
+
@receiver.close
|
109
|
+
@sender.close
|
110
|
+
end
|
111
|
+
|
112
|
+
it_behaves_like "any socket"
|
113
|
+
it_behaves_like "sockets without exposed envelopes"
|
114
|
+
|
115
|
+
end # describe 'non-blocking recvmsgs'
|
116
|
+
|
117
|
+
end # Pub
|
118
|
+
|
119
|
+
context "REQ" do
|
120
|
+
|
121
|
+
describe "non-blocking #recvmsgs where sender connects & receiver binds" do
|
122
|
+
include APIHelper
|
123
|
+
|
124
|
+
before(:each) do
|
125
|
+
@receiver = @ctx.socket XS::REP
|
126
|
+
port = bind_to_random_tcp_port(@receiver)
|
127
|
+
@sender = @ctx.socket XS::REQ
|
128
|
+
assert_ok(@sender.connect("tcp://127.0.0.1:#{port}"))
|
129
|
+
sleep 0.1
|
130
|
+
end
|
131
|
+
|
132
|
+
after(:each) do
|
133
|
+
@receiver.close
|
134
|
+
@sender.close
|
135
|
+
end
|
136
|
+
|
137
|
+
it_behaves_like "any socket"
|
138
|
+
it_behaves_like "sockets without exposed envelopes"
|
139
|
+
|
140
|
+
end # describe 'non-blocking recvmsgs'
|
141
|
+
|
142
|
+
describe "non-blocking #recvmsgs where sender binds & receiver connects" do
|
143
|
+
include APIHelper
|
144
|
+
|
145
|
+
before(:each) do
|
146
|
+
@receiver = @ctx.socket XS::REP
|
147
|
+
port = connect_to_random_tcp_port(@receiver)
|
148
|
+
@sender = @ctx.socket XS::REQ
|
149
|
+
assert_ok(@sender.bind("tcp://127.0.0.1:#{port}"))
|
150
|
+
sleep 0.1
|
151
|
+
end
|
152
|
+
|
153
|
+
after(:each) do
|
154
|
+
@receiver.close
|
155
|
+
@sender.close
|
156
|
+
end
|
157
|
+
|
158
|
+
it_behaves_like "any socket"
|
159
|
+
it_behaves_like "sockets without exposed envelopes"
|
160
|
+
|
161
|
+
end # describe 'non-blocking recvmsgs'
|
162
|
+
|
163
|
+
end # REQ
|
164
|
+
|
165
|
+
|
166
|
+
context "PUSH" do
|
167
|
+
|
168
|
+
describe "non-blocking #recvmsgs where sender connects & receiver binds" do
|
169
|
+
include APIHelper
|
170
|
+
|
171
|
+
before(:each) do
|
172
|
+
@receiver = @ctx.socket XS::PULL
|
173
|
+
port = bind_to_random_tcp_port(@receiver)
|
174
|
+
@sender = @ctx.socket XS::PUSH
|
175
|
+
assert_ok(@sender.connect("tcp://127.0.0.1:#{port}"))
|
176
|
+
sleep 0.1
|
177
|
+
end
|
178
|
+
|
179
|
+
after(:each) do
|
180
|
+
@receiver.close
|
181
|
+
@sender.close
|
182
|
+
end
|
183
|
+
|
184
|
+
it_behaves_like "any socket"
|
185
|
+
it_behaves_like "sockets without exposed envelopes"
|
186
|
+
|
187
|
+
end # describe 'non-blocking recvmsgs'
|
188
|
+
|
189
|
+
describe "non-blocking #recvmsgs where sender binds & receiver connects" do
|
190
|
+
include APIHelper
|
191
|
+
|
192
|
+
before(:each) do
|
193
|
+
@receiver = @ctx.socket XS::PULL
|
194
|
+
port = connect_to_random_tcp_port(@receiver)
|
195
|
+
@sender = @ctx.socket XS::PUSH
|
196
|
+
assert_ok(@sender.bind("tcp://127.0.0.1:#{port}"))
|
197
|
+
sleep 0.1
|
198
|
+
end
|
199
|
+
|
200
|
+
after(:each) do
|
201
|
+
@receiver.close
|
202
|
+
@sender.close
|
203
|
+
end
|
204
|
+
|
205
|
+
it_behaves_like "any socket"
|
206
|
+
it_behaves_like "sockets without exposed envelopes"
|
207
|
+
|
208
|
+
end # describe 'non-blocking recvmsgs'
|
209
|
+
|
210
|
+
end # PUSH
|
211
|
+
|
212
|
+
|
213
|
+
context "DEALER" do
|
214
|
+
|
215
|
+
describe "non-blocking #recvmsgs where sender connects & receiver binds" do
|
216
|
+
include APIHelper
|
217
|
+
|
218
|
+
before(:each) do
|
219
|
+
@receiver = @ctx.socket XS::ROUTER
|
220
|
+
port = bind_to_random_tcp_port(@receiver)
|
221
|
+
@sender = @ctx.socket XS::DEALER
|
222
|
+
assert_ok(@sender.connect("tcp://127.0.0.1:#{port}"))
|
223
|
+
sleep 0.1
|
224
|
+
end
|
225
|
+
|
226
|
+
after(:each) do
|
227
|
+
@receiver.close
|
228
|
+
@sender.close
|
229
|
+
end
|
230
|
+
|
231
|
+
it_behaves_like "any socket"
|
232
|
+
it_behaves_like "sockets with exposed envelopes"
|
233
|
+
|
234
|
+
end # describe 'non-blocking recvmsgs'
|
235
|
+
|
236
|
+
describe "non-blocking #recvmsgs where sender binds & receiver connects" do
|
237
|
+
include APIHelper
|
238
|
+
|
239
|
+
before(:each) do
|
240
|
+
@receiver = @ctx.socket XS::ROUTER
|
241
|
+
port = connect_to_random_tcp_port(@receiver)
|
242
|
+
@sender = @ctx.socket XS::DEALER
|
243
|
+
assert_ok(@sender.bind("tcp://127.0.0.1:#{port}"))
|
244
|
+
sleep 0.1
|
245
|
+
end
|
246
|
+
|
247
|
+
after(:each) do
|
248
|
+
@receiver.close
|
249
|
+
@sender.close
|
250
|
+
end
|
251
|
+
|
252
|
+
it_behaves_like "any socket"
|
253
|
+
it_behaves_like "sockets with exposed envelopes"
|
254
|
+
|
255
|
+
end # describe 'non-blocking recvmsgs'
|
256
|
+
|
257
|
+
end # DEALER
|
258
|
+
|
259
|
+
|
260
|
+
context "XREQ" do
|
261
|
+
|
262
|
+
describe "non-blocking #recvmsgs where sender connects & receiver binds" do
|
263
|
+
include APIHelper
|
264
|
+
|
265
|
+
before(:each) do
|
266
|
+
@receiver = @ctx.socket XS::XREP
|
267
|
+
port = bind_to_random_tcp_port(@receiver)
|
268
|
+
@sender = @ctx.socket XS::XREQ
|
269
|
+
assert_ok(@sender.connect("tcp://127.0.0.1:#{port}"))
|
270
|
+
sleep 0.1
|
271
|
+
end
|
272
|
+
|
273
|
+
after(:each) do
|
274
|
+
@receiver.close
|
275
|
+
@sender.close
|
276
|
+
end
|
277
|
+
|
278
|
+
it_behaves_like "any socket"
|
279
|
+
it_behaves_like "sockets with exposed envelopes"
|
280
|
+
|
281
|
+
end # describe 'non-blocking recvmsgs'
|
282
|
+
|
283
|
+
describe "non-blocking #recvmsgs where sender binds & receiver connects" do
|
284
|
+
include APIHelper
|
285
|
+
|
286
|
+
before(:each) do
|
287
|
+
@receiver = @ctx.socket XS::XREP
|
288
|
+
port = connect_to_random_tcp_port(@receiver)
|
289
|
+
@sender = @ctx.socket XS::XREQ
|
290
|
+
assert_ok(@sender.bind("tcp://127.0.0.1:#{port}"))
|
291
|
+
sleep 0.1
|
292
|
+
end
|
293
|
+
|
294
|
+
after(:each) do
|
295
|
+
@receiver.close
|
296
|
+
@sender.close
|
297
|
+
end
|
298
|
+
|
299
|
+
it_behaves_like "any socket"
|
300
|
+
it_behaves_like "sockets with exposed envelopes"
|
301
|
+
|
302
|
+
end # describe 'non-blocking recvmsgs'
|
303
|
+
|
304
|
+
end # XREQ
|
305
|
+
|
306
|
+
end # describe Socket
|
307
|
+
|
308
|
+
|
309
|
+
end # module XS
|
data/spec/poll_spec.rb
ADDED
@@ -0,0 +1,168 @@
|
|
1
|
+
$: << "." # added for ruby 1.9.2 compatibilty; it doesn't include the current directory on the load path anymore
|
2
|
+
|
3
|
+
require File.join(File.dirname(__FILE__), %w[spec_helper])
|
4
|
+
|
5
|
+
module XS
|
6
|
+
|
7
|
+
|
8
|
+
describe Poller do
|
9
|
+
|
10
|
+
context "when initializing" do
|
11
|
+
include APIHelper
|
12
|
+
|
13
|
+
it "should allocate a PollItems instance" do
|
14
|
+
PollItems.should_receive(:new)
|
15
|
+
|
16
|
+
Poller.new
|
17
|
+
end
|
18
|
+
|
19
|
+
end # context initializing
|
20
|
+
|
21
|
+
|
22
|
+
context "#register" do
|
23
|
+
|
24
|
+
let(:poller) { Poller.new }
|
25
|
+
let(:socket) { mock('socket') }
|
26
|
+
|
27
|
+
it "should return false when given a nil socket and no file descriptor" do
|
28
|
+
poller.register(nil, XS::POLLIN, 0).should be_false
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should return false when given 0 for +events+ (e.g. no registration)" do
|
32
|
+
poller.register(socket, 0).should be_false
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should return the registered event value when given a nil socket and a valid non-zero file descriptor" do
|
36
|
+
poller.register(nil, XS::POLLIN, 1).should == XS::POLLIN
|
37
|
+
end
|
38
|
+
|
39
|
+
it "should return the default registered event value when given a valid socket" do
|
40
|
+
poller.register(socket).should == (XS::POLLIN | XS::POLLOUT)
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should access the raw 0mq socket" do
|
44
|
+
raw_socket = FFI::MemoryPointer.new(4)
|
45
|
+
socket.should_receive(:kind_of?).with(XS::Socket).and_return(true)
|
46
|
+
socket.should_receive(:socket).and_return(raw_socket)
|
47
|
+
|
48
|
+
poller.register(socket)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
|
53
|
+
context "#delete" do
|
54
|
+
before(:all) do
|
55
|
+
@context = Context.new
|
56
|
+
end
|
57
|
+
|
58
|
+
before(:each) do
|
59
|
+
@socket = @context.socket(XREQ)
|
60
|
+
@socket.setsockopt(LINGER, 0)
|
61
|
+
@poller = Poller.new
|
62
|
+
end
|
63
|
+
|
64
|
+
after(:each) do
|
65
|
+
@socket.close
|
66
|
+
end
|
67
|
+
|
68
|
+
after(:all) do
|
69
|
+
@context.terminate
|
70
|
+
end
|
71
|
+
|
72
|
+
it "should return false for an unregistered socket (i.e. not found)" do
|
73
|
+
@poller.delete(@socket).should be_false
|
74
|
+
end
|
75
|
+
|
76
|
+
it "returns true for a sucessfully deleted socket when only 1 is registered" do
|
77
|
+
socket1 = @context.socket(REP)
|
78
|
+
socket1.setsockopt(LINGER, 0)
|
79
|
+
|
80
|
+
@poller.register socket1
|
81
|
+
@poller.delete(socket1).should be_true
|
82
|
+
socket1.close
|
83
|
+
end
|
84
|
+
|
85
|
+
it "returns true for a sucessfully deleted socket when more than 1 is registered" do
|
86
|
+
socket1 = @context.socket(REP)
|
87
|
+
socket2 = @context.socket(REP)
|
88
|
+
socket1.setsockopt(LINGER, 0)
|
89
|
+
socket2.setsockopt(LINGER, 0)
|
90
|
+
|
91
|
+
@poller.register socket1
|
92
|
+
@poller.register socket2
|
93
|
+
@poller.delete(socket2).should be_true
|
94
|
+
socket1.close
|
95
|
+
socket2.close
|
96
|
+
end
|
97
|
+
|
98
|
+
end
|
99
|
+
|
100
|
+
|
101
|
+
context "poll" do
|
102
|
+
include APIHelper
|
103
|
+
|
104
|
+
before(:all) do
|
105
|
+
@context = Context.new
|
106
|
+
end
|
107
|
+
|
108
|
+
before(:each) do
|
109
|
+
@socket = @context.socket(REQ)
|
110
|
+
@socket2 = @context.socket(REP)
|
111
|
+
@socket.setsockopt(LINGER, 0)
|
112
|
+
@socket2.setsockopt(LINGER, 0)
|
113
|
+
port = bind_to_random_tcp_port(@socket2)
|
114
|
+
@socket.connect(local_transport_string(port))
|
115
|
+
sleep 0.2
|
116
|
+
@poller = Poller.new
|
117
|
+
end
|
118
|
+
|
119
|
+
after(:each) do
|
120
|
+
@socket.close
|
121
|
+
@socket2.close
|
122
|
+
end
|
123
|
+
|
124
|
+
after(:all) do
|
125
|
+
#@context.terminate
|
126
|
+
end
|
127
|
+
|
128
|
+
it "returns 0 when there are no sockets to poll" do
|
129
|
+
rc = @poller.poll(0)
|
130
|
+
rc.should be_zero
|
131
|
+
end
|
132
|
+
|
133
|
+
it "returns 0 when there is a single socket to poll and no events" do
|
134
|
+
@poller.register(@socket, 0)
|
135
|
+
rc = @poller.poll(0)
|
136
|
+
rc.should be_zero
|
137
|
+
end
|
138
|
+
|
139
|
+
it "returns 1 when there is a read event on a socket" do
|
140
|
+
@poller.register_writable(@socket)
|
141
|
+
@poller.register_readable(@socket2)
|
142
|
+
sleep 0.2
|
143
|
+
|
144
|
+
@socket.send_string('test')
|
145
|
+
sleep 0.1
|
146
|
+
rc = @poller.poll(0)
|
147
|
+
rc.should == 1
|
148
|
+
end
|
149
|
+
|
150
|
+
it "returns 1 when there is a read event on one socket and the second socket has been removed from polling" do
|
151
|
+
@poller.register_readable(@socket2)
|
152
|
+
@poller.register_writable(@socket)
|
153
|
+
sleep 0.2
|
154
|
+
|
155
|
+
@socket.send_string('test')
|
156
|
+
@poller.deregister_writable(@socket)
|
157
|
+
@socket.close
|
158
|
+
sleep 0.1
|
159
|
+
rc = @poller.poll(0)
|
160
|
+
rc.should == 1
|
161
|
+
end
|
162
|
+
end # poll
|
163
|
+
|
164
|
+
|
165
|
+
end # describe Poll
|
166
|
+
|
167
|
+
|
168
|
+
end # module XS
|
@@ -0,0 +1,113 @@
|
|
1
|
+
|
2
|
+
require File.join(File.dirname(__FILE__), %w[spec_helper])
|
3
|
+
|
4
|
+
module XS
|
5
|
+
describe Context do
|
6
|
+
context "when running basic push pull" do
|
7
|
+
include APIHelper
|
8
|
+
|
9
|
+
let(:string) { "booga-booga" }
|
10
|
+
|
11
|
+
before(:each) do
|
12
|
+
$stdout.flush
|
13
|
+
@context = XS::Context.new
|
14
|
+
@push = @context.socket XS::PUSH
|
15
|
+
@pull = @context.socket XS::PULL
|
16
|
+
@push.setsockopt XS::LINGER, 0
|
17
|
+
@pull.setsockopt XS::LINGER, 0
|
18
|
+
port = connect_to_random_tcp_port(@pull)
|
19
|
+
@link = "tcp://127.0.0.1:#{port}"
|
20
|
+
#@link = "inproc://push_pull_test" # can't connect to inproc *before* bind
|
21
|
+
@push.bind @link
|
22
|
+
end
|
23
|
+
|
24
|
+
after(:each) do
|
25
|
+
@push.close
|
26
|
+
@pull.close
|
27
|
+
@context.terminate
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should receive an exact copy of the sent message using Message objects directly on one pull socket" do
|
31
|
+
@push.send_string string
|
32
|
+
received = ''
|
33
|
+
rc = @pull.recv_string received
|
34
|
+
assert_ok(rc)
|
35
|
+
received.should == string
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should receive an exact string copy of the message sent when receiving in non-blocking mode and using Message objects directly" do
|
39
|
+
sent_message = Message.new string
|
40
|
+
received_message = Message.new
|
41
|
+
|
42
|
+
rc = @push.sendmsg sent_message
|
43
|
+
rc.should == string.size
|
44
|
+
sleep 0.1 # give it time for delivery
|
45
|
+
rc = @pull.recvmsg received_message, XS::NonBlocking
|
46
|
+
rc.should == string.size
|
47
|
+
received_message.copy_out_string.should == string
|
48
|
+
end
|
49
|
+
|
50
|
+
|
51
|
+
|
52
|
+
it "should receive a single message for each message sent on each socket listening, when an equal number pulls to messages and a unique socket per thread" do
|
53
|
+
received = []
|
54
|
+
threads = []
|
55
|
+
count = 4
|
56
|
+
@pull.close # close this one since we aren't going to use it below and we don't want it to receive a message
|
57
|
+
mutex = Mutex.new
|
58
|
+
|
59
|
+
count.times do |i|
|
60
|
+
threads << Thread.new do
|
61
|
+
pull = @context.socket XS::PULL
|
62
|
+
rc = pull.setsockopt XS::LINGER, 0
|
63
|
+
rc = pull.connect @link
|
64
|
+
rc.should == 0
|
65
|
+
buffer = ''
|
66
|
+
rc = pull.recv_string buffer
|
67
|
+
rc.should == 11
|
68
|
+
mutex.synchronize { received << buffer }
|
69
|
+
pull.close
|
70
|
+
end
|
71
|
+
sleep 0.01 # give each thread time to spin up
|
72
|
+
end
|
73
|
+
|
74
|
+
count.times { @push.send_string(string) }
|
75
|
+
|
76
|
+
threads.each {|t| t.join}
|
77
|
+
|
78
|
+
received.find_all {|r| r == string}.length.should == count
|
79
|
+
end
|
80
|
+
|
81
|
+
it "should receive a single message for each message sent on each socket listening, when an equal number pulls to messages and a single shared socket protected by a mutex" do
|
82
|
+
received = []
|
83
|
+
threads = []
|
84
|
+
count = 4
|
85
|
+
@pull.close # close this one since we aren't going to use it below and we don't want it to receive a message
|
86
|
+
pull = @context.socket XS::PULL
|
87
|
+
rc = pull.setsockopt XS::LINGER, 0
|
88
|
+
rc = pull.connect @link
|
89
|
+
rc.should == 0
|
90
|
+
mutex = Mutex.new
|
91
|
+
|
92
|
+
count.times do |i|
|
93
|
+
threads << Thread.new do
|
94
|
+
buffer = ''
|
95
|
+
rc = 0
|
96
|
+
mutex.synchronize { rc = pull.recv_string buffer }
|
97
|
+
rc.should == 11
|
98
|
+
mutex.synchronize { received << buffer }
|
99
|
+
end
|
100
|
+
sleep 0.01 # give each thread time to spin up
|
101
|
+
end
|
102
|
+
|
103
|
+
count.times { @push.send_string(string) }
|
104
|
+
|
105
|
+
threads.each {|t| t.join}
|
106
|
+
pull.close
|
107
|
+
|
108
|
+
received.find_all {|r| r == string}.length.should == count
|
109
|
+
end
|
110
|
+
|
111
|
+
end # @context ping-pong
|
112
|
+
end # describe
|
113
|
+
end # module XS
|
data/spec/reqrep_spec.rb
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
|
2
|
+
require File.join(File.dirname(__FILE__), %w[spec_helper])
|
3
|
+
|
4
|
+
module XS
|
5
|
+
|
6
|
+
|
7
|
+
describe Context do
|
8
|
+
|
9
|
+
context "when running ping pong" do
|
10
|
+
include APIHelper
|
11
|
+
|
12
|
+
let(:string) { "booga-booga" }
|
13
|
+
|
14
|
+
before(:each) do
|
15
|
+
context = XS::Context.new
|
16
|
+
@ping = context.socket XS::REQ
|
17
|
+
@pong = context.socket XS::REP
|
18
|
+
port = bind_to_random_tcp_port(@pong)
|
19
|
+
@ping.connect "tcp://127.0.0.1:#{port}"
|
20
|
+
end
|
21
|
+
|
22
|
+
after(:each) do
|
23
|
+
@ping.close
|
24
|
+
@pong.close
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should receive an exact string copy of the string message sent" do
|
28
|
+
@ping.send_string string
|
29
|
+
received_message = ''
|
30
|
+
rc = @pong.recv_string received_message
|
31
|
+
|
32
|
+
received_message.should == string
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should receive an exact copy of the sent message using Message objects directly" do
|
36
|
+
sent_message = Message.new string
|
37
|
+
received_message = Message.new
|
38
|
+
|
39
|
+
rc = @ping.sendmsg sent_message
|
40
|
+
rc.should == string.size
|
41
|
+
rc = @pong.recvmsg received_message
|
42
|
+
rc.should == string.size
|
43
|
+
|
44
|
+
received_message.copy_out_string.should == string
|
45
|
+
end
|
46
|
+
|
47
|
+
it "should receive an exact copy of the sent message using Message objects directly in non-blocking mode" do
|
48
|
+
sent_message = Message.new string
|
49
|
+
received_message = Message.new
|
50
|
+
|
51
|
+
rc = @ping.sendmsg sent_message, XS::NonBlocking
|
52
|
+
rc.should == string.size
|
53
|
+
sleep 0.01 # give it time for delivery
|
54
|
+
rc = @pong.recvmsg received_message, XS::NonBlocking
|
55
|
+
rc.should == string.size
|
56
|
+
|
57
|
+
received_message.copy_out_string.should == string
|
58
|
+
end
|
59
|
+
|
60
|
+
end # context ping-pong
|
61
|
+
|
62
|
+
|
63
|
+
end # describe
|
64
|
+
|
65
|
+
|
66
|
+
end # module XS
|