about summary refs log tree commit homepage
diff options
context:
space:
mode:
authorEric Wong <normalperson@yhbt.net>2010-10-05 00:13:02 +0000
committerEric Wong <normalperson@yhbt.net>2010-10-05 00:31:26 +0000
commit29946368c45dce5da116adb426362ee93c507c4e (patch)
treebef89c7838ee2165c21f4416f0309e678a0f8d9a
parent9ef6b6f551a34922cfd831e2521495e89afe2f94 (diff)
downloadunicorn-29946368c45dce5da116adb426362ee93c507c4e.tar.gz
This should hopefully make the non-blocking accept()
situation more tolerable under Ruby 1.9.2.
-rw-r--r--lib/unicorn.rb28
-rw-r--r--lib/unicorn/http_request.rb3
-rw-r--r--lib/unicorn/socket_helper.rb8
-rwxr-xr-xscript/isolate_for_tests1
-rw-r--r--test/unit/test_request.rb4
-rw-r--r--unicorn.gemspec1
6 files changed, 24 insertions, 21 deletions
diff --git a/lib/unicorn.rb b/lib/unicorn.rb
index 418b138..3ee827f 100644
--- a/lib/unicorn.rb
+++ b/lib/unicorn.rb
@@ -4,6 +4,7 @@ require 'fcntl'
 require 'etc'
 require 'stringio'
 require 'rack'
+require 'kgio'
 require 'unicorn/socket_helper'
 require 'unicorn/const'
 require 'unicorn/http_request'
@@ -194,7 +195,7 @@ module Unicorn
       BasicSocket.do_not_reverse_lookup = true
 
       # inherit sockets from parents, they need to be plain Socket objects
-      # before they become UNIXServer or TCPServer
+      # before they become Kgio::UNIXServer or Kgio::TCPServer
       inherited = ENV['UNICORN_FD'].to_s.split(/,/).map do |fd|
         io = Socket.for_fd(fd.to_i)
         set_server_sockopt(io, listener_opts[sock_name(io)])
@@ -207,9 +208,10 @@ module Unicorn
       LISTENERS.replace(inherited)
 
       # we start out with generic Socket objects that get cast to either
-      # TCPServer or UNIXServer objects; but since the Socket objects
-      # share the same OS-level file descriptor as the higher-level *Server
-      # objects; we need to prevent Socket objects from being garbage-collected
+      # Kgio::TCPServer or Kgio::UNIXServer objects; but since the Socket
+      # objects share the same OS-level file descriptor as the higher-level
+      # *Server objects; we need to prevent Socket objects from being
+      # garbage-collected
       config_listeners -= listener_names
       if config_listeners.empty? && LISTENERS.empty?
         config_listeners << Unicorn::Const::DEFAULT_LISTEN
@@ -320,7 +322,7 @@ module Unicorn
       tries = opt[:tries] || 5
       begin
         io = bind_listen(address, opt)
-        unless TCPServer === io || UNIXServer === io
+        unless Kgio::TCPServer === io || Kgio::UNIXServer === io
           IO_PURGATORY << io
           io = server_cast(io)
         end
@@ -449,13 +451,11 @@ module Unicorn
     # Wake up every second anyways to run murder_lazy_workers
     def master_sleep(sec)
       IO.select([ SELF_PIPE[0] ], nil, nil, sec) or return
-      SELF_PIPE[0].read_nonblock(Const::CHUNK_SIZE)
-      rescue Errno::EAGAIN, Errno::EINTR
+      SELF_PIPE[0].kgio_tryread(Const::CHUNK_SIZE)
     end
 
     def awaken_master
-      SELF_PIPE[1].write_nonblock('.') # wakeup master process from select
-      rescue Errno::EAGAIN, Errno::EINTR
+      SELF_PIPE[1].kgio_trywrite('.') # wakeup master process from select
     end
 
     # reaps all unreaped workers
@@ -581,7 +581,7 @@ module Unicorn
         logger.error e.backtrace.join("\n")
         Const::ERROR_500_RESPONSE
       end
-      client.write_nonblock(msg)
+      client.kgio_trywrite(msg)
       client.close
       rescue
         nil
@@ -590,7 +590,6 @@ module Unicorn
     # once a client is accepted, it is processed in its entirety here
     # in 3 easy steps: read request, call app, write app response
     def process_client(client)
-      client.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
       r = app.call(env = REQUEST.read(client))
 
       if 100 == r[0].to_i
@@ -665,11 +664,10 @@ module Unicorn
         alive.chmod(m = 0 == m ? 1 : 0)
 
         ready.each do |sock|
-          begin
-            process_client(sock.accept_nonblock)
+          if client = sock.kgio_tryaccept
+            process_client(client)
             nr += 1
             alive.chmod(m = 0 == m ? 1 : 0)
