rainbows 0.7.0 → 0.8.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.
- data/Documentation/comparison.haml +22 -5
- data/GIT-VERSION-GEN +1 -1
- data/TODO +3 -2
- data/lib/rainbows.rb +4 -1
- data/lib/rainbows/base.rb +5 -5
- data/lib/rainbows/const.rb +1 -1
- data/lib/rainbows/rev/master.rb +29 -0
- data/lib/rainbows/rev/thread.rb +53 -0
- data/lib/rainbows/rev_thread_pool.rb +75 -0
- data/lib/rainbows/rev_thread_spawn.rb +13 -70
- data/lib/rainbows/revactor.rb +38 -30
- data/rainbows.1 +0 -0
- data/t/GNUmakefile +1 -0
- data/t/simple-http_RevThreadPool.ru +9 -0
- metadata +11 -2
@@ -85,13 +85,17 @@
|
|
85
85
|
%td.r19 Yes
|
86
86
|
%td.rbx No
|
87
87
|
%td.slow Yes
|
88
|
+
%tr.comp_row
|
89
|
+
%td.mod RevThreadPool
|
90
|
+
%td.tee No
|
91
|
+
%td.r18 Slow*
|
92
|
+
%td.r19 Yes
|
93
|
+
%td.rbx No
|
94
|
+
%td.slow Yes
|
88
95
|
%ul
|
89
96
|
%li
|
90
|
-
|
91
|
-
|
92
|
-
%a(href="http://rubyforge.org/mailman/listinfo/rev-talk")
|
93
|
-
rev-talk mailing list
|
94
|
-
for details.
|
97
|
+
RevThread* + 1.8 performance is bad with Rev <= 0.3.1.
|
98
|
+
Rev 0.3.2 (when it is released) should be much faster under 1.8.
|
95
99
|
%li
|
96
100
|
waiting on Rubinius for better signal handling
|
97
101
|
%li
|
@@ -180,6 +184,13 @@
|
|
180
184
|
%a(href="http://rubyeventmachine.com") EventMachine
|
181
185
|
%td.thr No
|
182
186
|
%td.reent Yes
|
187
|
+
%tr.comp_row
|
188
|
+
%td.mod RevThreadPool
|
189
|
+
%td.slowio
|
190
|
+
thread-safe Ruby,
|
191
|
+
%a(href="http://rev.rubyforge.org/") Rev
|
192
|
+
%td.thr Yes
|
193
|
+
%td.reent No
|
183
194
|
|
184
195
|
%ul
|
185
196
|
%li
|
@@ -276,6 +287,12 @@
|
|
276
287
|
%td.app_pool Yes*
|
277
288
|
%td.lock Yes*
|
278
289
|
%td.async NeverBlock, async_sinatra
|
290
|
+
%tr.comp_row
|
291
|
+
%td.mod RevThreadPool
|
292
|
+
%td.devfd Yes
|
293
|
+
%td.app_pool Yes
|
294
|
+
%td.lock Dumb
|
295
|
+
%td.async standard Ruby
|
279
296
|
|
280
297
|
%ul
|
281
298
|
%li
|
data/GIT-VERSION-GEN
CHANGED
data/TODO
CHANGED
@@ -9,9 +9,10 @@ care about.
|
|
9
9
|
|
10
10
|
* EventMachine.spawn - should be like Revactor, maybe?
|
11
11
|
|
12
|
-
*
|
12
|
+
* conditional app.deferred?(env) support
|
13
|
+
Merb uses it, some other servers support it
|
13
14
|
|
14
|
-
* {
|
15
|
+
* {Rev,EventMachine}+Fibers+streaming input
|
15
16
|
|
16
17
|
* Rev + callcc - current Rev model with callcc (should work with MBARI)
|
17
18
|
|
data/lib/rainbows.rb
CHANGED
@@ -8,14 +8,16 @@ module Rainbows
|
|
8
8
|
# global vars because class/instance variables are confusing me :<
|
9
9
|
# this struct is only accessed inside workers and thus private to each
|
10
10
|
# G.cur may not be used in the network concurrency model
|
11
|
-
class State < Struct.new(:alive,:m,:cur,:kato,:server,:tmp)
|
11
|
+
class State < Struct.new(:alive,:m,:cur,:kato,:server,:tmp,:expire)
|
12
12
|
def tick
|
13
13
|
tmp.chmod(self.m = m == 0 ? 1 : 0)
|
14
|
+
exit!(2) if expire && Time.now >= expire
|
14
15
|
alive && server.master_pid == Process.ppid or quit!
|
15
16
|
end
|
16
17
|
|
17
18
|
def quit!
|
18
19
|
self.alive = false
|
20
|
+
self.expire ||= Time.now + (server.timeout * 2.0)
|
19
21
|
server.class.const_get(:LISTENERS).map! { |s| s.close rescue nil }
|
20
22
|
false
|
21
23
|
end
|
@@ -78,6 +80,7 @@ module Rainbows
|
|
78
80
|
:ThreadPool => 10,
|
79
81
|
:Rev => 50,
|
80
82
|
:RevThreadSpawn => 50,
|
83
|
+
:RevThreadPool => 50,
|
81
84
|
:EventMachine => 50,
|
82
85
|
:FiberSpawn => 50,
|
83
86
|
:FiberPool => 50,
|
data/lib/rainbows/base.rb
CHANGED
@@ -70,11 +70,11 @@ module Rainbows
|
|
70
70
|
end
|
71
71
|
|
72
72
|
def join_threads(threads)
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
73
|
+
G.quit!
|
74
|
+
threads.delete_if do |thr|
|
75
|
+
G.tick
|
76
|
+
thr.alive? ? thr.join(0.01) : true
|
77
|
+
end until threads.empty?
|
78
78
|
end
|
79
79
|
|
80
80
|
def self.included(klass)
|
data/lib/rainbows/const.rb
CHANGED
@@ -0,0 +1,29 @@
|
|
1
|
+
# -*- encoding: binary -*-
|
2
|
+
require 'rainbows/rev'
|
3
|
+
|
4
|
+
RUBY_VERSION =~ %r{\A1\.8} && ::Rev::VERSION < "0.3.2" and
|
5
|
+
warn "Rainbows::RevThreadSpawn + Rev (< 0.3.2)" \
|
6
|
+
" does not work well under Ruby 1.8"
|
7
|
+
|
8
|
+
module Rainbows
|
9
|
+
|
10
|
+
module Rev
|
11
|
+
class Master < ::Rev::AsyncWatcher
|
12
|
+
|
13
|
+
def initialize(queue)
|
14
|
+
super()
|
15
|
+
@queue = queue
|
16
|
+
end
|
17
|
+
|
18
|
+
def <<(output)
|
19
|
+
@queue << output
|
20
|
+
signal
|
21
|
+
end
|
22
|
+
|
23
|
+
def on_signal
|
24
|
+
client, response = @queue.pop
|
25
|
+
client.response_write(response)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# -*- encoding: binary -*-
|
2
|
+
require 'thread'
|
3
|
+
require 'rainbows/rev/master'
|
4
|
+
|
5
|
+
module Rainbows
|
6
|
+
module Rev
|
7
|
+
|
8
|
+
class ThreadClient < Client
|
9
|
+
|
10
|
+
def app_call
|
11
|
+
KATO.delete(self)
|
12
|
+
disable
|
13
|
+
@env[RACK_INPUT] = @input
|
14
|
+
@input = nil # not sure why, @input seems to get closed otherwise...
|
15
|
+
app_dispatch # must be implemented by subclass
|
16
|
+
end
|
17
|
+
|
18
|
+
# this is only called in the master thread
|
19
|
+
def response_write(response)
|
20
|
+
enable
|
21
|
+
alive = @hp.keepalive? && G.alive
|
22
|
+
out = [ alive ? CONN_ALIVE : CONN_CLOSE ] if @hp.headers?
|
23
|
+
DeferredResponse.write(self, response, out)
|
24
|
+
return quit unless alive && G.alive
|
25
|
+
|
26
|
+
@env.clear
|
27
|
+
@hp.reset
|
28
|
+
@state = :headers
|
29
|
+
# keepalive requests are always body-less, so @input is unchanged
|
30
|
+
if @hp.headers(@env, @buf)
|
31
|
+
@input = HttpRequest::NULL_IO
|
32
|
+
app_call
|
33
|
+
else
|
34
|
+
KATO[self] = Time.now
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# fails-safe application dispatch, we absolutely cannot
|
39
|
+
# afford to fail or raise an exception (killing the thread)
|
40
|
+
# here because that could cause a deadlock and we'd leak FDs
|
41
|
+
def app_response
|
42
|
+
begin
|
43
|
+
@env[REMOTE_ADDR] = @remote_addr
|
44
|
+
APP.call(@env.update(RACK_DEFAULTS))
|
45
|
+
rescue => e
|
46
|
+
Error.app(e) # we guarantee this does not raise
|
47
|
+
[ 500, {}, [] ]
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
# -*- encoding: binary -*-
|
2
|
+
require 'rainbows/rev/thread'
|
3
|
+
|
4
|
+
module Rainbows
|
5
|
+
|
6
|
+
# A combination of the Rev and ThreadPool models. This allows Ruby
|
7
|
+
# Thread-based concurrency for application processing. It DOES NOT
|
8
|
+
# expose a streamable "rack.input" for upload processing within the
|
9
|
+
# app. DevFdResponse should be used with this class to proxy
|
10
|
+
# asynchronous responses. All network I/O between the client and
|
11
|
+
# server are handled by the main thread and outside of the core
|
12
|
+
# application dispatch.
|
13
|
+
#
|
14
|
+
# Unlike ThreadPool, Rev makes this model highly suitable for
|
15
|
+
# slow clients and applications with medium-to-slow response times
|
16
|
+
# (I/O bound), but less suitable for sleepy applications.
|
17
|
+
#
|
18
|
+
# WARNING: this model does not currently perform well under 1.8 with
|
19
|
+
# Rev 0.3.1. Rev 0.3.2 should include significant performance
|
20
|
+
# improvements under Ruby 1.8.
|
21
|
+
|
22
|
+
module RevThreadPool
|
23
|
+
|
24
|
+
DEFAULTS = {
|
25
|
+
:pool_size => 10, # same default size as ThreadPool (w/o Rev)
|
26
|
+
}
|
27
|
+
|
28
|
+
def self.setup
|
29
|
+
DEFAULTS.each { |k,v| O[k] ||= v }
|
30
|
+
Integer === O[:pool_size] && O[:pool_size] > 0 or
|
31
|
+
raise ArgumentError, "pool_size must a be an Integer > 0"
|
32
|
+
end
|
33
|
+
|
34
|
+
class PoolWatcher < ::Rev::TimerWatcher
|
35
|
+
def initialize(threads)
|
36
|
+
@threads = threads
|
37
|
+
super(G.server.timeout, true)
|
38
|
+
end
|
39
|
+
|
40
|
+
def on_timer
|
41
|
+
@threads.each { |t| t.join(0) and G.quit! }
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
class Client < Rainbows::Rev::ThreadClient
|
46
|
+
def app_dispatch
|
47
|
+
QUEUE << self
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
include Rainbows::Rev::Core
|
52
|
+
|
53
|
+
def init_worker_threads(master, queue)
|
54
|
+
O[:pool_size].times.map do
|
55
|
+
Thread.new do
|
56
|
+
begin
|
57
|
+
client = queue.pop
|
58
|
+
master << [ client, client.app_response ]
|
59
|
+
rescue => e
|
60
|
+
Error.listen_loop(e)
|
61
|
+
end while true
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def init_worker_process(worker)
|
67
|
+
super
|
68
|
+
master = Rev::Master.new(Queue.new).attach(::Rev::Loop.default)
|
69
|
+
queue = Client.const_set(:QUEUE, Queue.new)
|
70
|
+
threads = init_worker_threads(master, queue)
|
71
|
+
PoolWatcher.new(threads).attach(::Rev::Loop.default)
|
72
|
+
logger.info "RevThreadPool pool_size=#{O[:pool_size]}"
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -1,9 +1,5 @@
|
|
1
1
|
# -*- encoding: binary -*-
|
2
|
-
require 'rainbows/rev'
|
3
|
-
|
4
|
-
RUBY_VERSION =~ %r{\A1\.8} && ::Rev::VERSION < "0.3.2" and
|
5
|
-
warn "Rainbows::RevThreadSpawn + Rev (< 0.3.2)" \
|
6
|
-
" does not work well under Ruby 1.8"
|
2
|
+
require 'rainbows/rev/thread'
|
7
3
|
|
8
4
|
module Rainbows
|
9
5
|
|
@@ -15,73 +11,19 @@ module Rainbows
|
|
15
11
|
# server are handled by the main thread and outside of the core
|
16
12
|
# application dispatch.
|
17
13
|
#
|
18
|
-
#
|
19
|
-
#
|
20
|
-
#
|
21
|
-
#
|
14
|
+
# Unlike ThreadSpawn, Rev makes this model highly suitable for
|
15
|
+
# slow clients and applications with medium-to-slow response times
|
16
|
+
# (I/O bound), but less suitable for sleepy applications.
|
17
|
+
#
|
18
|
+
# WARNING: this model does not currently perform well under 1.8 with
|
19
|
+
# Rev 0.3.1. Rev 0.3.2 should include significant performance
|
20
|
+
# improvements under Ruby 1.8.
|
22
21
|
|
23
22
|
module RevThreadSpawn
|
24
23
|
|
25
|
-
class
|
26
|
-
|
27
|
-
|
28
|
-
super
|
29
|
-
@queue = Queue.new
|
30
|
-
end
|
31
|
-
|
32
|
-
def <<(output)
|
33
|
-
@queue << output
|
34
|
-
signal
|
35
|
-
end
|
36
|
-
|
37
|
-
def on_signal
|
38
|
-
client, response = @queue.pop
|
39
|
-
client.response_write(response)
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
class Client < Rainbows::Rev::Client
|
44
|
-
DR = Rainbows::Rev::DeferredResponse
|
45
|
-
KATO = Rainbows::Rev::KATO
|
46
|
-
|
47
|
-
def response_write(response)
|
48
|
-
enable
|
49
|
-
alive = @hp.keepalive? && G.alive
|
50
|
-
out = [ alive ? CONN_ALIVE : CONN_CLOSE ] if @hp.headers?
|
51
|
-
DR.write(self, response, out)
|
52
|
-
return quit unless alive && G.alive
|
53
|
-
|
54
|
-
@env.clear
|
55
|
-
@hp.reset
|
56
|
-
@state = :headers
|
57
|
-
# keepalive requests are always body-less, so @input is unchanged
|
58
|
-
if @hp.headers(@env, @buf)
|
59
|
-
@input = HttpRequest::NULL_IO
|
60
|
-
app_call
|
61
|
-
else
|
62
|
-
KATO[self] = Time.now
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
|
-
# fails-safe application dispatch, we absolutely cannot
|
67
|
-
# afford to fail or raise an exception (killing the thread)
|
68
|
-
# here because that could cause a deadlock and we'd leak FDs
|
69
|
-
def app_response
|
70
|
-
begin
|
71
|
-
@env[REMOTE_ADDR] = @remote_addr
|
72
|
-
APP.call(@env.update(RACK_DEFAULTS))
|
73
|
-
rescue => e
|
74
|
-
Error.app(e) # we guarantee this does not raise
|
75
|
-
[ 500, {}, [] ]
|
76
|
-
end
|
77
|
-
end
|
78
|
-
|
79
|
-
def app_call
|
80
|
-
KATO.delete(client = self)
|
81
|
-
disable
|
82
|
-
@env[RACK_INPUT] = @input
|
83
|
-
@input = nil # not sure why, @input seems to get closed otherwise...
|
84
|
-
Thread.new { MASTER << [ client, app_response ] }
|
24
|
+
class Client < Rainbows::Rev::ThreadClient
|
25
|
+
def app_dispatch
|
26
|
+
Thread.new(self) { |client| MASTER << [ client, app_response ] }
|
85
27
|
end
|
86
28
|
end
|
87
29
|
|
@@ -89,7 +31,8 @@ module Rainbows
|
|
89
31
|
|
90
32
|
def init_worker_process(worker)
|
91
33
|
super
|
92
|
-
|
34
|
+
master = Rev::Master.new(Queue.new).attach(::Rev::Loop.default)
|
35
|
+
Client.const_set(:MASTER, master)
|
93
36
|
end
|
94
37
|
|
95
38
|
end
|
data/lib/rainbows/revactor.rb
CHANGED
@@ -78,42 +78,46 @@ module Rainbows
|
|
78
78
|
def worker_loop(worker)
|
79
79
|
init_worker_process(worker)
|
80
80
|
RD_ARGS[:timeout] = G.kato if G.kato > 0
|
81
|
-
|
82
|
-
root = Actor.current
|
83
|
-
root.trap_exit = true
|
84
|
-
|
81
|
+
nr = 0
|
85
82
|
limit = worker_connections
|
86
|
-
|
87
|
-
clients = {}
|
83
|
+
actor_exit = Case[:exit, Actor, Object]
|
88
84
|
|
89
|
-
|
90
|
-
Actor.spawn(
|
85
|
+
revactorize_listeners.each do |l, close, accept|
|
86
|
+
Actor.spawn(l, close, accept) do |l, close, accept|
|
87
|
+
Actor.current.trap_exit = true
|
88
|
+
l.controller = l.instance_eval { @receiver = Actor.current }
|
91
89
|
begin
|
92
|
-
while
|
93
|
-
|
94
|
-
|
90
|
+
while nr >= limit
|
91
|
+
l.disable if l.enabled?
|
92
|
+
logger.info "busy: clients=#{nr} >= limit=#{limit}"
|
93
|
+
Actor.receive do |f|
|
94
|
+
f.when(close) {}
|
95
|
+
f.when(actor_exit) { nr -= 1 }
|
96
|
+
f.after(0.01) {} # another listener could've gotten an exit
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
l.enable unless l.enabled?
|
101
|
+
Actor.receive do |f|
|
102
|
+
f.when(close) {}
|
103
|
+
f.when(actor_exit) { nr -= 1 }
|
104
|
+
f.when(accept) do |_, _, s|
|
105
|
+
nr += 1
|
106
|
+
Actor.spawn_link(s) { |c| process_client(c) }
|
107
|
+
end
|
95
108
|
end
|
96
|
-
actor = Actor.spawn(l.accept) { |c| process_client(c) }
|
97
|
-
clients[actor.object_id] = actor
|
98
|
-
root.link(actor)
|
99
|
-
rescue Errno::EAGAIN, Errno::ECONNABORTED
|
100
109
|
rescue => e
|
101
110
|
Error.listen_loop(e)
|
102
111
|
end while G.alive
|
112
|
+
Actor.receive do |f|
|
113
|
+
f.when(close) {}
|
114
|
+
f.when(actor_exit) { nr -= 1 }
|
115
|
+
end while nr > 0
|
103
116
|
end
|
104
117
|
end
|
105
118
|
|
106
|
-
|
107
|
-
|
108
|
-
filter.after(1) { G.tick }
|
109
|
-
filter.when(Case[:exit, Actor, Object]) do |_,actor,_|
|
110
|
-
orig = clients.size
|
111
|
-
clients.delete(actor.object_id)
|
112
|
-
orig >= limit and listeners.each { |l| l << :resume }
|
113
|
-
G.tick
|
114
|
-
end
|
115
|
-
end
|
116
|
-
end while G.alive || clients.size > 0
|
119
|
+
Actor.sleep 1 while G.tick || nr > 0
|
120
|
+
rescue Errno::EMFILE => e
|
117
121
|
end
|
118
122
|
|
119
123
|
# if we get any error, try to write something back to the client
|
@@ -127,13 +131,17 @@ module Rainbows
|
|
127
131
|
rescue
|
128
132
|
end
|
129
133
|
|
130
|
-
def revactorize_listeners
|
131
|
-
LISTENERS.map
|
134
|
+
def revactorize_listeners
|
135
|
+
LISTENERS.map do |s|
|
132
136
|
case s
|
133
137
|
when TCPServer
|
134
|
-
::Revactor::TCP.listen(s, nil)
|
138
|
+
l = ::Revactor::TCP.listen(s, nil)
|
139
|
+
[ l, T[:tcp_closed, ::Revactor::TCP::Socket],
|
140
|
+
T[:tcp_connection, l, ::Revactor::TCP::Socket] ]
|
135
141
|
when UNIXServer
|
136
|
-
::Revactor::UNIX.listen(s)
|
142
|
+
l = ::Revactor::UNIX.listen(s)
|
143
|
+
[ l, T[:unix_closed, ::Revactor::UNIX::Socket ],
|
144
|
+
T[:unix_connection, l, ::Revactor::UNIX::Socket] ]
|
137
145
|
end
|
138
146
|
end
|
139
147
|
end
|
data/rainbows.1
ADDED
File without changes
|
data/t/GNUmakefile
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rainbows
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.8.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Rainbows! hackers
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-
|
12
|
+
date: 2009-12-02 00:00:00 +00:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -60,6 +60,9 @@ extra_rdoc_files:
|
|
60
60
|
- lib/rainbows/rev/core.rb
|
61
61
|
- lib/rainbows/rev/deferred_response.rb
|
62
62
|
- lib/rainbows/rev/heartbeat.rb
|
63
|
+
- lib/rainbows/rev/master.rb
|
64
|
+
- lib/rainbows/rev/thread.rb
|
65
|
+
- lib/rainbows/rev_thread_pool.rb
|
63
66
|
- lib/rainbows/rev_thread_spawn.rb
|
64
67
|
- lib/rainbows/revactor.rb
|
65
68
|
- lib/rainbows/revactor/tee_input.rb
|
@@ -67,6 +70,7 @@ extra_rdoc_files:
|
|
67
70
|
- lib/rainbows/thread_spawn.rb
|
68
71
|
- LICENSE
|
69
72
|
- NEWS
|
73
|
+
- rainbows.1
|
70
74
|
- README
|
71
75
|
- SIGNALS
|
72
76
|
- TODO
|
@@ -121,6 +125,9 @@ files:
|
|
121
125
|
- lib/rainbows/rev/core.rb
|
122
126
|
- lib/rainbows/rev/deferred_response.rb
|
123
127
|
- lib/rainbows/rev/heartbeat.rb
|
128
|
+
- lib/rainbows/rev/master.rb
|
129
|
+
- lib/rainbows/rev/thread.rb
|
130
|
+
- lib/rainbows/rev_thread_pool.rb
|
124
131
|
- lib/rainbows/rev_thread_spawn.rb
|
125
132
|
- lib/rainbows/revactor.rb
|
126
133
|
- lib/rainbows/revactor/tee_input.rb
|
@@ -155,6 +162,7 @@ files:
|
|
155
162
|
- t/simple-http_FiberSpawn.ru
|
156
163
|
- t/simple-http_NeverBlock.ru
|
157
164
|
- t/simple-http_Rev.ru
|
165
|
+
- t/simple-http_RevThreadPool.ru
|
158
166
|
- t/simple-http_RevThreadSpawn.ru
|
159
167
|
- t/simple-http_Revactor.ru
|
160
168
|
- t/simple-http_ThreadPool.ru
|
@@ -186,6 +194,7 @@ files:
|
|
186
194
|
- t/test-lib.sh
|
187
195
|
- t/worker-follows-master-to-death.ru
|
188
196
|
- vs_Unicorn
|
197
|
+
- rainbows.1
|
189
198
|
has_rdoc: true
|
190
199
|
homepage: http://rainbows.rubyforge.org/
|
191
200
|
licenses: []
|