rainbows 0.9.0 → 0.90.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.
@@ -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