neverblock 0.1.6.2
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.
- data/README +24 -0
 - data/Rakefile +24 -0
 - data/VERSION +1 -0
 - data/lib/active_record/connection_adapters/neverblock_mysql_adapter.rb +68 -0
 - data/lib/active_record/connection_adapters/neverblock_postgresql_adapter.rb +85 -0
 - data/lib/never_block.rb +102 -0
 - data/lib/never_block/db/fibered_db_connection.rb +72 -0
 - data/lib/never_block/db/fibered_mysql_connection.rb +62 -0
 - data/lib/never_block/db/fibered_postgres_connection.rb +64 -0
 - data/lib/never_block/db/pooled_db_connection.rb +43 -0
 - data/lib/never_block/extensions/fiber_extensions.rb +31 -0
 - data/lib/never_block/frameworks/activerecord.rb +37 -0
 - data/lib/never_block/frameworks/rails.rb +65 -0
 - data/lib/never_block/pool/fiber_pool.rb +74 -0
 - data/lib/never_block/pool/fibered_connection_pool.rb +130 -0
 - data/lib/never_block/servers/mongrel.rb +236 -0
 - data/lib/never_block/servers/thin.rb +32 -0
 - data/lib/neverblock-mysql.rb +5 -0
 - data/lib/neverblock-pg.rb +5 -0
 - data/lib/neverblock.rb +8 -0
 - data/neverblock.gemspec +73 -0
 - data/spec/fiber_extensions_spec.rb +24 -0
 - data/spec/fiber_pool_spec.rb +60 -0
 - data/spec/fibered_connection_pool_spec.rb +106 -0
 - data/tasks/spec.rake +12 -0
 - data/test/test_mysql.rb +36 -0
 - data/test/test_pg.rb +102 -0
 - metadata +97 -0
 
| 
         @@ -0,0 +1,236 @@ 
     | 
|
| 
      
 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
         
     | 
| 
         @@ -0,0 +1,32 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'rubygems'
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'neverblock' unless defined?(NeverBlock)
         
     | 
| 
      
 3 
     | 
    
         
            +
            require 'thin'
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            module Thin
         
     | 
| 
      
 6 
     | 
    
         
            +
              
         
     | 
| 
      
 7 
     | 
    
         
            +
              # Patch the thin server to use NeverBlock::Pool::FiberPool to be able to
         
     | 
| 
      
 8 
     | 
    
         
            +
              # wrap requests in fibers
         
     | 
| 
      
 9 
     | 
    
         
            +
              class Server
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                DEFAULT_FIBER_POOL_SIZE = 20
         
     | 
| 
      
 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 in a fiber from the fiber pool.
         
     | 
| 
      
 20 
     | 
    
         
            +
              # If all the fibers are busy the request will wait in a queue to be picked up
         
     | 
| 
      
 21 
     | 
    
         
            +
              # later. Meanwhile, the server will still be processing requests
         
     | 
| 
      
 22 
     | 
    
         
            +
              class Connection < EventMachine::Connection
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                def process
         
     | 
| 
      
 25 
     | 
    
         
            +
                    @request.threaded = false
         
     | 
| 
      
 26 
     | 
    
         
            +
                    @backend.server.fiber_pool.spawn {post_process(pre_process)}
         
     | 
| 
      
 27 
     | 
    
         
            +
                end
         
     | 
| 
      
 28 
     | 
    
         
            +
                
         
     | 
| 
      
 29 
     | 
    
         
            +
              end # Connection
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
            end # Thin
         
     | 
    
        data/lib/neverblock.rb
    ADDED
    
    
    
        data/neverblock.gemspec
    ADDED
    
    | 
         @@ -0,0 +1,73 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # Generated by jeweler
         
     | 
| 
      
 2 
     | 
    
         
            +
            # DO NOT EDIT THIS FILE DIRECTLY
         
     | 
| 
      
 3 
     | 
    
         
            +
            # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
         
     | 
| 
      
 4 
     | 
    
         
            +
            # -*- encoding: utf-8 -*-
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
            Gem::Specification.new do |s|
         
     | 
| 
      
 7 
     | 
    
         
            +
              s.name = %q{neverblock}
         
     | 
| 
      
 8 
     | 
    
         
            +
              s.version = "0.1.6.2"
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
              s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
         
     | 
