rainbows 3.2.0 → 3.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. data/.document +1 -0
  2. data/COPYING +617 -282
  3. data/Documentation/comparison.haml +81 -24
  4. data/FAQ +3 -0
  5. data/GIT-VERSION-GEN +1 -1
  6. data/LICENSE +14 -5
  7. data/README +10 -9
  8. data/Sandbox +25 -0
  9. data/TODO +2 -22
  10. data/lib/rainbows.rb +50 -49
  11. data/lib/rainbows/client.rb +6 -5
  12. data/lib/rainbows/configurator.rb +191 -37
  13. data/lib/rainbows/const.rb +1 -1
  14. data/lib/rainbows/coolio.rb +4 -1
  15. data/lib/rainbows/coolio/client.rb +2 -2
  16. data/lib/rainbows/coolio/heartbeat.rb +2 -1
  17. data/lib/rainbows/coolio_fiber_spawn.rb +12 -7
  18. data/lib/rainbows/coolio_thread_pool.rb +19 -10
  19. data/lib/rainbows/coolio_thread_spawn.rb +3 -0
  20. data/lib/rainbows/epoll.rb +27 -5
  21. data/lib/rainbows/epoll/client.rb +3 -3
  22. data/lib/rainbows/ev_core.rb +2 -1
  23. data/lib/rainbows/event_machine.rb +4 -0
  24. data/lib/rainbows/event_machine/client.rb +2 -1
  25. data/lib/rainbows/fiber.rb +5 -0
  26. data/lib/rainbows/fiber/base.rb +1 -0
  27. data/lib/rainbows/fiber/coolio/methods.rb +0 -1
  28. data/lib/rainbows/fiber/io.rb +10 -6
  29. data/lib/rainbows/fiber/io/pipe.rb +6 -1
  30. data/lib/rainbows/fiber/io/socket.rb +6 -1
  31. data/lib/rainbows/fiber_pool.rb +12 -7
  32. data/lib/rainbows/fiber_spawn.rb +11 -6
  33. data/lib/rainbows/http_server.rb +55 -59
  34. data/lib/rainbows/join_threads.rb +4 -0
  35. data/lib/rainbows/max_body.rb +29 -10
  36. data/lib/rainbows/never_block.rb +7 -10
  37. data/lib/rainbows/pool_size.rb +14 -0
  38. data/lib/rainbows/process_client.rb +23 -1
  39. data/lib/rainbows/queue_pool.rb +8 -6
  40. data/lib/rainbows/response.rb +12 -11
  41. data/lib/rainbows/revactor.rb +14 -7
  42. data/lib/rainbows/revactor/client.rb +2 -2
  43. data/lib/rainbows/stream_file.rb +11 -4
  44. data/lib/rainbows/thread_pool.rb +12 -28
  45. data/lib/rainbows/thread_spawn.rb +14 -13
  46. data/lib/rainbows/thread_timeout.rb +118 -30
  47. data/lib/rainbows/writer_thread_pool/client.rb +1 -1
  48. data/lib/rainbows/writer_thread_spawn/client.rb +2 -2
  49. data/lib/rainbows/xepoll.rb +13 -5
  50. data/lib/rainbows/xepoll/client.rb +19 -17
  51. data/lib/rainbows/xepoll_thread_pool.rb +82 -0
  52. data/lib/rainbows/xepoll_thread_pool/client.rb +129 -0
  53. data/lib/rainbows/xepoll_thread_spawn.rb +58 -0
  54. data/lib/rainbows/xepoll_thread_spawn/client.rb +121 -0
  55. data/pkg.mk +4 -0
  56. data/rainbows.gemspec +4 -1
  57. data/t/GNUmakefile +5 -1
  58. data/t/client_header_buffer_size.ru +5 -0
  59. data/t/simple-http_XEpollThreadPool.ru +10 -0
  60. data/t/simple-http_XEpollThreadSpawn.ru +10 -0
  61. data/t/t0022-copy_stream-byte-range.sh +1 -15
  62. data/t/t0026-splice-copy_stream-byte-range.sh +25 -0
  63. data/t/t0027-nil-copy_stream.sh +60 -0
  64. data/t/t0041-optional-pool-size.sh +2 -2
  65. data/t/t0042-client_header_buffer_size.sh +65 -0
  66. data/t/t9100-thread-timeout.sh +1 -6
  67. data/t/t9101-thread-timeout-threshold.sh +1 -6
  68. data/t/test-lib.sh +58 -0
  69. data/t/test_isolate.rb +9 -3
  70. metadata +47 -16
