rbczmq 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.
Files changed (101) hide show
  1. data/.gitignore +23 -0
  2. data/.travis.yml +19 -0
  3. data/Gemfile +5 -0
  4. data/Gemfile.lock +19 -0
  5. data/MIT-LICENSE +20 -0
  6. data/README.rdoc +247 -0
  7. data/Rakefile +67 -0
  8. data/examples/loop.rb +109 -0
  9. data/examples/poller.rb +37 -0
  10. data/examples/pub_sub.rb +101 -0
  11. data/examples/push_pull.rb +104 -0
  12. data/examples/req_rep.rb +100 -0
  13. data/ext/czmq.tar.gz +0 -0
  14. data/ext/rbczmq/context.c +280 -0
  15. data/ext/rbczmq/context.h +26 -0
  16. data/ext/rbczmq/extconf.rb +138 -0
  17. data/ext/rbczmq/frame.c +401 -0
  18. data/ext/rbczmq/frame.h +24 -0
  19. data/ext/rbczmq/jruby.h +22 -0
  20. data/ext/rbczmq/loop.c +413 -0
  21. data/ext/rbczmq/loop.h +24 -0
  22. data/ext/rbczmq/message.c +620 -0
  23. data/ext/rbczmq/message.h +24 -0
  24. data/ext/rbczmq/poller.c +308 -0
  25. data/ext/rbczmq/poller.h +29 -0
  26. data/ext/rbczmq/pollitem.c +251 -0
  27. data/ext/rbczmq/pollitem.h +25 -0
  28. data/ext/rbczmq/rbczmq_ext.c +198 -0
  29. data/ext/rbczmq/rbczmq_ext.h +94 -0
  30. data/ext/rbczmq/rbczmq_prelude.h +22 -0
  31. data/ext/rbczmq/rubinius.h +24 -0
  32. data/ext/rbczmq/ruby18.h +43 -0
  33. data/ext/rbczmq/ruby19.h +15 -0
  34. data/ext/rbczmq/socket.c +1570 -0
  35. data/ext/rbczmq/socket.h +136 -0
  36. data/ext/rbczmq/timer.c +110 -0
  37. data/ext/rbczmq/timer.h +23 -0
  38. data/ext/zeromq.tar.gz +0 -0
  39. data/lib/rbczmq.rb +3 -0
  40. data/lib/zmq.rb +77 -0
  41. data/lib/zmq/context.rb +50 -0
  42. data/lib/zmq/default_handler.rb +16 -0
  43. data/lib/zmq/frame.rb +11 -0
  44. data/lib/zmq/handler.rb +76 -0
  45. data/lib/zmq/loop.rb +131 -0
  46. data/lib/zmq/message.rb +9 -0
  47. data/lib/zmq/poller.rb +22 -0
  48. data/lib/zmq/pollitem.rb +31 -0
  49. data/lib/zmq/socket.rb +125 -0
  50. data/lib/zmq/socket/dealer.rb +33 -0
  51. data/lib/zmq/socket/pair.rb +39 -0
  52. data/lib/zmq/socket/pub.rb +30 -0
  53. data/lib/zmq/socket/pull.rb +29 -0
  54. data/lib/zmq/socket/push.rb +32 -0
  55. data/lib/zmq/socket/rep.rb +37 -0
  56. data/lib/zmq/socket/req.rb +37 -0
  57. data/lib/zmq/socket/router.rb +38 -0
  58. data/lib/zmq/socket/sub.rb +27 -0
  59. data/lib/zmq/timer.rb +12 -0
  60. data/lib/zmq/version.rb +5 -0
  61. data/perf/pair.rb +7 -0
  62. data/perf/pair/local.rb +22 -0
  63. data/perf/pair/remote.rb +25 -0
  64. data/perf/pub_sub.rb +7 -0
  65. data/perf/pub_sub/local.rb +22 -0
  66. data/perf/pub_sub/remote.rb +25 -0
  67. data/perf/push_pull.rb +7 -0
  68. data/perf/push_pull/local.rb +21 -0
  69. data/perf/push_pull/remote.rb +25 -0
  70. data/perf/req_rep.rb +7 -0
  71. data/perf/req_rep/local.rb +35 -0
  72. data/perf/req_rep/remote.rb +28 -0
  73. data/perf/runner.rb +142 -0
  74. data/rbczmq.gemspec +22 -0
  75. data/test/helper.rb +21 -0
  76. data/test/socket/test_dealer_socket.rb +14 -0
  77. data/test/socket/test_pair_socket.rb +24 -0
  78. data/test/socket/test_pair_sockets.rb +74 -0
  79. data/test/socket/test_pub_socket.rb +17 -0
  80. data/test/socket/test_pub_sub_sockets.rb +87 -0
  81. data/test/socket/test_pull_socket.rb +17 -0
  82. data/test/socket/test_push_pull_sockets.rb +81 -0
  83. data/test/socket/test_push_socket.rb +17 -0
  84. data/test/socket/test_rep_socket.rb +25 -0
  85. data/test/socket/test_req_rep_sockets.rb +42 -0
  86. data/test/socket/test_req_socket.rb +27 -0
  87. data/test/socket/test_router_socket.rb +14 -0
  88. data/test/socket/test_routing.rb +66 -0
  89. data/test/socket/test_sub_socket.rb +17 -0
  90. data/test/test_context.rb +86 -0
  91. data/test/test_frame.rb +78 -0
  92. data/test/test_handler.rb +28 -0
  93. data/test/test_loop.rb +252 -0
  94. data/test/test_message.rb +201 -0
  95. data/test/test_poller.rb +154 -0
  96. data/test/test_pollitem.rb +78 -0
  97. data/test/test_socket.rb +403 -0
  98. data/test/test_threading.rb +34 -0
  99. data/test/test_timer.rb +37 -0
  100. data/test/test_zmq.rb +62 -0
  101. metadata +208 -0
