Class: Volt::ForkingServer
Instance Method Summary collapse
- #call(env) ⇒ Object
- #call_on_child(env) ⇒ Object
-
#initialize(server) ⇒ ForkingServer
constructor
A new instance of ForkingServer.
- #reload(changed_files) ⇒ Object
- #start_change_listener ⇒ Object
-
#start_child ⇒ Object
Start child forks off a child process and sets up a DRb connection to the child.
- #stop_change_listener ⇒ Object
- #stop_child ⇒ Object
-
#watch_for_parent_exit ⇒ Object
In the even the parent gets killed without at_exit running, we watch the pipe and close if the pipe gets closed.
Constructor Details
#initialize(server) ⇒ ForkingServer
Returns a new instance of ForkingServer.
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
# File 'lib/volt/server/forking_server.rb', line 9 def initialize(server) # A read write lock for accessing and creating the lock @child_lock = ReadWriteLock.new # Trap exit at_exit do # Only run on parent if @child_id puts "Exiting..." @exiting = true stop_child end end @server = server start_child end |
Instance Method Details
#call(env) ⇒ Object
120 121 122 123 124 125 126 127 128 129 130 |
# File 'lib/volt/server/forking_server.rb', line 120 def call(env) @child_lock.with_read_lock do if @exiting [500, {}, 'Server Exiting'] else status, headers, body_str = @server_proxy.call_on_child(env) [status, headers, StringIO.new(body_str)] end end end |
#call_on_child(env) ⇒ Object
97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 |
# File 'lib/volt/server/forking_server.rb', line 97 def call_on_child(env) status, headers, body = @rack_app.call(env) # Extract the body to pass as a string. We need to do this # because after the call, the objects will be GC'ed, so we want # them to be able to be marshaled to be send over DRb. if body.respond_to?(:to_str) body_str = body else extracted_body = [] # Read the body.each do |str| extracted_body << str end body.close if body.respond_to?(:close) body_str = extracted_body.join end [status, headers, body_str] end |
#reload(changed_files) ⇒ Object
147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 |
# File 'lib/volt/server/forking_server.rb', line 147 def reload(changed_files) # only reload the server code if a non-view file was changed server_code_changed = changed_files.any? {|path| File.extname(path) == '.rb' } msg = 'file changed, reloading' if server_code_changed msg << ' server and' end msg << ' client...' Volt.logger.log_with_color(msg, :light_blue) begin SocketConnectionHandler.(nil, 'reload') rescue => e Volt.logger.error("Reload dispatch error: ") Volt.logger.error(e) end if server_code_changed @child_lock.with_write_lock do stop_child start_child end end end |
#start_change_listener ⇒ Object
174 175 176 177 178 179 180 181 182 183 |
# File 'lib/volt/server/forking_server.rb', line 174 def start_change_listener # Setup the listeners for file changes @listener = Listen.to("#{@server.app_path}/") do |modified, added, removed| Thread.new do # Run the reload in a new thread reload(modified + added + removed) end end @listener.start end |
#start_child ⇒ Object
Start child forks off a child process and sets up a DRb connection to the child. #start_child should be called from within the write lock.
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 |
# File 'lib/volt/server/forking_server.rb', line 30 def start_child # Aquire the write lock, so we prevent anyone from using the child until # its setup or recreated. unless @drb_object # Get the id of the parent process, so we can wait for exit in the child # so the child can exit if the parent closes. @parent_id = Process.pid @reader, @writer = IO.pipe if @child_id = fork # running as parent @writer.close # Read the url from the child uri = @reader.gets.strip # Setup a drb object to the child DRb.start_service @drb_object = DRbObject.new_with_uri(uri) @server_proxy = @drb_object[0] @dispatcher_proxy = @drb_object[1] SocketConnectionHandler.dispatcher = @dispatcher_proxy start_change_listener else # Running as child @reader.close @server.boot_volt @rack_app = @server.new_server # Set the drb object locally @dispatcher = Dispatcher.new drb_object = DRb.start_service('drbunix:', [self, @dispatcher]) @writer.puts(drb_object.uri) watch_for_parent_exit begin DRb.thread.join rescue Interrupt => e # Ignore interrupt exit end end end end |
#stop_change_listener ⇒ Object
185 186 187 |
# File 'lib/volt/server/forking_server.rb', line 185 def stop_change_listener @listener.stop end |
#stop_child ⇒ Object
132 133 134 135 136 137 138 139 140 141 142 143 144 145 |
# File 'lib/volt/server/forking_server.rb', line 132 def stop_child # clear the drb object and kill the child process. if @drb_object begin @drb_object = nil DRb.stop_service @reader.close stop_change_listener Process.kill(9, @child_id) rescue => e puts "Stop Child Error: #{e.inspect}" end end end |
#watch_for_parent_exit ⇒ Object
In the even the parent gets killed without at_exit running, we watch the pipe and close if the pipe gets closed.
84 85 86 87 88 89 90 91 92 93 94 95 |
# File 'lib/volt/server/forking_server.rb', line 84 def watch_for_parent_exit Thread.new do loop do if @writer.closed? puts "Parent process died" exit end sleep 3 end end end |