revactor 0.1.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.
@@ -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: