about summary refs log tree commit homepage
diff options
context:
space:
mode:
authorEric Wong <e@80x24.org>2018-08-20 20:56:52 +0000
committerEric Wong <e@80x24.org>2018-08-20 21:09:42 +0000
commit0ed746e9b9f03a5067aa97cc4ed54b9e42443727 (patch)
treeea9d4ae65457ce57902208295e22d8d61bad0e88
parentd03dd4e9e4ff29689752b7c82202008fefaf1210 (diff)
downloadunicorn-0ed746e9b9f03a5067aa97cc4ed54b9e42443727.tar.gz
We have never had any need for pipes with the default 64K
capacity on Linux.  Our pipes are only used for tiny writes
in signal handlers and to perform parent shutdown detection.

With the current /proc/sys/fs/pipe-user-pages-soft
default, only 1024 pipes can be created by an unprivileged
user before Linux clamps down the pipe size to 4K (a single page)
for newly-created pipes[1].

So avoid penalizing OTHER pipe users who could benefit from the
increased capacity and use only a single page for ourselves.

[1] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/fs/pipe.c?h=v4.18#n642
-rw-r--r--lib/unicorn.rb18
-rw-r--r--lib/unicorn/http_request.rb1
-rw-r--r--lib/unicorn/launcher.rb2
-rw-r--r--test/unit/test_util.rb25
4 files changed, 42 insertions, 4 deletions
diff --git a/lib/unicorn.rb b/lib/unicorn.rb
index 85e4df1..b6dae36 100644
--- a/lib/unicorn.rb
+++ b/lib/unicorn.rb
@@ -2,6 +2,7 @@
 require 'etc'
 require 'stringio'
 require 'kgio'
+require 'raindrops'
 require 'io/wait'
 
 begin
@@ -113,9 +114,22 @@ module Unicorn
     exc.backtrace.each { |line| logger.error(line) }
   end
 
-  # remove this when we only support Ruby >= 2.0
+  F_SETPIPE_SZ = 1031 if RUBY_PLATFORM =~ /linux/
+
   def self.pipe # :nodoc:
-    Kgio::Pipe.new.each { |io| io.close_on_exec = true }
+    Kgio::Pipe.new.each do |io|
+      io.close_on_exec = true  # remove this when we only support Ruby >= 2.0
+
+      # shrink pipes to minimize impact on /proc/sys/fs/pipe-user-pages-soft
+      # limits.
+      if defined?(F_SETPIPE_SZ)
+        begin
+          io.fcntl(F_SETPIPE_SZ, Raindrops::PAGE_SIZE)
+        rescue Errno::EINVAL
+          # old kernel
+        end
+      end
+    end
   end
   # :startdoc:
 end
diff --git a/lib/unicorn/http_request.rb b/lib/unicorn/http_request.rb
index d713b19..8bb884b 100644
--- a/lib/unicorn/http_request.rb
+++ b/lib/unicorn/http_request.rb
@@ -2,7 +2,6 @@
 # :enddoc:
 # no stable API here
 require 'unicorn_http'
-require 'raindrops'
 
 # TODO: remove redundant names
 Unicorn.const_set(:HttpRequest, Unicorn::HttpParser)
diff --git a/lib/unicorn/launcher.rb b/lib/unicorn/launcher.rb
index 5eafe5b..78e8f39 100644
--- a/lib/unicorn/launcher.rb
+++ b/lib/unicorn/launcher.rb
@@ -31,7 +31,7 @@ module Unicorn::Launcher
       #  \_ parent  - exits immediately ASAP
       #      \_ unicorn master - writes to pipe when ready
 
-      rd, wr = IO.pipe
+      rd, wr = Unicorn.pipe
       grandparent = $$
       if fork
         wr.close # grandparent does not write
diff --git a/test/unit/test_util.rb b/test/unit/test_util.rb
index dc6302e..9d5d4ef 100644
--- a/test/unit/test_util.rb
+++ b/test/unit/test_util.rb
@@ -102,4 +102,29 @@ class TestUtil < Test::Unit::TestCase
     }
     tmp.close!
   end
+
+  def test_pipe
+    r, w = Unicorn.pipe
+    assert r
+    assert w
+
+    return if RUBY_PLATFORM !~ /linux/
+
+    begin
+      f_getpipe_sz = 1032
+      IO.pipe do |a, b|
+        a_sz = a.fcntl(f_getpipe_sz)
+        b_sz = b.fcntl(f_getpipe_sz)
+        assert_kind_of Integer, a_sz
+        r_sz = r.fcntl(f_getpipe_sz)
+        assert_equal Raindrops::PAGE_SIZE, r_sz
+        assert_operator a_sz, :>=, r_sz
+      end
+    rescue Errno::EINVAL
+      # Linux <= 2.6.34
+    end
+  ensure
+    w.close
+    r.close
+  end
 end