rainbows.git  about / heads / tags
Unicorn for sleepy apps and slow clients
blob a3c098ae14aa7fc1460f10105f28fcead2157970 1976 bytes (raw)
$ git show HEAD:lib/rainbows/fiber/base.rb	# shows this blob on the CLI

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
 
# -*- encoding: binary -*-
# :enddoc:
require 'rainbows/fiber/io'

module Rainbows::Fiber::Base

  include Rainbows::Base

  # :stopdoc:
  RD = Rainbows::Fiber::RD
  WR = Rainbows::Fiber::WR
  ZZ = Rainbows::Fiber::ZZ
  # :startdoc:

  # the scheduler method that powers both FiberSpawn and FiberPool
  # concurrency models.  It times out idle clients and attempts to
  # schedules ones that were blocked on I/O.  At most it'll sleep
  # for one second (returned by the schedule_sleepers method) which
  # will cause it.
  def schedule
    begin
      Rainbows.tick
      t = schedule_sleepers
      ret = select(RD.compact.concat(LISTENERS), WR.compact, nil, t)
    rescue Errno::EINTR
      retry
    rescue Errno::EBADF, TypeError
      LISTENERS.compact!
      raise
    end or return

    # active writers first, then readers
    ret[1].concat(RD.compact & ret[0]).each { |c| c.f.resume }

    # accept is an expensive syscall, filter out listeners we don't want
    (ret[0] & LISTENERS).each { |x| yield x }
  end

  # wakes up any sleepers or keepalive-timeout violators that need to be
  # woken and returns an interval to IO.select on
  def schedule_sleepers
    max = nil
    now = Rainbows.now
    fibs = []
    ZZ.delete_if { |fib, time|
      if now >= time
        fibs << fib
      else
        max = time
        false
      end
    }
    fibs.each(&:resume)

    max_sleep = 1.0 # wake up semi-frequently to prevent SIGKILL from master
    if max
      max -= Rainbows.now
      return 0 if max < 0.0
      return max_sleep if max > max_sleep
      max
    else
      max_sleep
    end
  end

  def process(client)
    Rainbows.cur += 1
    client.process_loop
  ensure
    Rainbows.cur -= 1
    ZZ.delete(client.f)
  end

  def self.setup(klass, app)
    Rainbows::Client.__send__(:include, Rainbows::Fiber::IO::Methods)
    require 'rainbows/fiber/body'
    Rainbows::Client.__send__(:include, Rainbows::Fiber::Body)
    self.const_set(:APP, app)
  end
end

git clone https://yhbt.net/rainbows.git