data/perf/runner.rb ADDED
@@ -0,0 +1,142 @@
1
+ # encoding: utf-8
2
+
3
+ $:.unshift File.expand_path('lib')
4
+ require 'zmq'
5
+
6
+ class Runner
7
+ DEFAULT_MSG_COUNT = 100_000
8
+ DEFAULT_MSG_SIZE = 100
9
+ DEFAULT_ENCODING = :string
10
+
11
+ attr_reader :msg_count, :msg_size, :encoding, :workers_count, :stats_buf
12
+
13
+ def initialize(msg_count, msg_size, encoding, workers_count = 1)
14
+ @msg_count = (msg_count || DEFAULT_MSG_COUNT).to_i
15
+ @msg_size = (msg_size || DEFAULT_MSG_SIZE).to_i
16
+ @encoding = (encoding || DEFAULT_ENCODING).to_sym
17
+ @workers_count = (workers_count || 1).to_i
18
+ @stats_buf, @workers = [], []
19
+ register_signal_handlers
20
+ end
21
+
22
+ def start(test)
23
+ @workers_count.times{ fork_local(test) }
24
+ fork_remote(test)
25
+ puts "Workers: #{@workers.join(', ')}"
26
+ puts "Producer: #{@producer}"
27
+ after_start
28
+ ensure
29
+ stop
30
+ end
31
+
32
+ def process_msg_count
33
+ msg_count / workers_count
34
+ end
35
+
36
+ def endpoint
37
+ self.class.const_get(:ENDPOINT)
38
+ end
39
+
40
+ def payload
41
+ @payload ||= "#{'0'*msg_size}"
42
+ end
43
+
44
+ def stats(start_time)
45
+ end_time = Time.now
46
+ elapsed = (end_time.to_f - start_time.to_f) * 1000000
47
+ elapsed = 1 if elapsed == 0
48
+
49
+ throughput = process_msg_count * 1000000 / elapsed
50
+ megabits = throughput * msg_size * 8 / 1000000
51
+ stats_buf << "====== [#{Process.pid}] transfer stats ======"
52
+ stats_buf << "message encoding: %s" % encoding
53
+ stats_buf << "message size: %i [B]" % msg_size
54
+ stats_buf << "message count: %i" % process_msg_count
55
+ stats_buf << "mean throughput: %i [msg/s]" % throughput
56
+ stats_buf << "mean throughput: %.3f [Mb/s]" % megabits
57
+ end
58
+
59
+ private
60
+ def register_signal_handlers
61
+ %w(INT TERM QUIT).each do |sig|
62
+ trap(sig){ stop }
63
+ end
64
+ end
65
+
66
+ def before_fork
67
+ sample_mem(:before)
68
+ end
69
+
70
+ def after_fork
71
+ sample_mem(:after)
72
+ stats_buf.each{|s| puts(s) }
73
+ end
74
+
75
+ def sample_mem(w)
76
+ stats_buf << "[#{$$}] Memory used #{w}: %dkb" % `ps -o rss= -p #{$$}`.to_i
77
+ end
78
+ end
79
+
80
+ class ProcessRunner < Runner
81
+ ENDPOINT = "tcp://127.0.0.1:5221"
82
+
83
+ def stop
84
+ Process.kill(:INT, @producer) rescue nil
85
+ @workers.each{|p| Process.kill(:INT, p) rescue nil }
86
+ end
87
+
88
+ private
89
+ def after_start
90
+ Process.waitpid(@producer, Process::WNOHANG)
91
+ @workers.each{|p| Process.waitpid(p) }
92
+ end
93
+
94
+ def fork_local(test)
95
+ @workers << fork do
96
+ before_fork
97
+ require File.join(File.dirname(__FILE__), test.to_s, 'local')
98
+ after_fork
99
+ end
100
+ end
101
+
102
+ def fork_remote(test)
103
+ @producer = fork do
104
+ before_fork
105
+ sleep 1
106
+ require File.join(File.dirname(__FILE__), test.to_s, 'remote')
107
+ after_fork
108
+ end
109
+ end
110
+ end
111
+
112
+ class ThreadRunner < Runner
113
+ ENDPOINT = "inproc://perf"
114
+
115
+ def stop
116
+ @producer.kill rescue nil
117
+ @workers.each{|t| t.kill rescue nil }
118
+ end
119
+
120
+ private
121
+ def after_start
122
+ @producer.join
123
+ @workers.each{|t| t.join }
124
+ end
125
+
126
+ def fork_local(test)
127
+ @workers << Thread.new do
128
+ before_fork
129
+ require File.join(File.dirname(__FILE__), test.to_s, 'local')
130
+ after_fork
131
+ end
132
+ end
133
+
134
+ def fork_remote(test)
135
+ @producer = Thread.new do
136
+ before_fork
137
+ sleep 1
138
+ require File.join(File.dirname(__FILE__), test.to_s, 'remote')
139
+ after_fork
140
+ end
141
+ end
142
+ end
data/rbczmq.gemspec ADDED
@@ -0,0 +1,22 @@
1
+ # encoding: utf-8
2
+
3
+ require File.expand_path('../lib/zmq/version', __FILE__)
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "rbczmq"
7
+ s.version = ZMQ::VERSION
8
+ s.summary = "Ruby extension for CZMQ - High-level C Binding for ØMQ (http://czmq.zeromq.org)"
9
+ s.description = "Ruby extension for CZMQ - High-level C Binding for ØMQ (http://czmq.zeromq.org)"
10
+ s.authors = ["Lourens Naudé", "James Tucker"]
11
+ s.email = ["lourens@methodmissing.com", "jftucker@gmail.com"]
12
+ s.homepage = "http://github.com/methodmissing/rbczmq"
13
+ s.date = Time.now.utc.strftime('%Y-%m-%d')
14
+ s.platform = Gem::Platform::RUBY
15
+ s.extensions = "ext/rbczmq/extconf.rb"
16
+ s.has_rdoc = true
17
+ s.files = `git ls-files`.split("\n")
18
+ s.test_files = `git ls-files test`.split("\n")
19
+ s.rdoc_options = ["--charset=UTF-8"]
20
+ s.require_paths = ["lib"]
21
+ s.add_development_dependency('rake-compiler', '~> 0.8.0')
22
+ end
data/test/helper.rb ADDED
@@ -0,0 +1,21 @@
1
+ # encoding: utf-8
2
+
3
+ require 'test/unit'
4
+ require 'zmq'
5
+ require 'stringio'
6
+
7
+ Thread.abort_on_exception = true
8
+
9
+ class ZmqTestCase < Test::Unit::TestCase
10
+ undef_method :default_test if method_defined? :default_test
11
+
12
+ if ENV['STRESS_GC']
13
+ def setup
14
+ GC.stress = true
15
+ end
16
+
17
+ def teardown
18
+ GC.stress = false
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,14 @@
1
+ # encoding: utf-8
2
+
3
+ require File.join(File.dirname(__FILE__), '..', 'helper')
4
+
5
+ class TestDealerSocket < ZmqTestCase
6
+ def test_behavior
7
+ ctx = ZMQ::Context.new
8
+ sock = ctx.socket(:DEALER)
9
+ assert_equal ZMQ::DEALER, sock.type
10
+ assert_equal "DEALER socket", sock.to_s
11
+ ensure
12
+ ctx.destroy
13
+ end
14
+ end
@@ -0,0 +1,24 @@
1
+ # encoding: utf-8
2
+
3
+ require File.join(File.dirname(__FILE__), '..', 'helper')
4
+
5
+ class TestPairSocket < ZmqTestCase
6
+ def test_behavior
7
+ ctx = ZMQ::Context.new
8
+ sock = ctx.socket(:PAIR)
9
+ assert_equal ZMQ::PAIR, sock.type
10
+ assert_equal "PAIR socket", sock.to_s
11
+ ensure
12
+ ctx.destroy
13
+ end
14
+
15
+ def test_inproc_only_transport
16
+ ctx = ZMQ::Context.new
17
+ sock = ctx.socket(:PAIR)
18
+ assert_raises ZMQ::Error do
19
+ sock.bind("tcp://127.0.0.1:*")
20
+ end
21
+ ensure
22
+ ctx.destroy
23
+ end
24
+ end
@@ -0,0 +1,74 @@
1
+ # encoding: utf-8
2
+
3
+ require File.join(File.dirname(__FILE__), '..', 'helper')
4
+
5
+ class TestPairSockets < ZmqTestCase
6
+ def test_flow
7
+ ctx = ZMQ::Context.new
8
+ a = ctx.bind(:PAIR, "inproc://test.pair-flow")
9
+ b = ctx.connect(:PAIR, "inproc://test.pair-flow")
10
+ a.send("a")
11
+ b.send("b")
12
+ assert_equal "b", a.recv
13
+ assert_equal "a", b.recv
14
+ ensure
15
+ ctx.destroy
16
+ end
17
+
18
+ def test_transfer
19
+ ctx = ZMQ::Context.new
20
+ a = ctx.bind(:PAIR, "inproc://test.pair-transfer")
21
+ b = ctx.connect(:PAIR, "inproc://test.pair-transfer")
22
+ a.send("message")
23
+ assert_equal "message", b.recv
24
+
25
+ a.sendm("me")
26
+ a.sendm("ss")
27
+ a.send("age")
28
+ assert_equal "me", b.recv
29
+ assert_equal "ss", b.recv
30
+ assert_equal "age", b.recv
31
+
32
+ a.send_frame ZMQ::Frame("frame")
33
+ assert_equal ZMQ::Frame("frame"), b.recv_frame
34
+
35
+ 5.times do |i|
36
+ frame = ZMQ::Frame("m#{i}")
37
+ a.send_frame(frame, ZMQ::Frame::MORE)
38
+ end
39
+ a.send_frame(ZMQ::Frame("m5"))
40
+ expected, frames = %w(m0 m1 m2 m3 m4 m5), []
41
+ 5.times do
42
+ frames << b.recv_frame.data
43
+ end
44
+ frames << b.recv_frame.data
45
+ assert_equal expected, frames
46
+
47
+ msg = ZMQ::Message.new
48
+ msg.push ZMQ::Frame("header")
49
+
50
+ assert_nil a.send_message(msg)
51
+
52
+ recvd_msg = b.recv_message
53
+ assert_instance_of ZMQ::Message, recvd_msg
54
+ assert_equal ZMQ::Frame("header"), recvd_msg.pop
55
+ ensure
56
+ ctx.destroy
57
+ end
58
+
59
+ def test_distribution
60
+ ctx = ZMQ::Context.new
61
+ a = ctx.bind(:PAIR, "inproc://test.pair-distribution")
62
+ thread = Thread.new do
63
+ b = ctx.connect(:PAIR, "inproc://test.pair-distribution")
64
+ frame = b.recv_frame
65
+ b.close
66
+ frame
67
+ end
68
+
69
+ a.send_frame ZMQ::Frame("message")
70
+ assert_equal ZMQ::Frame("message"), thread.value
71
+ ensure
72
+ ctx.destroy
73
+ end
74
+ end
@@ -0,0 +1,17 @@
1
+ # encoding: utf-8
2
+
3
+ require File.join(File.dirname(__FILE__), '..', 'helper')
4
+
5
+ class TestPubSocket < ZmqTestCase
6
+ def test_behavior
7
+ ctx = ZMQ::Context.new
8
+ sock = ctx.socket(:PUB)
9
+ assert_equal ZMQ::PUB, sock.type
10
+ assert_equal "PUB socket", sock.to_s
11
+ assert_raises ZMQ::Error do
12
+ sock.recv
13
+ end
14
+ ensure
15
+ ctx.destroy
16
+ end
17
+ end
@@ -0,0 +1,87 @@
1
+ # encoding: utf-8
2
+
3
+ require File.join(File.dirname(__FILE__), '..', 'helper')
4
+
5
+ class TestPubSubSockets < ZmqTestCase
6
+ def test_flow
7
+ ctx = ZMQ::Context.new
8
+ pub = ctx.bind(:PUB, "inproc://test.pub-sub-flow")
9
+ sub = ctx.connect(:SUB, "inproc://test.pub-sub-flow")
10
+ sub.subscribe("")
11
+ pub.send("a")
12
+ assert_equal "a", sub.recv
13
+ ensure
14
+ ctx.destroy
15
+ end
16
+
17
+ def test_transfer
18
+ ctx = ZMQ::Context.new
19
+ pub = ctx.bind(:PUB, "inproc://test.pub-sub-transfer")
20
+ sub = ctx.connect(:SUB, "inproc://test.pub-sub-transfer")
21
+ sub.subscribe("")
22
+ pub.send("message")
23
+ assert_equal "message", sub.recv
24
+
25
+ pub.sendm("me")
26
+ pub.sendm("ss")
27
+ pub.send("age")
28
+ assert_equal "me", sub.recv
29
+ assert_equal "ss", sub.recv
30
+ assert_equal "age", sub.recv
31
+
32
+ pub.send_frame ZMQ::Frame("frame")
33
+ assert_equal ZMQ::Frame("frame"), sub.recv_frame
34
+
35
+ 5.times do |i|
36
+ frame = ZMQ::Frame("m#{i}")
37
+ pub.send_frame(frame, ZMQ::Frame::MORE)
38
+ end
39
+ pub.send_frame(ZMQ::Frame("m5"))
40
+ expected, frames = %w(m0 m1 m2 m3 m4 m5), []
41
+ 5.times do
42
+ frames << sub.recv_frame.data
43
+ end
44
+ frames << sub.recv_frame.data
45
+ assert_equal expected, frames
46
+
47
+ msg = ZMQ::Message.new
48
+ msg.push ZMQ::Frame("header")
49
+
50
+ assert_nil pub.send_message(msg)
51
+
52
+ recvd_msg = sub.recv_message
53
+ assert_instance_of ZMQ::Message, recvd_msg
54
+ assert_equal ZMQ::Frame("header"), recvd_msg.pop
55
+ ensure
56
+ ctx.destroy
57
+ end
58
+
59
+ def test_distribution
60
+ ctx = ZMQ::Context.new
61
+ pub = ctx.bind(:PUB, "inproc://test.pub-sub-distribution")
62
+ threads = []
63
+ 5.times do |i|
64
+ threads << Thread.new do
65
+ sub = ctx.connect(:SUB, "inproc://test.pub-sub-distribution")
66
+ sub.subscribe("")
67
+ messages = []
68
+ 5.times do
69
+ messages << sub.recv
70
+ end
71
+ sub.close
72
+ messages
73
+ end
74
+ end
75
+
76
+ sleep 0.5 # "slow joiner" syndrome
77
+ messages = %w(a b c d e)
78
+ messages.each do |m|
79
+ pub.send m
80
+ end
81
+
82
+ threads.each{|t| t.join }
83
+ assert threads.all?{|t| t.value == messages }
84
+ ensure
85
+ ctx.destroy
86
+ end
87
+ end
@@ -0,0 +1,17 @@
1
+ # encoding: utf-8
2
+
3
+ require File.join(File.dirname(__FILE__), '..', 'helper')
4
+
5
+ class TestPullSocket < ZmqTestCase
6
+ def test_behavior
7
+ ctx = ZMQ::Context.new
8
+ sock = ctx.socket(:PULL)
9
+ assert_equal ZMQ::PULL, sock.type
10
+ assert_equal "PULL socket", sock.to_s
11
+ assert_raises ZMQ::Error do
12
+ sock.send("message")
13
+ end
14
+ ensure
15
+ ctx.destroy
16
+ end
17
+ end
@@ -0,0 +1,81 @@
1
+ # encoding: utf-8
2
+
3
+ require File.join(File.dirname(__FILE__), '..', 'helper')
4
+
5
+ class TestPushPullSockets < ZmqTestCase
6
+ def test_flow
7
+ ctx = ZMQ::Context.new
8
+ push = ctx.bind(:PUSH, "inproc://test.push-pull-flow")
9
+ pull = ctx.connect(:PULL, "inproc://test.push-pull-flow")
10
+ push.send("a")
11
+ assert_equal "a", pull.recv
12
+ ensure
13
+ ctx.destroy
14
+ end
15
+
16
+ def test_transfer
17
+ ctx = ZMQ::Context.new
18
+ push = ctx.bind(:PUSH, "inproc://test.push-pull-transfer")
19
+ pull = ctx.connect(:PULL, "inproc://test.push-pull-transfer")
20
+ push.send("message")
21
+ assert_equal "message", pull.recv
22
+
23
+ push.sendm("me")
24
+ push.sendm("ss")
25
+ push.send("age")
26
+ assert_equal "me", pull.recv
27
+ assert_equal "ss", pull.recv
28
+ assert_equal "age", pull.recv
29
+
30
+ push.send_frame ZMQ::Frame("frame")
31
+ assert_equal ZMQ::Frame("frame"), pull.recv_frame
32
+
33
+ 5.times do |i|
34
+ frame = ZMQ::Frame("m#{i}")
35
+ push.send_frame(frame, ZMQ::Frame::MORE)
36
+ end
37
+ push.send_frame(ZMQ::Frame("m5"))
38
+ expected, frames = %w(m0 m1 m2 m3 m4 m5), []
39
+ 5.times do
40
+ frames << pull.recv_frame.data
41
+ end
42
+ frames << pull.recv_frame.data
43
+ assert_equal expected, frames
44
+
45
+ msg = ZMQ::Message.new
46
+ msg.push ZMQ::Frame("header")
47
+
48
+ assert_nil push.send_message(msg)
49
+
50
+ recvd_msg = pull.recv_message
51
+ assert_instance_of ZMQ::Message, recvd_msg
52
+ assert_equal ZMQ::Frame("header"), recvd_msg.pop
53
+ ensure
54
+ ctx.destroy
55
+ end
56
+
57
+ def test_distribution
58
+ ctx = ZMQ::Context.new
59
+ push = ctx.bind(:PUSH, "inproc://test.push-pull-distribution")
60
+ threads = []
61
+ 5.times do |i|
62
+ threads << Thread.new do
63
+ pull = ctx.connect(:PULL, "inproc://test.push-pull-distribution")
64
+ msg = pull.recv
65
+ pull.close
66
+ msg
67
+ end
68
+ end
69
+
70
+ sleep 0.5 # "slow joiner" syndrome
71
+ messages = %w(a b c d e f)
72
+ messages.each do |m|
73
+ push.send m
74
+ end
75
+
76
+ threads.each{|t| t.join }
77
+ assert threads.all?{|t| messages.include?(t.value) }
78
+ ensure
79
+ ctx.destroy
80
+ end
81
+ end