rack.multithread is only true for Thread* models
[rainbows.git] / lib / rainbows / thread_spawn.rb
blobf14ed1cc13d48e5d6d122616e7bb731791a74c7d
1 # -*- encoding: binary -*-
2 module Rainbows
4   # Spawns a new thread for every client connection we accept().  This
5   # model is recommended for platforms where spawning threads is
6   # inexpensive.
7   #
8   # If you're connecting to external services and need to perform DNS
9   # lookups, consider using the "resolv-replace" library which replaces
10   # parts of the core Socket package with concurrent DNS lookup
11   # capabilities
12   module ThreadSpawn
14     include Base
16     def worker_loop(worker)
17       init_worker_process(worker)
18       RACK_DEFAULTS["rack.multithread"] = true
19       threads = ThreadGroup.new
20       alive = worker.tmp
21       m = 0
22       limit = worker_connections
24       begin
25         ret = begin
26           alive.chmod(m = 0 == m ? 1 : 0)
27           IO.select(LISTENERS, nil, nil, timeout) or next
28         rescue Errno::EINTR
29           retry
30         rescue Errno::EBADF, TypeError
31           break
32         end
33         alive.chmod(m = 0 == m ? 1 : 0)
35         ret.first.each do |l|
36           # Sleep if we're busy, another less busy worker process may
37           # take it for us if we sleep. This is gross but other options
38           # still suck because they require expensive/complicated
39           # synchronization primitives for _every_ case, not just this
40           # unlikely one.  Since this case is (or should be) uncommon,
41           # just busy wait when we have to.
42           while threads.list.size > limit # unlikely
43             sleep(0.1) # hope another process took it
44             break # back to IO.select
45           end
46           begin
47             threads.add(Thread.new(l.accept_nonblock) {|c| process_client(c) })
48           rescue Errno::EAGAIN, Errno::ECONNABORTED
49           end
50         end
51       rescue Object => e
52         listen_loop_error(e) if LISTENERS.first
53       end while LISTENERS.first && master_pid == Process.ppid
54       join_threads(threads.list, worker)
55     end
57   end
58 end