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