We got nominated! Help us out and vote for GitHub as Best Bootstrapped Startup of 2008. (You can vote once a day.) [ hide ]

public
Description: Phusion Passenger (mod_rails)
Homepage: http://www.modrails.com/
Clone URL: git://github.com/FooBarWidget/passenger.git
Click here to lend your support to: passenger and make a donation at www.pledgie.com !
Continue implementing conservative spawning.
Hongli Lai (Phusion) (author)
Wed May 07 06:16:02 -0700 2008
commit  815b8bd1796aff393b8c7dd0986c2fba4a1aa299
tree    3a5cabd379cde2efe690e7154a7d46242619c3a6
parent  c9461e1101901766c335d2586923cdeffea9deaf
...
145
146
147
148
149
 
 
 
150
151
152
...
155
156
157
158
 
159
160
161
 
 
 
 
162
163
164
165
166
167
168
...
175
176
177
178
179
 
 
 
 
 
180
181
182
183
184
185
186
187
188
189
190
191
 
 
 
192
193
194
...
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
 
215
216
217
 
 
 
218
219
220
...
230
231
232
233
 
234
235
236
237
 
 
 
 
 
 
 
 
 
 
 
 
 
 
238
239
240
241
242
243
244
 
 
 
245
246
 
247
248
249
250
251
252
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
253
254
255
...
145
146
147
 
 
148
149
150
151
152
153
...
156
157
158
 
159
160
161
 
162
163
164
165
166
 
 
 
167
168
169
...
176
177
178
 
 
179
180
181
182
183
184
 
 
 
 
 
 
 
 
 
 
185
186
187
188
189
190
191
...
197
198
199
 
 
 
 
 
 
 
 
 
 
 
 
200
201
202
203
204
205
206
207
208
209
...
219
220
221
 
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
 
 
 
245
246
247
248
 
249
250
251
 
252
253
 
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
0
@@ -145,8 +145,9 @@ class ApplicationSpawner < AbstractServer
0
   # any source files.
0
   #
0
   # Raises:
0
- # - SystemCallError: Something went wrong.
0
- # - IOError: Something went wrong.
0
+ # - AppInitError: The Ruby on Rails application raised an exception
0
+ # or called exit() during startup.
0
+ # - SystemCallError, IOError, SocketError: Something went wrong.
0
   def spawn_application!
0
     # Double fork to prevent zombie processes.
0
     a, b = UNIXSocket.pair
0
@@ -155,14 +156,14 @@ class ApplicationSpawner < AbstractServer
0
         begin
0
           a.close
0
           channel = MessageChannel.new(b)
0
- ok = report_app_init_errors(channel) do
0
+ success = report_app_init_status(channel) do
0
             Dir.chdir(@app_root)
0
             lower_privilege! if @lower_privilege
0
- require 'config/enviroment'
0
+ require 'config/environment'
0
+ end
0
+ if success
0
+ start_request_handler(channel)
0
           end
0
- exit! if !ok
0
- channel.write('ok')
0
- start_request_handler(channel)
0
         rescue SignalException => signal
0
           if e.message != RequestHandler::HARD_TERMINATION_SIGNAL &&
0
            e.message != RequestHandler::SOFT_TERMINATION_SIGNAL
0
@@ -175,20 +176,16 @@ class ApplicationSpawner < AbstractServer
0
     Process.waitpid(pid)
0
     
0
     channel = MessageChannel.new(a)
0
- status = channel.read
0
- if status.nil?
0
+ unmarshal_and_raise_errors(channel)
0
+
0
+ # No exception was raised, so spawning succeeded.
0
+ pid, socket_name, using_abstract_namespace = channel.read
0
+ if pid.nil?
0
       raise IOError, "Connection closed"
0
- elsif status == "ok"
0
- pid, socket_name, using_abstract_namespace = channel.read
0
- if pid.nil?
0
- raise IOError, "Connection closed"
0
- end
0
- owner_pipe = server.recv_io
0
- return Application.new(@app_root, pid, socket_name,
0
- using_abstract_namespace == "true", owner_pipe)
0
- else
0
- # ...
0
     end
0
+ owner_pipe = channel.recv_io
0
+ return Application.new(@app_root, pid, socket_name,
0
+ using_abstract_namespace == "true", owner_pipe)
0
   end
0
   
0
   # Overrided from AbstractServer#start.
0
@@ -200,21 +197,13 @@ class ApplicationSpawner < AbstractServer
0
   def start
0
     super
0
     begin
0
- status = server.read[0]
0
- if status == 'exception'
0
- child_exception = unmarshal_exception(server.read_scalar)
0
- stop
0
- raise AppInitError.new(
0
- "Application '#{@app_root}' raised an exception: " <<
0
- "#{child_exception.class} (#{child_exception.message})",
0
- child_exception)
0
- elsif status == 'exit'
0
- stop
0
- raise AppInitError.new("Application '#{@app_root}' exited during startup")
0
- end
0
+ unmarshal_and_raise_errors(server)
0
     rescue IOError, SystemCallError, SocketError
0
       stop
0
       raise Error, "The application spawner server exited unexpectedly"
0
+ rescue
0
+ stop
0
+ raise
0
     end
0
   end
0
 
0
@@ -230,26 +219,65 @@ protected
0
   
0
   # Overrided method.
0
   def initialize_server # :nodoc:
0
- begin
0
+ report_app_init_status(client) do
0
       $0 = "Passenger ApplicationSpawner: #{@app_root}"
0
       Dir.chdir(@app_root)
0
       lower_privilege! if @lower_privilege
0
       preload_application
