rainbows 0.9.0 → 0.90.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Documentation/comparison.haml +21 -2
- data/GIT-VERSION-GEN +1 -1
- data/GNUmakefile +2 -2
- data/README +2 -1
- data/Rakefile +2 -2
- data/TODO +5 -1
- data/bin/rainbows +1 -1
- data/lib/rainbows.rb +34 -15
- data/lib/rainbows/app_pool.rb +1 -1
- data/lib/rainbows/base.rb +8 -15
- data/lib/rainbows/const.rb +5 -5
- data/lib/rainbows/dev_fd_response.rb +1 -1
- data/lib/rainbows/error.rb +10 -1
- data/lib/rainbows/ev_core.rb +0 -4
- data/lib/rainbows/event_machine.rb +1 -1
- data/lib/rainbows/fiber/base.rb +3 -2
- data/lib/rainbows/fiber/io.rb +15 -9
- data/lib/rainbows/fiber/rev.rb +160 -0
- data/lib/rainbows/rev/core.rb +1 -1
- data/lib/rainbows/rev_fiber_spawn.rb +29 -0
- data/lib/rainbows/revactor.rb +1 -12
- data/lib/rainbows/thread_pool.rb +8 -0
- data/t/GNUmakefile +1 -0
- data/t/simple-http_RevFiberSpawn.ru +10 -0
- data/t/simple-http_Revactor.ru +0 -1
- data/t/simple-http_ThreadPool.ru +0 -1
- data/t/simple-http_ThreadSpawn.ru +0 -1
- data/t/sleep.ru +1 -8
- data/t/t9000.ru +1 -8
- data/t/worker-follows-master-to-death.ru +1 -6
- metadata +7 -2
@@ -92,6 +92,13 @@
|
|
92
92
|
%td.r19 Yes
|
93
93
|
%td.rbx No
|
94
94
|
%td.slow Yes
|
95
|
+
%tr.comp_row
|
96
|
+
%td.mod RevFiberSpawn
|
97
|
+
%td.tee Yes
|
98
|
+
%td.r18 No
|
99
|
+
%td.r19 Yes
|
100
|
+
%td.rbx No
|
101
|
+
%td.slow Yes
|
95
102
|
%ul
|
96
103
|
%li
|
97
104
|
RevThread* + 1.8 performance is bad with Rev <= 0.3.1.
|
@@ -191,6 +198,12 @@
|
|
191
198
|
%a(href="http://rev.rubyforge.org/") Rev
|
192
199
|
%td.thr Yes
|
193
200
|
%td.reent No
|
201
|
+
%tr.comp_row
|
202
|
+
%td.mod RevFiberSpawn
|
203
|
+
%td.slowio
|
204
|
+
%a(href="Rainbows/Fiber/IO.html") Rainbows::Fiber::IO
|
205
|
+
%td.thr No
|
206
|
+
%td.reent Yes
|
194
207
|
|
195
208
|
%ul
|
196
209
|
%li
|
@@ -276,14 +289,14 @@
|
|
276
289
|
%td.devfd Yes
|
277
290
|
%td.app_pool Yes
|
278
291
|
%td.lock No!
|
279
|
-
%td.async Rainbows::Fiber
|
292
|
+
%td.async Rainbows::Fiber::IO, Rainbows.sleep
|
280
293
|
%td.ws Sunshowers
|
281
294
|
%tr.comp_row
|
282
295
|
%td.mod FiberPool
|
283
296
|
%td.devfd Yes
|
284
297
|
%td.app_pool Yes
|
285
298
|
%td.lock No!
|
286
|
-
%td.async Rainbows::Fiber
|
299
|
+
%td.async Rainbows::Fiber::IO, Rainbows.sleep
|
287
300
|
%td.ws Sunshowers
|
288
301
|
%tr.comp_row
|
289
302
|
%td.mod ActorSpawn
|
@@ -306,6 +319,12 @@
|
|
306
319
|
%td.lock Dumb
|
307
320
|
%td.async standard Ruby
|
308
321
|
%td.ws no
|
322
|
+
%tr.comp_row
|
323
|
+
%td.mod RevFiberSpawn
|
324
|
+
%td.devfd Yes
|
325
|
+
%td.app_pool Yes
|
326
|
+
%td.lock No!
|
327
|
+
%td.async Rainbows::Fiber::IO, Rainbows.sleep
|
309
328
|
|
310
329
|
%ul
|
311
330
|
%li
|
data/GIT-VERSION-GEN
CHANGED
data/GNUmakefile
CHANGED
@@ -58,7 +58,7 @@ NEWS: GIT-VERSION-FILE
|
|
58
58
|
$(RAKE) -s news_rdoc > $@+
|
59
59
|
mv $@+ $@
|
60
60
|
|
61
|
-
SINCE = 0.
|
61
|
+
SINCE = 0.9.0
|
62
62
|
ChangeLog: LOG_VERSION = \
|
63
63
|
$(shell git rev-parse -q "$(GIT_VERSION)" >/dev/null 2>&1 && \
|
64
64
|
echo $(GIT_VERSION) || git describe)
|
@@ -155,7 +155,7 @@ package: $(pkgtgz) $(pkggem)
|
|
155
155
|
release: verify package $(release_notes) $(release_changes)
|
156
156
|
# make tgz release on RubyForge
|
157
157
|
rubyforge add_release -f -n $(release_notes) -a $(release_changes) \
|
158
|
-
$(rfproject) $(rfpackage) $(VERSION) $(
|
158
|
+
$(rfproject) $(rfpackage) $(VERSION) $(pkgtgz)
|
159
159
|
# push gem to Gemcutter
|
160
160
|
gem push $(pkggem)
|
161
161
|
# in case of gem downloads from RubyForge releases page
|
data/README
CHANGED
@@ -13,6 +13,7 @@ suck; differently.
|
|
13
13
|
|
14
14
|
For network concurrency, models we currently support are:
|
15
15
|
|
16
|
+
* {RevFiberSpawn}[link:Rainbows/RevFiberSpawn.html]
|
16
17
|
* {Revactor}[link:Rainbows/Revactor.html]
|
17
18
|
* {ThreadPool}[link:Rainbows/ThreadPool.html]
|
18
19
|
* {Rev}[link:Rainbows/Rev.html]
|
@@ -155,7 +156,7 @@ and we'll try our best to fix it.
|
|
155
156
|
|
156
157
|
== Contact
|
157
158
|
|
158
|
-
All feedback (bug reports, user/development
|
159
|
+
All feedback (bug reports, user/development discussion, patches, pull
|
159
160
|
requests) go to the mailing list/newsgroup. Patches must be sent inline
|
160
161
|
(git format-patch -M + git send-email). No subscription is necessary
|
161
162
|
to post on the mailing list. No top posting. Address replies +To:+
|
data/Rakefile
CHANGED
@@ -164,8 +164,8 @@ task :fm_update do
|
|
164
164
|
require 'net/netrc'
|
165
165
|
require 'json'
|
166
166
|
version = ENV['VERSION'] or abort "VERSION= needed"
|
167
|
-
uri = URI.parse('http://freshmeat.net/projects/
|
168
|
-
rc = Net::Netrc.locate('
|
167
|
+
uri = URI.parse('http://freshmeat.net/projects/rainbows/releases.json')
|
168
|
+
rc = Net::Netrc.locate('rainbows-fm') or abort "~/.netrc not found"
|
169
169
|
api_token = rc.password
|
170
170
|
changelog = tags.find { |t| t[:tag] == "v#{version}" }[:body]
|
171
171
|
tmp = Tempfile.new('fm-changelog')
|
data/TODO
CHANGED
@@ -14,7 +14,11 @@ care about.
|
|
14
14
|
* conditional app.deferred?(env) support
|
15
15
|
Merb uses it, some other servers support it
|
16
16
|
|
17
|
-
*
|
17
|
+
* EventMachine+Fibers+streaming input
|
18
|
+
|
19
|
+
* RevFiberPool
|
20
|
+
|
21
|
+
* ThreadPoolRevFiber{Spawn,Pool}: just because
|
18
22
|
|
19
23
|
* Rev + callcc - current Rev model with callcc (should work with MBARI)
|
20
24
|
|
data/bin/rainbows
CHANGED
data/lib/rainbows.rb
CHANGED
@@ -34,11 +34,44 @@ module Rainbows
|
|
34
34
|
|
35
35
|
class << self
|
36
36
|
|
37
|
+
# Sleeps the current application dispatch. This will pick the
|
38
|
+
# optimal method to sleep depending on the concurrency model chosen
|
39
|
+
# (which may still suck and block the entire process). Using this
|
40
|
+
# with the basic :Rev or :EventMachine models is not recommended.
|
41
|
+
# This should be used within your Rack application.
|
42
|
+
def sleep(nr)
|
43
|
+
case G.server.use
|
44
|
+
when :FiberPool, :FiberSpawn
|
45
|
+
Rainbows::Fiber.sleep(nr)
|
46
|
+
when :RevFiberSpawn
|
47
|
+
Rainbows::Fiber::Rev::Sleeper.new(nr)
|
48
|
+
when :Revactor
|
49
|
+
Actor.sleep(nr)
|
50
|
+
else
|
51
|
+
Kernel.sleep(nr)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
37
55
|
# runs the Rainbows! HttpServer with +app+ and +options+ and does
|
38
56
|
# not return until the server has exited.
|
39
57
|
def run(app, options = {})
|
40
58
|
HttpServer.new(app, options).start.join
|
41
59
|
end
|
60
|
+
|
61
|
+
# returns nil if accept fails
|
62
|
+
if defined?(Fcntl::FD_CLOEXEC)
|
63
|
+
def accept(sock)
|
64
|
+
rv = sock.accept_nonblock
|
65
|
+
rv.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
|
66
|
+
rv
|
67
|
+
rescue Errno::EAGAIN, Errno::ECONNABORTED
|
68
|
+
end
|
69
|
+
else
|
70
|
+
def accept(sock)
|
71
|
+
sock.accept_nonblock
|
72
|
+
rescue Errno::EAGAIN, Errno::ECONNABORTED
|
73
|
+
end
|
74
|
+
end
|
42
75
|
end
|
43
76
|
|
44
77
|
# configures \Rainbows! with a given concurrency model to +use+ and
|
@@ -87,27 +120,13 @@ module Rainbows
|
|
87
120
|
:FiberPool => 50,
|
88
121
|
:ActorSpawn => 50,
|
89
122
|
:NeverBlock => 50,
|
123
|
+
:RevFiberSpawn => 50,
|
90
124
|
}.each do |model, _|
|
91
125
|
u = model.to_s.gsub(/([a-z0-9])([A-Z0-9])/) { "#{$1}_#{$2.downcase!}" }
|
92
126
|
autoload model, "rainbows/#{u.downcase!}"
|
93
127
|
end
|
94
128
|
autoload :Fiber, 'rainbows/fiber' # core class
|
95
129
|
|
96
|
-
# returns nil if accept fails
|
97
|
-
if defined?(Fcntl::FD_CLOEXEC)
|
98
|
-
def self.accept(sock)
|
99
|
-
rv = sock.accept_nonblock
|
100
|
-
rv.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
|
101
|
-
rv
|
102
|
-
rescue Errno::EAGAIN, Errno::ECONNABORTED
|
103
|
-
end
|
104
|
-
else
|
105
|
-
def self.accept(sock)
|
106
|
-
sock.accept_nonblock
|
107
|
-
rescue Errno::EAGAIN, Errno::ECONNABORTED
|
108
|
-
end
|
109
|
-
end
|
110
|
-
|
111
130
|
end
|
112
131
|
|
113
132
|
# inject the Rainbows! method into Unicorn::Configurator
|
data/lib/rainbows/app_pool.rb
CHANGED
@@ -91,7 +91,7 @@ module Rainbows
|
|
91
91
|
# concurrency models
|
92
92
|
self.re ||= begin
|
93
93
|
case env["rainbows.model"]
|
94
|
-
when :FiberSpawn, :FiberPool, :Revactor, :NeverBlock
|
94
|
+
when :FiberSpawn, :FiberPool, :Revactor, :NeverBlock, :RevFiberSpawn
|
95
95
|
self.pool = Rainbows::Fiber::Queue.new(pool)
|
96
96
|
end
|
97
97
|
true
|
data/lib/rainbows/base.rb
CHANGED
@@ -10,15 +10,16 @@ module Rainbows
|
|
10
10
|
include Rainbows::Const
|
11
11
|
G = Rainbows::G
|
12
12
|
|
13
|
-
def handle_error(client, e)
|
14
|
-
msg = Error.response(e) and client.write_nonblock(msg)
|
15
|
-
rescue
|
16
|
-
end
|
17
|
-
|
18
13
|
def init_worker_process(worker)
|
19
14
|
super(worker)
|
20
15
|
G.tmp = worker.tmp
|
21
16
|
|
17
|
+
# avoid spurious wakeups and blocking-accept() with 1.8 green threads
|
18
|
+
if RUBY_VERSION.to_f < 1.9
|
19
|
+
require "io/nonblock"
|
20
|
+
HttpServer::LISTENERS.each { |l| l.nonblock = true }
|
21
|
+
end
|
22
|
+
|
22
23
|
# we're don't use the self-pipe mechanism in the Rainbows! worker
|
23
24
|
# since we don't defer reopening logs
|
24
25
|
HttpServer::SELF_PIPE.each { |x| x.close }.clear
|
@@ -65,17 +66,9 @@ module Rainbows
|
|
65
66
|
# if the socket is already closed or broken. We'll always ensure
|
66
67
|
# the socket is closed at the end of this function
|
67
68
|
rescue => e
|
68
|
-
|
69
|
+
Error.write(client, e)
|
69
70
|
ensure
|
70
|
-
client.close
|
71
|
-
end
|
72
|
-
|
73
|
-
def join_threads(threads)
|
74
|
-
G.quit!
|
75
|
-
threads.delete_if do |thr|
|
76
|
-
G.tick
|
77
|
-
thr.alive? ? thr.join(0.01) : true
|
78
|
-
end until threads.empty?
|
71
|
+
client.close unless client.closed?
|
79
72
|
end
|
80
73
|
|
81
74
|
def self.included(klass)
|
data/lib/rainbows/const.rb
CHANGED
@@ -3,21 +3,21 @@
|
|
3
3
|
module Rainbows
|
4
4
|
|
5
5
|
module Const
|
6
|
-
RAINBOWS_VERSION = '0.
|
6
|
+
RAINBOWS_VERSION = '0.90.0'
|
7
7
|
|
8
8
|
include Unicorn::Const
|
9
9
|
|
10
|
-
RACK_DEFAULTS =
|
10
|
+
RACK_DEFAULTS = Unicorn::HttpRequest::DEFAULTS.update({
|
11
11
|
"SERVER_SOFTWARE" => "Rainbows! #{RAINBOWS_VERSION}",
|
12
12
|
|
13
13
|
# using the Rev model, we'll automatically chunk pipe and socket objects
|
14
|
-
# if they're the response body
|
15
|
-
|
14
|
+
# if they're the response body. Unset by default.
|
15
|
+
# "rainbows.autochunk" => false,
|
16
16
|
})
|
17
17
|
|
18
18
|
CONN_CLOSE = "Connection: close\r\n"
|
19
19
|
CONN_ALIVE = "Connection: keep-alive\r\n"
|
20
|
-
LOCALHOST =
|
20
|
+
LOCALHOST = Unicorn::HttpRequest::LOCALHOST
|
21
21
|
|
22
22
|
# client IO object that supports reading and writing directly
|
23
23
|
# without filtering it through the HTTP chunk parser.
|
@@ -39,7 +39,7 @@ module Rainbows
|
|
39
39
|
|
40
40
|
# we need to make sure our pipe output is Fiber-compatible
|
41
41
|
case env["rainbows.model"]
|
42
|
-
when :FiberSpawn, :FiberPool
|
42
|
+
when :FiberSpawn, :FiberPool, :RevFiberSpawn
|
43
43
|
return [ status, headers.to_hash, Fiber::IO.new(io,::Fiber.current) ]
|
44
44
|
end
|
45
45
|
else # unlikely, char/block device file, directory, ...
|
data/lib/rainbows/error.rb
CHANGED
@@ -4,6 +4,15 @@ module Rainbows
|
|
4
4
|
class Error
|
5
5
|
class << self
|
6
6
|
|
7
|
+
# if we get any error, try to write something back to the client
|
8
|
+
# assuming we haven't closed the socket, but don't get hung up
|
9
|
+
# if the socket is already closed or broken. We'll always ensure
|
10
|
+
# the socket is closed at the end of this function
|
11
|
+
def write(io, e)
|
12
|
+
msg = Error.response(e) and io.write_nonblock(msg)
|
13
|
+
rescue
|
14
|
+
end
|
15
|
+
|
7
16
|
def app(e)
|
8
17
|
G.server.logger.error "app error: #{e.inspect}"
|
9
18
|
G.server.logger.error e.backtrace.join("\n")
|
@@ -19,7 +28,7 @@ module Rainbows
|
|
19
28
|
|
20
29
|
def response(e)
|
21
30
|
case e
|
22
|
-
when EOFError,
|
31
|
+
when EOFError,Errno::ECONNRESET,Errno::EPIPE,Errno::EINVAL,Errno::EBADF
|
23
32
|
# swallow error if client shuts down one end or disconnects
|
24
33
|
when Unicorn::HttpParserError
|
25
34
|
Const::ERROR_400_RESPONSE # try to tell the client they're bad
|
data/lib/rainbows/ev_core.rb
CHANGED
@@ -11,10 +11,6 @@ module Rainbows
|
|
11
11
|
# Apps may return this Rack response: AsyncResponse = [ -1, {}, [] ]
|
12
12
|
ASYNC_CALLBACK = "async.callback".freeze
|
13
13
|
|
14
|
-
def self.setup(klass)
|
15
|
-
klass.const_set(:APP, G.server.app)
|
16
|
-
end
|
17
|
-
|
18
14
|
def post_init
|
19
15
|
@remote_addr = ::TCPSocket === @_io ? @_io.peeraddr.last : LOCALHOST
|
20
16
|
@env = {}
|
@@ -190,7 +190,7 @@ module Rainbows
|
|
190
190
|
client_class = Rainbows.const_get(@use).const_get(:Client)
|
191
191
|
Server.const_set(:MAX, worker_connections + LISTENERS.size)
|
192
192
|
Server.const_set(:CL, client_class)
|
193
|
-
|
193
|
+
client_class.const_set(:APP, G.server.app)
|
194
194
|
EM.run {
|
195
195
|
conns = EM.instance_variable_get(:@conns) or
|
196
196
|
raise RuntimeError, "EM @conns instance variable not accessible!"
|
data/lib/rainbows/fiber/base.rb
CHANGED
@@ -16,7 +16,8 @@ module Rainbows
|
|
16
16
|
# puts the current Fiber into uninterruptible sleep for at least
|
17
17
|
# +seconds+. Unlike Kernel#sleep, this it is not possible to sleep
|
18
18
|
# indefinitely to be woken up (nobody wants that in a web server,
|
19
|
-
# right?).
|
19
|
+
# right?). Calling this directly is deprecated, use
|
20
|
+
# Rainbows.sleep(seconds) instead.
|
20
21
|
def self.sleep(seconds)
|
21
22
|
ZZ[::Fiber.current] = Time.now + seconds
|
22
23
|
::Fiber.yield
|
@@ -99,7 +100,7 @@ module Rainbows
|
|
99
100
|
HttpResponse.write(client, response, out)
|
100
101
|
end while alive and hp.reset.nil? and env.clear
|
101
102
|
rescue => e
|
102
|
-
|
103
|
+
Error.write(io, e)
|
103
104
|
ensure
|
104
105
|
G.cur -= 1
|
105
106
|
RD.delete(client)
|
data/lib/rainbows/fiber/io.rb
CHANGED
@@ -22,14 +22,24 @@ module Rainbows
|
|
22
22
|
to_io.close
|
23
23
|
end
|
24
24
|
|
25
|
+
def wait_readable
|
26
|
+
RD[self] = false
|
27
|
+
::Fiber.yield
|
28
|
+
RD.delete(self)
|
29
|
+
end
|
30
|
+
|
31
|
+
def wait_writable
|
32
|
+
WR[self] = false
|
33
|
+
::Fiber.yield
|
34
|
+
WR.delete(self)
|
35
|
+
end
|
36
|
+
|
25
37
|
def write(buf)
|
26
38
|
begin
|
27
39
|
(w = to_io.write_nonblock(buf)) == buf.size and return
|
28
40
|
buf = buf[w..-1]
|
29
41
|
rescue Errno::EAGAIN
|
30
|
-
|
31
|
-
::Fiber.yield
|
32
|
-
WR.delete(self)
|
42
|
+
wait_writable
|
33
43
|
retry
|
34
44
|
end while true
|
35
45
|
end
|
@@ -41,10 +51,8 @@ module Rainbows
|
|
41
51
|
to_io.read_nonblock(16384)
|
42
52
|
rescue Errno::EAGAIN
|
43
53
|
return if expire && expire < Time.now
|
44
|
-
RD[self] = false
|
45
54
|
expire ||= Time.now + G.kato
|
46
|
-
|
47
|
-
RD.delete(self)
|
55
|
+
wait_readable
|
48
56
|
retry
|
49
57
|
end
|
50
58
|
end
|
@@ -53,9 +61,7 @@ module Rainbows
|
|
53
61
|
begin
|
54
62
|
to_io.read_nonblock(length, buf)
|
55
63
|
rescue Errno::EAGAIN
|
56
|
-
|
57
|
-
::Fiber.yield
|
58
|
-
RD.delete(self)
|
64
|
+
wait_readable
|
59
65
|
retry
|
60
66
|
end
|
61
67
|
end
|
@@ -0,0 +1,160 @@
|
|
1
|
+
# -*- encoding: binary -*-
|
2
|
+
require 'rev'
|
3
|
+
require 'rainbows/fiber'
|
4
|
+
require 'rainbows/fiber/io'
|
5
|
+
|
6
|
+
module Rainbows::Fiber
|
7
|
+
module Rev
|
8
|
+
G = Rainbows::G
|
9
|
+
|
10
|
+
# keep-alive timeout class
|
11
|
+
class Kato < ::Rev::TimerWatcher
|
12
|
+
def initialize
|
13
|
+
@watch = []
|
14
|
+
super(1, true)
|
15
|
+
end
|
16
|
+
|
17
|
+
def <<(fiber)
|
18
|
+
@watch << fiber
|
19
|
+
enable unless enabled?
|
20
|
+
end
|
21
|
+
|
22
|
+
def on_timer
|
23
|
+
@watch.uniq!
|
24
|
+
while f = @watch.shift
|
25
|
+
f.resume if f.alive?
|
26
|
+
end
|
27
|
+
disable
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
class Heartbeat < ::Rev::TimerWatcher
|
32
|
+
def on_timer
|
33
|
+
exit if (! G.tick && G.cur <= 0)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
class Sleeper < ::Rev::TimerWatcher
|
38
|
+
|
39
|
+
def initialize(seconds)
|
40
|
+
@f = ::Fiber.current
|
41
|
+
super(seconds, false)
|
42
|
+
attach(::Rev::Loop.default)
|
43
|
+
::Fiber.yield
|
44
|
+
end
|
45
|
+
|
46
|
+
def on_timer
|
47
|
+
@f.resume
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
class Server < ::Rev::IOWatcher
|
52
|
+
include Unicorn
|
53
|
+
include Rainbows
|
54
|
+
include Rainbows::Const
|
55
|
+
FIO = Rainbows::Fiber::IO
|
56
|
+
|
57
|
+
def to_io
|
58
|
+
@io
|
59
|
+
end
|
60
|
+
|
61
|
+
def initialize(io)
|
62
|
+
@io = io
|
63
|
+
super(self, :r)
|
64
|
+
end
|
65
|
+
|
66
|
+
def close
|
67
|
+
detach if attached?
|
68
|
+
@io.close
|
69
|
+
end
|
70
|
+
|
71
|
+
def on_readable
|
72
|
+
return if G.cur >= MAX
|
73
|
+
c = Rainbows.accept(@io) and ::Fiber.new { process(c) }.resume
|
74
|
+
end
|
75
|
+
|
76
|
+
def process(io)
|
77
|
+
G.cur += 1
|
78
|
+
client = FIO.new(io, ::Fiber.current)
|
79
|
+
buf = client.read_timeout or return
|
80
|
+
hp = HttpParser.new
|
81
|
+
env = {}
|
82
|
+
alive = true
|
83
|
+
remote_addr = TCPSocket === io ? io.peeraddr.last : LOCALHOST
|
84
|
+
|
85
|
+
begin # loop
|
86
|
+
buf << (client.read_timeout or return) until hp.headers(env, buf)
|
87
|
+
|
88
|
+
env[CLIENT_IO] = client
|
89
|
+
env[RACK_INPUT] = 0 == hp.content_length ?
|
90
|
+
HttpRequest::NULL_IO : TeeInput.new(client, env, hp, buf)
|
91
|
+
env[REMOTE_ADDR] = remote_addr
|
92
|
+
response = APP.call(env.update(RACK_DEFAULTS))
|
93
|
+
|
94
|
+
if 100 == response.first.to_i
|
95
|
+
client.write(EXPECT_100_RESPONSE)
|
96
|
+
env.delete(HTTP_EXPECT)
|
97
|
+
response = APP.call(env)
|
98
|
+
end
|
99
|
+
|
100
|
+
alive = hp.keepalive? && G.alive
|
101
|
+
out = [ alive ? CONN_ALIVE : CONN_CLOSE ] if hp.headers?
|
102
|
+
HttpResponse.write(client, response, out)
|
103
|
+
end while alive and hp.reset.nil? and env.clear
|
104
|
+
rescue => e
|
105
|
+
Error.write(io, e)
|
106
|
+
ensure
|
107
|
+
G.cur -= 1
|
108
|
+
client.close
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
class IO # see rainbows/fiber/io for original definition
|
114
|
+
|
115
|
+
class Watcher < ::Rev::IOWatcher
|
116
|
+
def initialize(fio, flag)
|
117
|
+
@fiber = fio.f
|
118
|
+
super(fio, flag)
|
119
|
+
attach(::Rev::Loop.default)
|
120
|
+
end
|
121
|
+
|
122
|
+
def on_readable
|
123
|
+
@fiber.resume
|
124
|
+
end
|
125
|
+
|
126
|
+
alias on_writable on_readable
|
127
|
+
end
|
128
|
+
|
129
|
+
undef_method :wait_readable
|
130
|
+
undef_method :wait_writable
|
131
|
+
undef_method :close
|
132
|
+
|
133
|
+
def initialize(*args)
|
134
|
+
super
|
135
|
+
@r = @w = false
|
136
|
+
end
|
137
|
+
|
138
|
+
def close
|
139
|
+
@w.detach if @w
|
140
|
+
@r.detach if @r
|
141
|
+
@r = @w = false
|
142
|
+
to_io.close unless to_io.closed?
|
143
|
+
end
|
144
|
+
|
145
|
+
def wait_writable
|
146
|
+
@w ||= Watcher.new(self, :w)
|
147
|
+
@w.enable unless @w.enabled?
|
148
|
+
::Fiber.yield
|
149
|
+
@w.disable
|
150
|
+
end
|
151
|
+
|
152
|
+
def wait_readable
|
153
|
+
@r ||= Watcher.new(self, :r)
|
154
|
+
@r.enable unless @r.enabled?
|
155
|
+
KATO << f
|
156
|
+
::Fiber.yield
|
157
|
+
@r.disable
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
data/lib/rainbows/rev/core.rb
CHANGED
@@ -27,7 +27,7 @@ module Rainbows
|
|
27
27
|
rloop = Server.const_set(:LOOP, ::Rev::Loop.default)
|
28
28
|
Server.const_set(:MAX, @worker_connections)
|
29
29
|
Server.const_set(:CL, mod.const_get(:Client))
|
30
|
-
EvCore.
|
30
|
+
EvCore.const_set(:APP, G.server.app)
|
31
31
|
Heartbeat.new(1, true).attach(rloop)
|
32
32
|
LISTENERS.map! { |s| Server.new(s).attach(rloop) }
|
33
33
|
rloop.run
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# -*- encoding: binary -*-
|
2
|
+
require 'rainbows/fiber/rev'
|
3
|
+
|
4
|
+
module Rainbows
|
5
|
+
|
6
|
+
# A combination of the Rev and FiberSpawn models. This allows Ruby
|
7
|
+
# 1.9 Fiber-based concurrency for application processing while
|
8
|
+
# exposing a synchronous execution model and using scalable network
|
9
|
+
# concurrency provided by Rev. A "rack.input" is exposed as well
|
10
|
+
# being Sunshowers-compatible. Applications are strongly advised to
|
11
|
+
# wrap all slow IO objects (sockets, pipes) using the
|
12
|
+
# Rainbows::Fiber::IO or a Rev-compatible class whenever possible.
|
13
|
+
module RevFiberSpawn
|
14
|
+
|
15
|
+
include Base
|
16
|
+
include Fiber::Rev
|
17
|
+
|
18
|
+
def worker_loop(worker)
|
19
|
+
init_worker_process(worker)
|
20
|
+
Server.const_set(:MAX, @worker_connections)
|
21
|
+
Server.const_set(:APP, G.server.app)
|
22
|
+
Heartbeat.new(1, true).attach(::Rev::Loop.default)
|
23
|
+
kato = Kato.new.attach(::Rev::Loop.default)
|
24
|
+
Rainbows::Fiber::IO.const_set(:KATO, kato)
|
25
|
+
LISTENERS.map! { |s| Server.new(s).attach(::Rev::Loop.default) }
|
26
|
+
::Rev::Loop.default.run
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/lib/rainbows/revactor.rb
CHANGED
@@ -68,7 +68,7 @@ module Rainbows
|
|
68
68
|
end while alive and hp.reset.nil? and env.clear
|
69
69
|
rescue ::Revactor::TCP::ReadError
|
70
70
|
rescue => e
|
71
|
-
|
71
|
+
Error.write(client.instance_eval { @_io }, e)
|
72
72
|
ensure
|
73
73
|
client.close
|
74
74
|
end
|
@@ -121,17 +121,6 @@ module Rainbows
|
|
121
121
|
rescue Errno::EMFILE => e
|
122
122
|
end
|
123
123
|
|
124
|
-
# if we get any error, try to write something back to the client
|
125
|
-
# assuming we haven't closed the socket, but don't get hung up
|
126
|
-
# if the socket is already closed or broken. We'll always ensure
|
127
|
-
# the socket is closed at the end of this function
|
128
|
-
def handle_error(client, e)
|
129
|
-
# this is Revactor implementation dependent
|
130
|
-
msg = Error.response(e) and
|
131
|
-
client.instance_eval { @_io.write_nonblock(msg) }
|
132
|
-
rescue
|
133
|
-
end
|
134
|
-
|
135
124
|
def revactorize_listeners
|
136
125
|
LISTENERS.map do |s|
|
137
126
|
case s
|
data/lib/rainbows/thread_pool.rb
CHANGED
data/t/GNUmakefile
CHANGED
data/t/simple-http_Revactor.ru
CHANGED
data/t/simple-http_ThreadPool.ru
CHANGED
data/t/sleep.ru
CHANGED
@@ -7,14 +7,7 @@ run lambda { |env|
|
|
7
7
|
nr = 1
|
8
8
|
env["PATH_INFO"] =~ %r{/([\d\.]+)\z} and nr = $1.to_f
|
9
9
|
|
10
|
-
(
|
11
|
-
when :FiberPool, :FiberSpawn
|
12
|
-
Rainbows::Fiber
|
13
|
-
when :Revactor
|
14
|
-
Actor
|
15
|
-
else
|
16
|
-
Kernel
|
17
|
-
end).sleep(nr)
|
10
|
+
Rainbows.sleep(nr)
|
18
11
|
|
19
12
|
[ 200, {'Content-Type' => 'text/plain'}, [ "Hello\n" ] ]
|
20
13
|
}
|
data/t/t9000.ru
CHANGED
@@ -3,14 +3,7 @@ use Rack::ContentType
|
|
3
3
|
use Rainbows::AppPool, :size => ENV['APP_POOL_SIZE'].to_i
|
4
4
|
class Sleeper
|
5
5
|
def call(env)
|
6
|
-
(
|
7
|
-
when :FiberPool, :FiberSpawn
|
8
|
-
Rainbows::Fiber
|
9
|
-
when :Revactor
|
10
|
-
Actor
|
11
|
-
else
|
12
|
-
Kernel
|
13
|
-
end).sleep(1)
|
6
|
+
Rainbows.sleep(1)
|
14
7
|
[ 200, {}, [ "#{object_id}\n" ] ]
|
15
8
|
end
|
16
9
|
end
|
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.90.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-
|
12
|
+
date: 2009-12-22 00:00:00 +00:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -51,6 +51,7 @@ extra_rdoc_files:
|
|
51
51
|
- lib/rainbows/fiber/base.rb
|
52
52
|
- lib/rainbows/fiber/io.rb
|
53
53
|
- lib/rainbows/fiber/queue.rb
|
54
|
+
- lib/rainbows/fiber/rev.rb
|
54
55
|
- lib/rainbows/fiber_pool.rb
|
55
56
|
- lib/rainbows/fiber_spawn.rb
|
56
57
|
- lib/rainbows/http_response.rb
|
@@ -64,6 +65,7 @@ extra_rdoc_files:
|
|
64
65
|
- lib/rainbows/rev/heartbeat.rb
|
65
66
|
- lib/rainbows/rev/master.rb
|
66
67
|
- lib/rainbows/rev/thread.rb
|
68
|
+
- lib/rainbows/rev_fiber_spawn.rb
|
67
69
|
- lib/rainbows/rev_thread_pool.rb
|
68
70
|
- lib/rainbows/rev_thread_spawn.rb
|
69
71
|
- lib/rainbows/revactor.rb
|
@@ -118,6 +120,7 @@ files:
|
|
118
120
|
- lib/rainbows/fiber/base.rb
|
119
121
|
- lib/rainbows/fiber/io.rb
|
120
122
|
- lib/rainbows/fiber/queue.rb
|
123
|
+
- lib/rainbows/fiber/rev.rb
|
121
124
|
- lib/rainbows/fiber_pool.rb
|
122
125
|
- lib/rainbows/fiber_spawn.rb
|
123
126
|
- lib/rainbows/http_response.rb
|
@@ -131,6 +134,7 @@ files:
|
|
131
134
|
- lib/rainbows/rev/heartbeat.rb
|
132
135
|
- lib/rainbows/rev/master.rb
|
133
136
|
- lib/rainbows/rev/thread.rb
|
137
|
+
- lib/rainbows/rev_fiber_spawn.rb
|
134
138
|
- lib/rainbows/rev_thread_pool.rb
|
135
139
|
- lib/rainbows/rev_thread_spawn.rb
|
136
140
|
- lib/rainbows/revactor.rb
|
@@ -167,6 +171,7 @@ files:
|
|
167
171
|
- t/simple-http_FiberSpawn.ru
|
168
172
|
- t/simple-http_NeverBlock.ru
|
169
173
|
- t/simple-http_Rev.ru
|
174
|
+
- t/simple-http_RevFiberSpawn.ru
|
170
175
|
- t/simple-http_RevThreadPool.ru
|
171
176
|
- t/simple-http_RevThreadSpawn.ru
|
172
177
|
- t/simple-http_Revactor.ru
|