green 0.1 → 0.1.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.
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- green (0.1)
4
+ green (0.1.1)
5
5
  kgio (= 2.7.4)
6
6
 
7
7
  GEM
@@ -23,15 +23,15 @@ GEM
23
23
  ansi (1.4.2)
24
24
  arel (3.0.2)
25
25
  builder (3.0.0)
26
- columnize (0.3.5)
26
+ columnize (0.3.6)
27
27
  cookiejar (0.3.0)
28
- debugger (1.1.3)
28
+ debugger (1.2.2)
29
29
  columnize (>= 0.3.1)
30
30
  debugger-linecache (~> 1.1.1)
31
- debugger-ruby_core_source (~> 1.1.2)
32
- debugger-linecache (1.1.1)
31
+ debugger-ruby_core_source (~> 1.1.5)
32
+ debugger-linecache (1.1.2)
33
33
  debugger-ruby_core_source (>= 1.1.1)
34
- debugger-ruby_core_source (1.1.2)
34
+ debugger-ruby_core_source (1.1.5)
35
35
  em-http-request (1.0.2)
36
36
  addressable (>= 2.2.3)
37
37
  cookiejar
@@ -4,8 +4,10 @@ Gem::Specification.new do |s|
4
4
  s.rubygems_version = '1.3.5'
5
5
 
6
6
  s.name = 'green'
7
- s.version = '0.1'
8
- s.date = '2012-12-13'
7
+
8
+ s.version = '0.1.1'
9
+ s.date = '2013-01-28'
10
+
9
11
  s.rubyforge_project = 'green'
10
12
 
11
13
  s.summary = "Cooperative multitasking fo Ruby"
@@ -30,7 +32,9 @@ Gem::Specification.new do |s|
30
32
  app.ru
31
33
  green.gemspec
32
34
  lib/active_record/connection_adapters/green_mysql2_adapter.rb
35
+ lib/green-em.rb
33
36
  lib/green-em/em-http.rb
37
+ lib/green-em/tarantool.rb
34
38
  lib/green.rb
35
39
  lib/green/activerecord.rb
36
40
  lib/green/connection_pool.rb
@@ -64,4 +68,4 @@ Gem::Specification.new do |s|
64
68
  ## Test files will be grabbed from the file list. Make sure the path glob
65
69
  ## matches what you actually use.
66
70
  s.test_files = s.files.select { |path| path =~ /^spec\/.*_spec\.rb/ }
67
- end
71
+ end
@@ -0,0 +1,24 @@
1
+ require 'green'
2
+ class Green
3
+ module EM
4
+ extend self
5
+ def sync(deferrable, params = {})
6
+ g = Green.current
7
+ deferrable.callback { |*args| g.switch(*get_args(args, params[:errback_args])) }
8
+ deferrable.errback { |*args| g.throw(get_args(args, params[:errback_args]).first) }
9
+ Green.hub.wait { deferrable.green_cancel }
10
+ end
11
+
12
+ def get_args(args, proc_args)
13
+ case proc_args
14
+ when Proc
15
+ proc.call(*args)
16
+ when nil
17
+ args
18
+ else
19
+ proc_args
20
+ end
21
+ end
22
+
23
+ end
24
+ end
@@ -3,6 +3,7 @@ begin
3
3
  rescue LoadError => error
4
4
  raise "Missing EM-Synchrony dependency: gem install em-http-request"
5
5
  end
6
+ require 'green-em'
6
7
 
7
8
  module EventMachine
8
9
  class HTTPException < RuntimeError; end
