Class: Sandbox::IRBServer
- Inherits:
-
Object
- Object
- Sandbox::IRBServer
- Defined in:
- lib/sandbox/server.rb
Instance Attribute Summary collapse
-
#acceptor ⇒ Object
readonly
Returns the value of attribute acceptor.
-
#host ⇒ Object
readonly
Returns the value of attribute host.
-
#num_processors ⇒ Object
readonly
Returns the value of attribute num_processors.
-
#port ⇒ Object
readonly
Returns the value of attribute port.
-
#timeout ⇒ Object
readonly
Returns the value of attribute timeout.
-
#workers ⇒ Object
readonly
Returns the value of attribute workers.
Instance Method Summary collapse
-
#graceful_shutdown ⇒ Object
Performs a wait on all the currently running threads and kills any that take too long.
-
#initialize(host, port, num_processors = (2**30-1), timeout = 0) ⇒ IRBServer
constructor
A new instance of IRBServer.
- #new_sandbox ⇒ Object
- #process_client(client) ⇒ Object
- #randid ⇒ Object
-
#reap_dead_workers(reason = 'unknown') ⇒ Object
Used internally to kill off any worker threads that have taken too long to complete processing.
-
#run ⇒ Object
Runs the thing.
-
#stop ⇒ Object
Stops the acceptor thread and then causes the worker threads to finish off the request queue before finally exiting.
Constructor Details
#initialize(host, port, num_processors = (2**30-1), timeout = 0) ⇒ IRBServer
Returns a new instance of IRBServer.
12 13 14 15 16 17 18 19 20 21 |
# File 'lib/sandbox/server.rb', line 12 def initialize(host, port, num_processors=(2**30-1), timeout=0) @socket = TCPServer.new(host, port) @host = host @port = port @workers = ThreadGroup.new @timeout = timeout @num_processors = num_processors @death_time = 60 @sessions = {} end |
Instance Attribute Details
#acceptor ⇒ Object (readonly)
Returns the value of attribute acceptor.
5 6 7 |
# File 'lib/sandbox/server.rb', line 5 def acceptor @acceptor end |
#host ⇒ Object (readonly)
Returns the value of attribute host.
7 8 9 |
# File 'lib/sandbox/server.rb', line 7 def host @host end |
#num_processors ⇒ Object (readonly)
Returns the value of attribute num_processors.
10 11 12 |
# File 'lib/sandbox/server.rb', line 10 def num_processors @num_processors end |
#port ⇒ Object (readonly)
Returns the value of attribute port.
8 9 10 |
# File 'lib/sandbox/server.rb', line 8 def port @port end |
#timeout ⇒ Object (readonly)
Returns the value of attribute timeout.
9 10 11 |
# File 'lib/sandbox/server.rb', line 9 def timeout @timeout end |
#workers ⇒ Object (readonly)
Returns the value of attribute workers.
6 7 8 |
# File 'lib/sandbox/server.rb', line 6 def workers @workers end |
Instance Method Details
#graceful_shutdown ⇒ Object
Performs a wait on all the currently running threads and kills any that take too long. Right now it just waits 60 seconds, but will expand this to allow setting. The @timeout setting does extend this waiting period by that much longer.
81 82 83 84 85 86 |
# File 'lib/sandbox/server.rb', line 81 def graceful_shutdown while reap_dead_workers("shutdown") > 0 STDERR.print "Waiting for #{@workers.list.length} requests to finish, could take #{@death_time + @timeout} seconds." sleep @death_time / 10 end end |
#new_sandbox ⇒ Object
28 29 30 |
# File 'lib/sandbox/server.rb', line 28 def new_sandbox Sandbox.safe(:timeout => 10) end |
#process_client(client) ⇒ Object
32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
# File 'lib/sandbox/server.rb', line 32 def process_client(client) begin case client.gets when /^LOGIN (\w+)/ sess = $1 else sess = randid end @sessions[sess] ||= new_sandbox client.puts sess Sandbox::IRB.new(@sessions[sess]).start(client) rescue EOFError,Errno::ECONNRESET,Errno::EPIPE,Errno::EINVAL,Errno::EBADF # ignored rescue Errno::EMFILE reap_dead_workers('too many files') rescue Object STDERR.puts "#{Time.now}: ERROR: #$!" STDERR.puts $!.backtrace.join("\n") ensure client.close unless client.closed? end end |
#randid ⇒ Object
23 24 25 26 |
# File 'lib/sandbox/server.rb', line 23 def randid abc = %{ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz} (1..20).map { abc[rand(abc.size),1] }.join end |
#reap_dead_workers(reason = 'unknown') ⇒ Object
Used internally to kill off any worker threads that have taken too long to complete processing. Only called if there are too many processors currently servicing. It returns the count of workers still active after the reap is done. It only runs if there are workers to reap.
60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
# File 'lib/sandbox/server.rb', line 60 def reap_dead_workers(reason='unknown') if @workers.list.length > 0 STDERR.puts "#{Time.now}: Reaping #{@workers.list.length} threads for slow workers because of '#{reason}'" mark = Time.now @workers.list.each do |w| w[:started_on] = Time.now if not w[:started_on] if mark - w[:started_on] > @death_time + @timeout STDERR.puts "Thread #{w.inspect} is too old, killing." w.raise(TimeoutError.new("Timed out thread.")) end end end return @workers.list.length end |
#run ⇒ Object
Runs the thing. It returns the thread used so you can “join” it. You can also access the HttpServer::acceptor attribute to get the thread later.
91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 |
# File 'lib/sandbox/server.rb', line 91 def run BasicSocket.do_not_reverse_lookup=true @acceptor = Thread.new do while true begin client = @socket.accept worker_list = @workers.list if worker_list.length >= @num_processors STDERR.puts "Server overloaded with #{worker_list.length} processors (#@num_processors max). Dropping connection." client.close reap_dead_workers("max processors") else thread = Thread.new { process_client(client) } thread.abort_on_exception = true thread[:started_on] = Time.now @workers.add(thread) sleep @timeout/100 if @timeout > 0 end rescue StopServer @socket.close if not @socket.closed? break rescue Errno::EMFILE reap_dead_workers("too many open files") sleep 0.5 rescue Errno::ECONNABORTED # client closed the socket even before accept client.close if not client.closed? end end graceful_shutdown end return @acceptor end |
#stop ⇒ Object
Stops the acceptor thread and then causes the worker threads to finish off the request queue before finally exiting.
132 133 134 135 136 137 138 |
# File 'lib/sandbox/server.rb', line 132 def stop stopper = Thread.new do exc = StopServer.new @acceptor.raise(exc) end stopper.priority = 10 end |