oldmoe-neverblock 0.1.6 → 1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (31) hide show
  1. data/lib/never_block.rb +5 -78
  2. data/lib/neverblock.rb +3 -1
  3. data/lib/{never_block/extensions/fiber_extensions.rb → neverblock/core/fiber.rb} +20 -6
  4. data/lib/neverblock/core/pool.rb +72 -0
  5. data/lib/neverblock/core/reactor.rb +50 -0
  6. data/lib/neverblock/core/system/system.rb +38 -0
  7. data/lib/neverblock/core/system/timeout.rb +67 -0
  8. data/lib/{never_block/db/pooled_db_connection.rb → neverblock/io/db/connection.rb} +16 -2
  9. data/lib/neverblock/io/db/drivers/mysql.rb +73 -0
  10. data/lib/neverblock/io/db/drivers/postgres.rb +63 -0
  11. data/lib/neverblock/io/db/fibered_connection_pool.rb +130 -0
  12. data/lib/{never_block → neverblock/io}/db/fibered_mysql_connection.rb +5 -18
  13. data/lib/{never_block/pool/fibered_connection_pool.rb → neverblock/io/db/pool.rb} +25 -40
  14. data/lib/neverblock/io/file.rb +24 -0
  15. data/lib/neverblock/io/io.rb +219 -0
  16. data/lib/neverblock/io/socket.rb +75 -0
  17. data/lib/neverblock_io.rb +6 -0
  18. data/lib/system.rb +4 -0
  19. data/neverblock.gemspec +23 -21
  20. metadata +23 -20
  21. data/lib/active_record/connection_adapters/neverblock_mysql_adapter.rb +0 -68
  22. data/lib/active_record/connection_adapters/neverblock_postgresql_adapter.rb +0 -85
  23. data/lib/never_block/db/fibered_db_connection.rb +0 -72
  24. data/lib/never_block/db/fibered_postgres_connection.rb +0 -64
  25. data/lib/never_block/frameworks/activerecord.rb +0 -37
  26. data/lib/never_block/frameworks/rails.rb +0 -65
  27. data/lib/never_block/pool/fiber_pool.rb +0 -74
  28. data/lib/never_block/servers/mongrel.rb +0 -236
  29. data/lib/never_block/servers/thin.rb +0 -32
  30. data/lib/neverblock-mysql.rb +0 -5
  31. data/lib/neverblock-pg.rb +0 -5
@@ -1,96 +1,23 @@
1
1
  # Author:: Mohammad A. Ali (mailto:oldmoe@gmail.com)
2
- # Copyright:: Copyright (c) 2008 eSpace, Inc.
2
+ # Copyright:: Copyright (c) 2009 eSpace, Inc.
3
3
  # License:: Distributes under the same terms as Ruby
4
4
 
5
5
  $:.unshift File.expand_path(File.dirname(__FILE__))
6
6
 
7
- # Thanks to Aman Gupta for writing the Ruby 1.8 Fiber simulator
8
- unless defined? Fiber
9
- require 'thread'
10
- require 'singleton'
11
- class FiberError < StandardError; end
12
- class Fiber
13
- def initialize
14
- raise ArgumentError, 'new Fiber requires a block' unless block_given?
15
-
16
- @yield = Queue.new
17
- @resume = Queue.new
18
-
19
- @thread = Thread.new{ @yield.push [ *yield(*@resume.pop) ] }
20
- @thread.abort_on_exception = true
21
- @thread[:fiber] = self
22
- end
23
- attr_reader :thread
24
-
25
- def resume *args
26
- raise FiberError, 'dead fiber called' unless @thread.alive?
27
- @resume.push(args)
28
- result = @yield.pop
29
- result.size > 1 ? result : result.first
30
- end
31
-
32
- def yield *args
33
- @yield.push(args)
34
- result = @resume.pop
35
- result.size > 1 ? result : result.first
36
- end
37
-
38
- def self.yield *args
39
- raise FiberError, "can't yield from root fiber" unless fiber = Thread.current[:fiber]
40
- fiber.yield(*args)
41
- end
42
-
43
- def self.current
44
- Thread.current[:fiber] or raise FiberError, 'not inside a fiber'
45
- end
46
-
47
- def inspect
48
- "#<#{self.class}:0x#{self.object_id.to_s(16)}>"
49
- end
50
- end
51
-
52
- class RootFiber < Fiber
53
- include Singleton
54
- def initialize
55
- end
56
-
57
- def resume *args
58
- raise FiberError, "can't resume root fiber"
59
- end
60
-
61
- def yield *args
62
- raise FiberError, "can't yield from root fiber"
63
- end
64
- end
65
-
66
- #attach the root fiber to the main thread
67
- Thread.main[:fiber] = RootFiber.instance
68
- else
69
- require 'fiber'
70
- end
71
-
72
- require 'never_block/extensions/fiber_extensions'
73
- require 'never_block/pool/fiber_pool'
74
- require 'never_block/pool/fibered_connection_pool'
75
-
76
7
  module NeverBlock
77
8
 
78
9
  # Checks if we should be working in a non-blocking mode
79
10
  def self.neverblocking?
80
- Fiber.respond_to?(:current) && Fiber.current[:neverblock]
81
- end
82
-
83
- def self.event_loop_available?
84
- defined?(EM) && EM.reactor_running?
11
+ NB::Fiber.respond_to?(:current) && NB::Fiber.current.respond_to?('[]') && NB::Fiber.current[:neverblock] && NB.reactor.running?
85
12
  end
86
13
 
87
14
  # The given block will run its queries either in blocking or non-blocking
88
15
  # mode based on the first parameter
89
16
  def self.neverblock(nb = true, &block)
90
- status = Fiber.current[:neverblock]
91
- Fiber.current[:neverblock] = nb
17
+ status = NB::Fiber.current[:neverblock]
18
+ NB::Fiber.current[:neverblock] = nb
92
19
  block.call
93
- Fiber.current[:neverblock] = status
20
+ NB::Fiber.current[:neverblock] = status
94
21
  end
95
22
 
96
23
  # Exception to be thrown for all neverblock internal errors
@@ -5,4 +5,6 @@
5
5
  $:.unshift File.expand_path(File.dirname(__FILE__))
6
6
 
7
7
  require 'never_block'
8
-
8
+ require 'neverblock/core/reactor'
9
+ require 'neverblock/core/fiber'
10
+ require 'neverblock/core/pool'
@@ -1,13 +1,18 @@
1
1
  # Author:: Mohammad A. Ali (mailto:oldmoe@gmail.com)
2
- # Copyright:: Copyright (c) 2008 eSpace, Inc.
2
+ # Copyright:: Copyright (c) 2009 eSpace, Inc.
3
3
  # License:: Distributes under the same terms as Ruby
4
4
 
5
- # If this file is meant to be used out of neverblock, then uncomment
6
- # the following line
7
- #require 'fiber'
5
+ require 'fiber'
6
+ require File.expand_path(File.dirname(__FILE__)+'/../../never_block')
7
+
8
+ class NeverBlock::Fiber < Fiber
9
+
10
+ def initialize(neverblock = true, &block)
11
+ self[:neverblock] = neverblock
12
+ super()
13
+ end
14
+
8
15
 
9
- class Fiber
10
-
11
16
  #Attribute Reference--Returns the value of a fiber-local variable, using
12
17
  #either a symbol or a string name. If the specified variable does not exist,
13
18
  #returns nil.
@@ -21,6 +26,15 @@ class Fiber
21
26
  local_fiber_variables[key] = value
22
27
  end
23
28
 
29
+ #Sending an exception instance to resume will yield the fiber
30
+ #and then raise the exception. This is necessary to raise exceptions
31
+ #in their correct context.
32
+ def self.yield(*args)
33
+ result = super
34
+ raise result if result.is_a? Exception
35
+ result
36
+ end
37
+
24
38
  private
25
39
 
26
40
  def local_fiber_variables
@@ -0,0 +1,72 @@
1
+
2
+ require File.expand_path(File.dirname(__FILE__)+'/fiber')
3
+
4
+ module NeverBlock
5
+ # Author:: Mohammad A. Ali (mailto:oldmoe@gmail.com)
6
+ # Copyright:: Copyright (c) 2008 eSpace, Inc.
7
+ # License:: Distributes under the same terms as Ruby
8
+ #
9
+ # A pool of initialized fibers
10
+ # It does not grow in size or create transient fibers
11
+ # It will queue code blocks when needed (if all its fibers are busy)
12
+ #
13
+ # This class is particulary useful when you use the fibers
14
+ # to connect to evented back ends. It also does not generate
15
+ # transient objects and thus saves memory.
16
+ #
17
+ # Example:
18
+ # fiber_pool = NeverBlock::FiberPool.new(150)
19
+ #
20
+ # loop do
21
+ # fiber_pool.spawn do
22
+ # #fiber body goes here
23
+ # end
24
+ # end
25
+ #
26
+ class FiberPool
27
+
28
+ # gives access to the currently free fibers
29
+ attr_reader :fibers
30
+
31
+ # Prepare a list of fibers that are able to run different blocks of code
32
+ # every time. Once a fiber is done with its block, it attempts to fetch
33
+ # another one from the queue
34
+ def initialize(count = 50)
35
+ @fibers,@busy_fibers,@queue = [],{},[]
36
+ count.times do |i|
37
+ fiber = NB::Fiber.new do |block|
38
+ loop do
39
+ block.call
40
+ # callbacks are called in a reverse order, much like c++ destructor
41
+ NB::Fiber.current[:callbacks].pop.call while NB::Fiber.current[:callbacks].length > 0
42
+ unless @queue.empty?
43
+ block = @queue.shift
44
+ else
45
+ @busy_fibers.delete(NB::Fiber.current.object_id)
46
+ @fibers << NB::Fiber.current
47
+ block = NB::Fiber.yield
48
+ end
49
+ end
50
+ end
51
+ fiber[:callbacks] = []
52
+ @fibers << fiber
53
+ end
54
+ end
55
+
56
+ # If there is an available fiber use it, otherwise, leave it to linger
57
+ # in a queue
58
+ def spawn(evented = true, &block)
59
+ if fiber = @fibers.shift
60
+ fiber[:callbacks] = []
61
+ @busy_fibers[fiber.object_id] = fiber
62
+ fiber[:neverblock] = evented
63
+ fiber.resume(block)
64
+ else
65
+ @queue << block
66
+ end
67
+ self # we are keen on hiding our queue
68
+ end
69
+
70
+ end # FiberPool
71
+ end # NeverBlock
72
+
@@ -0,0 +1,50 @@
1
+ require 'reactor'
2
+ require 'thread'
3
+ require File.expand_path(File.dirname(__FILE__)+'/fiber')
4
+
5
+ module NeverBlock
6
+
7
+ @@reactors = {}
8
+
9
+ @@queue = Queue.new
10
+
11
+ @@thread_pool = []
12
+
13
+ 20.times do
14
+ @@thread_pool << Thread.new do
15
+ loop do
16
+ io, method, params, fiber, reactor = *(@@queue.shift)
17
+ begin
18
+ reactor.next_tick{fiber.resume(io.__send__(method, *params))} if fiber.alive?
19
+ rescue Exception => e
20
+ reactor.next_tick{fiber.resume(e)} if fiber.alive?
21
+ end
22
+ end
23
+ end
24
+ end
25
+
26
+ def self.reactor
27
+ @@reactors[Thread.current.object_id] ||= ::Reactor::Base.new
28
+ end
29
+
30
+ def self.wait(mode, io)
31
+ fiber = NB::Fiber.current
32
+ NB.reactor.attach(mode, io){fiber.resume}
33
+ NB::Fiber.yield
34
+ NB.reactor.detach(mode, io)
35
+ end
36
+
37
+ def self.sleep(time)
38
+ NB::Fiber.yield if time.nil?
39
+ return if time <= 0
40
+ fiber = NB::Fiber.current
41
+ NB.reactor.add_timer(time){fiber.resume}
42
+ NB::Fiber.yield
43
+ end
44
+
45
+ def self.defer(io, action, args)
46
+ @@queue << [io, action, args, NB::Fiber.current, NB.reactor]
47
+ NB::Fiber.yield
48
+ end
49
+
50
+ end
@@ -0,0 +1,38 @@
1
+
2
+ require File.expand_path(File.dirname(__FILE__)+'/../../../neverblock')
3
+
4
+ module Kernel
5
+
6
+ alias_method :rb_sleep, :sleep
7
+
8
+ def sleep(time=nil)
9
+ return rb_sleep(time) unless NB.neverblocking?
10
+ NB.sleep(time)
11
+ end
12
+
13
+ alias_method :rb_system, :system
14
+
15
+ def system(cmd, *args)
16
+ return rb_system(cmd, *args) unless NB.neverblocking?
17
+ begin
18
+ backticks(cmd, *args)
19
+ result = $?.exitstatus
20
+ return true if result.zero?
21
+ return nil if result == 127
22
+ return false
23
+ rescue Errno::ENOENT => e
24
+ return nil
25
+ end
26
+ end
27
+
28
+ def backticks(cmd, *args)
29
+ myargs = "#{cmd} "
30
+ myargs << args.join(' ') if args
31
+ res = ''
32
+ IO.popen(myargs) do |f|
33
+ res << f.read
34
+ end
35
+ res
36
+ end
37
+
38
+ end
@@ -0,0 +1,67 @@
1
+ require 'timeout'
2
+ require File.expand_path(File.dirname(__FILE__)+'/../../../neverblock')
3
+
4
+ module Timeout
5
+
6
+ alias_method :rb_timeout, :timeout
7
+
8
+ def timeout(time, klass=Timeout::Error, &block)
9
+ return rb_timeout(time, klass,&block) unless NB.neverblocking?
10
+ if time.nil? || time <= 0
11
+ block.call
12
+ else
13
+
14
+
15
+ fiber = NB::Fiber.current
16
+
17
+ timer = NB.reactor.add_timer(time) do
18
+ fiber[:timeouts].last.each do |event|
19
+ if event.is_a? Reactor::Timer
20
+ event.cancel
21
+ else
22
+ NB.reactor.detach(event[0], event[1])
23
+ end
24
+ end
25
+ fiber.resume(klass.new)
26
+ end
27
+ (fiber[:timeouts] ||= []) << []
28
+ begin
29
+ block.call
30
+ rescue Exception => e
31
+ raise e
32
+ ensure
33
+ timer.cancel
34
+ fiber[:timeouts].pop
35
+ end
36
+ end
37
+ end
38
+
39
+ module_function :timeout
40
+ module_function :rb_timeout
41
+
42
+ end
43
+
44
+ NB.reactor.on_add_timer do |timer|
45
+ Kernel.puts "on add"
46
+ timeouts = NB::Fiber.current[:timeouts]
47
+ unless timeouts.nil? || timeouts.empty?
48
+ timeouts.last << timer
49
+ end
50
+ end
51
+
52
+ NB.reactor.on_attach do |mode, io|
53
+ Kernel.puts "on attach"
54
+ timeouts = NB::Fiber.current[:timeouts]
55
+ unless timeouts.nil? || timeouts.empty?
56
+ timeouts.last << [mode, io]
57
+ end
58
+ end
59
+
60
+ NB.reactor.on_detach do |mode, io|
61
+ Kernel.puts "on detach"
62
+ timeouts = NB::Fiber.current[:timeouts]
63
+ unless timeouts.nil? || timeouts.empty?
64
+ timeouts.delete_if{|to|to.is_a? Array && to[0] == mode && to[1] == io}
65
+ end
66
+ end
67
+
@@ -1,11 +1,14 @@
1
+ require File.expand_path(File.dirname(__FILE__)+'/pool')
2
+
1
3
  module NeverBlock
4
+
2
5
  module DB
3
6
  # a proxy for pooled fibered connections
4
- class PooledDBConnection
7
+ class Connection
5
8
  # Requires a block with connection parameters
6
9
  # and a pool size (defaults to 4)
7
10
  def initialize(size=4, &block)
8
- @pool = NB::Pool::FiberedConnectionPool.new(:size=>size, :eager=>true) do
11
+ @pool = NB::DB::Pool.new(:size=>size, :eager=>true) do
9
12
  yield
10
13
  end
11
14
  end
@@ -37,7 +40,18 @@ module NeverBlock
37
40
  @pool.hold do |conn|
38
41
  conn.respond_to?(method)
39
42
  end
43
+ end
44
+
45
+ protected
46
+
47
+ # are we in a transaction?
48
+ # if no then just hold a connection and run the block
49
+ # else get a connection, pass it to the block
50
+ # and move away
51
+ def hold_connection
52
+
40
53
  end
54
+
41
55
  end
42
56
  end
43
57
  end
@@ -0,0 +1,73 @@
1
+
2
+ require 'mysqlplus'
3
+ require File.expand_path(File.dirname(__FILE__)+'/../../../../neverblock')
4
+ require File.expand_path(File.dirname(__FILE__)+'/../connection')
5
+ require File.expand_path(File.dirname(__FILE__)+'/../fibered_mysql_connection')
6
+ require File.expand_path(File.dirname(__FILE__)+'/../pool')
7
+
8
+
9
+ module NeverBlock
10
+
11
+ module DB
12
+ # A modified mysql connection driver. It builds on the original pg driver.
13
+ # This driver is able to register the socket at a certain backend (EM)
14
+ # and then whenever the query is executed within the scope of a friendly
15
+ # fiber. It will be done in async mode and the fiber will yield
16
+ class Mysql < ::Mysql
17
+
18
+ # Initializes the connection and remembers the connection params
19
+ def initialize(*args)
20
+ @connection_params = args
21
+ super(*@connection_params)
22
+ end
23
+
24
+ # Does a normal real_connect if arguments are passed. If no arguments are
25
+ # passed it uses the ones it remembers
26
+ def real_connect(*args)
27
+ @connection_params = args unless args.empty?
28
+ super(*@connection_params)
29
+ end
30
+
31
+ alias_method :connect, :real_connect
32
+
33
+ # Assuming the use of NeverBlock fiber extensions and that the exec is run in
34
+ # the context of a fiber. One that have the value :neverblock set to true.
35
+ # All neverblock IO classes check this value, setting it to false will force
36
+ # the execution in a blocking way.
37
+ def query(sql)
38
+ if NB.neverblocking? && NB.reactor.running?
39
+ send_query sql
40
+ NB.wait(:read, IO.new(socket))
41
+ get_result
42
+ else
43
+ super(sql)
44
+ end
45
+ end
46
+
47
+ alias_method :exec, :query
48
+
49
+ end #MySQL
50
+
51
+ class PooledMySQL < ::NeverBlock::DB::Connection
52
+
53
+ def initialize(*args)
54
+ options = {}
55
+ if args #&& (options = args.last).is_a? Hash
56
+ size = options[:size] || 4
57
+ eager = options[:eager] || true
58
+ args.pop
59
+ end
60
+ @pool = NB::DB::Pool.new(:size=>size, :eager=>eager) do
61
+ MySQL.new(*args)
62
+ end
63
+ end
64
+
65
+ end #PooledMySQL
66
+
67
+ end #DB
68
+
69
+
70
+ end #NeverBlock
71
+
72
+
73
+