Rainbows! 5.2.1
[rainbows.git] / lib / rainbows / revactor.rb
blob69a791c0a5237cd56e592753b007c65ef4c0bb31
1 # -*- encoding: binary -*-
2 require 'revactor'
3 Revactor::VERSION >= '0.1.5' or abort 'revactor 0.1.5 is required'
5 # Enables use of the Actor model through {Revactor}[http://revactor.org]
6 # under Ruby 1.9.
8 # \Revactor dormant upstream, so the use of this is NOT recommended for
9 # new applications.
11 # It spawns one long-lived Actor for every listen socket in the process
12 # and spawns a new Actor for every client connection accept()-ed.
13 # +worker_connections+ will limit the number of client Actors we have
14 # running at any one time.
16 # Applications using this model are required to be reentrant, but do
17 # not have to worry about race conditions unless they use threads
18 # internally.  \Rainbows! does not spawn threads under this model.
19 # Multiple instances of the same app may run in the same address space
20 # sequentially (but at interleaved points).  Any network dependencies
21 # in the application using this model should be implemented using the
22 # \Revactor library as well, to take advantage of the networking
23 # concurrency features this model provides.
25 # === RubyGem Requirements
26 # * revactor 0.1.5 or later
27 module Rainbows::Revactor
28   autoload :Client, 'rainbows/revactor/client'
29   autoload :Proxy, 'rainbows/revactor/proxy'
31   include Rainbows::Base
33   # runs inside each forked worker, this sits around and waits
34   # for connections and doesn't die until the parent dies (or is
35   # given a INT, QUIT, or TERM signal)
36   def worker_loop(worker) #:nodoc:
37     Client.setup
38     init_worker_process(worker)
39     nr = 0
40     limit = worker_connections
41     actor_exit = Case[:exit, Actor, Object]
43     revactorize_listeners.each do |l,close,accept|
44       Actor.spawn do
45         Actor.current.trap_exit = true
46         l.controller = l.instance_variable_set(:@receiver, Actor.current)
47         begin
48           while nr >= limit
49             l.disable if l.enabled?
50             logger.info "busy: clients=#{nr} >= limit=#{limit}"
51             Actor.receive do |f|
52               f.when(close) {}
53               f.when(actor_exit) { nr -= 1 }
54               f.after(0.01) {} # another listener could've gotten an exit
55             end
56           end
58           l.enable unless l.enabled?
59           Actor.receive do |f|
60             f.when(close) {}
61             f.when(actor_exit) { nr -= 1 }
62             f.when(accept) do |_, _, s|
63               nr += 1
64               Actor.spawn_link(s) { |c| Client.new(c).process_loop }
65             end
66           end
67         rescue => e
68           Rainbows::Error.listen_loop(e)
69         end while Rainbows.alive
70         Actor.receive do |f|
71           f.when(close) {}
72           f.when(actor_exit) { nr -= 1 }
73         end while nr > 0
74       end
75     end
77     Actor.sleep 1 while Rainbows.tick || nr > 0
78   rescue Errno::EMFILE
79     # ignore, let another worker process take it
80   end
82   def revactorize_listeners #:nodoc:
83     LISTENERS.map do |s|
84       case s
85       when TCPServer
86         l = Revactor::TCP.listen(s, nil)
87         [ l, T[:tcp_closed, Revactor::TCP::Socket],
88           T[:tcp_connection, l, Revactor::TCP::Socket] ]
89       when UNIXServer
90         l = Revactor::UNIX.listen(s)
91         [ l, T[:unix_closed, Revactor::UNIX::Socket ],
92           T[:unix_connection, l, Revactor::UNIX::Socket] ]
93       end
94     end
95   end
96   # :startdoc:
97 end