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/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 [![Build Status](https://secure.travis-ci.org/prepor/green.png)](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
|