about summary refs log tree commit homepage
path: root/lib/rainbows
diff options
context:
space:
mode:
authorEric Wong <normalperson@yhbt.net>2009-11-25 19:01:34 -0800
committerEric Wong <normalperson@yhbt.net>2009-11-25 19:07:01 -0800
commitdd2b2274ef0cd8a121cf7655ade939a1f63bc971 (patch)
tree053dbbd07b6dc3b1339f1ea19ad22afd8add52f3 /lib/rainbows
parent7f11b212f78a5070bea17bc20af43395b6cc621d (diff)
downloadrainbows-dd2b2274ef0cd8a121cf7655ade939a1f63bc971.tar.gz
This enables the safe use of Rainbows::AppPool with all
concurrency models, not just threaded ones.  AppPool is now
effective with *all* Fiber-based concurrency models including
Revactor (and of course the new Fiber{Pool,Spawn} ones).
Diffstat (limited to 'lib/rainbows')
-rw-r--r--lib/rainbows/app_pool.rb23
-rw-r--r--lib/rainbows/fiber.rb2
-rw-r--r--lib/rainbows/fiber/queue.rb33
3 files changed, 52 insertions, 6 deletions
diff --git a/lib/rainbows/app_pool.rb b/lib/rainbows/app_pool.rb
index f4c22a2..5c85e0a 100644
--- a/lib/rainbows/app_pool.rb
+++ b/lib/rainbows/app_pool.rb
@@ -42,11 +42,8 @@ module Rainbows
   # AppPool has no effect on the Rev or EventMachine concurrency models
   # as those are single-threaded/single-instance as far as application
   # concurrency goes.  In other words, +P+ is always +one+ when using
-  # Rev or EventMachine.  AppPool currently only works with the
-  # ThreadSpawn and ThreadPool models.  It does not yet work reliably
-  # with the Revactor model, but actors are far more lightweight and
-  # probably better suited for lightweight applications that would
-  # not benefit from AppPool.
+  # Rev or EventMachine.  As of \Rainbows! 0.7.0, it is safe to use with
+  # Revactor and the new FiberSpawn and FiberPool concurrency models.
   #
   # Since this is Rack middleware, you may load this in your Rack
   # config.ru file and even use it in threaded servers other than
@@ -60,7 +57,7 @@ module Rainbows
   # You may to load this earlier or later in your middleware chain
   # depending on the concurrency/copy-friendliness of your middleware(s).
 
-  class AppPool < Struct.new(:pool)
+  class AppPool < Struct.new(:pool, :re)
 
     # +opt+ is a hash, +:size+ is the size of the pool (default: 6)
     # meaning you can have up to 6 concurrent instances of +app+
@@ -86,6 +83,20 @@ module Rainbows
 
     # Rack application endpoint, +env+ is the Rack environment
     def call(env)
+
+      # we have to do this check at call time (and not initialize)
+      # because of preload_app=true and models being changeable with SIGHUP
+      # fortunately this is safe for all the reentrant (but not multithreaded)
+      # classes that depend on it and a safe no-op for multithreaded
+      # concurrency models
+      self.re ||= begin
+        case env["rainbows.model"]
+        when :FiberSpawn, :FiberPool, :Revactor
+          self.pool = Rainbows::Fiber::Queue.new(pool)
+        end
+        true
+      end
+
       app = pool.shift
       app.call(env)
       ensure
diff --git a/lib/rainbows/fiber.rb b/lib/rainbows/fiber.rb
index 94502a3..f0755aa 100644
--- a/lib/rainbows/fiber.rb
+++ b/lib/rainbows/fiber.rb
@@ -4,6 +4,8 @@ require 'rainbows/fiber/io'
 
 module Rainbows
   module Fiber
+    autoload :Queue, 'rainbows/fiber/queue'
+
     RD = {}
     WR = {}
     ZZ = {}
diff --git a/lib/rainbows/fiber/queue.rb b/lib/rainbows/fiber/queue.rb
new file mode 100644
index 0000000..4c14f19
--- /dev/null
+++ b/lib/rainbows/fiber/queue.rb
@@ -0,0 +1,33 @@
+module Rainbows
+  module Fiber
+
+    # a self-sufficient Queue implmentation for Fiber-based concurrency
+    # models
+    class Queue < Struct.new(:queue, :waiters)
+
+      def initialize(queue = [], waiters = [])
+        # move elements of the Queue into an Array
+        if queue.class.name == "Queue"
+          queue = queue.length.times.map { queue.pop }
+        end
+        super queue, waiters
+      end
+
+      def shift
+        # ah the joys of not having to deal with race conditions
+        if queue.empty?
+          waiters << ::Fiber.current
+          ::Fiber.yield
+        end
+        queue.shift
+      end
+
+      def <<(obj)
+        queue << obj
+        blocked = waiters.shift and blocked.resume
+        queue # not quite 100% compatible but no-one's looking :>
+      end
+
+    end
+  end
+end