rainbows 0.9.0 → 0.90.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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{::IO,.sleep}
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{::IO,.sleep}
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
@@ -1,7 +1,7 @@
1
1
  #!/bin/sh
2
2
 
3
3
  GVF=GIT-VERSION-FILE
4
- DEF_VER=v0.9.0.GIT
4
+ DEF_VER=v0.90.0.GIT
5
5
 
6
6
  LF='
7
7
  '
@@ -58,7 +58,7 @@ NEWS: GIT-VERSION-FILE
58
58
  $(RAKE) -s news_rdoc > $@+
59
59
  mv $@+ $@
60
60
 
61
- SINCE = 0.8.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) $(pkggem)
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 dicussion, patches, pull
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/unicorn/releases.json')
168
- rc = Net::Netrc.locate('unicorn-fm') or abort "~/.netrc not found"
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
- * {Rev,EventMachine}+Fibers+streaming input
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
 
@@ -1,4 +1,4 @@
1
- #!/home/ew/bin/ruby
1
+ #!/this/will/be/overwritten/or/wrapped/anyways/do/not/worry/ruby
2
2
  # -*- encoding: binary -*-
3
3
  require 'unicorn/launcher'
4
4
  require 'rainbows'
@@ -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
@@ -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
@@ -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
- handle_error(client, e)
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)
@@ -3,21 +3,21 @@
3
3
  module Rainbows
4
4
 
5
5
  module Const
6
- RAINBOWS_VERSION = '0.9.0'
6
+ RAINBOWS_VERSION = '0.90.0'
7
7
 
8
8
  include Unicorn::Const
9
9
 
10
- RACK_DEFAULTS = ::Unicorn::HttpRequest::DEFAULTS.merge({
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
- 'rainbows.autochunk' => false,
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 = "127.0.0.1"
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, ...
@@ -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, Errno::ECONNRESET,Errno::EPIPE,Errno::EINVAL,Errno::EBADF
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
@@ -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
- EvCore.setup(client_class)
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!"
@@ -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
- handle_error(io, e)
103
+ Error.write(io, e)
103
104
  ensure
104
105
  G.cur -= 1
105
106
  RD.delete(client)
@@ -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
- WR[self] = false
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
- ::Fiber.yield
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
- RD[self] = false
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
@@ -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.setup(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
@@ -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
- handle_error(client, e)
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
@@ -67,5 +67,13 @@ module Rainbows
67
67
  end while G.alive
68
68
  end
69
69
 
70
+ def join_threads(threads)
71
+ G.quit!
72
+ threads.delete_if do |thr|
73
+ G.tick
74
+ thr.alive? ? thr.join(0.01) : true
75
+ end until threads.empty?
76
+ end
77
+
70
78
  end
71
79
  end
@@ -28,6 +28,7 @@ ONENINE := $(shell case $(RUBY_VERSION) in 1.9.*$(rp) echo true;;esac)
28
28
  ifeq ($(ONENINE),true)
29
29
  models += Revactor
30
30
  models += FiberSpawn
31
+ models += RevFiberSpawn
31
32
  models += FiberPool
32
33
 
33
34
  # technically this works under 1.8, but wait until rev 0.3.2
@@ -0,0 +1,10 @@
1
+ use Rack::ContentLength
2
+ use Rack::ContentType
3
+ run lambda { |env|
4
+ if env['rack.multithread'] == false &&
5
+ env['rainbows.model'] == :RevFiberSpawn
6
+ [ 200, {}, [ Thread.current.inspect << "\n" ] ]
7
+ else
8
+ raise env.inspect
9
+ end
10
+ }
@@ -1,7 +1,6 @@
1
1
  use Rack::ContentLength
2
2
  use Rack::ContentType
3
3
  run lambda { |env|
4
- Actor.sleep 1
5
4
  if env['rack.multithread'] == false && env['rainbows.model'] == :Revactor
6
5
  [ 200, {}, [ Thread.current.inspect << "\n" ] ]
7
6
  else
@@ -1,7 +1,6 @@
1
1
  use Rack::ContentLength
2
2
  use Rack::ContentType
3
3
  run lambda { |env|
4
- sleep 1
5
4
  if env['rack.multithread'] && env['rainbows.model'] == :ThreadPool
6
5
  [ 200, {}, [ Thread.current.inspect << "\n" ] ]
7
6
  else
@@ -1,7 +1,6 @@
1
1
  use Rack::ContentLength
2
2
  use Rack::ContentType
3
3
  run lambda { |env|
4
- sleep 1
5
4
  if env['rack.multithread'] && env['rainbows.model'] == :ThreadSpawn
6
5
  [ 200, {}, [ Thread.current.inspect << "\n" ] ]
7
6
  else
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
- (case env['rainbows.model']
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
- (case env['rainbows.model']
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
@@ -6,12 +6,7 @@ run lambda { |env|
6
6
 
7
7
  case env["PATH_INFO"]
8
8
  when %r{/sleep/(\d+)}
9
- (case env['rainbows.model']
10
- when :Revactor
11
- Actor
12
- else
13
- Kernel
14
- end).sleep($1.to_i)
9
+ Rainbows.sleep($1.to_i)
15
10
  end
16
11
  [ 200, headers, [ "#$$\n" ] ]
17
12
  }
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.9.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-13 00:00:00 +00:00
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