| 
      
 11 
     | 
    
         
            +
              s.authors = ["Muhammad A. Ali", "Ahmed Sobhi", "Osama Brekaa", "Nicholas Silva"]
         
     | 
| 
      
 12 
     | 
    
         
            +
              s.date = %q{2010-02-03}
         
     | 
| 
      
 13 
     | 
    
         
            +
              s.description = %q{NeverBlock is a collection of classes and modules that help you write evented non-blocking applications in a seemingly blocking mannner.}
         
     | 
| 
      
 14 
     | 
    
         
            +
              s.email = %q{conickal@gmail.com}
         
     | 
| 
      
 15 
     | 
    
         
            +
              s.extra_rdoc_files = [
         
     | 
| 
      
 16 
     | 
    
         
            +
                "README"
         
     | 
| 
      
 17 
     | 
    
         
            +
              ]
         
     | 
| 
      
 18 
     | 
    
         
            +
              s.files = [
         
     | 
| 
      
 19 
     | 
    
         
            +
                "README",
         
     | 
| 
      
 20 
     | 
    
         
            +
                 "Rakefile",
         
     | 
| 
      
 21 
     | 
    
         
            +
                 "VERSION",
         
     | 
| 
      
 22 
     | 
    
         
            +
                 "lib/active_record/connection_adapters/neverblock_mysql_adapter.rb",
         
     | 
| 
      
 23 
     | 
    
         
            +
                 "lib/active_record/connection_adapters/neverblock_postgresql_adapter.rb",
         
     | 
| 
      
 24 
     | 
    
         
            +
                 "lib/never_block.rb",
         
     | 
| 
      
 25 
     | 
    
         
            +
                 "lib/never_block/db/fibered_db_connection.rb",
         
     | 
| 
      
 26 
     | 
    
         
            +
                 "lib/never_block/db/fibered_mysql_connection.rb",
         
     | 
| 
      
 27 
     | 
    
         
            +
                 "lib/never_block/db/fibered_postgres_connection.rb",
         
     | 
| 
      
 28 
     | 
    
         
            +
                 "lib/never_block/db/pooled_db_connection.rb",
         
     | 
| 
      
 29 
     | 
    
         
            +
                 "lib/never_block/extensions/fiber_extensions.rb",
         
     | 
| 
      
 30 
     | 
    
         
            +
                 "lib/never_block/frameworks/activerecord.rb",
         
     | 
| 
      
 31 
     | 
    
         
            +
                 "lib/never_block/frameworks/rails.rb",
         
     | 
| 
      
 32 
     | 
    
         
            +
                 "lib/never_block/pool/fiber_pool.rb",
         
     | 
| 
      
 33 
     | 
    
         
            +
                 "lib/never_block/pool/fibered_connection_pool.rb",
         
     | 
| 
      
 34 
     | 
    
         
            +
                 "lib/never_block/servers/mongrel.rb",
         
     | 
| 
      
 35 
     | 
    
         
            +
                 "lib/never_block/servers/thin.rb",
         
     | 
| 
      
 36 
     | 
    
         
            +
                 "lib/neverblock-mysql.rb",
         
     | 
| 
      
 37 
     | 
    
         
            +
                 "lib/neverblock-pg.rb",
         
     | 
| 
      
 38 
     | 
    
         
            +
                 "lib/neverblock.rb",
         
     | 
| 
      
 39 
     | 
    
         
            +
                 "neverblock.gemspec",
         
     | 
| 
      
 40 
     | 
    
         
            +
                 "spec/fiber_extensions_spec.rb",
         
     | 
| 
      
 41 
     | 
    
         
            +
                 "spec/fiber_pool_spec.rb",
         
     | 
| 
      
 42 
     | 
    
         
            +
                 "spec/fibered_connection_pool_spec.rb",
         
     | 
| 
      
 43 
     | 
    
         
            +
                 "tasks/spec.rake",
         
     | 
| 
      
 44 
     | 
    
         
            +
                 "test/test_mysql.rb",
         
     | 
| 
      
 45 
     | 
    
         
            +
                 "test/test_pg.rb"
         
     | 
| 
      
 46 
     | 
    
         
            +
              ]
         
     | 