@@ -11,6 +12,7 @@ module EventMachine
11
12
  class_eval %[
12
13
  alias :a#{type} :#{type}
13
14
  def #{type}(options = {}, &blk)
15
+ <<<<<<< HEAD
14
16
  g = Green.current
15
17
  conn = setup_request(:#{type}, options, &blk)
16
18
  if conn.error.nil?
@@ -18,6 +20,11 @@ module EventMachine
18
20
  conn.errback { g.throw(HTTPException.new) }
19
21
 
20
22
  Green.hub.wait { conn.green_cancel }
23
+ =======
24
+ conn = setup_request(:"#{type}", options, &blk)
25
+ if conn.error.nil?
26
+ Green::EM.sync conn, callback_args: [conn], errback_args: [HTTPException.new(conn)]
27
+ >>>>>>> - Green::EM.sync
21
28
  else
22
29
  raise HTTPException.new
23
30
  end
@@ -25,4 +32,4 @@ module EventMachine
25
32
  ]
26
33
  end
27
34
  end
28
- end
35
+ end
File without changes
@@ -3,7 +3,8 @@ require 'pp'
3
3
  require 'thread'
4
4
 
5
5
  class Green
6
- VERSION = "0.1"
6
+ VERSION = "0.1.1"
7
+
7
8
 
8
9
  module GreenMethods
9
10
  def switch(*args)
@@ -33,6 +34,12 @@ class Green
33
34
  def []=(name, val)
34
35
  locals[name] = val
35
36
  end
37
+
38
+ def schedule(*args)
39
+ Green.hub.callback { self.switch(*args) }
40
+ end
41
+
42
+ alias call schedule
36
43
  end
37
44
 
38
45
  class Proxy
@@ -112,6 +119,10 @@ class Green
112
119
  def list
113
120
  list_hash.values
114
121
  end
122
+
123
+ def init
124
+ hub
125
+ end
115
126
  end
116
127
 
117
128
  class GreenError < StandardError; end
@@ -136,11 +147,9 @@ class Green
136
147
  begin
137
148
  res = yield
138
149
  rescue GreenKill => e
139
- rescue => e
140
- Green.main.throw e
141
150
  end
142
151
  @alive = false
143
- @callbacks.each { |c|
152
+ @callbacks.each { |c|
144
153
  c.call(res)
145
154
  }
146
155
  Green.list_hash.delete self
@@ -1,72 +1,74 @@
1
1
  class Green
2
2
  class ConnectionPool
3
- undef :send
3
+ class Proxy < BasicObject
4
+ def initialize(pool)
5
+ @pool = pool
6
+ end
7
+
8
+ def method_missing(method, *args, &blk)
9
+ @pool.execute do |conn|
10
+ conn.__send__(method, *args, &blk)
11
+ end
12
+ end
13
+ end
4
14
 
5
- def initialize(opts, &block)
6
- @reserved = {} # map of in-progress connections
15
+ attr_accessor :available, :pending, :disconnect_class, :new_block
16
+
17
+ def initialize(opts = {}, &block)
7
18
  @available = [] # pool of free connections
8
19
  @pending = [] # pending reservations (FIFO)
9
20
 
21
+ @disconnect_class = opts[:disconnect_class]
22
+ @new_block = block
10
23
  opts[:size].times do
11
- @available.push(block.call) if block_given?
24
+ @available.push(@new_block.call)
12
25
  end
13
26
  end
14
27
 
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
28
  def execute
19
- g = Green.current
20
29
  begin
21
- conn = acquire(g)
30
+ conn = acquire
22
31
  yield conn
32
+ rescue => e
33
+ if @disconnect_class && e.is_a?(@disconnect_class)
34
+ disconnected = true
35
+ @available << @new_block.call
36
+ else
37
+ raise
38
+ end
23
39
  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
40
+ if disconnected
41
+ try_next
37
42
  else
38
- @pending.push green
39
- Green.hub.wait { @pending.delete green }
40
- acquire(green)
43
+ release conn
44
+ try_next
41
45
  end
42
46
  end
47
+ end
43
48
 
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)
49
+ def proxy
50
+ @proxy ||= Proxy.new(self)
51
+ end
50
52
 
51
- if pending = @pending.shift
52
- Green.hub.callback { pending.switch }
53
- end
53
+ def acquire
54
+ g = Green.current
55
+ if conn = @available.pop
56
+ conn
57
+ else
58
+ @pending.push g
59
+ Green.hub.wait { @pending.delete g }
60
+ acquire
54
61
  end
62
+ end
55
63
 
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
64
+ def release(conn)
65
+ @available.push conn
66
+ end
67
+
68
+ def try_next
69
+ if pending = @pending.shift
70
+ Green.hub.callback { pending.switch }
69
71
  end
72
+ end
70
73
  end
71
-
72
- end
74
+ end
@@ -2,14 +2,25 @@ class Green
2
2
  class Hub
3
3
  attr_reader :g
4
4
  def initialize
5
+
6
+ start_hub
7
+ end
8
+
9
+ def start_hub
5
10
  c = Green.current
6
- callback { c.switch }
11
+ callback { c.switch }
7
12
  @g = Green.new do
8
- run
13
+ begin
14
+ run
15
+ rescue => e
16
+ start_hub
17
+ raise
18
+ end
9
19
  end
10
20
  g.switch
11
21
  end
12
22
 
23
+
13
24
  def switch
14
25
  g.switch
15
26
  end
@@ -48,4 +59,4 @@ class Green
48
59
  raise "override"
49
60
  end
50
61
  end
51
- end
62
+ end
@@ -1,3 +1,4 @@
1
+ # -*- coding: utf-8 -*-
1
2
  require 'eventmachine'
2
3
 
3
4
  class ::EM::Timer
@@ -20,7 +21,7 @@ class Green
20
21
  class SocketWaiter < Green::SocketWaiter
21
22
  class Handler < ::EM::Connection
22
23
  attr_accessor :green
23
- def notify_readable
24
+ def notify_readable
24
25
  green.switch
25
26
  end
26
27
 
@@ -55,7 +56,7 @@ class Green
55
56
  end
56
57
  # если мы запускаем приложение внутри thin или rainbows с EM, то значит мы уже внутри EM-реактора, а hub должен переключиться в main тред.
57
58
  def run
58
- if ::EM.reactor_running?
59
+ if ::EM.reactor_running?
59
60
  loop do
60
61
  Green.main.switch
61
62
  end
@@ -81,4 +82,4 @@ class Green
81
82
  end
82
83
  end
83
84
  end
84
- end
85
+ end
@@ -1,6 +1,3 @@
1
- require 'nio'
2
- require 'algorithms'
3
-
4
1
  class Green
5
2
  class Hub
6
3
  class Nio4r < Hub
@@ -56,12 +53,15 @@ class Green
56
53
 
57
54
  attr_reader :callbacks, :timers, :cancel_timers
58
55
  def initialize(*args)
56
+ require 'nio'
57
+ require 'algorithms'
58
+
59
59
  @callbacks = []
60
60
  @timers = Containers::MinHeap.new
61
61
  @cancel_timers = {}
62
62
  super
63
63
  end
64
-
64
+
65
65
  def reactor_running?
66
66
  @reactor_running
67
67
  end
@@ -105,7 +105,7 @@ class Green
105
105
  i += 1
106
106
  end
107
107
  ensure
108
- @callbacks[0...0] = jobs[i..-1] if i < jobs.size
108
+ @callbacks[0...0] = jobs[(i + 1)..-1] if i < jobs.size
109
109
  end
110
110
  end
111
111
 
@@ -47,7 +47,7 @@ class Green
47
47
  return counter
48
48
  else
49
49
  g = Green.current
50
- clb = proc do
50
+ clb = proc do
51
51
  if counter >= v && @links.size == 0
52
52
  wait_links.delete clb
53
53
  Green.hub.callback { g.switch }
@@ -1,100 +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
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
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
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
@@ -8,8 +8,7 @@ QUERY = "select sleep(#{DELAY})"
8
8
 
9
9
  describe Green::ConnectionPool do
10
10
 
11
-
12
- let(:pool) { Green::ConnectionPool.new(size: size) { Green::Mysql2::Client.new } }
11
+ let(:pool) { Green::ConnectionPool.new(size: size) { Green::Mysql2::Client.new }.proxy }
13
12
 
14
13
  describe "pool size is exceeded" do
15
14
  let(:size) { 1 }
@@ -17,7 +16,7 @@ describe Green::ConnectionPool do
17
16
  start = Time.now.to_f
18
17
 
19
18
  g = Green::Group.new
20
- res = []
19
+ res = []
21
20
  g.spawn { res << pool.query(QUERY) }
22
21
  g.spawn { res << pool.query(QUERY) }
23
22
  g.join
@@ -42,4 +41,4 @@ describe Green::ConnectionPool do
42
41
  res.size.must_equal 2
43
42
  end
44
43
  end
45
- end
44
+ end
@@ -39,7 +39,8 @@ end
39
39
  describe Green::Pool do
40
40
  let(:e1) { Green::Event.new }
41
41
  let(:e2) { Green::Event.new }
42
- let(:p) { Green::Pool.new size: 1}
42
+ let(:size) { 1 }
43
+ let(:p) { Green::Pool.new size: size }
43
44
 
44
45
  it "should wait each task" do
45
46
  result = ""
@@ -49,6 +50,27 @@ describe Green::Pool do
49
50
  result.must_equal "fizbaz"
50
51
  end
51
52
 
53
+ describe "spawn attack" do
54
+ let(:size) { 10 }
55
+ it "should work correctly" do
56
+ i = 0
57
+ 100.times { p.spawn { i += 1 } }
58
+ p.join
59
+ i.must_equal 100
60
+ end
61
+
62
+ describe "with random timer" do
63
+ it "should work correctly" do
64
+ i = 0
65
+ 100.times { p.spawn { Green.sleep(rand); i += 1 } }
66
+ p.join
67
+ i.must_equal 100
68
+ end
69
+ end
70
+ end
71
+
72
+
73
+
52
74
  # ugly test :(
53
75
  it "should block after limit" do
54
76
  i = 0
@@ -30,7 +30,7 @@ describe Green::Mysql2 do
30
30
  it "should fire simultaneous requests via pool" do
31
31
  db = Green::ConnectionPool.new(size: 2) do
32
32
  Green::Mysql2::Client.new
33
- end
33
+ end.proxy
34
34
 
35
35
  start = Time.now.to_f
36
36
  res = []
@@ -49,4 +49,4 @@ describe Green::Mysql2 do
49
49
  db.query("SELECT * FROM i_hope_this_table_does_not_exist;")
50
50
  }.must_raise(Mysql2::Error)
