oldmoe-neverblock 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/README ADDED
@@ -0,0 +1,21 @@
1
+ == NeverBlock
2
+ Never, ever!
3
+
4
+ NeverBlock is a collection of classes and modules that help you write evented non-blocking applications in a seemingly blocking mannner.
5
+
6
+ NeverBlock currently provides the following Libraries:
7
+
8
+ === FiberExtensions
9
+ A set of extenstions to the standard Fiber implementation
10
+
11
+ === NeverBlock::Pool::FiberPool
12
+ A pool of fibers that can be used to provide an upper limit to the numbers of active fibers in an application
13
+
14
+ === NeverBlock::Pool::FiberedConnectionPool
15
+ A generic fibered connection pool for all sorts of connections with support for transactions. This was mostly copied from Sequel::ConnectionPool
16
+
17
+ NeverBlock should be the basis for providing completely async Ruby application development that does not require the usual twisted style of evented programming. For example, you will be able to develop in Rails in the usual style and deploy to a NeverBlock server which will do all the IO in an evented manner without you even noticing this.
18
+
19
+ === License
20
+ Ruby License, http://www.ruby-lang.org/en/LICENSE.txt.
21
+
@@ -0,0 +1,15 @@
1
+ # Author:: Mohammad A. Ali (mailto:oldmoe@gmail.com)
2
+ # Copyright:: Copyright (c) 2008 eSpace, Inc.
3
+ # License:: Distributes under the same terms as Ruby
4
+
5
+ $:.unshift File.expand_path(File.dirname(__FILE__))
6
+
7
+ require 'fiber'
8
+ require 'never_block/extensions/fiber_extensions'
9
+ require 'never_block/pool/fiber_pool'
10
+ require 'never_block/pool/fibered_connection_pool'
11
+
12
+ module NeverBlock
13
+ end
14
+
15
+ NB = NeverBlock
@@ -0,0 +1,25 @@
1
+ # Author:: Mohammad A. Ali (mailto:oldmoe@gmail.com)
2
+ # Copyright:: Copyright (c) 2008 eSpace, Inc.
3
+ # License:: Distributes under the same terms as Ruby
4
+
5
+ require 'fiber'
6
+
7
+ class Fiber
8
+
9
+ #Attribute Reference--Returns the value of a fiber-local variable, using either a symbol or a string name. If the specified variable does not exist, returns nil.
10
+ def [](key)
11
+ local_fiber_variables[key]
12
+ end
13
+
14
+ #Attribute Assignment--Sets or creates the value of a fiber-local variable, using either a symbol or a string. See also Fiber#[].
15
+ def []=(key,value)
16
+ local_fiber_variables[key] = value
17
+ end
18
+
19
+ private
20
+
21
+ def local_fiber_variables
22
+ @local_fiber_variables ||= {}
23
+ end
24
+ end
25
+
@@ -0,0 +1,31 @@
1
+ require 'never_block/frameworks/rails'
2
+ require 'activerecord'
3
+
4
+ # Patch ActiveRecord to store transaction depth information
5
+ # in fibers instead of threads. AR does not support nested
6
+ # transactions which makes the job easy.
7
+ class ActiveRecord::Base
8
+
9
+ def self.transaction(&block)
10
+ increment_open_transactions
11
+ begin
12
+ connection.transaction(Fiber.current['start_db_transaction'], &block)
13
+ ensure
14
+ decrement_open_transactions
15
+ end
16
+ end
17
+
18
+ private
19
+
20
+ def self.increment_open_transactions #:nodoc:
21
+ open = Fiber.current['open_transactions'] ||= 0
22
+ Fiber.current['start_db_transaction'] = open.zero?
23
+ Fiber.current['open_transactions'] = open + 1
24
+ end
25
+
26
+ def self.decrement_open_transactions #:nodoc:
27
+ Fiber.current['open_transactions'] -= 1
28
+ end
29
+
30
+ end
31
+
@@ -0,0 +1,21 @@
1
+ require 'actionpack'
2
+
3
+ # Rails tries to protect dispatched actions
4
+ # by wrapping them in a synchronized code
5
+ # block, since fibers hate synchronized
6
+ # blocks we will trick the guard and
7
+ # transform it (without it knowing) to
8
+ # something more subtle
9
+
10
+ class ActionController::Dispatcher
11
+
12
+ # let's show this guard who is
13
+ # the man of the house
14
+ @@guard = Object.new
15
+
16
+ # now you synchronize
17
+ def @@guard.synchronize(&block)
18
+ # now you don't!
19
+ block.call
20
+ end
21
+ end
@@ -0,0 +1,76 @@
1
+ # we need Fiber.current
2
+ # so we must require
3
+ # fiber
4
+ # Author:: Mohammad A. Ali (mailto:oldmoe@gmail.com)
5
+ # Copyright:: Copyright (c) 2008 eSpace, Inc.
6
+ # License:: Distributes under the same terms as Ruby
7
+
8
+ require 'fiber'
9
+
10
+ module NeverBlock
11
+ module Pool
12
+
13
+ # Author:: Mohammad A. Ali (mailto:oldmoe@gmail.com)
14
+ # Copyright:: Copyright (c) 2008 eSpace, Inc.
15
+ # License:: Distributes under the same terms as Ruby
16
+ #
17
+ # A pool of initialized fibers
18
+ # It does not grow in size or create transient fibers
19
+ # It will queue code blocks when needed (if all its fibers are busy)
20
+ #
21
+ # This class is particulary useful when you use the fibers
22
+ # to connect to evented back ends. It also does not generate
23
+ # transient objects and thus saves memory.
24
+ #
25
+ # Example:
26
+ # fiber_pool = NeverBlock::Pool::FiberPool.new(150)
27
+ #
28
+ # loop do
29
+ # fiber_pool.spawn do
30
+ # #fiber body goes here
31
+ # end
32
+ # end
33
+ #
34
+ class FiberPool
35
+
36
+ # gives access to the currently free fibers
37
+ attr_reader :fibers
38
+
39
+ # Prepare a list of fibers
40
+ # that are able to run different
41
+ # blocks of code every time
42
+ # once a fiber is done with its block
43
+ # it attempts to fetch another one
44
+ # from the queue.
45
+ def initialize(count = 50)
46
+ @fibers,@queue = [],[]
47
+ count.times do |i|
48
+ fiber = Fiber.new do |block|
49
+ loop do
50
+ block.call
51
+ unless @queue.empty?
52
+ block = @queue.shift
53
+ else
54
+ block = Fiber.yield @fibers << Fiber.current
55
+ end
56
+ end
57
+ end
58
+ fiber[:neverblock] = true
59
+ @fibers << fiber
60
+ end
61
+ end
62
+
63
+ # If there is an available fiber
64
+ # use it, otherwise, leave it to linger in a queue
65
+ def spawn(evented = true, &block)
66
+ if fiber = @fibers.shift
67
+ fiber[:evented] = evented
68
+ fiber.resume(block)
69
+ else
70
+ @queue << block
71
+ end
72
+ self # we are keen on hiding our queue
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,114 @@
1
+ # Author:: Mohammad A. Ali (mailto:oldmoe@gmail.com)
2
+ # Copyright:: Copyright (c) 2008 eSpace, Inc.
3
+ # License:: Distributes under the same terms as Ruby
4
+
5
+ module NeverBlock
6
+ module Pool
7
+
8
+ # Author:: Mohammad A. Ali (mailto:oldmoe@gmail.com)
9
+ # Copyright:: Copyright (c) 2008 eSpace, Inc.
10
+ # License:: Distributes under the same terms as Ruby
11
+ #
12
+ # This class represents a pool of connections,
13
+ # you hold or release conncetions from the pool
14
+ # hold requests that cannot be fullfiled will be queued
15
+ # the fiber will be paused and resumed later when
16
+ # a connection is avaialble
17
+ #
18
+ # Large portions of this class were copied and pasted
19
+ # form Sequel's threaded connection pool
20
+ #
21
+ # Example:
22
+ #
23
+ # pool = NeverBlock::Pool::FiberedConnectionPool.new(:size=>16)do
24
+ # # connection creation code goes here
25
+ # end
26
+ # 32.times do
27
+ # Fiber.new do
28
+ # conn = pool.hold # hold will pause the fiber until a connection is available
29
+ # conn.execute('something') # you can use the connection normally now
30
+ # end.resume
31
+ # end
32
+ #
33
+ # The pool has support for transactions, just pass true to the pool#hold method
34
+ # and the connection will not be released after the block is finished
35
+ # It is the responsibility of client code to release the connection
36
+ class FiberedConnectionPool
37
+
38
+ # initialize the connection pool
39
+ # using the supplied proc to create the connections
40
+ # you can choose to start them eagerly or lazily (lazy by default)
41
+ def initialize(options = {}, &block)
42
+ @connections, @busy_connections, @queue = [], {},[]
43
+ @connection_proc = block
44
+ @size = options[:size] || 8
45
+ if options[:eager]
46
+ @size.times do
47
+ @connections << @connection_proc.call
48
+ end
49
+ end
50
+ end
51
+
52
+ # If a connection is available,
53
+ # pass it to the block, otherwise
54
+ # pass the fiber to the queue
55
+ # till a connection is available
56
+ # when done with a connection
57
+ # try to porcess other fibers in the queue
58
+ # before releasing the connection
59
+ # if inside a transaction, don't release the fiber
60
+ def hold(transactional = false)
61
+ fiber = Fiber.current
62
+ if conn = @busy_connections[fiber]
63
+ return yield(conn)
64
+ end
65
+ conn = acquire(fiber)
66
+ begin
67
+ yield conn
68
+ ensure
69
+ release(fiber, conn) unless transactional
70
+ process_queue
71
+ end
72
+ end
73
+
74
+ # Give the fiber back to the pool
75
+ # you have to call this explicitly if
76
+ # you held a connection for a transaction
77
+ def release(fiber, conn)
78
+ @busy_connections.delete(fiber)
79
+ @connections << conn
80
+ end
81
+
82
+ private
83
+
84
+ # Can we find a connection?
85
+ # Can we create one?
86
+ # Wait in the queue then
87
+ def acquire(fiber)
88
+ if !@connections.empty?
89
+ @busy_connections[fiber] = @connections.shift
90
+ elsif (@connections.length + @busy_connections.length) < @size
91
+ conn = @connection_proc.call
92
+ @busy_connections[fiber] = conn
93
+ else
94
+ Fiber.yield @queue << fiber
95
+ end
96
+ end
97
+
98
+ # Check if there are waiting fibers and
99
+ # try to process them
100
+ def process_queue
101
+ while !@connections.empty? and !@queue.empty?
102
+ fiber = @queue.shift
103
+ # What is really happening here?
104
+ # we are resuming a fiber from within
105
+ # another, should we call transfer insted?
106
+ fiber.resume @busy_connections[fiber] = @connections.shift
107
+ end
108
+ end
109
+
110
+ end #FiberedConnectionPool
111
+
112
+ end #Pool
113
+
114
+ end #NeverBlock
@@ -0,0 +1,34 @@
1
+ require 'thin'
2
+
3
+ module Thin
4
+
5
+ # Patch the thin server to use
6
+ # NeverBlock::Pool::FiberPool
7
+ # to be able to wrap requests
8
+ # in fibers
9
+ class Server
10
+
11
+ DEFAULT_FIBER_POOL_SIZE = 50
12
+
13
+ def fiber_pool
14
+ @fiber_pool ||= NB::Pool::FiberPool.new(DEFAULT_FIBER_POOL_SIZE)
15
+ end
16
+
17
+ end # Server
18
+
19
+ # A request is processed by wrapping it
20
+ # in a fiber from the fiber pool. If all
21
+ # the fibers are busy the request will
22
+ # wait in a queue to be picked up later.
23
+ # Meanwhile, the server will still be
24
+ # processing requests
25
+ class Connection < EventMachine::Connection
26
+
27
+ def process
28
+ @request.threaded = false
29
+ @backend.server.fiber_pool.spawn{post_process(pre_process)}
30
+ end
31
+
32
+ end # Connection
33
+
34
+ end # Thin
data/lib/neverblock.rb ADDED
@@ -0,0 +1,8 @@
1
+ # Author:: Mohammad A. Ali (mailto:oldmoe@gmail.com)
2
+ # Copyright:: Copyright (c) 2008 eSpace, Inc.
3
+ # License:: Distributes under the same terms as Ruby
4
+
5
+ $:.unshift File.expand_path(File.dirname(__FILE__))
6
+
7
+ require 'never_block'
8
+
@@ -0,0 +1,25 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = "neverblock"
3
+ s.version = "0.1.0"
4
+ s.date = "2008-08-13"
5
+ s.summary = "Utilities for non-blocking stack components"
6
+ s.email = "oldmoe@gmail.com"
7
+ s.homepage = "http://github.com/oldmoe/neverblock"
8
+ s.description = "NeverBlock is a collection of classes and modules that help you write evented non-blocking applications in a seemingly blocking mannner."
9
+ s.has_rdoc = true
10
+ s.authors = ["Muhammad A. Ali", "Ahmed Sobhi"]
11
+ s.files = [
12
+ "neverblock.gemspec",
13
+ "README",
14
+ "lib/neverblock.rb",
15
+ "lib/never_block.rb",
16
+ "lib/never_block/extensions/fiber_extensions.rb",
17
+ "lib/never_block/pool/fiber_pool.rb",
18
+ "lib/never_block/pool/fibered_connection_pool.rb",
19
+ "lib/never_block/frameworks/rails.rb",
20
+ "lib/never_block/frameworks/activerecord.rb",
21
+ "lib/never_block/servers/thin.rb"]
22
+ s.rdoc_options = ["--main", "README"]
23
+ s.extra_rdoc_files = ["README"]
24
+ end
25
+
metadata ADDED
@@ -0,0 +1,64 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: oldmoe-neverblock
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Muhammad A. Ali
8
+ - Ahmed Sobhi
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2008-08-13 00:00:00 -07:00
14
+ default_executable:
15
+ dependencies: []
16
+
17
+ description: NeverBlock is a collection of classes and modules that help you write evented non-blocking applications in a seemingly blocking mannner.
18
+ email: oldmoe@gmail.com
19
+ executables: []
20
+
21
+ extensions: []
22
+
23
+ extra_rdoc_files:
24
+ - README
25
+ files:
26
+ - neverblock.gemspec
27
+ - README
28
+ - lib/neverblock.rb
29
+ - lib/never_block.rb
30
+ - lib/never_block/extensions/fiber_extensions.rb
31
+ - lib/never_block/pool/fiber_pool.rb
32
+ - lib/never_block/pool/fibered_connection_pool.rb
33
+ - lib/never_block/frameworks/rails.rb
34
+ - lib/never_block/frameworks/activerecord.rb
35
+ - lib/never_block/servers/thin.rb
36
+ has_rdoc: true
37
+ homepage: http://github.com/oldmoe/neverblock
38
+ post_install_message:
39
+ rdoc_options:
40
+ - --main
41
+ - README
42
+ require_paths:
43
+ - lib
44
+ required_ruby_version: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ version: "0"
49
+ version:
50
+ required_rubygems_version: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: "0"
55
+ version:
56
+ requirements: []
57
+
58
+ rubyforge_project:
59
+ rubygems_version: 1.2.0
60
+ signing_key:
61
+ specification_version: 2
62
+ summary: Utilities for non-blocking stack components
63
+ test_files: []
64
+