| 
      
 47 
     | 
    
         
            +
              s.homepage = %q{http://github.com/conickal/neverblock}
         
     | 
| 
      
 48 
     | 
    
         
            +
              s.rdoc_options = ["--charset=UTF-8"]
         
     | 
| 
      
 49 
     | 
    
         
            +
              s.require_paths = ["lib"]
         
     | 
| 
      
 50 
     | 
    
         
            +
              s.rubygems_version = %q{1.3.5}
         
     | 
| 
      
 51 
     | 
    
         
            +
              s.summary = %q{Utilities for non-blocking stack components}
         
     | 
| 
      
 52 
     | 
    
         
            +
              s.test_files = [
         
     | 
| 
      
 53 
     | 
    
         
            +
                "spec/fiber_extensions_spec.rb",
         
     | 
| 
      
 54 
     | 
    
         
            +
                 "spec/fiber_pool_spec.rb",
         
     | 
| 
      
 55 
     | 
    
         
            +
                 "spec/fibered_connection_pool_spec.rb",
         
     | 
| 
      
 56 
     | 
    
         
            +
                 "test/test_mysql.rb",
         
     | 
| 
      
 57 
     | 
    
         
            +
                 "test/test_pg.rb"
         
     | 
| 
      
 58 
     | 
    
         
            +
              ]
         
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
      
 60 
     | 
    
         
            +
              if s.respond_to? :specification_version then
         
     | 
| 
      
 61 
     | 
    
         
            +
                current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
         
     | 
| 
      
 62 
     | 
    
         
            +
                s.specification_version = 3
         
     | 
| 
      
 63 
     | 
    
         
            +
             
     | 
| 
      
 64 
     | 
    
         
            +
                if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
         
     | 
| 
      
 65 
     | 
    
         
            +
                  s.add_runtime_dependency(%q<eventmachine>, [">= 0.12.2"])
         
     | 
| 
      
 66 
     | 
    
         
            +
                else
         
     | 
| 
      
 67 
     | 
    
         
            +
                  s.add_dependency(%q<eventmachine>, [">= 0.12.2"])
         
     | 
| 
      
 68 
     | 
    
         
            +
                end
         
     | 
| 
      
 69 
     | 
    
         
            +
              else
         
     | 
| 
      
 70 
     | 
    
         
            +
                s.add_dependency(%q<eventmachine>, [">= 0.12.2"])
         
     | 
| 
      
 71 
     | 
    
         
            +
              end
         
     | 
| 
      
 72 
     | 
    
         
            +
            end
         
     | 
| 
      
 73 
     | 
    
         
            +
             
     | 
| 
         @@ -0,0 +1,24 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            $:.unshift File.expand_path('..')
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'lib/neverblock'
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            describe Fiber do
         
     | 
| 
      
 5 
     | 
    
         
            +
              before(:all) do
         
     | 
| 
      
 6 
     | 
    
         
            +
                @fiber = Fiber.new {puts "I'm a new fiber"}
         
     | 
| 
      
 7 
     | 
    
         
            +
              end
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
              it "should be able to set fiber local variable" do
         
     | 
| 
      
 10 
     | 
    
         
            +
                @fiber[:x] = "wow"
         
     | 
| 
      
 11 
     | 
    
         
            +
              end
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
              it "should be able to retrieve an already set fiber local variable" do
         
     | 
| 
      
 14 
     | 
    
         
            +
                @fiber[:x].should == "wow"
         
     | 
| 
      
 15 
     | 
    
         
            +
              end
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
              it "should return nil when trying to retrieve an unset fiber local variable" do
         
     | 
| 
      
 18 
     | 
    
         
            +
                @fiber[:y].should == nil
         
     | 
| 
      
 19 
     | 
    
         
            +
              end
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
              after(:all) do
         
     | 
| 
      
 22 
     | 
    
         
            +
                @fiber = nil
         
     | 
| 
      
 23 
     | 
    
         
            +
              end
         
     | 
| 
      
 24 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,60 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            $:.unshift File.expand_path('..')
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'lib/neverblock'
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            describe NeverBlock::Pool::FiberPool do
         
     | 
| 
      
 5 
     | 
    
         
            +
              before(:each) do
         
     | 
| 
      
 6 
     | 
    
         
            +
                @fiber_pool = NeverBlock::Pool::FiberPool.new(10)
         
     | 
| 
      
 7 
     | 
    
         
            +
              end
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
              it "should have all fibers ready and an empty queue initially" do
         
     | 
| 
      
 10 
     | 
    
         
            +
                @fiber_pool.fibers.length.should == 10
         
     | 
| 
      
 11 
     | 
    
         
            +
                @fiber_pool.instance_variable_get(:@queue).length.should == 0
         
     | 
| 
      
 12 
     | 
    
         
            +
              end
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
              it "should have fibers with :neverblock fiber variable set to true" do
         
     | 
| 
      
 15 
     | 
    
         
            +
                @fiber_pool.fibers.each {|f| f[:neverblock].should == true}
         
     | 
| 
      
 16 
     | 
    
         
            +
              end
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
              it "should process a new block if there are available fibers" do
         
     | 
| 
      
 19 
     | 
    
         
            +
                x = false
         
     | 
| 
      
 20 
     | 
    
         
            +
                @fiber_pool.spawn {x  = true}
         
     | 
| 
      
 21 
     | 
    
         
            +
                x.should == true
         
     | 
| 
      
 22 
     | 
    
         
            +
              end
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
              it "should queue requests if requests are more than fibers" do
         
     | 
| 
      
 25 
     | 
    
         
            +
                progress = Array.new(15, false)
         
     | 
| 
      
 26 
     | 
    
         
            +
                fibers = []
         
     | 
| 
      
 27 
     | 
    
         
            +
                @fiber_pool.fibers.each {|f| fibers << f}
         
     | 
| 
      
 28 
     | 
    
         
            +
                10.times do |i|
         
     | 
| 
      
 29 
     | 
    
         
            +
                  @fiber_pool.spawn {Fiber.yield; progress[i] = true}
         
     | 
| 
      
 30 
     | 
    
         
            +
                end
         
     | 
| 
      
 31 
     | 
    
         
            +
                @fiber_pool.fibers.length.should == 0
         
     | 
| 
      
 32 
     | 
    
         
            +
                @fiber_pool.instance_variable_get(:@queue).length.should == 0
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
                #it should now queue
         
     | 
| 
      
 35 
     | 
    
         
            +
                 (10..14).each {|i| @fiber_pool.spawn {progress[i] = true}}
         
     | 
| 
      
 36 
     | 
    
         
            +
                @fiber_pool.fibers.length.should == 0
         
     | 
| 
      
 37 
     | 
    
         
            +
                @fiber_pool.instance_variable_get(:@queue).length.should == 5
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
                #resume the first fiber, this should also process the queued requests
         
     | 
| 
      
 40 
     | 
    
         
            +
                fibers[0].resume
         
     | 
| 
      
 41 
     | 
    
         
            +
                @fiber_pool.fibers.length.should == 1
         
     | 
| 
      
 42 
     | 
    
         
            +
                @fiber_pool.instance_variable_get(:@queue).length.should == 0
         
     | 
| 
      
 43 
     | 
    
         
            +
                [0,*10..14].each {|i| progress[i].should == true}
         
     | 
| 
      
 44 
     | 
    
         
            +
                 (1..9).to_a.each {|i| progress[i].should == false}
         
     | 
| 
      
 45 
     | 
    
         
            +
                fibers_count = 1
         
     | 
| 
      
 46 
     | 
    
         
            +
                 (1..9).to_a.each do |i|
         
     | 
| 
      
 47 
     | 
    
         
            +
                  fibers[i].resume
         
     | 
| 
      
 48 
     | 
    
         
            +
                  fibers_count = fibers_count + 1
         
     | 
| 
      
 49 
     | 
    
         
            +
                  progress[i].should == true
         
     | 
| 
      
 50 
     | 
    
         
            +
                  @fiber_pool.fibers.length.should == fibers_count
         
     | 
| 
      
 51 
     | 
    
         
            +
                end
         
     | 
| 
      
 52 
     | 
    
         
            +
                @fiber_pool.instance_variable_get(:@queue).length.should == 0
         
     | 
| 
      
 53 
     | 
    
         
            +
              end
         
     | 
| 
      
 54 
     | 
    
         
            +
             
     | 
| 
      
 55 
     | 
    
         
            +
              after(:each) do
         
     | 
| 
      
 56 
     | 
    
         
            +
                @fiber_pool = nil
         
     | 
| 
      
 57 
     | 
    
         
            +
              end
         
     | 
| 
      
 58 
     | 
    
         
            +
             
     | 
| 
      
 59 
     | 
    
         
            +
            end
         
     | 
| 
      
 60 
     | 
    
         
            +
             
     |