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,64 +0,0 @@
1
- require 'pg'
2
-
3
- module NeverBlock
4
-
5
- module DB
6
-
7
- # A modified postgres connection driver
8
- # builds on the original pg driver.
9
- # This driver is able to register the socket
10
- # at a certain backend (EM or Rev)
11
- # and then whenever the query is executed
12
- # within the scope of a friendly fiber
13
- # it will be done in async mode and the fiber
14
- # will yield
15
- class FiberedPostgresConnection < PGconn
16
-
17
- include FiberedDBConnection
18
-
19
- # Assuming the use of NeverBlock fiber extensions and that the exec is run in
20
- # the context of a fiber. One that have the value :neverblock set to true.
21
- # All neverblock IO classes check this value, setting it to false will force
22
- # the execution in a blocking way.
23
- def exec(sql)
24
- # TODO Still not "killing the query process"-proof
25
- # In some cases, the query is simply sent but the fiber never yields
26
- if NB.event_loop_available? && NB.neverblocking?
27
- begin
28
- send_query sql
29
- @fiber = Fiber.current
30
- Fiber.yield register_with_event_loop
31
- while is_busy
32
- consume_input
33
- Fiber.yield if is_busy
34
- end
35
- res, data = 0, []
36
- while res != nil
37
- res = self.get_result
38
- data << res unless res.nil?
39
- end
40
- data.last
41
- rescue Exception => e
42
- if error = ['not connected', 'gone away', 'Lost connection','no connection'].detect{|msg| e.message.include? msg}
43
- #event_loop_connection_close
44
- unregister_from_event_loop
45
- reset
46
- end
47
- raise e
48
- ensure
49
- unregister_from_event_loop
50
- end
51
- else
52
- super(sql)
53
- end
54
- end
55
-
56
- alias_method :query, :exec
57
-
58
- end #FiberedPostgresConnection
59
-
60
- end #DB
61
-
62
- end #NeverBlock
63
-
64
- NeverBlock::DB::FPGconn = NeverBlock::DB::FiberedPostgresConnection
@@ -1,37 +0,0 @@
1
- require 'activerecord'
2
-
3
- # Patch ActiveRecord to store transaction depth information
4
- # in fibers instead of threads. AR does not support nested
5
- # transactions which makes the job easy.
6
- # We also need to override the scoped methods to store
7
- # the scope in the fiber context
8
- class ActiveRecord::Base
9
-
10
- def single_threaded_scoped_methods #:nodoc:
11
- scoped_methods = (Fiber.current[:scoped_methods] ||= {})
12
- scoped_methods[self] ||= []
13
- end
14
-
15
- def self.transaction(&block)
16
- increment_open_transactions
17
- begin
18
- connection.transaction(Fiber.current['start_db_transaction'], &block)
19
- ensure
20
- decrement_open_transactions
21
- end
22
- end
23
-
24
- private
25
-
26
- def self.increment_open_transactions #:nodoc:
27
- open = Fiber.current['open_transactions'] ||= 0
28
- Fiber.current['start_db_transaction'] = open.zero?
29
- Fiber.current['open_transactions'] = open + 1
30
- end
31
-
32
- def self.decrement_open_transactions #:nodoc:
33
- Fiber.current['open_transactions'] -= 1
34
- end
35
-
36
- end
37
-
@@ -1,65 +0,0 @@
1
- require 'neverblock' unless defined?(NeverBlock)
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
- require 'thread'
11
- # now you synchronize
12
- class Mutex
13
- def synchronize(&block)
14
- # now you don't!
15
- block.call
16
- end
17
- end
18
-
19
- require 'action_controller'
20
- class ActionController::Base
21
-
22
- # Mark some actions to execute in a blocking manner overriding the default
23
- # settings.
24
- # Example:
25
- # class UsersController < ApplicationController
26
- # .
27
- # allowblock :index
28
- # .
29
- # end
30
- def self.allowblock(*actions)
31
- actions.each do |action|
32
- class_eval <<-"end_eval"
33
- def allowblock_#{action}
34
- status = Fiber.current[:neverblock]
35
- Fiber.current[:neverblock] = false
36
- yield
37
- Fiber.current[:neverblock] = status
38
- end
39
- around_filter :allowblock_#{action}, :only => [:#{action}]
40
- end_eval
41
- end
42
- end
43
-
44
- # Mark some actions to execute in a non-blocking manner overriding the default
45
- # settings.
46
- # Example:
47
- # class UsersController < ApplicationController
48
- # .
49
- # allowblock :index
50
- # .
51
- # end
52
- def self.neverblock(*actions)
53
- actions.each do |action|
54
- class_eval <<-"end_eval"
55
- def neverblock_#{action}
56
- status = Fiber.current[:neverblock]
57
- Fiber.current[:neverblock] = true
58
- yield
59
- Fiber.current[:neverblock] = status
60
- end
61
- around_filter :allowblock_#{action}, :only => [:#{action}]
62
- end_eval
63
- end
64
- end
65
- end
@@ -1,74 +0,0 @@
1
- module NeverBlock
2
- module Pool
3
-
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
- # A pool of initialized fibers
9
- # It does not grow in size or create transient fibers
10
- # It will queue code blocks when needed (if all its fibers are busy)
11
- #
12
- # This class is particulary useful when you use the fibers
13
- # to connect to evented back ends. It also does not generate
14
- # transient objects and thus saves memory.
15
- #
16
- # Example:
17
- # fiber_pool = NeverBlock::Pool::FiberPool.new(150)
18
- #
19
- # loop do
20
- # fiber_pool.spawn do
21
- # #fiber body goes here
22
- # end
23
- # end
24
- #
25
- class FiberPool
26
-
27
- # gives access to the currently free fibers
28
- attr_reader :fibers
29
-
30
- # Prepare a list of fibers that are able to run different blocks of code
31
- # every time. Once a fiber is done with its block, it attempts to fetch
32
- # another one from the queue
33
- def initialize(count = 50)
34
- @fibers,@busy_fibers,@queue = [],{},[]
35
- count.times do |i|
36
- fiber = Fiber.new do |block|
37
- loop do
38
- block.call
39
- # callbacks are called in a reverse order, much like c++ destructor
40
- Fiber.current[:callbacks].pop.call while Fiber.current[:callbacks].length > 0
41
- unless @queue.empty?
42
- block = @queue.shift
43
- else
44
- @busy_fibers.delete(Fiber.current.object_id)
45
- @fibers << Fiber.current
46
- block = Fiber.yield
47
- end
48
- end
49
- end
50
- fiber[:callbacks] = []
51
- fiber[:em_keys] = []
52
- fiber[:neverblock] = true
53
- @fibers << fiber
54
- end
55
- end
56
-
57
- # If there is an available fiber use it, otherwise, leave it to linger
58
- # in a queue
59
- def spawn(evented = true, &block)
60
- if fiber = @fibers.shift
61
- fiber[:callbacks] = []
62
- @busy_fibers[fiber.object_id] = fiber
63
- fiber[:neverblock] = evented
64
- fiber.resume(block)
65
- else
66
- @queue << block
67
- end
68
- self # we are keen on hiding our queue
69
- end
70
-
71
- end # FiberPool
72
- end # Pool
73
- end # NeverBlock
74
-
@@ -1,236 +0,0 @@
1
- # This module rewrites pieces of the very good Mongrel web server in
2
- # order to change it from a threaded application to an event based
3
- # application running inside an EventMachine event loop. It should
4
- # be compatible with the existing Mongrel handlers for Rails,
5
- # Camping, Nitro, etc....
6
-
7
- # NeverBlock support added
8
-
9
- begin
10
- load_attempted ||= false
11
- require 'eventmachine'
12
- rescue LoadError
13
- unless load_attempted
14
- load_attempted = true
15
- require 'rubygems'
16
- retry
17
- end
18
- end
19
-
20
- require 'rubygems'
21
- require 'neverblock'
22
- require 'mongrel'
23
-
24
- module Mongrel
25
- class MongrelProtocol < EventMachine::Connection
26
- def post_init
27
- @parser = HttpParser.new
28
- @params = HttpParams.new
29
- @nparsed = 0
30
- @request = nil
31
- @request_len = nil
32
- @linebuffer = ''
33
- end
34
-
35
- def receive_data data
36
- @linebuffer << data
37
- @nparsed = @parser.execute(@params, @linebuffer, @nparsed) unless @parser.finished?
38
- if @parser.finished?
39
- if @request_len.nil?
40
- @request_len = @params[::Mongrel::Const::CONTENT_LENGTH].to_i
41
- script_name, path_info, handlers = ::Mongrel::HttpServer::Instance.classifier.resolve(@params[::Mongrel::Const::REQUEST_PATH])
42
- if handlers
43
- @params[::Mongrel::Const::PATH_INFO] = path_info
44
- @params[::Mongrel::Const::SCRIPT_NAME] = script_name
45
- @params[::Mongrel::Const::REMOTE_ADDR] = @params[::Mongrel::Const::HTTP_X_FORWARDED_FOR] || ::Socket.unpack_sockaddr_in(get_peername)[1]
46
- @notifiers = handlers.select { |h| h.request_notify }
47
- end
48
- if @request_len > ::Mongrel::Const::MAX_BODY
49
- new_buffer = Tempfile.new(::Mongrel::Const::MONGREL_TMP_BASE)
50
- new_buffer.binmode
51
- new_buffer << @linebuffer[@nparsed..-1]
52
- @linebuffer = new_buffer
53
- else
54
- @linebuffer = StringIO.new(@linebuffer[@nparsed..-1])
55
- @linebuffer.pos = @linebuffer.length
56
- end
57
- end
58
- if @linebuffer.length >= @request_len
59
- @linebuffer.rewind
60
- ::Mongrel::HttpServer::Instance.process_http_request(@params,@linebuffer,self)
61
- @linebuffer.delete if Tempfile === @linebuffer
62
- end
63
- elsif @linebuffer.length > ::Mongrel::Const::MAX_HEADER
64
- close_connection
65
- raise ::Mongrel::HttpParserError.new("HEADER is longer than allowed, aborting client early.")
66
- end
67
- rescue ::Mongrel::HttpParserError
68
- if $mongrel_debug_client
69
- STDERR.puts "#{Time.now}: BAD CLIENT (#{params[Const::HTTP_X_FORWARDED_FOR] || client.peeraddr.last}): #$!"
70
- STDERR.puts "#{Time.now}: REQUEST DATA: #{data.inspect}\n---\nPARAMS: #{params.inspect}\n---\n"
71
- end
72
- close_connection
73
- rescue Exception => e
74
- close_connection
75
- raise e
76
- end
77
-
78
- def write data
79
- send_data data
80
- end
81
-
82
- def closed?
83
- false
84
- end
85
-
86
- end
87
-
88
- class HttpServer
89
- DEFAULT_FIBER_POOL_SIZE = 20
90
- def initialize(host, port, num_processors=950, x=0, y=nil) # Deal with Mongrel 1.0.1 or earlier, as well as later.
91
- @socket = nil
92
- @classifier = URIClassifier.new
93
- @host = host
94
- @port = port
95
- @workers = ThreadGroup.new
96
- if y
97
- @throttle = x
98
- @timeout = y || 60
99
- else
100
- @timeout = x
101
- end
102
- @num_processors = num_processors #num_processors is pointless for evented....
103
- @death_time = 60
104
- self.class.const_set(:Instance,self)
105
- end
106
-
107
- def fiber_pool
108
- @fiber_pool ||= NB::Pool::FiberPool.new(DEFAULT_FIBER_POOL_SIZE)
109
- end
110
-
111
- def run
112
- trap('INT') { raise StopServer }
113
- trap('TERM') { raise StopServer }
114
- @acceptor = Thread.new do
115
- EventMachine.epoll
116
- EventMachine.set_descriptor_table_size(4096)
117
- EventMachine.run do
118
- begin
119
- EventMachine.start_server(@host,@port,MongrelProtocol)
120
- rescue StopServer
121
- EventMachine.stop_event_loop
122
- end
123
- end
124
- end
125
- end
126
-
127
- def process_http_request(params,linebuffer,client)
128
- if not params[Const::REQUEST_PATH]
129
- uri = URI.parse(params[Const::REQUEST_URI])
130
- params[Const::REQUEST_PATH] = uri.request_uri
131
- end
132
-
133
- raise "No REQUEST PATH" if not params[Const::REQUEST_PATH]
134
-
135
- script_name, path_info, handlers = @classifier.resolve(params[Const::REQUEST_PATH])
136
-
137
- if handlers
138
- notifiers = handlers.select { |h| h.request_notify }
139
- request = HttpRequest.new(params, linebuffer, notifiers)
140
-
141
- # request is good so far, continue processing the response
142
- response = HttpResponse.new(client)
143
-
144
- # Process each handler in registered order until we run out or one finalizes the response.
145
- fiber_pool.spawn do
146
- dispatch_to_handlers(handlers,request,response)
147
- end
148
- # And finally, if nobody closed the response off, we finalize it.
149
- unless response.done
150
- response.finished
151
- else
152
- response.close_connection_after_writing
153
- end
154
- else
155
- # Didn't find it, return a stock 404 response.
156
- client.send_data(Const::ERROR_404_RESPONSE)
157
- client.close_connection_after_writing
158
- end
159
- end
160
-
161
- def dispatch_to_handlers(handlers,request,response)
162
- handlers.each do |handler|
163
- handler.process(request, response)
164
- break if response.done
165
- end
166
- end
167
- end
168
-
169
- class HttpRequest
170
- def initialize(params, linebuffer, dispatchers)
171
- @params = params
172
- @dispatchers = dispatchers
173
- @body = linebuffer
174
- end
175
- end
176
-
177
- class HttpResponse
178
- def send_file(path, small_file = false)
179
- File.open(path, "rb") do |f|
180
- while chunk = f.read(Const::CHUNK_SIZE) and chunk.length > 0
181
- begin
182
- write(chunk)
183
- rescue Object => exc
184
- break
185
- end
186
- end
187
- end
188
- @body_sent = true
189
- end
190
-
191
- def write(data)
192
- @socket.send_data data
193
- end
194
-
195
- def close_connection_after_writing
196
- @socket.close_connection_after_writing
197
- end
198
-
199
- def socket_error(details)
200
- @socket.close_connection
201
- done = true
202
- raise details
203
- end
204
-
205
- def finished
206
- send_status
207
- send_header
208
- send_body
209
- @socket.close_connection_after_writing
210
- end
211
- end
212
-
213
- class Configurator
214
- # This version fixes a bug in the regular Mongrel version by adding
215
- # initialization of groups.
216
- def change_privilege(user, group)
217
- if user and group
218
- log "Initializing groups for {#user}:{#group}."
219
- Process.initgroups(user,Etc.getgrnam(group).gid)
220
- end
221
-
222
- if group
223
- log "Changing group to #{group}."
224
- Process::GID.change_privilege(Etc.getgrnam(group).gid)
225
- end
226
-
227
- if user
228
- log "Changing user to #{user}."
229
- Process::UID.change_privilege(Etc.getpwnam(user).uid)
230
- end
231
- rescue Errno::EPERM
232
- log "FAILED to change user:group #{user}:#{group}: #$!"
233
- exit 1
234
- end
235
- end
236
- end