ffi-rzmq 0.9.6 → 0.9.7
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/.travis.yml +16 -0
- data/AUTHORS.txt +6 -0
- data/Gemfile +2 -0
- data/History.txt +34 -0
- data/README.rdoc +33 -65
- data/ffi-rzmq.gemspec +2 -2
- data/lib/ffi-rzmq.rb +2 -5
- data/lib/ffi-rzmq/libzmq.rb +12 -5
- data/lib/ffi-rzmq/poll.rb +48 -96
- data/lib/ffi-rzmq/poll_item.rb +56 -0
- data/lib/ffi-rzmq/poll_items.rb +35 -70
- data/lib/ffi-rzmq/socket.rb +30 -43
- data/lib/ffi-rzmq/version.rb +1 -1
- data/lib/io_extensions.rb +19 -0
- data/spec/poll_spec.rb +203 -92
- data/spec/spec_helper.rb +9 -5
- data/spec/support/test.crt +15 -0
- data/spec/support/test.key +15 -0
- metadata +42 -40
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
require 'io_extensions'
|
3
|
+
|
4
|
+
module ZMQ
|
5
|
+
class PollItem
|
6
|
+
extend Forwardable
|
7
|
+
|
8
|
+
def_delegators :@poll_item, :pointer, :readable?, :writable?
|
9
|
+
attr_accessor :pollable, :poll_item
|
10
|
+
|
11
|
+
def initialize(zmq_poll_item = nil)
|
12
|
+
@poll_item = zmq_poll_item || LibZMQ::PollItem.new
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.from_pointer(pointer)
|
16
|
+
self.new(LibZMQ::PollItem.new(pointer))
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.from_pollable(pollable)
|
20
|
+
item = self.new
|
21
|
+
item.pollable = pollable
|
22
|
+
case
|
23
|
+
when pollable.respond_to?(:socket)
|
24
|
+
item.socket = pollable.socket
|
25
|
+
when pollable.respond_to?(:posix_fileno)
|
26
|
+
item.fd = pollable.posix_fileno
|
27
|
+
when pollable.respond_to?(:io)
|
28
|
+
item.fd = pollable.io.posix_fileno
|
29
|
+
end
|
30
|
+
item
|
31
|
+
end
|
32
|
+
|
33
|
+
def closed?
|
34
|
+
case
|
35
|
+
when pollable.respond_to?(:closed?)
|
36
|
+
pollable.closed?
|
37
|
+
when pollable.respond_to?(:socket)
|
38
|
+
pollable.socket.nil?
|
39
|
+
when pollable.respond_to?(:io)
|
40
|
+
pollable.io.closed?
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def socket=(arg); @poll_item[:socket] = arg; end
|
45
|
+
|
46
|
+
def socket; @poll_item[:socket]; end
|
47
|
+
|
48
|
+
def fd=(arg); @poll_item[:fd] = arg; end
|
49
|
+
|
50
|
+
def fd; @poll_item[:fd]; end
|
51
|
+
|
52
|
+
def events=(arg); @poll_item[:events] = arg; end
|
53
|
+
|
54
|
+
def events; @poll_item[:events]; end
|
55
|
+
end
|
56
|
+
end
|
data/lib/ffi-rzmq/poll_items.rb
CHANGED
@@ -1,100 +1,65 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
require 'ostruct'
|
1
3
|
|
2
4
|
module ZMQ
|
3
5
|
class PollItems
|
4
6
|
include Enumerable
|
7
|
+
extend Forwardable
|
8
|
+
|
9
|
+
def_delegators :@pollables, :size, :empty?
|
5
10
|
|
6
11
|
def initialize
|
7
|
-
@
|
8
|
-
@
|
9
|
-
@
|
12
|
+
@pollables = {}
|
13
|
+
@item_size = LibZMQ::PollItem.size
|
14
|
+
@item_store = nil
|
10
15
|
end
|
11
16
|
|
12
|
-
def size; @items.size; end
|
13
|
-
|
14
|
-
def empty?; @items.empty?; end
|
15
|
-
|
16
17
|
def address
|
17
18
|
clean
|
18
|
-
@
|
19
|
+
@item_store
|
19
20
|
end
|
20
21
|
|
21
|
-
def get
|
22
|
-
unless
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
# cast the memory to a PollItem
|
29
|
-
LibZMQ::PollItem.new pointer
|
30
|
-
end
|
22
|
+
def get pollable
|
23
|
+
return unless entry = @pollables[pollable]
|
24
|
+
clean
|
25
|
+
pointer = @item_store + (@item_size * entry.index)
|
26
|
+
item = ZMQ::PollItem.from_pointer(pointer)
|
27
|
+
item.pollable = pollable
|
28
|
+
item
|
31
29
|
end
|
32
30
|
alias :[] :get
|
33
31
|
|
34
|
-
def <<(
|
32
|
+
def <<(poll_item)
|
35
33
|
@dirty = true
|
36
|
-
@
|
34
|
+
@pollables[poll_item.pollable] = OpenStruct.new(:index => size, :data => poll_item)
|
37
35
|
end
|
38
36
|
alias :push :<<
|
39
|
-
|
40
|
-
def delete
|
41
|
-
|
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
|
37
|
+
|
38
|
+
def delete pollable
|
39
|
+
if @pollables.delete(pollable)
|
63
40
|
@dirty = true
|
64
41
|
clean
|
42
|
+
true
|
43
|
+
else
|
44
|
+
false
|
65
45
|
end
|
66
|
-
|
67
|
-
value
|
68
46
|
end
|
69
47
|
|
70
48
|
def each &blk
|
71
49
|
clean
|
72
|
-
|
73
|
-
|
74
|
-
struct = get index
|
75
|
-
yield struct
|
76
|
-
index += 1
|
50
|
+
@pollables.each_key do |pollable|
|
51
|
+
yield get(pollable)
|
77
52
|
end
|
78
53
|
end
|
79
54
|
|
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
55
|
def inspect
|
91
56
|
clean
|
92
57
|
str = ""
|
93
58
|
each { |item| str << "ptr [#{item[:socket]}], events [#{item[:events]}], revents [#{item[:revents]}], " }
|
94
59
|
str.chop.chop
|
95
60
|
end
|
96
|
-
|
97
|
-
def to_s
|
61
|
+
|
62
|
+
def to_s; inspect; end
|
98
63
|
|
99
64
|
private
|
100
65
|
|
@@ -103,18 +68,18 @@ module ZMQ
|
|
103
68
|
# it is garbage collected that native memory should be automatically freed.
|
104
69
|
def clean
|
105
70
|
if @dirty
|
106
|
-
@
|
71
|
+
@item_store = FFI::MemoryPointer.new @item_size, size, true
|
107
72
|
|
108
|
-
# copy over
|
109
73
|
offset = 0
|
110
|
-
@
|
111
|
-
|
112
|
-
offset
|
74
|
+
@pollables.each_with_index do |(pollable, entry), index|
|
75
|
+
entry.index = index
|
76
|
+
LibC.memcpy(@item_store + offset, entry.data.pointer, @item_size)
|
77
|
+
offset += @item_size
|
113
78
|
end
|
114
79
|
|
115
80
|
@dirty = false
|
116
81
|
end
|
117
82
|
end
|
118
83
|
|
119
|
-
end
|
120
|
-
end
|
84
|
+
end
|
85
|
+
end
|
data/lib/ffi-rzmq/socket.rb
CHANGED
@@ -263,15 +263,7 @@ module ZMQ
|
|
263
263
|
# cause.
|
264
264
|
#
|
265
265
|
def send_strings parts, flags = 0
|
266
|
-
|
267
|
-
flags = NonBlocking if dontwait?(flags)
|
268
|
-
|
269
|
-
parts[0..-2].each do |part|
|
270
|
-
rc = send_string part, (flags | ZMQ::SNDMORE)
|
271
|
-
return rc unless Util.resultcode_ok?(rc)
|
272
|
-
end
|
273
|
-
|
274
|
-
send_string parts[-1], flags
|
266
|
+
send_multiple(parts, flags, :send_string)
|
275
267
|
end
|
276
268
|
|
277
269
|
# Send a sequence of messages as a multipart message out of the +parts+
|
@@ -289,15 +281,7 @@ module ZMQ
|
|
289
281
|
# cause.
|
290
282
|
#
|
291
283
|
def sendmsgs parts, flags = 0
|
292
|
-
|
293
|
-
flags = NonBlocking if dontwait?(flags)
|
294
|
-
|
295
|
-
parts[0..-2].each do |part|
|
296
|
-
rc = sendmsg part, (flags | ZMQ::SNDMORE)
|
297
|
-
return rc unless Util.resultcode_ok?(rc)
|
298
|
-
end
|
299
|
-
|
300
|
-
sendmsg parts[-1], flags
|
284
|
+
send_multiple(parts, flags, :sendmsg)
|
301
285
|
end
|
302
286
|
|
303
287
|
# Sends a message. This will automatically close the +message+ for both successful
|
@@ -444,6 +428,23 @@ module ZMQ
|
|
444
428
|
|
445
429
|
|
446
430
|
private
|
431
|
+
|
432
|
+
def send_multiple(parts, flags, method_name)
|
433
|
+
if !parts || parts.empty?
|
434
|
+
-1
|
435
|
+
else
|
436
|
+
flags = NonBlocking if dontwait?(flags)
|
437
|
+
rc = nil
|
438
|
+
|
439
|
+
parts[0..-2].each do |part|
|
440
|
+
rc = send(method_name, part, (flags | ZMQ::SNDMORE))
|
441
|
+
break unless Util.resultcode_ok?(rc)
|
442
|
+
end
|
443
|
+
|
444
|
+
Util.resultcode_ok?(rc) ? send(method_name, parts[-1], flags) : rc
|
445
|
+
end
|
446
|
+
|
447
|
+
end
|
447
448
|
|
448
449
|
def __getsockopt__ name, array
|
449
450
|
# a small optimization so we only have to determine the option
|
@@ -473,39 +474,19 @@ module ZMQ
|
|
473
474
|
def sockopt_buffers option_type
|
474
475
|
if 1 == option_type
|
475
476
|
# int64_t or uint64_t
|
476
|
-
|
477
|
-
length = FFI::MemoryPointer.new :size_t
|
478
|
-
length.write_int 8
|
479
|
-
@longlong_cache = [FFI::MemoryPointer.new(:int64), length]
|
480
|
-
end
|
481
|
-
|
482
|
-
@longlong_cache
|
477
|
+
@longlong_cache ||= alloc_pointer(:int64, 8)
|
483
478
|
|
484
479
|
elsif 0 == option_type
|
485
480
|
# int, 0mq assumes int is 4-bytes
|
486
|
-
|
487
|
-
length = FFI::MemoryPointer.new :size_t
|
488
|
-
length.write_int 4
|
489
|
-
@int_cache = [FFI::MemoryPointer.new(:int32), length]
|
490
|
-
end
|
491
|
-
|
492
|
-
@int_cache
|
481
|
+
@int_cache ||= alloc_pointer(:int32, 4)
|
493
482
|
|
494
483
|
elsif 2 == option_type
|
495
|
-
|
496
|
-
|
497
|
-
length.write_int 255
|
498
|
-
[FFI::MemoryPointer.new(255), length]
|
484
|
+
# could be a string of up to 255 bytes, so allocate for worst case
|
485
|
+
alloc_pointer(255, 255)
|
499
486
|
|
500
487
|
else
|
501
488
|
# uh oh, someone passed in an unknown option; use a slop buffer
|
502
|
-
|
503
|
-
length = FFI::MemoryPointer.new :size_t
|
504
|
-
length.write_int 4
|
505
|
-
@int_cache = [FFI::MemoryPointer.new(:int32), length]
|
506
|
-
end
|
507
|
-
|
508
|
-
@int_cache
|
489
|
+
@int_cache ||= alloc_pointer(:int32, 4)
|
509
490
|
end
|
510
491
|
end
|
511
492
|
|
@@ -529,6 +510,12 @@ module ZMQ
|
|
529
510
|
(NonBlocking & flags) == NonBlocking
|
530
511
|
end
|
531
512
|
alias :noblock? :dontwait?
|
513
|
+
|
514
|
+
def alloc_pointer(kind, length)
|
515
|
+
pointer = FFI::MemoryPointer.new :size_t
|
516
|
+
pointer.write_int(length)
|
517
|
+
[FFI::MemoryPointer.new(kind), pointer]
|
518
|
+
end
|
532
519
|
end # module CommonSocketBehavior
|
533
520
|
|
534
521
|
|
data/lib/ffi-rzmq/version.rb
CHANGED
@@ -0,0 +1,19 @@
|
|
1
|
+
class IO
|
2
|
+
if defined? JRUBY_VERSION
|
3
|
+
require 'jruby'
|
4
|
+
def posix_fileno
|
5
|
+
case self
|
6
|
+
when STDIN, $stdin
|
7
|
+
0
|
8
|
+
when STDOUT, $stdout
|
9
|
+
1
|
10
|
+
when STDERR, $stderr
|
11
|
+
2
|
12
|
+
else
|
13
|
+
JRuby.reference(self).getOpenFile.getMainStream.getDescriptor.getChannel.getFDVal
|
14
|
+
end
|
15
|
+
end
|
16
|
+
else
|
17
|
+
alias :posix_fileno :fileno
|
18
|
+
end
|
19
|
+
end
|
data/spec/poll_spec.rb
CHANGED
@@ -1,10 +1,7 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require File.join(File.dirname(__FILE__), %w[spec_helper])
|
1
|
+
require 'spec_helper'
|
4
2
|
|
5
3
|
module ZMQ
|
6
4
|
|
7
|
-
|
8
5
|
describe Poller do
|
9
6
|
|
10
7
|
context "when initializing" do
|
@@ -12,67 +9,123 @@ module ZMQ
|
|
12
9
|
|
13
10
|
it "should allocate a PollItems instance" do
|
14
11
|
PollItems.should_receive(:new)
|
15
|
-
|
16
12
|
Poller.new
|
17
13
|
end
|
18
14
|
|
19
|
-
end
|
20
|
-
|
21
|
-
|
15
|
+
end
|
16
|
+
|
22
17
|
context "#register" do
|
23
|
-
|
18
|
+
|
19
|
+
let(:pollable) { mock('pollable') }
|
24
20
|
let(:poller) { Poller.new }
|
25
|
-
let(:socket) {
|
26
|
-
|
27
|
-
|
28
|
-
|
21
|
+
let(:socket) { FFI::MemoryPointer.new(4) }
|
22
|
+
let(:io) { stub(:posix_fileno => fd) }
|
23
|
+
let(:fd) { 1 }
|
24
|
+
|
25
|
+
it "returns false when given a nil pollable" do
|
26
|
+
poller.register(nil, ZMQ::POLLIN).should be_false
|
29
27
|
end
|
30
|
-
|
31
|
-
it "
|
32
|
-
poller.register(
|
28
|
+
|
29
|
+
it "returns false when given 0 for +events+ (e.g. no registration)" do
|
30
|
+
poller.register(pollable, 0).should be_false
|
33
31
|
end
|
34
|
-
|
35
|
-
it "
|
36
|
-
poller.register(
|
32
|
+
|
33
|
+
it "returns the default registered event value when given a valid pollable" do
|
34
|
+
poller.register(pollable).should == (ZMQ::POLLIN | ZMQ::POLLOUT)
|
37
35
|
end
|
38
|
-
|
39
|
-
it "
|
40
|
-
|
36
|
+
|
37
|
+
it "returns the registered event value when given a pollable responding to socket (ZMQ::Socket)" do
|
38
|
+
pollable.should_receive(:socket).and_return(socket)
|
39
|
+
poller.register(pollable, ZMQ::POLLIN).should == ZMQ::POLLIN
|
40
|
+
end
|
41
|
+
|
42
|
+
it "returns the registered event value when given a pollable responding to file descriptor (IO, BasicSocket)" do
|
43
|
+
pollable.should_receive(:posix_fileno).and_return(fd)
|
44
|
+
poller.register(pollable, ZMQ::POLLIN).should == ZMQ::POLLIN
|
41
45
|
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(ZMQ::Socket).and_return(true)
|
46
|
-
socket.should_receive(:socket).and_return(raw_socket)
|
47
46
|
|
48
|
-
|
47
|
+
it "returns the registered event value when given a pollable responding to io (SSLSocket)" do
|
48
|
+
pollable.should_receive(:io).and_return(io)
|
49
|
+
poller.register(pollable, ZMQ::POLLIN).should == ZMQ::POLLIN
|
49
50
|
end
|
51
|
+
|
50
52
|
end
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
53
|
+
|
54
|
+
context "#deregister" do
|
55
|
+
|
56
|
+
let(:pollable) { mock('pollable') }
|
57
|
+
let(:poller) { Poller.new }
|
58
|
+
let(:socket) { FFI::MemoryPointer.new(4) }
|
59
|
+
let(:io) { stub(:posix_fileno => fd) }
|
60
|
+
let(:fd) { 1 }
|
61
|
+
|
62
|
+
it "returns true when deregistered pollable from event" do
|
63
|
+
pollable.should_receive(:socket).at_least(:once).and_return(socket)
|
64
|
+
poller.register(pollable)
|
65
|
+
poller.deregister(pollable, ZMQ::POLLIN).should be_true
|
56
66
|
end
|
57
|
-
|
67
|
+
|
68
|
+
it "returns false when pollable not registered" do
|
69
|
+
poller.deregister(pollable, ZMQ::POLLIN).should be_false
|
70
|
+
end
|
71
|
+
|
72
|
+
it "returns false when pollable not registered for deregistered event" do
|
73
|
+
pollable.should_receive(:socket).at_least(:once).and_return(socket)
|
74
|
+
poller.register(pollable, ZMQ::POLLOUT)
|
75
|
+
poller.deregister(pollable, ZMQ::POLLIN).should be_false
|
76
|
+
end
|
77
|
+
|
78
|
+
it "deletes pollable when no events left" do
|
79
|
+
poller.register(pollable, ZMQ::POLLIN)
|
80
|
+
poller.deregister(pollable, ZMQ::POLLIN).should be_true
|
81
|
+
poller.size.should == 0
|
82
|
+
end
|
83
|
+
|
84
|
+
it "deletes closed pollable responding to socket (ZMQ::Socket)" do
|
85
|
+
pollable.should_receive(:socket).and_return(socket)
|
86
|
+
poller.register(pollable)
|
87
|
+
pollable.should_receive(:socket).and_return(nil)
|
88
|
+
poller.deregister(pollable, ZMQ::POLLIN).should be_true
|
89
|
+
poller.size.should == 0
|
90
|
+
end
|
91
|
+
|
92
|
+
it "deletes closed pollable responding to fileno (IO, BasicSocket)" do
|
93
|
+
pollable.should_receive(:posix_fileno).and_return(fd)
|
94
|
+
poller.register(pollable)
|
95
|
+
pollable.should_receive(:closed?).and_return(true)
|
96
|
+
poller.deregister(pollable, ZMQ::POLLIN).should be_true
|
97
|
+
poller.size.should == 0
|
98
|
+
end
|
99
|
+
|
100
|
+
it "deletes closed pollable responding to io (SSLSocket)" do
|
101
|
+
pollable.should_receive(:io).at_least(:once).and_return(io)
|
102
|
+
poller.register(pollable)
|
103
|
+
io.should_receive(:closed?).and_return(true)
|
104
|
+
poller.deregister(pollable, ZMQ::POLLIN).should be_true
|
105
|
+
poller.size.should == 0
|
106
|
+
end
|
107
|
+
|
108
|
+
end
|
109
|
+
|
110
|
+
context "#delete" do
|
111
|
+
|
112
|
+
before(:all) { @context = Context.new }
|
113
|
+
after(:all) { @context.terminate }
|
114
|
+
|
58
115
|
before(:each) do
|
59
116
|
@socket = @context.socket(XREQ)
|
60
117
|
@socket.setsockopt(LINGER, 0)
|
61
118
|
@poller = Poller.new
|
62
119
|
end
|
63
|
-
|
120
|
+
|
64
121
|
after(:each) do
|
65
122
|
@socket.close
|
66
123
|
end
|
67
|
-
|
68
|
-
after(:all) do
|
69
|
-
@context.terminate
|
70
|
-
end
|
71
|
-
|
124
|
+
|
72
125
|
it "should return false for an unregistered socket (i.e. not found)" do
|
73
126
|
@poller.delete(@socket).should be_false
|
74
127
|
end
|
75
|
-
|
128
|
+
|
76
129
|
it "returns true for a sucessfully deleted socket when only 1 is registered" do
|
77
130
|
socket1 = @context.socket(REP)
|
78
131
|
socket1.setsockopt(LINGER, 0)
|
@@ -81,7 +134,7 @@ module ZMQ
|
|
81
134
|
@poller.delete(socket1).should be_true
|
82
135
|
socket1.close
|
83
136
|
end
|
84
|
-
|
137
|
+
|
85
138
|
it "returns true for a sucessfully deleted socket when more than 1 is registered" do
|
86
139
|
socket1 = @context.socket(REP)
|
87
140
|
socket2 = @context.socket(REP)
|
@@ -94,72 +147,130 @@ module ZMQ
|
|
94
147
|
socket1.close
|
95
148
|
socket2.close
|
96
149
|
end
|
97
|
-
|
150
|
+
|
151
|
+
it "returns true for a successfully deleted socket when the socket has been previously closed" do
|
152
|
+
socket1 = @context.socket(REP)
|
153
|
+
socket1.setsockopt(LINGER, 0)
|
154
|
+
|
155
|
+
@poller.register socket1
|
156
|
+
socket1.close
|
157
|
+
@poller.delete(socket1).should be_true
|
158
|
+
end
|
159
|
+
|
98
160
|
end
|
99
|
-
|
100
|
-
|
161
|
+
|
101
162
|
context "poll" do
|
102
163
|
include APIHelper
|
103
|
-
|
104
|
-
before(:all) do
|
105
|
-
end
|
106
|
-
|
107
|
-
before(:each) do
|
108
|
-
# Must recreate context for each test otherwise some poll tests fail.
|
109
|
-
# This is likely due to a race condition in event handling when reusing
|
110
|
-
# the same inproc inside the same context over and over. Making a new
|
111
|
-
# context solves it.
|
112
|
-
@context = Context.new
|
113
|
-
endpoint = "inproc://poll_test"
|
114
|
-
@socket = @context.socket(DEALER)
|
115
|
-
@socket2 = @context.socket(ROUTER)
|
116
|
-
@socket.setsockopt(LINGER, 0)
|
117
|
-
@socket2.setsockopt(LINGER, 0)
|
118
|
-
@socket.bind(endpoint)
|
119
|
-
connect_to_inproc(@socket2, endpoint)
|
120
164
|
|
165
|
+
before(:all) { @context = Context.new }
|
166
|
+
after(:all) { @context.terminate }
|
167
|
+
|
168
|
+
before(:each) do
|
169
|
+
endpoint = "inproc://poll_test_#{SecureRandom.hex}"
|
170
|
+
@sockets = [@context.socket(DEALER), @context.socket(ROUTER)]
|
171
|
+
@sockets.each { |s| s.setsockopt(LINGER, 0) }
|
172
|
+
@sockets.first.bind(endpoint)
|
173
|
+
connect_to_inproc(@sockets.last, endpoint)
|
121
174
|
@poller = Poller.new
|
122
175
|
end
|
123
|
-
|
124
|
-
after(:each)
|
125
|
-
|
126
|
-
@socket2.close
|
127
|
-
@context.terminate
|
128
|
-
end
|
129
|
-
|
176
|
+
|
177
|
+
after(:each) { @sockets.each(&:close) }
|
178
|
+
|
130
179
|
it "returns 0 when there are no sockets to poll" do
|
131
|
-
|
132
|
-
rc.should be_zero
|
180
|
+
@poller.poll(100).should be_zero
|
133
181
|
end
|
134
|
-
|
182
|
+
|
135
183
|
it "returns 0 when there is a single socket to poll and no events" do
|
136
|
-
@poller.register(@
|
137
|
-
|
138
|
-
rc.should be_zero
|
184
|
+
@poller.register(@sockets.first, 0)
|
185
|
+
@poller.poll(100).should be_zero
|
139
186
|
end
|
140
|
-
|
187
|
+
|
141
188
|
it "returns 1 when there is a read event on a socket" do
|
142
|
-
@
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
189
|
+
first, last = @sockets
|
190
|
+
@poller.register_readable(last)
|
191
|
+
|
192
|
+
first.send_string('test')
|
193
|
+
@poller.poll(1000).should == 1
|
147
194
|
end
|
148
|
-
|
195
|
+
|
149
196
|
it "returns 1 when there is a read event on one socket and the second socket has been removed from polling" do
|
150
|
-
@
|
151
|
-
@poller.
|
152
|
-
|
153
|
-
@socket.send_string('test')
|
154
|
-
@poller.deregister_writable(@socket)
|
197
|
+
first, last = @sockets
|
198
|
+
@poller.register_readable(last)
|
199
|
+
@poller.register_writable(first)
|
155
200
|
|
156
|
-
|
157
|
-
|
201
|
+
first.send_string('test')
|
202
|
+
@poller.deregister_writable(first)
|
203
|
+
@poller.poll(1000).should == 1
|
158
204
|
end
|
159
|
-
end # poll
|
160
205
|
|
206
|
+
it "works with BasiSocket" do
|
207
|
+
server = TCPServer.new("127.0.0.1", 0)
|
208
|
+
f, port, host, addr = server.addr
|
209
|
+
client = TCPSocket.new("127.0.0.1", port)
|
210
|
+
s = server.accept
|
161
211
|
|
162
|
-
|
212
|
+
@poller.register(s, ZMQ::POLLIN)
|
213
|
+
@poller.register(client, ZMQ::POLLOUT)
|
214
|
+
|
215
|
+
client.send("message", 0)
|
216
|
+
|
217
|
+
@poller.poll.should == 2
|
218
|
+
@poller.readables.should == [s]
|
219
|
+
@poller.writables.should == [client]
|
220
|
+
|
221
|
+
msg = s.read_nonblock(7)
|
222
|
+
msg.should == "message"
|
223
|
+
end
|
224
|
+
|
225
|
+
it "works with IO objects" do
|
226
|
+
r, w = IO.pipe
|
227
|
+
@poller.register(r, ZMQ::POLLIN)
|
228
|
+
@poller.register(w, ZMQ::POLLOUT)
|
229
|
+
|
230
|
+
w.write("message")
|
231
|
+
|
232
|
+
@poller.poll.should == 2
|
233
|
+
@poller.readables.should == [r]
|
234
|
+
@poller.writables.should == [w]
|
235
|
+
|
236
|
+
msg = r.read(7)
|
237
|
+
msg.should == "message"
|
238
|
+
end
|
239
|
+
|
240
|
+
it "works with SSLSocket" do
|
241
|
+
crt, key = %w[crt key].map { |ext| File.read(File.join(File.dirname(__FILE__), "support", "test." << ext)) }
|
242
|
+
|
243
|
+
ctx = OpenSSL::SSL::SSLContext.new
|
244
|
+
ctx.key = OpenSSL::PKey::RSA.new(key)
|
245
|
+
ctx.cert = OpenSSL::X509::Certificate.new(crt)
|
246
|
+
|
247
|
+
server = TCPServer.new("127.0.0.1", 0)
|
248
|
+
f, port, host, addr = server.addr
|
249
|
+
client = TCPSocket.new("127.0.0.1", port)
|
250
|
+
s = server.accept
|
251
|
+
|
252
|
+
client = OpenSSL::SSL::SSLSocket.new(client)
|
253
|
+
|
254
|
+
server = OpenSSL::SSL::SSLSocket.new(s, ctx)
|
255
|
+
|
256
|
+
t = Thread.new { client.connect }
|
257
|
+
s = server.accept
|
258
|
+
t.join
|
259
|
+
|
260
|
+
@poller.register_readable(s)
|
261
|
+
@poller.register_writable(client)
|
262
|
+
|
263
|
+
client.write("message")
|
264
|
+
|
265
|
+
@poller.poll.should == 2
|
266
|
+
@poller.readables.should == [s]
|
267
|
+
@poller.writables.should == [client]
|
268
|
+
|
269
|
+
msg = s.read(7)
|
270
|
+
msg.should == "message"
|
271
|
+
end
|
272
|
+
end
|
163
273
|
|
274
|
+
end
|
164
275
|
|
165
|
-
end
|
276
|
+
end
|