@@ -4,19 +4,22 @@ require "io/wait"
4
4
 
5
5
  # this class is used for most synchronous concurrency models
6
6
  class Rainbows::Client < Kgio::Socket
7
+ include Rainbows::ProcessClient
8
+ Rainbows.config!(self, :keepalive_timeout)
9
+
7
10
  def read_expire
8
- Time.now + Rainbows.keepalive_timeout
11
+ Time.now + KEEPALIVE_TIMEOUT
9
12
  end
10
13
 
11
14
  def kgio_wait_readable
12
- wait Rainbows.keepalive_timeout
15
+ wait KEEPALIVE_TIMEOUT
13
16
  end
14
17
 
15
18
  # used for reading headers (respecting keepalive_timeout)
16
19
  def timed_read(buf)
17
20
  expire = nil
18
21
  begin
19
- case rv = kgio_tryread(16384, buf)
22
+ case rv = kgio_tryread(CLIENT_HEADER_BUFFER_SIZE, buf)
20
23
  when :wait_readable
21
24
  return if expire && expire < Time.now
22
25
  expire ||= read_expire
@@ -26,6 +29,4 @@ class Rainbows::Client < Kgio::Socket
26
29
  end
27
30
  end while true
28
31
  end
29
-
30
- include Rainbows::ProcessClient
31
32
  end
@@ -2,49 +2,203 @@
2
2
 
3
3
  # This module adds \Rainbows! to the
4
4
  # {Unicorn::Configurator}[http://unicorn.bogomips.org/Unicorn/Configurator.html]
5
+ # \Rainbows!-specific configuration options must be inside a the Rainbows!
6
+ # block, otherwise Unicorn::Configurator directives may be used anwwhere
7
+ # in the file.
8
+ #
9
+ # Rainbows! do
10
+ # use :ThreadSpawn # concurrency model to use
11
+ # worker_connections 400
12
+ # keepalive_timeout 0 # zero disables keepalives entirely
13
+ # client_max_body_size 5*1024*1024 # 5 megabytes
14
+ # keepalive_requests 666 # default:100
15
+ # client_header_buffer_size 2 * 1024 # 2 kilobytes
16
+ # end
17
+ #
18
+ # # the rest of the Unicorn configuration...
19
+ # worker_processes 8
20
+ # stderr_path "/path/to/error.log"
21
+ # stdout_path "/path/to/output.log"
5
22
  module Rainbows::Configurator
23
+ Unicorn::Configurator::DEFAULTS.merge!({
24
+ :use => Rainbows::Base,
25
+ :worker_connections => 50,
26
+ :keepalive_timeout => 5,
27
+ :keepalive_requests => 100,
28
+ :client_max_body_size => 1024 * 1024,
29
+ :client_header_buffer_size => 1024,
30
+ :copy_stream => IO.respond_to?(:copy_stream) ? IO : false,
31
+ })
6
32
 
7
- # configures \Rainbows! with a given concurrency model to +use+ and
8
- # a +worker_connections+ upper-bound. This method may be called
9
- # inside a Unicorn/\Rainbows! configuration file:
33
+ # Configures \Rainbows! with a given concurrency model to +use+ and
34
+ # a +worker_connections+ upper-bound. This method should be called
35
+ # inside a Unicorn/\Rainbows! configuration file.
36
+ #
37
+ # All other methods in Rainbows::Configurator must be called
38
+ # inside this block.
39
+ def Rainbows!(&block)
40
+ block_given? or raise ArgumentError, "Rainbows! requires a block"
41
+ @block = true
42
+ instance_eval(&block)
43
+ ensure
44
+ @block = false
45
+ end
46
+
47
+ def check! # :nodoc:
48
+ @block or abort "must be inside a Rainbows! block"
49
+ end
50
+
51
+ # This limits the number of connected clients per-process. The total
52
+ # number of clients on a server is +worker_processes+ * +worker_connections+.
53
+ #
54
+ # This option has no effect with the Base concurrency model, which is
55
+ # limited to +1+.
56
+ #
57
+ # Default: 50
58
+ def worker_connections(clients)
59
+ check!
60
+ set_int(:worker_connections, clients, 1)
61
+ end
62
+
63
+ # Select a concurrency model for use with \Rainbows!. You must select
64
+ # this with a Symbol (prefixed with ":"). Thus if you wish to select
65
+ # the Rainbows::ThreadSpawn concurrency model, you would use:
10
66
  #
11
67
  # Rainbows! do
12
- # use :ThreadSpawn # concurrency model to use
13
- # worker_connections 400
14
- # keepalive_timeout 0 # zero disables keepalives entirely
15
- # client_max_body_size 5*1024*1024 # 5 megabytes
16
- # keepalive_requests 666 # default:100
68
+ # use :ThreadSpawn
17
69
  # end
18
70
  #
19
- # # the rest of the Unicorn configuration
20
- # worker_processes 8
21
- #
22
- # See the documentation for the respective Revactor, ThreadSpawn,
23
- # and ThreadPool classes for descriptions and recommendations for
24
- # each of them. The total number of clients we're able to serve is
25
- # +worker_processes+ * +worker_connections+, so in the above example
26
- # we can serve 8 * 400 = 3200 clients concurrently.
27
- #
28
- # The default is +keepalive_timeout+ is 5 seconds, which should be
29
- # enough under most conditions for browsers to render the page and
30
- # start retrieving extra elements for. Increasing this beyond 5
31
- # seconds is not recommended. Zero disables keepalive entirely
32
- # (but pipelining fully-formed requests is still works).
33
- #
34
- # The default +client_max_body_size+ is 1 megabyte (1024 * 1024 bytes),
35
- # setting this to +nil+ will disable body size checks and allow any
36
- # size to be specified.
37
- #
38
- # The default +keepalive_requests+ is 100, meaning a client may
39
- # complete 100 keepalive requests after the initial request before
40
- # \Rainbows! forces a disconnect. Lowering this can improve
41
- # load-balancing characteristics as it forces HTTP/1.1 clients to
42
- # reconnect after the specified number of requests, hopefully to a
43
- # less busy host or worker process. This may also be used to mitigate
44
- # denial-of-service attacks that use HTTP pipelining.
45
- def Rainbows!(&block)
46
- block_given? or raise ArgumentError, "Rainbows! requires a block"
47
- Rainbows::HttpServer.setup(block)
71
+ # See the {Summary}[link:Summary.html] document for a summary of
72
+ # supported concurrency models. +options+ may be specified for some
73
+ # concurrency models, but the majority do not support them.
74
+ #
75
+ # Default: :Base (no concurrency)
76
+ def use(model, *options)
77
+ check!
78
+ mod = begin
79
+ Rainbows.const_get(model)
80
+ rescue NameError => e
81
+ warn "error loading #{model.inspect}: #{e}"
82
+ e.backtrace.each { |l| warn l }
83
+ abort "concurrency model #{model.inspect} not supported"
84
+ end
85
+ Module === mod or abort "concurrency model #{model.inspect} not supported"
86
+ options.each do |opt|
87
+ case opt
88
+ when Hash
89
+ Rainbows::O.merge!(opt)
90
+ when Symbol
91
+ Rainbows::O[opt] = true
92
+ else
93
+ abort "cannot handle option: #{opt.inspect} in #{options.inspect}"
94
+ end
95
+ end
96
+ mod.setup if mod.respond_to?(:setup)
97
+ set[:use] = mod
98
+ end
99
+
100
+ # Sets the value (in seconds) the server will wait for a client in
101
+ # between requests. The default value should be enough under most
102
+ # conditions for browsers to render the page and start retrieving
103
+ # extra elements.
104
+ #
105
+ # Setting this value to +0+ disables keepalive entirely
106
+ #
107
+ # Default: 5 seconds
108
+ def keepalive_timeout(seconds)
109
+ check!
110
+ set_int(:keepalive_timeout, seconds, 0)
111
+ end
112
+
113
+ # This limits the number of requests which can be made over a keep-alive
114
+ # connection. This is used to prevent single client from monopolizing
115
+ # the server and to improve fairness when load-balancing across multiple
116
+ # machines by forcing a client to reconnect. This may be helpful
117
+ # in mitigating some denial-of-service attacks.
118
+ #
119
+ # Default: 100 requests
120
+ def keepalive_requests(count)
121
+ check!
122
+ case count
123
+ when nil, Integer
124
+ set[:keepalive_requests] = count
125
+ else
126
+ abort "not an integer or nil: keepalive_requests=#{count.inspect}"
127
+ end
128
+ end
129
+
130
+ # Limits the maximum size of a request body for all requests.
131
+ # Setting this to +nil+ disables the maximum size check.
132
+ #
133
+ # Default: 1 megabyte (1048576 bytes)
134
+ #
135
+ # If you want endpoint-specific upload limits and use a
136
+ # "rack.input"-streaming concurrency model, see the Rainbows::MaxBody
137
+ def client_max_body_size(bytes)
138
+ check!
139
+ err = "client_max_body_size must be nil or a non-negative Integer"
140
+ case bytes
141
+ when nil
142
+ when Integer
143
+ bytes >= 0 or abort err
144
+ else
145
+ abort err
146
+ end
147
+ set[:client_max_body_size] = bytes
148
+ end
149
+
150
+ # This governs the amount of memory allocated for an individual read(2) or
151
+ # recv(2) system call when reading headers. Applications that make minimal
152
+ # use of cookies should not increase this from the default.
153
+ #
154
+ # Rails applications using session cookies may want to increase this to
155
+ # 2048 bytes or more depending on expected request sizes.
156
+ #
157
+ # Increasing this will increase overall memory usage to your application,
158
+ # as you will need at least this amount of memory for every connected client.
159
+ #
160
+ # Default: 1024 bytes
161
+ def client_header_buffer_size(bytes)
162
+ check!
163
+ set_int(:client_header_buffer_size, bytes, 1)
164
+ end
165
+
166
+ # Allows overriding the +klass+ where the +copy_stream+ method is
167
+ # used to do efficient copying of regular files, pipes, and sockets.
168
+ #
169
+ # This is only used with multi-threaded concurrency models:
170
+ #
171
+ # * ThreadSpawn
172
+ # * ThreadPool
173
+ # * WriterThreadSpawn
174
+ # * WriterThreadPool
175
+ # * XEpollThreadSpawn
176
+ # * XEpollThreadPool
177
+ #
178
+ # Due to existing {bugs}[http://redmine.ruby-lang.org/search?q=copy_stream]
179
+ # in the Ruby IO.copy_stream implementation, \Rainbows! uses the
180
+ # "sendfile" RubyGem that instead of copy_stream to transfer regular files
181
+ # to clients. The "sendfile" RubyGem also supports more operating systems,
182
+ # and works with more concurrency models.
183
+ #
184
+ # Recent Linux 2.6 users may override this with "IO::Splice" from the
185
+ # "io_splice" RubyGem:
186
+ #
187
+ # require "io/splice"
188
+ # Rainbows! do
189
+ # copy_stream IO::Splice
190
+ # end
191
+ #
192
+ # Keep in mind that splice(2) itself is a relatively new system call
193
+ # and has been buggy in many older Linux kernels.
194
+ #
195
+ # Default: IO on Ruby 1.9+, false otherwise
196
+ def copy_stream(klass)
197
+ check!
198
+ if klass && ! klass.respond_to?(:copy_stream)
199
+ abort "#{klass} must respond to `copy_stream' or be `false'"
200
+ end
201
+ set[:copy_stream] = klass
48
202
  end
49
203
  end
50
204
 
@@ -2,7 +2,7 @@
2
2
  # :enddoc:
3
3
  module Rainbows::Const
4
4
 
5
- RAINBOWS_VERSION = '3.2.0'
5
+ RAINBOWS_VERSION = '3.3.0'
6
6
 
7
7
  include Unicorn::Const
8
8
 
@@ -21,6 +21,9 @@ require 'rainbows/coolio_support'
21
21
  #
22
22
  # This model is mostly compatible with users of "async.callback" in
23
23
  # the Rack environment as long as they do not depend on EventMachine.
24
+ #
25
+ # === RubyGem Requirements
26
+ # * cool.io 1.0.0 or later
24
27
  module Rainbows::Coolio
25
28
  # :stopdoc:
26
29
  # keep-alive timeout scoreboard
@@ -39,10 +42,10 @@ module Rainbows::Coolio
39
42
  autoload :ThreadClient, 'rainbows/coolio/thread_client'
40
43
  autoload :ResponsePipe, 'rainbows/coolio/response_pipe'
41
44
  autoload :ResponseChunkPipe, 'rainbows/coolio/response_chunk_pipe'
45
+ autoload :Heartbeat, 'rainbows/coolio/heartbeat'
42
46
  # :startdoc:
43
47
  end
44
48
  # :enddoc:
45
- require 'rainbows/coolio/heartbeat'
46
49
  require 'rainbows/coolio/server'
47
50
  require 'rainbows/coolio/core'
48
51
  Rainbows::Coolio.__send__ :include, Rainbows::Coolio::Core
@@ -45,7 +45,7 @@ class Rainbows::Coolio::Client < Coolio::IO
45
45
  end
46
46
 
47
47
  def on_readable
48
- buf = @_io.kgio_tryread(16384, RBUF)
48
+ buf = @_io.kgio_tryread(CLIENT_HEADER_BUFFER_SIZE, RBUF)
49
49
  case buf
50
50
  when :wait_readable
51
51
  when nil # eof
@@ -134,7 +134,7 @@ class Rainbows::Coolio::Client < Coolio::IO
134
134
  close if @_write_buffer.empty?
135
135
  when :headers
136
136
  if @buf.empty?
137
- buf = @_io.kgio_tryread(16384, RBUF) or return close
137
+ buf = @_io.kgio_tryread(CLIENT_HEADER_BUFFER_SIZE, RBUF) or return close
138
138
  String === buf and return on_read(buf)
139
139
  # buf == :wait_readable
140
140
  unless enabled?
@@ -8,9 +8,10 @@
8
8
  class Rainbows::Coolio::Heartbeat < Coolio::TimerWatcher
9
9
  KATO = Rainbows::Coolio::KATO
10
10
  CONN = Rainbows::Coolio::CONN
11
+ Rainbows.config!(self, :keepalive_timeout)
11
12
 
12
13
  def on_timer
13
- if (ot = Rainbows.keepalive_timeout) >= 0
14
+ if (ot = KEEPALIVE_TIMEOUT) >= 0
14
15
  ot = Time.now - ot
15
16
  KATO.delete_if { |client, time| time < ot and client.timeout? }
16
17
  end
@@ -1,13 +1,17 @@
1
1
  # -*- encoding: binary -*-
2
2
  require 'rainbows/fiber/coolio'
3
3
 
4
- # A combination of the Coolio and FiberSpawn models. This allows Ruby
5
- # 1.9 Fiber-based concurrency for application processing while exposing
6
- # a synchronous execution model and using scalable network concurrency
7
- # provided by Cool.io. A streaming "rack.input" is exposed.
8
- # Applications are strongly advised to wrap all slow IO objects
9
- # (sockets, pipes) using the Rainbows::Fiber::IO or a Cool.io-compatible
10
- # class whenever possible.
4
+ # A combination of the Coolio and FiberSpawn models.
5
+ #
6
+ # This concurrency model is difficult to use with existing applications,
7
+ # lacks third-party support, and is thus NOT recommended.
8
+ #
9
+ # This allows Ruby 1.9 Fiber-based concurrency for application
10
+ # processing while exposing a synchronous execution model and using
11
+ # scalable network concurrency provided by Cool.io. A streaming
12
+ # "rack.input" is exposed. Applications are strongly advised to wrap
13
+ # all slow IO objects (sockets, pipes) using the Rainbows::Fiber::IO or
14
+ # a Cool.io-compatible class whenever possible.
11
15
  module Rainbows::CoolioFiberSpawn
12
16
 
13
17
  include Rainbows::Base
@@ -21,6 +25,7 @@ module Rainbows::CoolioFiberSpawn
21
25
  Server.const_set(:APP, Rainbows.server.app)
22
26
  Heartbeat.new(1, true).attach(Coolio::Loop.default)
23
27
  LISTENERS.map! { |s| Server.new(s).attach(Coolio::Loop.default) }
28
+ Rainbows::Client.__send__ :include, Rainbows::Fiber::Coolio::Methods
24
29
  Coolio::Loop.default.run
25
30
  end
26
31
  end
@@ -14,20 +14,29 @@
14
14
  #
15
15
  # This concurrency model is designed for Ruby 1.9, and Ruby 1.8
16
16
  # users are NOT advised to use this due to high CPU usage.
17
+ #
18
+ # === :pool_size vs worker_connections
19
+ #
20
+ # In your Rainbows! config block, you may specify a Thread pool size
21
+ # to limit your application concurrency independently of
22
+ # worker_connections.
23
+ #
24
+ # Rainbows! do
25
+ # use :CoolioThreadPool, :pool_size => 50
26
+ # worker_connections 100
27
+ # end
28
+ #
29
+ # In extremely rare cases, this may be combined with Rainbows::AppPool
30
+ # if you have different concurrency capabilities for different parts of
31
+ # your Rack application.
32
+ #
33
+ # === RubyGem Requirements
34
+ # * cool.io 1.0.0 or later
17
35
  module Rainbows::CoolioThreadPool
18
36
  # :stopdoc:
19
37
  autoload :Client, 'rainbows/coolio_thread_pool/client'
20
- DEFAULTS = {
21
- :pool_size => 20, # same default size as ThreadPool (w/o Coolio)
22
- }
38
+ extend Rainbows::PoolSize
23
39
  #:startdoc:
24
-
25
- def self.setup # :nodoc:
26
- o = Rainbows::O
27
- DEFAULTS.each { |k,v| o[k] ||= v }
28
- Integer === o[:pool_size] && o[:pool_size] > 0 or
29
- raise ArgumentError, "pool_size must a be an Integer > 0"
30
- end
31
40
  include Rainbows::Coolio::Core
32
41
 
33
42
  def init_worker_threads(master, queue) # :nodoc:
@@ -13,6 +13,9 @@
13
13
  #
14
14
  # This concurrency model is designed for Ruby 1.9, and Ruby 1.8
15
15
  # users are NOT advised to use this due to high CPU usage.
16
+ #
17
+ # === RubyGem Requirements
18
+ # * cool.io 1.0.0 or later
16
19
  module Rainbows::CoolioThreadSpawn
17
20
  include Rainbows::Coolio::Core
18
21
  autoload :Client, 'rainbows/coolio_thread_spawn/client'
@@ -1,11 +1,32 @@
1
1
  # -*- encoding: binary -*-
2
- # :enddoc:
3
2
  require 'sleepy_penguin'
4
3
  require 'sendfile'
5
4
 
6
- # Edge-triggered epoll concurrency model. This is extremely unfair
7
- # and optimized for throughput at the expense of fairness
5
+ # Edge-triggered epoll concurrency model using
6
+ # {sleepy_penguin}[http://bogomips.org/sleepy_penguin/] for epoll.
7
+ #
8
+ # Unlike more portable options like Coolio and EventMachine, this
9
+ # is Linux-only, but uses edge-triggering instead of level-triggering,
10
+ # so it may perform better in some cases. Coolio and EventMachine have
11
+ # better library support and may be widely-used, however.
12
+ #
13
+ # Consider using XEpoll instead of this if you are using Ruby 1.9,
14
+ # it will avoid accept()-scalability issues with many worker processes.
15
+ #
16
+ # When serving static files, this is extremely unfair and optimized
17
+ # for throughput at the expense of fairness. This is not an issue
18
+ # if you're not serving static files, or if your working set is
19
+ # small enough to aways be in your kernel page cache. This concurrency
20
+ # model may starve clients if you have slow disks and large static files.
21
+ #
22
+ # === RubyGem Requirements
23
+ #
24
+ # * raindrops 0.6.0 or later
25
+ # * sleepy_penguin 2.0.0 or later
26
+ # * sendfile 1.1.0 or later
27
+ #
8
28
  module Rainbows::Epoll
29
+ # :stopdoc:
9
30
  include Rainbows::Base
10
31
  ReRun = []
11
32
  autoload :Server, 'rainbows/epoll/server'
@@ -17,9 +38,8 @@ module Rainbows::Epoll
17
38
  end
18
39
 
19
40
  def self.loop
20
- timeout = Rainbows.server.timeout
21
41
  begin
22
- EP.wait(nil, timeout) { |flags, obj| obj.epoll_run }
42
+ EP.wait(nil, 1000) { |_, obj| obj.epoll_run }
23
43
  while obj = ReRun.shift
24
44
  obj.epoll_run
25
45
  end
@@ -33,6 +53,7 @@ module Rainbows::Epoll
33
53
  def init_worker_process(worker)
34
54
  super
35
55
  Rainbows::Epoll.const_set :EP, SleepyPenguin::Epoll.new
56
+ Rainbows.at_quit { Rainbows::Epoll::EP.close }
36
57
  Rainbows::Client.__send__ :include, Client
37
58
  end
38
59
 
@@ -40,4 +61,5 @@ module Rainbows::Epoll
40
61
  init_worker_process(worker)
41
62
  Server.run
42
63
  end
64
+ # :startdoc:
43
65
  end