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,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