-          rescue Errno::EAGAIN, Errno::ECONNABORTED
           end
           break if nr < 0
         end
@@ -773,7 +771,7 @@ module Unicorn
 
     def init_self_pipe!
       SELF_PIPE.each { |io| io.close rescue nil }
-      SELF_PIPE.replace(IO.pipe)
+      SELF_PIPE.replace(Kgio::Pipe.new)
       SELF_PIPE.each { |io| io.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC) }
     end
 
diff --git a/lib/unicorn/http_request.rb b/lib/unicorn/http_request.rb
index 4355ad7..7519170 100644
--- a/lib/unicorn/http_request.rb
+++ b/lib/unicorn/http_request.rb
@@ -18,7 +18,6 @@ class Unicorn::HttpRequest
   }
 
   NULL_IO = StringIO.new("")
-  LOCALHOST = '127.0.0.1'
 
   # :stopdoc:
   # A frozen format for this is about 15% faster
@@ -62,7 +61,7 @@ class Unicorn::HttpRequest
     #  identify the client for the immediate request to the server;
     #  that client may be a proxy, gateway, or other intermediary
     #  acting on behalf of the actual source client."
-    @env[REMOTE_ADDR] = TCPSocket === socket ? socket.peeraddr[-1] : LOCALHOST
+    @env[REMOTE_ADDR] = socket.kgio_addr
 
     # short circuit the common case with small GET requests first
     if @parser.headers(@env, socket.readpartial(16384, @buf)).nil?
diff --git a/lib/unicorn/socket_helper.rb b/lib/unicorn/socket_helper.rb
index 1d03eab..7364937 100644
--- a/lib/unicorn/socket_helper.rb
+++ b/lib/unicorn/socket_helper.rb
@@ -126,12 +126,12 @@ module Unicorn
         end
         old_umask = File.umask(opt[:umask] || 0)
         begin
-          UNIXServer.new(address)
+          Kgio::UNIXServer.new(address)
         ensure
           File.umask(old_umask)
         end
       elsif address =~ /^(\d+\.\d+\.\d+\.\d+):(\d+)$/
-        TCPServer.new($1, $2.to_i)
+        Kgio::TCPServer.new($1, $2.to_i)
       else
         raise ArgumentError, "Don't know how to bind: #{address}"
       end
@@ -166,9 +166,9 @@ module Unicorn
     def server_cast(sock)
       begin
         Socket.unpack_sockaddr_in(sock.getsockname)
-        TCPServer.for_fd(sock.fileno)
+        Kgio::TCPServer.for_fd(sock.fileno)
       rescue ArgumentError
-        UNIXServer.for_fd(sock.fileno)
+        Kgio::UNIXServer.for_fd(sock.fileno)
       end
     end
 
diff --git a/script/isolate_for_tests b/script/isolate_for_tests
index 1919289..ac856a0 100755
--- a/script/isolate_for_tests
+++ b/script/isolate_for_tests
@@ -17,6 +17,7 @@ opts = {
 pid = fork do
   Isolate.now!(opts) do
     gem 'sqlite3-ruby', '1.2.5'
+    gem 'kgio', '1.1.0'
     gem 'rack', '1.1.0'
   end
 end
diff --git a/test/unit/test_request.rb b/test/unit/test_request.rb
index 1896300..8aae344 100644
--- a/test/unit/test_request.rb
+++ b/test/unit/test_request.rb
@@ -12,6 +12,9 @@ class RequestTest < Test::Unit::TestCase
   class MockRequest < StringIO
     alias_method :readpartial, :sysread
     alias_method :read_nonblock, :sysread
+    def kgio_addr
+      '127.0.0.1'
+    end
   end
 
   def setup
@@ -159,6 +162,7 @@ class RequestTest < Test::Unit::TestCase
     buf = (' ' * bs).freeze
     length = bs * count
     client = Tempfile.new('big_put')
+    def client.kgio_addr; '127.0.0.1'; end
     client.syswrite(
       "PUT / HTTP/1.1\r\n" \
       "Host: foo\r\n" \
diff --git a/unicorn.gemspec b/unicorn.gemspec
index 44a6d56..cb155e9 100644
--- a/unicorn.gemspec
+++ b/unicorn.gemspec
@@ -48,6 +48,7 @@ Gem::Specification.new do |s|
   # commented out.  Nevertheless, upgrading to Rails 2.3.4 or later is
   # *strongly* recommended for security reasons.
   s.add_dependency(%q<rack>)
+  s.add_dependency(%q<kgio>, '~> 1.1.0')
 
   s.add_development_dependency('isolate', '~> 2.0.2')