green 0.0.1 → 0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +17 -1
- data/Gemfile.lock +61 -8
- data/README.md +46 -1
- data/green.gemspec +24 -5
- data/lib/active_record/connection_adapters/green_mysql2_adapter.rb +92 -0
- data/lib/green/activerecord.rb +21 -0
- data/lib/green/connection_pool.rb +72 -0
- data/lib/green/event.rb +3 -7
- data/lib/green/ext.rb +10 -2
- data/lib/green/group.rb +20 -6
- data/lib/green/hub/em.rb +49 -9
- data/lib/green/hub/nio4r.rb +147 -0
- data/lib/green/hub.rb +12 -3
- data/lib/green/monkey.rb +11 -13
- data/lib/green/mysql2.rb +16 -0
- data/lib/green/semaphore.rb +136 -6
- data/lib/green/socket.rb +124 -0
- data/lib/green/zmq.rb +92 -0
- data/lib/green-em/em-http.rb +3 -4
- data/lib/green.rb +92 -33
- data/spec/green/activerecord_spec.rb +100 -0
- data/spec/green/connection_pool_spec.rb +45 -0
- data/spec/green/event_spec.rb +24 -0
- data/spec/green/group_spec.rb +68 -0
- data/spec/green/monkey_spec.rb +22 -0
- data/spec/green/mysql2_spec.rb +52 -0
- data/spec/green/semaphore_spec.rb +169 -0
- data/spec/green/socket_spec.rb +26 -0
- data/spec/green/tcpsocket_spec.rb +417 -0
- data/spec/green/zmq_spec.rb +121 -0
- data/spec/green_spec.rb +37 -0
- data/spec/helpers.rb +0 -0
- data/spec/spec_helper.rb +32 -0
- metadata +56 -9
- data/Readme.md +0 -55
- data/lib/green/tcp_socket.rb +0 -25
data/lib/green.rb
CHANGED
@@ -3,26 +3,65 @@ require 'pp'
|
|
3
3
|
require 'thread'
|
4
4
|
|
5
5
|
class Green
|
6
|
-
VERSION = "0.
|
6
|
+
VERSION = "0.1"
|
7
|
+
|
8
|
+
module GreenMethods
|
9
|
+
def switch(*args)
|
10
|
+
f.transfer(*args).tap do |*res|
|
11
|
+
if res.size == 1 && res.first.is_a?(ThrowException)
|
12
|
+
raise(res.first.exc)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def throw(exc = RuntimeException.new)
|
18
|
+
Green.hub.callback { switch(ThrowException.new exc) }
|
19
|
+
end
|
20
|
+
|
21
|
+
def kill
|
22
|
+
self.throw(GreenKill.new)
|
23
|
+
end
|
24
|
+
|
25
|
+
def locals
|
26
|
+
f.local_fiber_variables
|
27
|
+
end
|
28
|
+
|
29
|
+
def [](name)
|
30
|
+
locals[name]
|
31
|
+
end
|
32
|
+
|
33
|
+
def []=(name, val)
|
34
|
+
locals[name] = val
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
7
38
|
class Proxy
|
39
|
+
include GreenMethods
|
8
40
|
attr_reader :f
|
9
41
|
def initialize
|
10
42
|
@f = Fiber.current
|
11
43
|
end
|
12
44
|
|
13
|
-
def
|
14
|
-
f.
|
45
|
+
def alive?
|
46
|
+
f.alive?
|
15
47
|
end
|
16
48
|
end
|
17
49
|
|
18
|
-
|
19
|
-
|
20
|
-
|
50
|
+
class SocketWaiter
|
51
|
+
attr_reader :socket
|
52
|
+
def initialize(socket)
|
53
|
+
@socket = socket
|
54
|
+
end
|
55
|
+
|
56
|
+
def wait_read
|
57
|
+
end
|
58
|
+
|
59
|
+
def wait_write
|
21
60
|
end
|
22
61
|
end
|
23
62
|
|
24
63
|
class << self
|
25
|
-
|
64
|
+
|
26
65
|
def thread_locals
|
27
66
|
@thread_locals ||= {}
|
28
67
|
@thread_locals[Thread.current] ||= {}
|
@@ -41,7 +80,10 @@ class Green
|
|
41
80
|
end
|
42
81
|
|
43
82
|
def make_hub
|
44
|
-
|
83
|
+
hub_name = ENV['GREEN_HUB'] || 'Nio4r'
|
84
|
+
Hub.const_get(hub_name.to_sym).new
|
85
|
+
# Hub::Nio4r.new
|
86
|
+
# Hub::EM.new
|
45
87
|
end
|
46
88
|
|
47
89
|
def hub
|
@@ -50,68 +92,85 @@ class Green
|
|
50
92
|
end
|
51
93
|
|
52
94
|
def spawn(&blk)
|
53
|
-
|
95
|
+
new(&blk).tap { |o| o.start }
|
54
96
|
end
|
55
97
|
|
56
98
|
def timeout(n, &blk)
|
57
99
|
g = current
|
58
100
|
timer = hub.timer(n) do
|
59
|
-
g.
|
101
|
+
g.throw Timeout::Error.new
|
60
102
|
end
|
61
103
|
res = blk.call
|
62
104
|
timer.cancel
|
63
105
|
res
|
64
106
|
end
|
107
|
+
|
108
|
+
def list_hash
|
109
|
+
@list_hash ||= {}
|
110
|
+
end
|
111
|
+
|
112
|
+
def list
|
113
|
+
list_hash.values
|
114
|
+
end
|
65
115
|
end
|
66
116
|
|
67
|
-
|
68
|
-
|
117
|
+
class GreenError < StandardError; end
|
118
|
+
class GreenKill < GreenError; end
|
119
|
+
|
120
|
+
class ThrowException < Struct.new(:exc); end
|
69
121
|
|
70
122
|
require 'green/ext'
|
71
123
|
require 'green/hub'
|
72
124
|
|
73
125
|
require 'green/hub/em'
|
126
|
+
require 'green/hub/nio4r'
|
127
|
+
|
128
|
+
include GreenMethods
|
74
129
|
|
75
130
|
attr_reader :f, :callbacks
|
76
|
-
def initialize
|
131
|
+
def initialize
|
77
132
|
@callbacks = []
|
133
|
+
@alive = true
|
134
|
+
Green.list_hash[self] = self
|
78
135
|
@f = Fiber.new do
|
79
|
-
|
80
|
-
|
136
|
+
begin
|
137
|
+
res = yield
|
138
|
+
rescue GreenKill => e
|
139
|
+
rescue => e
|
140
|
+
Green.main.throw e
|
141
|
+
end
|
142
|
+
@alive = false
|
143
|
+
@callbacks.each { |c|
|
144
|
+
c.call(res)
|
145
|
+
}
|
146
|
+
Green.list_hash.delete self
|
81
147
|
Green.hub.switch
|
82
148
|
end
|
83
149
|
@f[:green] = self
|
84
150
|
end
|
85
151
|
|
86
|
-
|
87
|
-
|
88
|
-
return unless f.alive?
|
89
|
-
f.transfer(*args).tap do |*res|
|
90
|
-
res.size == 1 && res.first.is_a?(Exception) && raise(res.first)
|
91
|
-
end
|
92
|
-
end
|
93
|
-
|
94
|
-
def throw(exc = RuntimeException.new)
|
95
|
-
switch(exc)
|
152
|
+
def alive?
|
153
|
+
@alive
|
96
154
|
end
|
97
155
|
|
98
156
|
def start
|
99
157
|
Green.hub.callback { self.switch }
|
100
158
|
end
|
101
159
|
|
102
|
-
def callback(&blk)
|
103
|
-
|
160
|
+
def callback(cb=nil, &blk)
|
161
|
+
cb ||= blk
|
162
|
+
if alive?
|
163
|
+
callbacks << cb
|
164
|
+
else
|
165
|
+
Green.hub.callback(cb)
|
166
|
+
end
|
104
167
|
end
|
105
168
|
|
106
169
|
def join
|
107
170
|
g = Green.current
|
108
|
-
callback {
|
171
|
+
callback { |res| Green.hub.callback { g.switch(res) } }
|
109
172
|
Green.hub.switch
|
110
173
|
end
|
111
174
|
|
112
|
-
# def kill
|
113
|
-
# self.throw(GreenKill.new)
|
114
|
-
# end
|
115
|
-
|
116
175
|
MAIN = Fiber.current[:green] = Proxy.new
|
117
|
-
end
|
176
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
# DO NOT WORK
|
2
|
+
|
3
|
+
require "spec_helper"
|
4
|
+
require "green/activerecord"
|
5
|
+
require 'green/group'
|
6
|
+
|
7
|
+
|
8
|
+
# create database widgets;
|
9
|
+
# use widgets;
|
10
|
+
# create table widgets (
|
11
|
+
# id INT NOT NULL AUTO_INCREMENT,
|
12
|
+
# title varchar(255),
|
13
|
+
# PRIMARY KEY (`id`)
|
14
|
+
# );
|
15
|
+
|
16
|
+
class Widget < ActiveRecord::Base; end;
|
17
|
+
|
18
|
+
describe Green::ActiveRecord do
|
19
|
+
DELAY = 0.25
|
20
|
+
QUERY = "SELECT sleep(#{DELAY})"
|
21
|
+
|
22
|
+
def establish_connection
|
23
|
+
# ActiveRecord::Base.logger = Logger.new STDOUT
|
24
|
+
ActiveRecord::Base.establish_connection(
|
25
|
+
:adapter => 'green_mysql2',
|
26
|
+
:database => 'widgets',
|
27
|
+
:username => 'root',
|
28
|
+
:pool => 50
|
29
|
+
)
|
30
|
+
Widget.delete_all
|
31
|
+
end
|
32
|
+
|
33
|
+
before do
|
34
|
+
establish_connection
|
35
|
+
end
|
36
|
+
|
37
|
+
after do
|
38
|
+
ActiveRecord::Base.connection_pool.disconnect!
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should establish AR connection" do
|
42
|
+
result = Widget.find_by_sql(QUERY)
|
43
|
+
result.size.must_equal 1
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should fire sequential, synchronous requests within single green" do
|
47
|
+
start = Time.now.to_f
|
48
|
+
res = []
|
49
|
+
|
50
|
+
res.push Widget.find_by_sql(QUERY)
|
51
|
+
res.push Widget.find_by_sql(QUERY)
|
52
|
+
|
53
|
+
(Time.now.to_f - start.to_f).must_be_within_delta DELAY * res.size, DELAY * res.size * 0.15
|
54
|
+
res.size.must_equal 2
|
55
|
+
end
|
56
|
+
|
57
|
+
# it "should fire 100 requests" do
|
58
|
+
# pool = Green::Pool.new size: 40, klass: Green::ActiveRecord
|
59
|
+
|
60
|
+
# 100.times do
|
61
|
+
# pool.spawn do
|
62
|
+
# widget = Widget.create title: 'hi'
|
63
|
+
# widget.update_attributes title: 'hello'
|
64
|
+
# end
|
65
|
+
# end
|
66
|
+
# pool.join
|
67
|
+
# end
|
68
|
+
|
69
|
+
it "should create widget" do
|
70
|
+
Widget.create
|
71
|
+
Widget.create
|
72
|
+
Widget.count.must_equal 2
|
73
|
+
end
|
74
|
+
|
75
|
+
it "should update widget" do
|
76
|
+
widget = Widget.create title: 'hi'
|
77
|
+
widget.update_attributes title: 'hello'
|
78
|
+
Widget.find(widget.id).title.must_equal 'hello'
|
79
|
+
end
|
80
|
+
|
81
|
+
# describe "transactions" do
|
82
|
+
# it "should work properly" do
|
83
|
+
# pool = Green::Pool.new size: 40, klass: Green::ActiveRecord
|
84
|
+
# 50.times do |i|
|
85
|
+
# pool.spawn do
|
86
|
+
# widget = Widget.create title: "hello"
|
87
|
+
# ActiveRecord::Base.transaction do
|
88
|
+
# widget.update_attributes title: "hi#{i}"
|
89
|
+
# raise ActiveRecord::Rollback
|
90
|
+
# end
|
91
|
+
# end
|
92
|
+
# end
|
93
|
+
# pool.join
|
94
|
+
# Widget.all.each do |widget|
|
95
|
+
# widget.title.must_equal 'hello'
|
96
|
+
# end
|
97
|
+
# end
|
98
|
+
# end
|
99
|
+
|
100
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'green/connection_pool'
|
3
|
+
require 'green/mysql2'
|
4
|
+
require 'green/group'
|
5
|
+
|
6
|
+
DELAY = 0.25
|
7
|
+
QUERY = "select sleep(#{DELAY})"
|
8
|
+
|
9
|
+
describe Green::ConnectionPool do
|
10
|
+
|
11
|
+
|
12
|
+
let(:pool) { Green::ConnectionPool.new(size: size) { Green::Mysql2::Client.new } }
|
13
|
+
|
14
|
+
describe "pool size is exceeded" do
|
15
|
+
let(:size) { 1 }
|
16
|
+
it "should queue requests" do
|
17
|
+
start = Time.now.to_f
|
18
|
+
|
19
|
+
g = Green::Group.new
|
20
|
+
res = []
|
21
|
+
g.spawn { res << pool.query(QUERY) }
|
22
|
+
g.spawn { res << pool.query(QUERY) }
|
23
|
+
g.join
|
24
|
+
|
25
|
+
(Time.now.to_f - start.to_f).must_be_within_delta DELAY * 2, DELAY * 2 * 0.15
|
26
|
+
res.size.must_equal 2
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe "pool size is enough" do
|
31
|
+
let(:size) { 2 }
|
32
|
+
it "should parallel requests" do
|
33
|
+
start = Time.now.to_f
|
34
|
+
|
35
|
+
g = Green::Group.new
|
36
|
+
res = []
|
37
|
+
g.spawn { res << pool.query(QUERY) }
|
38
|
+
g.spawn { res << pool.query(QUERY) }
|
39
|
+
g.join
|
40
|
+
|
41
|
+
(Time.now.to_f - start.to_f).must_be_within_delta DELAY, DELAY * 0.30
|
42
|
+
res.size.must_equal 2
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'green/event'
|
3
|
+
require 'green/group'
|
4
|
+
describe Green::Event do
|
5
|
+
let(:e) { Green::Event.new }
|
6
|
+
let(:g) { Green::Group.new }
|
7
|
+
it "should wakeup all greens" do
|
8
|
+
i = 0
|
9
|
+
5.times do
|
10
|
+
g.spawn { e.wait; i += 1}
|
11
|
+
end
|
12
|
+
i.must_equal 0
|
13
|
+
e.set
|
14
|
+
g.join
|
15
|
+
i.must_equal 5
|
16
|
+
end
|
17
|
+
|
18
|
+
describe "set before wait" do
|
19
|
+
it "should work properly" do
|
20
|
+
e.set(:ok)
|
21
|
+
e.wait.must_equal :ok
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'green/group'
|
3
|
+
require 'green/event'
|
4
|
+
require 'set'
|
5
|
+
describe Green::Group do
|
6
|
+
let(:g) { Green::Group.new }
|
7
|
+
|
8
|
+
describe "join" do
|
9
|
+
it "should wait each task" do
|
10
|
+
result = ""
|
11
|
+
g.spawn { result << "fiz" }
|
12
|
+
g.spawn { result << "baz" }
|
13
|
+
g.join
|
14
|
+
result.must_equal "fizbaz"
|
15
|
+
end
|
16
|
+
|
17
|
+
# it "return each result" do
|
18
|
+
# g.spawn { 1 }
|
19
|
+
# g.spawn { 2 }
|
20
|
+
# g.join.reduce(0, &:+).must_equal 3
|
21
|
+
# end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe "apply" do
|
25
|
+
it "should wait result" do
|
26
|
+
g.apply { :foo }.must_equal :foo
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe "enumerator" do
|
31
|
+
it "should return enumerator" do
|
32
|
+
en = g.enumerator([1, 2, 3]) { |o| o }
|
33
|
+
en.must_be_instance_of Enumerator
|
34
|
+
en.map { |o| o }.must_equal [1, 2, 3]
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
describe Green::Pool do
|
40
|
+
let(:e1) { Green::Event.new }
|
41
|
+
let(:e2) { Green::Event.new }
|
42
|
+
let(:p) { Green::Pool.new size: 1}
|
43
|
+
|
44
|
+
it "should wait each task" do
|
45
|
+
result = ""
|
46
|
+
p.spawn { result << "fiz"; Green.sleep(0) }
|
47
|
+
Green.spawn { p.spawn { result << "baz"; Green.sleep(0) } }
|
48
|
+
p.join
|
49
|
+
result.must_equal "fizbaz"
|
50
|
+
end
|
51
|
+
|
52
|
+
# ugly test :(
|
53
|
+
it "should block after limit" do
|
54
|
+
i = 0
|
55
|
+
p.spawn { i += 1; e1.set; e2.wait }
|
56
|
+
Green.spawn { p.spawn { i += 1 } }
|
57
|
+
p.size.must_equal 1
|
58
|
+
e1.wait
|
59
|
+
i.must_equal 1
|
60
|
+
Green.sleep(0)
|
61
|
+
i.must_equal 1
|
62
|
+
p.size.must_equal 1
|
63
|
+
e2.set
|
64
|
+
p.join
|
65
|
+
i.must_equal 2
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# require 'spec_helper'
|
2
|
+
# require 'green/monkey'
|
3
|
+
|
4
|
+
# describe "green/monkey" do
|
5
|
+
# let(:server) { TCPServer.new("127.0.0.1", 2225) }
|
6
|
+
# let(:client) { TCPSocket.open("127.0.0.1", 2225) }
|
7
|
+
|
8
|
+
# after { server.close; client.close }
|
9
|
+
# it "should read and write" do
|
10
|
+
# Green.spawn do
|
11
|
+
# server.listen(1)
|
12
|
+
# c, a = server.accept
|
13
|
+
# c.write "hello"
|
14
|
+
# c.close
|
15
|
+
# end
|
16
|
+
|
17
|
+
# g = Green.spawn do
|
18
|
+
# client.read.must_equal "hello"
|
19
|
+
# end
|
20
|
+
# g.join
|
21
|
+
# end
|
22
|
+
# end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "green/mysql2"
|
3
|
+
require "green/connection_pool"
|
4
|
+
require "green/group"
|
5
|
+
|
6
|
+
describe Green::Mysql2 do
|
7
|
+
|
8
|
+
DELAY = 0.25
|
9
|
+
QUERY = "SELECT sleep(#{DELAY})"
|
10
|
+
|
11
|
+
it "should support queries" do
|
12
|
+
res = []
|
13
|
+
db = Green::Mysql2::Client.new
|
14
|
+
res = db.query QUERY
|
15
|
+
|
16
|
+
res.first.keys.must_include "sleep(#{DELAY})"
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should fire sequential, synchronous requests" do
|
20
|
+
db = Green::Mysql2::Client.new
|
21
|
+
|
22
|
+
start = Time.now.to_f
|
23
|
+
res = []
|
24
|
+
|
25
|
+
res.push db.query QUERY
|
26
|
+
res.push db.query QUERY
|
27
|
+
(Time.now.to_f - start.to_f).must_be_within_delta DELAY * res.size, DELAY * res.size * 0.15
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should fire simultaneous requests via pool" do
|
31
|
+
db = Green::ConnectionPool.new(size: 2) do
|
32
|
+
Green::Mysql2::Client.new
|
33
|
+
end
|
34
|
+
|
35
|
+
start = Time.now.to_f
|
36
|
+
res = []
|
37
|
+
g = Green::Group.new
|
38
|
+
g.spawn { res << db.query(QUERY) }
|
39
|
+
g.spawn { res << db.query(QUERY) }
|
40
|
+
g.join
|
41
|
+
|
42
|
+
(Time.now.to_f - start.to_f).must_be_within_delta DELAY, DELAY * 0.30
|
43
|
+
res.size.must_equal 2
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should raise Mysql::Error in case of error" do
|
47
|
+
db = Green::Mysql2::Client.new
|
48
|
+
proc {
|
49
|
+
db.query("SELECT * FROM i_hope_this_table_does_not_exist;")
|
50
|
+
}.must_raise(Mysql2::Error)
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,169 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'green/semaphore'
|
3
|
+
require 'green/event'
|
4
|
+
require 'green/group'
|
5
|
+
|
6
|
+
describe Green::Mutex do
|
7
|
+
let(:m) { Green::Mutex.new }
|
8
|
+
it "should synchronize" do
|
9
|
+
e = Green::Event.new
|
10
|
+
i = 0
|
11
|
+
Green.spawn do
|
12
|
+
m.synchronize do
|
13
|
+
e.wait
|
14
|
+
i += 1
|
15
|
+
end
|
16
|
+
end
|
17
|
+
Green.spawn do
|
18
|
+
e.set
|
19
|
+
m.synchronize do
|
20
|
+
i.must_equal 1
|
21
|
+
end
|
22
|
+
end.join
|
23
|
+
end
|
24
|
+
|
25
|
+
describe "lock" do
|
26
|
+
describe "when mutex already locked" do
|
27
|
+
it "should raise GreenError" do
|
28
|
+
proc {
|
29
|
+
m.lock
|
30
|
+
m.lock
|
31
|
+
}.must_raise Green::GreenError
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe "sleep" do
|
37
|
+
describe "without timeout" do
|
38
|
+
it "should sleep until switch" do
|
39
|
+
m.lock
|
40
|
+
i = 0
|
41
|
+
g = Green.current
|
42
|
+
Green.hub.callback { i += 1; g.switch }
|
43
|
+
res = m.sleep
|
44
|
+
i.must_equal 1
|
45
|
+
end
|
46
|
+
|
47
|
+
it "should release lock" do
|
48
|
+
i = 0
|
49
|
+
e = Green::Event.new
|
50
|
+
group = Green::Group.new
|
51
|
+
g1 = group.spawn do
|
52
|
+
m.lock
|
53
|
+
e.wait
|
54
|
+
i += 1
|
55
|
+
m.sleep
|
56
|
+
end
|
57
|
+
group.spawn do
|
58
|
+
e.set
|
59
|
+
m.lock
|
60
|
+
i.must_equal 1
|
61
|
+
m.unlock
|
62
|
+
Green.hub.callback { g1.switch }
|
63
|
+
end
|
64
|
+
group.join
|
65
|
+
end
|
66
|
+
|
67
|
+
it "should wait unlock after switch" do
|
68
|
+
i = 0
|
69
|
+
group = Green::Group.new
|
70
|
+
g1 = group.spawn do
|
71
|
+
m.lock
|
72
|
+
m.sleep
|
73
|
+
i.must_equal 1
|
74
|
+
end
|
75
|
+
group.spawn do
|
76
|
+
m.lock
|
77
|
+
i += 1
|
78
|
+
m.unlock
|
79
|
+
Green.hub.callback { g1.switch }
|
80
|
+
end
|
81
|
+
group.join
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
describe "with timeout" do
|
86
|
+
it "should sleep for timeout" do
|
87
|
+
m.lock
|
88
|
+
i = 0
|
89
|
+
Green.hub.callback { i += 1 }
|
90
|
+
m.sleep(0.1)
|
91
|
+
i.must_equal 1
|
92
|
+
end
|
93
|
+
describe "and resume before timeout" do
|
94
|
+
it "should not raise any execptions" do
|
95
|
+
m.lock
|
96
|
+
g = Green.current
|
97
|
+
Green.hub.callback { g.switch }
|
98
|
+
m.sleep(0.05)
|
99
|
+
Green.spawn { Green.sleep(0.1) }.join
|
100
|
+
end
|
101
|
+
it "should resume in nested Green" do
|
102
|
+
Green.spawn do
|
103
|
+
m.synchronize do
|
104
|
+
t = m.sleep(0.05)
|
105
|
+
t.must_be :>=, 0.05
|
106
|
+
end
|
107
|
+
end.join
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
describe Green::ConditionVariable do
|
113
|
+
let(:c){ Green::ConditionVariable.new }
|
114
|
+
it "should wakeup waiter" do
|
115
|
+
i = ''
|
116
|
+
g = Green::Group.new
|
117
|
+
g.spawn do
|
118
|
+
m.synchronize do
|
119
|
+
i << 'a'
|
120
|
+
c.wait(m)
|
121
|
+
i << 'c'
|
122
|
+
end
|
123
|
+
end
|
124
|
+
g.spawn do
|
125
|
+
i << 'b'
|
126
|
+
c.signal
|
127
|
+
end
|
128
|
+
g.join
|
129
|
+
i.must_equal 'abc'
|
130
|
+
end
|
131
|
+
|
132
|
+
it 'should allow to play ping-pong' do
|
133
|
+
i = ''
|
134
|
+
g = Green::Group.new
|
135
|
+
g.spawn do
|
136
|
+
m.synchronize do
|
137
|
+
i << 'pi'
|
138
|
+
c.wait(m)
|
139
|
+
i << '-po'
|
140
|
+
c.signal
|
141
|
+
end
|
142
|
+
end
|
143
|
+
g.spawn do
|
144
|
+
m.synchronize do
|
145
|
+
i << 'ng'
|
146
|
+
c.signal
|
147
|
+
c.wait(m)
|
148
|
+
i << 'ng'
|
149
|
+
end
|
150
|
+
end
|
151
|
+
g.join
|
152
|
+
i.must_equal 'ping-pong'
|
153
|
+
end
|
154
|
+
it 'should not raise, when timer wakes up green between `signal` and `next_tick`' do
|
155
|
+
e = Green::Event.new
|
156
|
+
g = Green.spawn do
|
157
|
+
m.synchronize do
|
158
|
+
c.wait(m, 0.0001)
|
159
|
+
end
|
160
|
+
end
|
161
|
+
Green.hub.callback do
|
162
|
+
c.signal
|
163
|
+
Green.hub.callback { e.set }
|
164
|
+
end
|
165
|
+
e.wait
|
166
|
+
g.join
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'green/socket'
|
3
|
+
describe Green::Socket do
|
4
|
+
let(:s1) { Green::Socket.new(:INET, :STREAM, 0).tap { |s| s.setsockopt(Socket::SOL_SOCKET, Socket::SO_REUSEADDR, 1) } }
|
5
|
+
let(:s2) { Green::Socket.new(:INET, :STREAM, 0) }
|
6
|
+
|
7
|
+
after do
|
8
|
+
s1.close
|
9
|
+
s2.close
|
10
|
+
end
|
11
|
+
it "should read and write" do
|
12
|
+
Green.spawn do
|
13
|
+
s1.bind(Addrinfo.tcp("127.0.0.1", 2225))
|
14
|
+
s1.listen(1)
|
15
|
+
c, a = s1.accept
|
16
|
+
c.write "hello"
|
17
|
+
c.close
|
18
|
+
end
|
19
|
+
|
20
|
+
g = Green.spawn do
|
21
|
+
s2.connect(Addrinfo.tcp("127.0.0.1", 2225))
|
22
|
+
s2.read.must_equal "hello"
|
23
|
+
end
|
24
|
+
g.join
|
25
|
+
end
|
26
|
+
end
|