revactor 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,127 @@
1
+ #--
2
+ # Copyright (C)2007 Tony Arcieri
3
+ # You can redistribute this under the terms of the Ruby license
4
+ # See file LICENSE for details
5
+ #++
6
+
7
+ require File.dirname(__FILE__) + '/../lib/revactor/actor'
8
+
9
+ describe Actor do
10
+ describe "creation" do
11
+ it "creates a base Actor with Actor.start" do
12
+ Actor.start do
13
+ Fiber.current.should be_an_instance_of(Actor)
14
+ end
15
+ end
16
+
17
+ it "allows creation of new Actors with Actor.new" do
18
+ Actor.new do
19
+ Fiber.current.should be_an_instance_of(Actor)
20
+ end
21
+ end
22
+
23
+ it "allows creation of new Actors with Actor.spawn" do
24
+ Actor.spawn do
25
+ Fiber.current.should be_an_instance_of(Actor)
26
+ end
27
+ end
28
+
29
+ it "allows arguments to be passed when an Actor is created" do
30
+ [:new, :spawn].each do |meth|
31
+ Actor.send(meth, 1, 2, 3) do |foo, bar, baz|
32
+ [foo, bar, baz].should == [1, 2, 3]
33
+ end
34
+ end
35
+ end
36
+ end
37
+
38
+ describe "current" do
39
+ it "allows the current Actor to be retrieved" do
40
+ Actor.new do
41
+ Actor.current.should be_an_instance_of(Actor)
42
+ end
43
+ end
44
+
45
+ it "disallows retrieving the current Actor unless the Actor environment is started" do
46
+ proc { Actor.current }.should raise_error(ActorError)
47
+ end
48
+ end
49
+
50
+ describe "receive" do
51
+ before :each do
52
+ @actor_run = false
53
+ end
54
+
55
+ it "returns the value of the matching filter action" do
56
+ actor = Actor.new do
57
+ Actor.receive do |filter|
58
+ filter.when(:foo) { |message| :bar }
59
+ end.should == :bar
60
+
61
+ # Make sure the spec actually ran the actor
62
+ @actor_run = true
63
+ end
64
+
65
+ actor << :foo
66
+ @actor_run.should be_true
67
+ end
68
+
69
+ it "filters messages with ===" do
70
+ actor = Actor.new do
71
+ results = []
72
+ 3.times do
73
+ results << Actor.receive do |filter|
74
+ filter.when(/third/) { |m| m }
75
+ filter.when(/first/) { |m| m }
76
+ filter.when(/second/) { |m| m }
77
+ end
78
+ end
79
+ results.should == ['first message', 'second message', 'third message']
80
+ @actor_run = true
81
+ end
82
+
83
+ ['first message', 'second message', 'third message'].each { |m| actor << m }
84
+ @actor_run.should be_true
85
+ end
86
+
87
+ it "times out if a message isn't received after the specifed interval" do
88
+ actor = Actor.new do
89
+ Actor.receive do |filter|
90
+ filter.when(:foo) { :wrong }
91
+ filter.after(0.01) { :right }
92
+ end.should == :right
93
+ @actor_run = true
94
+ end
95
+ @actor_run.should be_true
96
+ end
97
+
98
+ it "matches any message with Object" do
99
+ actor = Actor.new do
100
+ result = []
101
+ 3.times do
102
+ result << Actor.receive do |filter|
103
+ filter.when(Object) { |m| m }
104
+ end
105
+ end
106
+
107
+ result.should == [:foo, :bar, :baz]
108
+ @actor_run = true
109
+ end
110
+
111
+ [:foo, :bar, :baz].each { |m| actor << m }
112
+ @actor_run.should be_true
113
+ end
114
+ end
115
+
116
+ it "detects dead actors" do
117
+ actor = Actor.new do
118
+ Actor.receive do |filter|
119
+ filter.when(Object) {}
120
+ end
121
+ end
122
+
123
+ actor.dead?.should be_false
124
+ actor << :foobar
125
+ actor.dead?.should be_true
126
+ end
127
+ end
@@ -0,0 +1,36 @@
1
+ #--
2
+ # Copyright (C)2007 Tony Arcieri
3
+ # You can redistribute this under the terms of the Ruby license
4
+ # See file LICENSE for details
5
+ #++
6
+
7
+ require File.dirname(__FILE__) + '/../lib/revactor/filters/line'
8
+
9
+ describe Revactor::Filter::Line do
10
+ before(:each) do
11
+ @payload = "foo\nbar\r\nbaz\n"
12
+ @filter = Revactor::Filter::Line.new
13
+ end
14
+
15
+ it "decodes lines from an input buffer" do
16
+ @filter.decode(@payload).should == %w{foo bar baz}
17
+ end
18
+
19
+ it "encodes lines" do
20
+ @filter.encode(*%w{foo bar baz}).should == "foo\nbar\nbaz\n"
21
+ end
22
+
23
+ it "reassembles fragmented lines" do
24
+ msg1 = "foobar\r\n"
25
+ msg2 = "baz\n"
26
+ msg3 = "quux\r\n"
27
+
28
+ chunks = []
29
+ chunks[0] = msg1.slice(0, 1)
30
+ chunks[1] = msg1.slice(1, msg1.size) << msg2.slice(0, 1)
31
+ chunks[2] = msg2.slice(1, msg2.size - 1)
32
+ chunks[3] = msg2.slice(msg2.size, 1) << msg3
33
+
34
+ chunks.reduce([]) { |a, chunk| a + @filter.decode(chunk) }.should == %w{foobar baz quux}
35
+ end
36
+ end
@@ -0,0 +1,59 @@
1
+ #--
2
+ # Copyright (C)2007 Tony Arcieri
3
+ # You can redistribute this under the terms of the Ruby license
4
+ # See file LICENSE for details
5
+ #++
6
+
7
+ require File.dirname(__FILE__) + '/../lib/revactor/filters/packet'
8
+
9
+ describe Revactor::Filter::Packet do
10
+ before(:each) do
11
+ @payload = "A test string"
12
+ end
13
+
14
+ it "decodes frames with 2-byte prefixes" do
15
+ filter = Revactor::Filter::Packet.new(2)
16
+ filter.decode([@payload.length].pack('n') << @payload).should == [@payload]
17
+ end
18
+
19
+ it "encodes frames with 2-byte prefixes" do
20
+ filter = Revactor::Filter::Packet.new(2)
21
+ filter.encode(@payload).should == [@payload.length].pack('n') << @payload
22
+ end
23
+
24
+ it "decodes frames with 4-byte prefixes" do
25
+ @filter = Revactor::Filter::Packet.new(4)
26
+ @filter.decode([@payload.length].pack('N') << @payload).should == [@payload]
27
+ end
28
+
29
+ it "encodes frames with 4-byte prefixes" do
30
+ @filter = Revactor::Filter::Packet.new(4)
31
+ @filter.encode(@payload).should == [@payload.length].pack('N') << @payload
32
+ end
33
+
34
+ it "reassembles fragmented frames" do
35
+ filter = Revactor::Filter::Packet.new(2)
36
+
37
+ msg1 = 'foobar'
38
+ msg2 = 'baz'
39
+ msg3 = 'quux'
40
+
41
+ packet1 = [msg1.length].pack('n') << msg1
42
+ packet2 = [msg2.length].pack('n') << msg2
43
+ packet3 = [msg3.length].pack('n') << msg3
44
+
45
+ chunks = []
46
+ chunks[0] = packet1.slice(0, 1)
47
+ chunks[1] = packet1.slice(1, packet1.size) << packet2.slice(0, 1)
48
+ chunks[2] = packet2.slice(1, packet2.size - 1)
49
+ chunks[3] = packet2.slice(packet2.size, 1) << packet3
50
+
51
+ chunks.reduce([]) { |a, chunk| a + filter.decode(chunk) }.should == [msg1, msg2, msg3]
52
+ end
53
+
54
+ it "raises an exception for overlength frames" do
55
+ filter = Revactor::Filter::Packet.new(2)
56
+ payload = 'X' * 65537
57
+ proc { filter.encode payload }.should raise_error(ArgumentError)
58
+ end
59
+ end
data/spec/tcp_spec.rb ADDED
@@ -0,0 +1,84 @@
1
+ #--
2
+ # Copyright (C)2007 Tony Arcieri
3
+ # You can redistribute this under the terms of the Ruby license
4
+ # See file LICENSE for details
5
+ #++
6
+
7
+ require File.dirname(__FILE__) + '/../lib/revactor/tcp'
8
+
9
+ TEST_HOST = '127.0.0.1'
10
+
11
+ # Chosen with dice, guaranteed to be random!
12
+ RANDOM_PORT = 10103
13
+
14
+ describe Revactor::TCP do
15
+ before :each do
16
+ @actor_run = false
17
+ @server = TCPServer.new(TEST_HOST, RANDOM_PORT)
18
+ end
19
+
20
+ after :each do
21
+ @server.close unless @server.closed?
22
+ end
23
+
24
+ it "connects to remote servers" do
25
+ Actor.start do
26
+ sock = Revactor::TCP.connect(TEST_HOST, RANDOM_PORT)
27
+ sock.should be_an_instance_of(Revactor::TCP::Socket)
28
+ @server.accept.should be_an_instance_of(TCPSocket)
29
+
30
+ sock.close
31
+ @actor_run = true
32
+ end
33
+
34
+ @actor_run.should be_true
35
+ end
36
+
37
+ it "listens for remote connections" do
38
+ @server.close # Don't use it for this one...
39
+
40
+ Actor.start do
41
+ server = Revactor::TCP.listen(TEST_HOST, RANDOM_PORT)
42
+ server.should be_an_instance_of(Revactor::TCP::Listener)
43
+
44
+ s1 = TCPSocket.open(TEST_HOST, RANDOM_PORT)
45
+ s2 = server.accept
46
+
47
+ server.close
48
+ s2.close
49
+ @actor_run = true
50
+ end
51
+
52
+ @actor_run.should be_true
53
+ end
54
+
55
+ it "reads data" do
56
+ Actor.start do
57
+ s1 = Revactor::TCP.connect(TEST_HOST, RANDOM_PORT)
58
+ s2 = @server.accept
59
+
60
+ s2.write 'foobar'
61
+ s1.read(6).should == 'foobar'
62
+
63
+ s1.close
64
+ @actor_run = true
65
+ end
66
+
67
+ @actor_run.should be_true
68
+ end
69
+
70
+ it "writes data" do
71
+ Actor.start do
72
+ s1 = Revactor::TCP.connect(TEST_HOST, RANDOM_PORT)
73
+ s2 = @server.accept
74
+
75
+ s1.write 'foobar'
76
+ s2.read(6).should == 'foobar'
77
+
78
+ s1.close
79
+ @actor_run = true
80
+ end
81
+
82
+ @actor_run.should be_true
83
+ end
84
+ end
@@ -0,0 +1,33 @@
1
+ require File.dirname(__FILE__) + '/../lib/revactor'
2
+
3
+ NTIMES=100000
4
+
5
+ begin_time = Time.now
6
+
7
+ puts "#{begin_time.strftime('%H:%M:%S')} -- Sending #{NTIMES} messages"
8
+
9
+ Actor.start do
10
+ parent = Actor.current
11
+ child = Actor.spawn do
12
+ (NTIMES / 2).times do
13
+ Actor.receive do |f|
14
+ f.when(:foo) { parent << :bar }
15
+ end
16
+ end
17
+ end
18
+
19
+ child << :foo
20
+ (NTIMES / 2).times do
21
+ Actor.receive do |f|
22
+ f.when(:bar) { child << :foo }
23
+ end
24
+ end
25
+ end
26
+
27
+ end_time = Time.now
28
+ duration = end_time - begin_time
29
+ throughput = NTIMES / duration
30
+
31
+ puts "#{end_time.strftime('%H:%M:%S')} -- Finished"
32
+ puts "Duration: #{sprintf("%0.2f", duration)} seconds"
33
+ puts "Throughput: #{sprintf("%0.2f", throughput)} messages per second"
metadata ADDED
@@ -0,0 +1,92 @@
1
+ --- !ruby/object:Gem::Specification
2
+ rubygems_version: 0.9.4
3
+ specification_version: 1
4
+ name: revactor
5
+ version: !ruby/object:Gem::Version
6
+ version: 0.1.0
7
+ date: 2008-01-15 00:00:00 -07:00
8
+ summary: Revactor is an Actor implementation for writing high performance concurrent programs
9
+ require_paths:
10
+ - lib
11
+ email: tony@medioh.com
12
+ homepage: http://revactor.org
13
+ rubyforge_project: revactor
14
+ description:
15
+ autorequire:
16
+ default_executable:
17
+ bindir: bin
18
+ has_rdoc: true
19
+ required_ruby_version: !ruby/object:Gem::Version::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 1.9.0
24
+ version:
25
+ platform: ruby
26
+ signing_key:
27
+ cert_chain:
28
+ post_install_message:
29
+ authors:
30
+ - Tony Arcieri
31
+ files:
32
+ - lib/revactor
33
+ - lib/revactor/actor.rb
34
+ - lib/revactor/behaviors
35
+ - lib/revactor/behaviors/server.rb
36
+ - lib/revactor/filters
37
+ - lib/revactor/filters/line.rb
38
+ - lib/revactor/filters/packet.rb
39
+ - lib/revactor/mongrel.rb
40
+ - lib/revactor/server.rb
41
+ - lib/revactor/tcp.rb
42
+ - lib/revactor.rb
43
+ - examples/echo_server.rb
44
+ - examples/google.rb
45
+ - examples/mongrel.rb
46
+ - tools/messaging_throughput.rb
47
+ - spec/actor_spec.rb
48
+ - spec/line_filter_spec.rb
49
+ - spec/packet_filter_spec.rb
50
+ - spec/tcp_spec.rb
51
+ - Rakefile
52
+ - revactor.gemspec
53
+ - LICENSE
54
+ - README
55
+ - CHANGES
56
+ test_files: []
57
+
58
+ rdoc_options:
59
+ - --title
60
+ - Revactor
61
+ - --main
62
+ - README
63
+ - --line-numbers
64
+ extra_rdoc_files:
65
+ - LICENSE
66
+ - README
67
+ - CHANGES
68
+ executables: []
69
+
70
+ extensions: []
71
+
72
+ requirements: []
73
+
74
+ dependencies:
75
+ - !ruby/object:Gem::Dependency
76
+ name: rev
77
+ version_requirement:
78
+ version_requirements: !ruby/object:Gem::Version::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: 0.1.3
83
+ version:
84
+ - !ruby/object:Gem::Dependency
85
+ name: case
86
+ version_requirement:
87
+ version_requirements: !ruby/object:Gem::Version::Requirement
88
+ requirements:
89
+ - - ">="
90
+ - !ruby/object:Gem::Version
91
+ version: "0.3"
92
+ version: