oldmoe-neverblock 0.1.6 → 1.0

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.
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
+