about summary refs log tree commit homepage
path: root/lib
diff options
context:
space:
mode:
authorEric Wong <normalperson@yhbt.net>2010-07-29 07:05:48 +0000
committerEric Wong <normalperson@yhbt.net>2010-07-29 08:04:03 +0000
commit5479b15c766204e31495e87a64fa689141cc38a3 (patch)
tree022b17ec84c6ceebc95b5cdf6088878245bf12e5 /lib
parentf309cfaf70cbffd7a39208da869e47784e4cb41b (diff)
downloadrainbows-5479b15c766204e31495e87a64fa689141cc38a3.tar.gz
Proxying regular Ruby IO objects while Revactor is in use is
highly suboptimal, so proxy it with an Actor-aware wrapper for
better scheduling.
Diffstat (limited to 'lib')
-rw-r--r--lib/rainbows/dev_fd_response.rb2
-rw-r--r--lib/rainbows/revactor.rb2
-rw-r--r--lib/rainbows/revactor/proxy.rb55
3 files changed, 59 insertions, 0 deletions
diff --git a/lib/rainbows/dev_fd_response.rb b/lib/rainbows/dev_fd_response.rb
index 691526c..d839803 100644
--- a/lib/rainbows/dev_fd_response.rb
+++ b/lib/rainbows/dev_fd_response.rb
@@ -53,6 +53,8 @@ class Rainbows::DevFdResponse < Struct.new(:app)
       case env["rainbows.model"]
       when :FiberSpawn, :FiberPool, :RevFiberSpawn
         io = Rainbows::Fiber::IO.new(io,::Fiber.current)
+      when :Revactor
+        io = Rainbows::Revactor::Proxy.new(io)
       end
     else # unlikely, char/block device file, directory, ...
       return response
diff --git a/lib/rainbows/revactor.rb b/lib/rainbows/revactor.rb
index 0120ebe..8ec791d 100644
--- a/lib/rainbows/revactor.rb
+++ b/lib/rainbows/revactor.rb
@@ -22,6 +22,8 @@ module Rainbows::Revactor
   # :stopdoc:
   RD_ARGS = {}
 
+  autoload :Proxy, 'rainbows/revactor/proxy'
+
   include Rainbows::Base
   LOCALHOST = Unicorn::HttpRequest::LOCALHOST
   TCP = ::Revactor::TCP::Socket
diff --git a/lib/rainbows/revactor/proxy.rb b/lib/rainbows/revactor/proxy.rb
new file mode 100644
index 0000000..a7d3be1
--- /dev/null
+++ b/lib/rainbows/revactor/proxy.rb
@@ -0,0 +1,55 @@
+# -*- encoding: binary -*-
+# :enddoc:
+# Generic IO wrapper for proxying pipe and socket objects
+# this behaves more like Rainbows::Fiber::IO than anything,
+# making it highly suitable for proxying data from pipes/sockets
+class Rainbows::Revactor::Proxy < Rev::IO
+  def initialize(io)
+    @receiver = Actor.current
+    super(io)
+    attach(Rev::Loop.default)
+  end
+
+  def close
+    if @_io
+      super
+      @_io = nil
+    end
+  end
+
+  def each(&block)
+    # when yield-ing, Revactor::TCP#write may raise EOFError
+    # (instead of Errno::EPIPE), so we need to limit the rescue
+    # to just readpartial and let EOFErrors during yield bubble up
+    begin
+      buf = readpartial(INPUT_SIZE)
+    rescue EOFError
+      break
+    end while yield(buf) || true
+  end
+
+  # this may return more than the specified length, Rainbows! won't care...
+  def readpartial(length)
+    @receiver = Actor.current
+    enable if attached? && ! enabled?
+
+    Actor.receive do |filter|
+      filter.when(T[:rainbows_io_input, self]) do |_, _, data|
+        return data
+      end
+
+      filter.when(T[:rainbows_io_closed, self]) do
+        raise EOFError, "connection closed"
+      end
+    end
+  end
+
+  def on_close
+    @receiver << T[:rainbows_io_closed, self]
+  end
+
+  def on_read(data)
+    @receiver << T[:rainbows_io_input, self, data ]
+    disable
+  end
+end