51
51
  end
52
- end
52
+ end
@@ -34,4 +34,11 @@ describe Green do
34
34
  (Time.now - t).must_be :<, 0.1
35
35
  end
36
36
  end
37
- end
37
+
38
+ describe "exceptions" do
39
+ it "should raise exception from reactor to main fiber" do
40
+ Green.hub.callback { raise "ololo" }
41
+ proc { Green.sleep(0.1) }.must_raise RuntimeError
42
+ end
43
+ end
44
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: green
3
3
  version: !ruby/object:Gem::Version
4
- version: '0.1'
4
+ version: 0.1.1
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-12-13 00:00:00.000000000 Z
12
+ date: 2013-01-28 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: kgio
@@ -41,7 +41,9 @@ files:
41
41
  - app.ru
42
42
  - green.gemspec
43
43
  - lib/active_record/connection_adapters/green_mysql2_adapter.rb
44
+ - lib/green-em.rb
44
45
  - lib/green-em/em-http.rb
46
+ - lib/green-em/tarantool.rb
45
47
  - lib/green.rb
46
48
  - lib/green/activerecord.rb
47
49
  - lib/green/connection_pool.rb
@@ -84,7 +86,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
84
86
  version: '0'
85
87
  segments:
86
88
  - 0
87
- hash: 1130764657698221389
89
+ hash: 4284378326440113504
88
90
  required_rubygems_version: !ruby/object:Gem::Requirement
89
91
  none: false
90
92
  requirements: