rainbows 3.2.0 → 3.3.0

Sign up to get free protection for your applications and to get access to all the features.
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