preliminary NeverBlock support with EventMachine
[rainbows.git] / lib / rainbows.rb
blobc8f77c071c037cf680b4c9ea075327e5459abd69
1 # -*- encoding: binary -*-
2 require 'unicorn'
3 require 'rainbows/error'
4 require 'fcntl'
6 module Rainbows
8   # global vars because class/instance variables are confusing me :<
9   # this struct is only accessed inside workers and thus private to each
10   # G.cur may not be used in the network concurrency model
11   class State < Struct.new(:alive,:m,:cur,:kato,:server,:tmp)
12     def tick
13       tmp.chmod(self.m = m == 0 ? 1 : 0)
14       alive && server.master_pid == Process.ppid or quit!
15     end
17     def quit!
18       self.alive = false
19       server.class.const_get(:LISTENERS).map! { |s| s.close rescue nil }
20       false
21     end
22   end
23   G = State.new(true, 0, 0, 2)
24   O = {}
26   require 'rainbows/const'
27   require 'rainbows/http_server'
28   require 'rainbows/http_response'
29   require 'rainbows/base'
30   autoload :AppPool, 'rainbows/app_pool'
31   autoload :DevFdResponse, 'rainbows/dev_fd_response'
33   class << self
35     # runs the Rainbows! HttpServer with +app+ and +options+ and does
36     # not return until the server has exited.
37     def run(app, options = {})
38       HttpServer.new(app, options).start.join
39     end
40   end
42   # configures \Rainbows! with a given concurrency model to +use+ and
43   # a +worker_connections+ upper-bound.  This method may be called
44   # inside a Unicorn/Rainbows configuration file:
45   #
46   #   Rainbows! do
47   #     use :Revactor # this may also be :ThreadSpawn or :ThreadPool
48   #     worker_connections 400
49   #     keepalive_timeout 0 # zero disables keepalives entirely
50   #   end
51   #
52   #   # the rest of the Unicorn configuration
53   #   worker_processes 8
54   #
55   # See the documentation for the respective Revactor, ThreadSpawn,
56   # and ThreadPool classes for descriptions and recommendations for
57   # each of them.  The total number of clients we're able to serve is
58   # +worker_processes+ * +worker_connections+, so in the above example
59   # we can serve 8 * 400 = 3200 clients concurrently.
60   #
61   # The default is +keepalive_timeout+ is 2 seconds, which should be
62   # enough under most conditions for browsers to render the page and
63   # start retrieving extra elements for.  Increasing this beyond 5
64   # seconds is not recommended.  Zero disables keepalive entirely
65   # (but pipelining fully-formed requests is still works).
66   def Rainbows!(&block)
67     block_given? or raise ArgumentError, "Rainbows! requires a block"
68     HttpServer.setup(block)
69   end
71   # maps models to default worker counts, default worker count numbers are
72   # pretty arbitrary and tuning them to your application and hardware is
73   # highly recommended
74   MODEL_WORKER_CONNECTIONS = {
75     :Base => 1, # this one can't change
76     :Revactor => 50,
77     :ThreadSpawn => 30,
78     :ThreadPool => 10,
79     :Rev => 50,
80     :RevThreadSpawn => 50,
81     :EventMachine => 50,
82     :FiberSpawn => 50,
83     :FiberPool => 50,
84     :ActorSpawn => 50,
85     :NeverBlock => 50,
86   }.each do |model, _|
87     u = model.to_s.gsub(/([a-z0-9])([A-Z0-9])/) { "#{$1}_#{$2.downcase!}" }
88     autoload model, "rainbows/#{u.downcase!}"
89   end
90   autoload :Fiber, 'rainbows/fiber' # core class
92   # returns nil if accept fails
93   if defined?(Fcntl::FD_CLOEXEC)
94     def self.accept(sock)
95       rv = sock.accept_nonblock
96       rv.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
97       rv
98     rescue Errno::EAGAIN, Errno::ECONNABORTED
99     end
100   else
101     def self.accept(sock)
102       sock.accept_nonblock
103     rescue Errno::EAGAIN, Errno::ECONNABORTED
104     end
105   end
109 # inject the Rainbows! method into Unicorn::Configurator
110 Unicorn::Configurator.class_eval { include Rainbows }