green 0.0.1 → 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.
- 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/Gemfile
CHANGED
@@ -1,7 +1,23 @@
|
|
1
1
|
source "http://rubygems.org"
|
2
2
|
|
3
|
-
gem "eventmachine", "
|
3
|
+
gem "eventmachine", "= 1.0.0.beta.4"
|
4
4
|
gem "em-http-request"
|
5
|
+
gem "ffi-rzmq"
|
6
|
+
gem "mysql2"
|
5
7
|
gem "unicorn"
|
8
|
+
gem "activerecord"
|
9
|
+
gem "algorithms"
|
10
|
+
gem "nio4r"
|
11
|
+
gem "rake"
|
12
|
+
|
13
|
+
group :test do
|
14
|
+
gem "rr"
|
15
|
+
gem "minitest"
|
16
|
+
gem "minitest-reporters"
|
17
|
+
end
|
18
|
+
|
19
|
+
group :development do
|
20
|
+
gem "debugger"
|
21
|
+
end
|
6
22
|
|
7
23
|
gemspec
|
data/Gemfile.lock
CHANGED
@@ -1,25 +1,68 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
green (0.
|
4
|
+
green (0.1)
|
5
|
+
kgio (= 2.7.4)
|
5
6
|
|
6
7
|
GEM
|
7
8
|
remote: http://rubygems.org/
|
8
9
|
specs:
|
9
|
-
|
10
|
-
|
10
|
+
activemodel (3.2.3)
|
11
|
+
activesupport (= 3.2.3)
|
12
|
+
builder (~> 3.0.0)
|
13
|
+
activerecord (3.2.3)
|
14
|
+
activemodel (= 3.2.3)
|
15
|
+
activesupport (= 3.2.3)
|
16
|
+
arel (~> 3.0.2)
|
17
|
+
tzinfo (~> 0.3.29)
|
18
|
+
activesupport (3.2.3)
|
19
|
+
i18n (~> 0.6)
|
20
|
+
multi_json (~> 1.0)
|
21
|
+
addressable (2.2.8)
|
22
|
+
algorithms (0.5.0)
|
23
|
+
ansi (1.4.2)
|
24
|
+
arel (3.0.2)
|
25
|
+
builder (3.0.0)
|
26
|
+
columnize (0.3.5)
|
27
|
+
cookiejar (0.3.0)
|
28
|
+
debugger (1.1.3)
|
29
|
+
columnize (>= 0.3.1)
|
30
|
+
debugger-linecache (~> 1.1.1)
|
31
|
+
debugger-ruby_core_source (~> 1.1.2)
|
32
|
+
debugger-linecache (1.1.1)
|
33
|
+
debugger-ruby_core_source (>= 1.1.1)
|
34
|
+
debugger-ruby_core_source (1.1.2)
|
35
|
+
em-http-request (1.0.2)
|
11
36
|
addressable (>= 2.2.3)
|
37
|
+
cookiejar
|
12
38
|
em-socksify
|
13
|
-
eventmachine (>= 1.0.0.beta.
|
14
|
-
http_parser.rb (>= 0.5.
|
39
|
+
eventmachine (>= 1.0.0.beta.4)
|
40
|
+
http_parser.rb (>= 0.5.3)
|
15
41
|
em-socksify (0.2.0)
|
16
42
|
eventmachine (>= 1.0.0.beta.4)
|
17
|
-
eventmachine (1.0.0.
|
43
|
+
eventmachine (1.0.0.beta.4)
|
44
|
+
ffi (1.2.0)
|
45
|
+
ffi-rzmq (0.9.6)
|
46
|
+
ffi
|
18
47
|
http_parser.rb (0.5.3)
|
48
|
+
i18n (0.6.0)
|
19
49
|
kgio (2.7.4)
|
50
|
+
minitest (3.0.0)
|
51
|
+
minitest-reporters (0.7.1)
|
52
|
+
ansi
|
53
|
+
builder
|
54
|
+
minitest (>= 2.0, < 4.0)
|
55
|
+
ruby-progressbar
|
56
|
+
multi_json (1.3.6)
|
57
|
+
mysql2 (0.3.11)
|
58
|
+
nio4r (0.3.3)
|
20
59
|
rack (1.4.1)
|
21
60
|
raindrops (0.8.0)
|
22
|
-
|
61
|
+
rake (0.9.2.2)
|
62
|
+
rr (1.0.4)
|
63
|
+
ruby-progressbar (0.0.10)
|
64
|
+
tzinfo (0.3.33)
|
65
|
+
unicorn (4.3.1)
|
23
66
|
kgio (~> 2.6)
|
24
67
|
rack
|
25
68
|
raindrops (~> 0.7)
|
@@ -28,7 +71,17 @@ PLATFORMS
|
|
28
71
|
ruby
|
29
72
|
|
30
73
|
DEPENDENCIES
|
74
|
+
activerecord
|
75
|
+
algorithms
|
76
|
+
debugger
|
31
77
|
em-http-request
|
32
|
-
eventmachine (
|
78
|
+
eventmachine (= 1.0.0.beta.4)
|
79
|
+
ffi-rzmq
|
33
80
|
green!
|
81
|
+
minitest
|
82
|
+
minitest-reporters
|
83
|
+
mysql2
|
84
|
+
nio4r
|
85
|
+
rake
|
86
|
+
rr
|
34
87
|
unicorn
|
data/README.md
CHANGED
@@ -1,3 +1,6 @@
|
|
1
|
+
Green [](http://travis-ci.org/prepor/green)
|
2
|
+
=====
|
3
|
+
|
1
4
|
Cooperative multitasking for Ruby. Proof of concept.
|
2
5
|
|
3
6
|
Based on Ruby 1.9 Fibers, but unlike EM::Synchrony it uses symmetric coroutines (only #current and #transfer used) and HUB-orientend architecture. So coroutines transfer control to HUB and HUB transfer control to coroutines. Coroutines never tranfer control to each other.
|
@@ -51,5 +54,47 @@ rescue Timeout::Error
|
|
51
54
|
end
|
52
55
|
```
|
53
56
|
|
54
|
-
|
57
|
+
You can use net/http (and any gem over it):
|
58
|
+
|
59
|
+
```ruby
|
60
|
+
require 'green'
|
61
|
+
require 'green/group'
|
62
|
+
require 'green/monkey'
|
63
|
+
require 'net/http'
|
64
|
+
|
65
|
+
g = Green::Pool.new(size: 2)
|
66
|
+
|
67
|
+
hosts = ['google.com', 'yandex.ru']
|
68
|
+
|
69
|
+
results = g.enumerator(hosts) do |host|
|
70
|
+
Net::HTTP.get host, '/'
|
71
|
+
end.map { |i| i }
|
72
|
+
|
73
|
+
p results
|
74
|
+
```
|
75
|
+
|
76
|
+
You can run nonblock Web application with any webserver:
|
77
|
+
|
78
|
+
```ruby
|
79
|
+
require 'green'
|
80
|
+
require 'green/group'
|
81
|
+
|
82
|
+
app = proc do |env|
|
83
|
+
start = Time.now
|
84
|
+
g = Green::Group.new
|
85
|
+
results = []
|
86
|
+
g.spawn do
|
87
|
+
Green.sleep 1
|
88
|
+
results << :fiz
|
89
|
+
end
|
90
|
+
g.spawn do
|
91
|
+
Green.sleep 1
|
92
|
+
results << :buz
|
93
|
+
end
|
94
|
+
g.join
|
95
|
+
[200, {"Content-Type" => 'plain/text'}, ["Execution time: #{Time.now - start}; Results: #{results.inspect}"]]
|
96
|
+
end
|
97
|
+
run app
|
98
|
+
```
|
55
99
|
|
100
|
+
Run it with `unicorn app.ru`
|
data/green.gemspec
CHANGED
@@ -4,8 +4,8 @@ Gem::Specification.new do |s|
|
|
4
4
|
s.rubygems_version = '1.3.5'
|
5
5
|
|
6
6
|
s.name = 'green'
|
7
|
-
s.version = '0.
|
8
|
-
s.date = '2012-
|
7
|
+
s.version = '0.1'
|
8
|
+
s.date = '2012-12-13'
|
9
9
|
s.rubyforge_project = 'green'
|
10
10
|
|
11
11
|
s.summary = "Cooperative multitasking fo Ruby"
|
@@ -20,25 +20,44 @@ Gem::Specification.new do |s|
|
|
20
20
|
s.rdoc_options = ["--charset=UTF-8"]
|
21
21
|
s.extra_rdoc_files = %w[README.md]
|
22
22
|
|
23
|
-
|
23
|
+
s.add_runtime_dependency("kgio", "2.7.4")
|
24
24
|
# = MANIFEST =
|
25
25
|
s.files = %w[
|
26
26
|
Gemfile
|
27
27
|
Gemfile.lock
|
28
|
+
README.md
|
28
29
|
Rakefile
|
29
|
-
Readme.md
|
30
30
|
app.ru
|
31
31
|
green.gemspec
|
32
|
+
lib/active_record/connection_adapters/green_mysql2_adapter.rb
|
32
33
|
lib/green-em/em-http.rb
|
33
34
|
lib/green.rb
|
35
|
+
lib/green/activerecord.rb
|
36
|
+
lib/green/connection_pool.rb
|
34
37
|
lib/green/event.rb
|
35
38
|
lib/green/ext.rb
|
36
39
|
lib/green/group.rb
|
37
40
|
lib/green/hub.rb
|
38
41
|
lib/green/hub/em.rb
|
42
|
+
lib/green/hub/nio4r.rb
|
39
43
|
lib/green/monkey.rb
|
44
|
+
lib/green/mysql2.rb
|
40
45
|
lib/green/semaphore.rb
|
41
|
-
lib/green/
|
46
|
+
lib/green/socket.rb
|
47
|
+
lib/green/zmq.rb
|
48
|
+
spec/green/activerecord_spec.rb
|
49
|
+
spec/green/connection_pool_spec.rb
|
50
|
+
spec/green/event_spec.rb
|
51
|
+
spec/green/group_spec.rb
|
52
|
+
spec/green/monkey_spec.rb
|
53
|
+
spec/green/mysql2_spec.rb
|
54
|
+
spec/green/semaphore_spec.rb
|
55
|
+
spec/green/socket_spec.rb
|
56
|
+
spec/green/tcpsocket_spec.rb
|
57
|
+
spec/green/zmq_spec.rb
|
58
|
+
spec/green_spec.rb
|
59
|
+
spec/helpers.rb
|
60
|
+
spec/spec_helper.rb
|
42
61
|
]
|
43
62
|
# = MANIFEST =
|
44
63
|
|
@@ -0,0 +1,92 @@
|
|
1
|
+
# AR adapter for using a green mysql2 connection
|
2
|
+
# Just update your database.yml's adapter to be 'green_mysql2'
|
3
|
+
|
4
|
+
require 'green/mysql2'
|
5
|
+
require 'green/activerecord'
|
6
|
+
require 'active_record/connection_adapters/mysql2_adapter'
|
7
|
+
|
8
|
+
# module ActiveRecord
|
9
|
+
# class Base
|
10
|
+
# def self.green_mysql2_connection(config)
|
11
|
+
# client = Green::ActiveRecord::ConnectionPool.new(size: config[:pool]) do
|
12
|
+
# conn = ActiveRecord::ConnectionAdapters::GreenMysql2Adapter::Client.new(config.symbolize_keys)
|
13
|
+
# # From Mysql2Adapter#configure_connection
|
14
|
+
# conn.query_options.merge!(:as => :array)
|
15
|
+
|
16
|
+
# # By default, MySQL 'where id is null' selects the last inserted id.
|
17
|
+
# # Turn this off. http://dev.rubyonrails.org/ticket/6778
|
18
|
+
# variable_assignments = ['SQL_AUTO_IS_NULL=0']
|
19
|
+
# encoding = config[:encoding]
|
20
|
+
# variable_assignments << "NAMES '#{encoding}'" if encoding
|
21
|
+
|
22
|
+
# wait_timeout = config[:wait_timeout]
|
23
|
+
# wait_timeout = 2592000 unless wait_timeout.is_a?(Fixnum)
|
24
|
+
# variable_assignments << "@@wait_timeout = #{wait_timeout}"
|
25
|
+
|
26
|
+
# conn.query("SET #{variable_assignments.join(', ')}")
|
27
|
+
# conn
|
28
|
+
# end
|
29
|
+
# options = [config[:host], config[:username], config[:password], config[:database], config[:port], config[:socket], 0]
|
30
|
+
# ActiveRecord::ConnectionAdapters::GreenMysql2Adapter.new(client, logger, options, config)
|
31
|
+
# end
|
32
|
+
# end
|
33
|
+
|
34
|
+
# module ConnectionAdapters
|
35
|
+
# class GreenMysql2Adapter < ::ActiveRecord::ConnectionAdapters::Mysql2Adapter
|
36
|
+
|
37
|
+
# class Column < AbstractMysqlAdapter::Column # :nodoc:
|
38
|
+
# def adapter
|
39
|
+
# GreenMysql2Adapter
|
40
|
+
# end
|
41
|
+
# end
|
42
|
+
|
43
|
+
# ADAPTER_NAME = 'GreenMysql2'
|
44
|
+
|
45
|
+
# class Client < Green::Mysql2::Client
|
46
|
+
# include Green::ActiveRecord::Client
|
47
|
+
# end
|
48
|
+
|
49
|
+
# include Green::ActiveRecord::Adapter
|
50
|
+
|
51
|
+
# def connect
|
52
|
+
|
53
|
+
# end
|
54
|
+
# end
|
55
|
+
# end
|
56
|
+
# end
|
57
|
+
|
58
|
+
|
59
|
+
|
60
|
+
module ActiveRecord
|
61
|
+
class Base
|
62
|
+
def self.green_mysql2_connection(config)
|
63
|
+
config[:username] = 'root' if config[:username].nil?
|
64
|
+
|
65
|
+
if Mysql2::Client.const_defined? :FOUND_ROWS
|
66
|
+
config[:flags] = Mysql2::Client::FOUND_ROWS
|
67
|
+
end
|
68
|
+
|
69
|
+
client = Green::Mysql2::Client.new(config.symbolize_keys)
|
70
|
+
options = [config[:host], config[:username], config[:password], config[:database], config[:port], config[:socket], 0]
|
71
|
+
ConnectionAdapters::GreenMysql2Adapter.new(client, logger, options, config)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
module ConnectionAdapters
|
76
|
+
class GreenMysql2Adapter < ::ActiveRecord::ConnectionAdapters::Mysql2Adapter
|
77
|
+
|
78
|
+
class Column < AbstractMysqlAdapter::Column # :nodoc:
|
79
|
+
def adapter
|
80
|
+
GreenMysql2Adapter
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
ADAPTER_NAME = 'GreenMysql2'
|
85
|
+
|
86
|
+
def connect
|
87
|
+
@connection = Green::Mysql2::Client.new(@config)
|
88
|
+
configure_connection
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'green'
|
2
|
+
require 'active_record'
|
3
|
+
require 'active_record/connection_adapters/abstract/connection_pool'
|
4
|
+
require 'active_record/connection_adapters/abstract_adapter'
|
5
|
+
require 'green/semaphore'
|
6
|
+
require 'green/connection_pool'
|
7
|
+
|
8
|
+
suppress_warnings do
|
9
|
+
::Thread = Green
|
10
|
+
::Mutex = Green::Mutex
|
11
|
+
::ConditionVariable = Green::ConditionVariable
|
12
|
+
end
|
13
|
+
|
14
|
+
class Green
|
15
|
+
class ActiveRecord < ::Green
|
16
|
+
def initialize
|
17
|
+
super
|
18
|
+
callback { ::ActiveRecord::Base.clear_active_connections! }
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
class Green
|
2
|
+
class ConnectionPool
|
3
|
+
undef :send
|
4
|
+
|
5
|
+
def initialize(opts, &block)
|
6
|
+
@reserved = {} # map of in-progress connections
|
7
|
+
@available = [] # pool of free connections
|
8
|
+
@pending = [] # pending reservations (FIFO)
|
9
|
+
|
10
|
+
opts[:size].times do
|
11
|
+
@available.push(block.call) if block_given?
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
# Choose first available connection and pass it to the supplied
|
16
|
+
# block. This will block indefinitely until there is an available
|
17
|
+
# connection to service the request.
|
18
|
+
def execute
|
19
|
+
g = Green.current
|
20
|
+
begin
|
21
|
+
conn = acquire(g)
|
22
|
+
yield conn
|
23
|
+
ensure
|
24
|
+
release(g)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
# Acquire a lock on a connection and assign it to executing fiber
|
31
|
+
# - if connection is available, pass it back to the calling block
|
32
|
+
# - if pool is full, yield the current fiber until connection is available
|
33
|
+
def acquire(green)
|
34
|
+
if conn = @available.pop
|
35
|
+
@reserved[green.object_id] = conn
|
36
|
+
conn
|
37
|
+
else
|
38
|
+
@pending.push green
|
39
|
+
Green.hub.wait { @pending.delete green }
|
40
|
+
acquire(green)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# Release connection assigned to the supplied fiber and
|
45
|
+
# resume any other pending connections (which will
|
46
|
+
# immediately try to run acquire on the pool)
|
47
|
+
def release(green)
|
48
|
+
conn = @reserved.delete(green.object_id)
|
49
|
+
@available.push(conn)
|
50
|
+
|
51
|
+
if pending = @pending.shift
|
52
|
+
Green.hub.callback { pending.switch }
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# Allow the pool to behave as the underlying connection
|
57
|
+
#
|
58
|
+
# If the requesting method begins with "a" prefix, then
|
59
|
+
# hijack the callbacks and errbacks to fire a connection
|
60
|
+
# pool release whenever the request is complete. Otherwise
|
61
|
+
# yield the connection within execute method and release
|
62
|
+
# once it is complete (assumption: fiber will yield until
|
63
|
+
# data is available, or request is complete)
|
64
|
+
#
|
65
|
+
def method_missing(method, *args, &blk)
|
66
|
+
execute do |conn|
|
67
|
+
conn.__send__(method, *args, &blk)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
data/lib/green/event.rb
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
class Green
|
2
2
|
class Event
|
3
|
-
include Green::Waiter
|
4
3
|
attr_reader :waiters
|
5
4
|
def initialize
|
6
5
|
@waiters = []
|
@@ -17,13 +16,10 @@ class Green
|
|
17
16
|
if @setted
|
18
17
|
@result
|
19
18
|
else
|
20
|
-
|
21
|
-
|
19
|
+
g = Green.current
|
20
|
+
waiters << g
|
21
|
+
Green.hub.wait { waiters.delete g }
|
22
22
|
end
|
23
23
|
end
|
24
|
-
|
25
|
-
def green_cancel(waiter)
|
26
|
-
waiters.delete waiter
|
27
|
-
end
|
28
24
|
end
|
29
25
|
end
|
data/lib/green/ext.rb
CHANGED
@@ -17,9 +17,17 @@ class Fiber
|
|
17
17
|
local_fiber_variables[key] = value
|
18
18
|
end
|
19
19
|
|
20
|
-
private
|
21
|
-
|
22
20
|
def local_fiber_variables
|
23
21
|
@local_fiber_variables ||= {}
|
24
22
|
end
|
23
|
+
end
|
24
|
+
|
25
|
+
module Kernel
|
26
|
+
def suppress_warnings
|
27
|
+
original_verbosity = $VERBOSE
|
28
|
+
$VERBOSE = nil
|
29
|
+
result = yield
|
30
|
+
$VERBOSE = original_verbosity
|
31
|
+
return result
|
32
|
+
end
|
25
33
|
end
|
data/lib/green/group.rb
CHANGED
@@ -3,13 +3,14 @@ require 'green/semaphore'
|
|
3
3
|
class Green
|
4
4
|
class Group
|
5
5
|
attr_reader :options, :greens
|
6
|
-
def initialize(options = {})
|
6
|
+
def initialize(options = {})
|
7
7
|
@options = options
|
8
|
+
@options[:klass] ||= ::Green
|
8
9
|
@greens = []
|
9
10
|
end
|
10
11
|
|
11
12
|
def spawn(*args, &blk)
|
12
|
-
g =
|
13
|
+
g = @options[:klass].spawn do
|
13
14
|
blk.call(*args)
|
14
15
|
end
|
15
16
|
add g
|
@@ -30,11 +31,19 @@ class Green
|
|
30
31
|
end
|
31
32
|
|
32
33
|
def join
|
33
|
-
while (g = greens.
|
34
|
+
while (g = greens.first)
|
34
35
|
g.join
|
35
36
|
end
|
36
37
|
end
|
37
38
|
|
39
|
+
def kill
|
40
|
+
greens.each(&:kill)
|
41
|
+
end
|
42
|
+
|
43
|
+
def size
|
44
|
+
greens.size
|
45
|
+
end
|
46
|
+
|
38
47
|
def enumerator(iterable, &blk)
|
39
48
|
iter = iterable.each
|
40
49
|
Enumerator.new do |y|
|
@@ -47,9 +56,9 @@ class Green
|
|
47
56
|
spawn(i) do |item|
|
48
57
|
y << blk.call(item)
|
49
58
|
waiting -= 1
|
50
|
-
e.set if waiting == 0
|
59
|
+
e.set if waiting == 0
|
51
60
|
end
|
52
|
-
end
|
61
|
+
end
|
53
62
|
rescue StopIteration
|
54
63
|
e.wait
|
55
64
|
end
|
@@ -61,6 +70,7 @@ class Green
|
|
61
70
|
attr_reader :semaphore
|
62
71
|
def initialize(*args)
|
63
72
|
super
|
73
|
+
raise ArgumentError.new("Undefined option :size") unless options[:size]
|
64
74
|
@semaphore = Semaphore.new(options[:size])
|
65
75
|
end
|
66
76
|
|
@@ -74,5 +84,9 @@ class Green
|
|
74
84
|
end
|
75
85
|
end
|
76
86
|
end
|
87
|
+
|
88
|
+
def join
|
89
|
+
semaphore.wait
|
90
|
+
end
|
77
91
|
end
|
78
|
-
end
|
92
|
+
end
|
data/lib/green/hub/em.rb
CHANGED
@@ -1,26 +1,58 @@
|
|
1
1
|
require 'eventmachine'
|
2
2
|
|
3
3
|
class ::EM::Timer
|
4
|
-
include Green::Waiter
|
5
|
-
|
6
4
|
def green_cancel
|
7
5
|
cancel
|
8
6
|
end
|
9
7
|
end
|
10
8
|
|
11
9
|
module ::EM::Deferrable
|
12
|
-
include Green::Waiter
|
13
|
-
|
14
10
|
def green_cancel
|
15
|
-
|
16
|
-
|
17
|
-
|
11
|
+
instance_variable_get(:@callbacks).each { |c| cancel_callback c }
|
12
|
+
instance_variable_get(:@errbacks).each { |c| cancel_errback c }
|
13
|
+
cancel_timeout
|
18
14
|
end
|
19
15
|
end
|
20
16
|
|
21
17
|
class Green
|
22
18
|
class Hub
|
23
19
|
class EM < Hub
|
20
|
+
class SocketWaiter < Green::SocketWaiter
|
21
|
+
class Handler < ::EM::Connection
|
22
|
+
attr_accessor :green
|
23
|
+
def notify_readable
|
24
|
+
green.switch
|
25
|
+
end
|
26
|
+
|
27
|
+
def notify_writable
|
28
|
+
green.switch
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def wait_read
|
33
|
+
make_handler(:readable)
|
34
|
+
end
|
35
|
+
|
36
|
+
def wait_write
|
37
|
+
make_handler(:writable)
|
38
|
+
end
|
39
|
+
|
40
|
+
def make_handler(mode)
|
41
|
+
h = ::EM.watch socket, Handler do |c|
|
42
|
+
c.green = Green.current
|
43
|
+
end
|
44
|
+
case mode
|
45
|
+
when :readable
|
46
|
+
h.notify_readable = true
|
47
|
+
when :writable
|
48
|
+
h.notify_writable = true
|
49
|
+
end
|
50
|
+
Green.hub.switch
|
51
|
+
ensure
|
52
|
+
h.detach
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
24
56
|
# если мы запускаем приложение внутри thin или rainbows с EM, то значит мы уже внутри EM-реактора, а hub должен переключиться в main тред.
|
25
57
|
def run
|
26
58
|
if ::EM.reactor_running?
|
@@ -36,8 +68,16 @@ class Green
|
|
36
68
|
::EM::Timer.new(n, &blk)
|
37
69
|
end
|
38
70
|
|
39
|
-
def callback(&blk)
|
40
|
-
::EM.next_tick(
|
71
|
+
def callback(cb=nil, &blk)
|
72
|
+
::EM.next_tick(cb || blk)
|
73
|
+
end
|
74
|
+
|
75
|
+
def socket_waiter(socket)
|
76
|
+
SocketWaiter.new socket
|
77
|
+
end
|
78
|
+
|
79
|
+
def stop
|
80
|
+
EM.stop
|
41
81
|
end
|
42
82
|
end
|
43
83
|
end
|