0
+ end
0
+ end
0
+
0
+private
0
+ # Run the given block. A message will be sent through _channel_, telling
0
+ # the remote side whether the block raised an exception, called exit(),
0
+ # or succeeded.
0
+ # Returns whether the block succeeded.
0
+ # Exceptions are not propagated, except for SystemExit.
0
+ def report_app_init_status(channel)
0
+ begin
0
+ yield
0
+ channel.write('success')
0
+ return true
0
     rescue StandardError, ScriptError, NoMemoryError => e
0
       if ENV['TESTING_PASSENGER'] == '1'
0
         print_exception(self.class.to_s, e)
0
       end
0
- client.write('exception')
0
- client.write_scalar(marshal_exception(e))
0
- return
0
+ channel.write('exception')
0
+ channel.write_scalar(marshal_exception(e))
0
+ return false
0
     rescue SystemExit
0
- client.write('exit')
0
+ channel.write('exit')
0
       raise
0
     end
0
- client.write('success')
0
   end
0
   
0
-private
0
+ # Receive status information that was sent to _channel_ by
0
+ # report_app_init_status. If an error occured according to the
0
+ # received information, then an appropriate exception will be
0
+ # raised.
0
+ #
0
+ # Raises:
0
+ # - AppInitError
0
+ # - IOError, SystemCallError, SocketError
0
+ def unmarshal_and_raise_errors(channel)
0
+ args = channel.read
0
+ if args.nil?
0
+ raise EOFError, "Unexpected end-of-file detected."
0
+ end
0
+ status = args[0]
0
+ if status == 'exception'
0
+ child_exception = unmarshal_exception(channel.read_scalar)
0
+ raise AppInitError.new(
0
+ "Application '#{@app_root}' raised an exception: " <<
0
+ "#{child_exception.class} (#{child_exception.message})",
0
+ child_exception)
0
+ elsif status == 'exit'
0
+ raise AppInitError.new("Application '#{@app_root}' exited during startup")
0
+ end
0
+ end
0
+
0
+ # Lower the current process's privilege to the owner of config/environment.rb.
0
+ # No exceptions will be raised in the event that privilege lowering fails.
0
   def lower_privilege!
0
     stat = File.stat("config/environment.rb")
0
     begin
...
14
15
16
17
18
19
20
21
22
23
24
 
 
 
 
 
 
 
 
25
26
27
28
29
 
 
 
 
30
31
32
 
 
33
34
35
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
36
37
38
39
40
41
42
 
 
43
44
45
46
47
48
49
50
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
51
52
53
...
14
15
16
 
 
 
 
 
 
 
 
17
18
19
20
21
22
23
24
25
 
 
 
 
26
27
28
29
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
81
82
83
0
@@ -14,40 +14,70 @@ include Passenger
0
 describe ApplicationSpawner do
0
   include TestHelper
0
 
0
- before :each do
0
- ENV['RAILS_ENV'] = 'production'
0
- @stub = setup_rails_stub('foobar')
0
- @app_root = @stub.app_root
0
- @spawner = ApplicationSpawner.new(@app_root)
0
- @spawner.start
0
- @server = @spawner
0
- end
0
+ describe "regular spawning" do
0
+ before :each do
0
+ ENV['RAILS_ENV'] = 'production'
0
+ @stub = setup_rails_stub('foobar')
0
+ @spawner = ApplicationSpawner.new(@stub.app_root)
0
+ @spawner.start
0
+ @server = @spawner
0
+ end
0
   
0
- after :each do
0
- @spawner.stop
0
- teardown_rails_stub
0
- end
0
+ after :each do
0
+ @spawner.stop
0
+ @stub.destroy
0
+ end
0
   
0
- it_should_behave_like "a minimal spawner"
0
- it_should_behave_like "a spawn server"
0
+ it_should_behave_like "a minimal spawner"
0
+ it_should_behave_like "a spawn server"
0
   
0
- def spawn_arbitrary_application
0
- @spawner.spawn_application
0
+ def spawn_arbitrary_application
0
+ @spawner.spawn_application
0
+ end
0
+ end
0
+
0
+ describe "conservative spawning" do
0
+ before :each do
0
+ ENV['RAILS_ENV'] = 'production'
0
+ @stub = setup_rails_stub('foobar')
0
+ @spawner = ApplicationSpawner.new(@stub.app_root)
0
+ end
0
+
0
+ after :each do
0
+ @stub.destroy
0
+ end
0
+
0
+ it_should_behave_like "a minimal spawner"
0
+
0
+ def spawn_arbitrary_application
0
+ @spawner.spawn_application!
0
+ end
0
   end
0
 end
0
 
0
 describe ApplicationSpawner do
0
   include TestHelper
0
   
0
- it_should_behave_like "handling errors in application initialization"
0
+ describe "regular spawning" do
0
+ it_should_behave_like "handling errors in application initialization"
0
   
0
- def spawn_application(app_root)
0
- @spawner = ApplicationSpawner.new(app_root)
0
- begin
0
- @spawner.start
0
- return @spawner.spawn_application
0
- ensure
0
- @spawner.stop rescue nil
0
+ def spawn_application(app_root)
0
+ @spawner = ApplicationSpawner.new(app_root)
0
+ begin
0
+ @spawner.start
0
+ return @spawner.spawn_application
0
+ ensure
0
+ @spawner.stop rescue nil
0
+ end
0
+ end
0
+ end
0
+
0
+ describe "conservative spawning" do
0
+ it_should_behave_like "handling errors in application initialization"
0
+
0
+ def spawn_application(app_root)
0
+ @spawner = ApplicationSpawner.new(app_root)
0
+ return @spawner.spawn_application!
0
     end
0
   end
0
 end

Comments

    No one has commented yet.